我打麻将只要一听牌 别人每摸一张牌 总感觉每天都很紧张张

为什么打麻将一听牌就放炮-游戏杂谈-亿酷棋牌世界官网
Hi,欢迎来到亿酷棋牌世界!
当前位置: &
为什么打麻将一听牌就放炮
来源:亿酷棋牌世界&&&&时间: 11:18:01&&&&编辑:亿酷编辑
& &上听点炮是指手牌处在一入听牌姿,吃碰或摸进一张待牌,而后须打出一张才能入听。而这张牌没有打住,给别人点了炮。 & &上听点炮虽属情有可缘,尚可自慰,但却也着实令人沮丧。好不容易将一手牌组合起来,于成胡在望之际,一招失误,前功尽弃不算,还要“倒挂”,怎不令人遗憾。 & &上听时这张牌能不能打住,是检验一个牌手牌技高低的一把尺子。高手们对此颇有研究。他们往往宁可在听牌之前给人点炮,也不在听牌之时给人点炮。 & &其实做到上听时不点炮,并非多么困难。只要手里扣压一张安全牌,也叫“保腚牌”,做为入听前的舍牌就行了。这件事说起来很简单。可实际做起来,却有不少人偏偏“走型”。有些牌手缺少经验。更谈不上预见性,把可以做为安全牌的牌(多指风箭牌)打得精光,理由是第一没用,第二安全。也有的牌手,留了一阵子,但虎头蛇尾,待到别人上听时,或者摸来较好的张子舍不得打,就又把它打出去了。轮到逼近入听时,没有安全牌来替换险张,“上听不要命”,又不得不打一张,于是也只有去冒险了。打住了暗自庆幸,打不住唉声叹气。 & &聪明的牌手,绝不这么干。几巡牌过去,估量自己牌势尚可,只要力争胡牌,手中必扣住一张安全牌,不到叫听时不会动用。他们手里并非没有险牌,而是把险牌适时地打出去了。 & &经验证明,手牌中扣压一张保险牌是防止上听点炮的最好方法。尤其当自己坐庄时。即便成胡希望不是很大,也应保留一张安全牌于手中,以便关键时刻替换危险牌,防止给人点炮。感觉网上关于麻将的源码资源很少,一般这种算法都是用递归,把牌堆分解成若干子牌堆然后针对2-3张牌的情形给出一个出口。
和牌算法比较常见,毕竟只要是麻将编程都要用到,后面两种虽然普通的麻将编程用不到,但是要编写AI对策以及某些特殊规则(例如日本麻将)就有用了,尤其一向听的算法。
三种算法原理差不多,都是先分析牌数较少的情形,然后牌数较多的情形通过牌堆分解后,对提取剩余牌堆调用自身。
本源码使用C++,分MDeck和MDeckExtract类。这两个类定义如下:
typedef int MT
class MDeckE
class MDeck
MTile tile[200];
class MDeckExtract
int methodC
MDeck extracted[10];
MDeck remained[10];
其中MDeck类实际上就是一个由MTile(int)组成的数组和牌堆长度,跟CArray类很像。
而MDeckExtract类则用于存储牌堆的提取结果。其中methodCount是提取方案的数目,extracted存储提取方案中可以提取的目标牌堆,remain则存储剩余牌堆。举个简单的例子:
例如一个牌堆为 3,3,4,5,6,若对其进行顺子提取操作,则MDeckExtract应该是这样的:
methodCount 为 2,即两种提取方案。
extracted[0]为3,4,5,remain[0]为3,6;extracted[1]为4,5,6,remain[1]为3,3。
接下来是提取方法(其中有的较为简单的方法的定义例如牌堆排序、创建不包含重复的牌的副本、牌堆连接、加入等方法就不帖进来了,很容易实现)的源码。
&MDeckExtract MDeck::extractDuizi(){
sortTile();
int i = 0;
MDeck dr = removeRepeatedTile();
for(i = 0;i&dr.i++){
t = dr.tile[i];
dt = (*this);
de.length = 0;
if(dt.countTile(t)&1){
dt.removeTile(t);
dt.removeTile(t);
de.pushTile(t);
de.pushTile(t);
e.extracted[e.methodCount] =
e.remained[e.methodCount] =
e.methodCount ++;
MDeckExtract MDeck::extractShunzi(){
sortTile();
int i = 0;
MDeck dr = removeRepeatedTile();
for(i = 0;i&dr.i++){
t = dr.tile[i];
dt = (*this);
de.length = 0;
if(dt.countTile(t+1)&0 && dt.countTile(t+2)&0){
dt.removeTile(t);
dt.removeTile(t+1);
dt.removeTile(t+2);
de.pushTile(t);
de.pushTile(t+1);
de.pushTile(t+2);
e.extracted[e.methodCount] =
e.remained[e.methodCount] =
e.methodCount ++;
MDeckExtract MDeck::extractKezi(){
sortTile();
int i = 0;
MDeck dr = removeRepeatedTile();
for(i = 0;i&dr.i++){
t = dr.tile[i];
dt = (*this);
de.length = 0;
if(dt.countTile(t)&2){
dt.removeTile(t);
dt.removeTile(t);
dt.removeTile(t);
de.pushTile(t);
de.pushTile(t);
de.pushTile(t);
e.extracted[e.methodCount] =
e.remained[e.methodCount] =
e.methodCount ++;
接下来就是基于牌堆分解递归的和牌算法。大致步骤如下(这个大家似乎都经常用):
1. 检查牌堆长度是否被3除余2(因为和牌情况只能是2、5、8、11、14等张数的情形),若不是,返回false。
2. 检查牌堆是否满足特殊情况,例如七对子(有的规则里有国士无双等),若是,返回true,否则继续。
3. 检查牌堆长度是否为2,若是,则牌堆两张相同则返回true,否则返回false;若长度不为2,则继续。
4. 对牌堆进行顺子提取,然后对提取结果中的remain(Deck数组)里的每个元素(Deck)分别执行该算法,只要有一个元素(Deck)执行结果为true,则直接返回true,否则继续。
5. 对牌堆进行刻子提取,然后对提取结果中的remain(Deck数组)里的每个元素(Deck)分别执行该算法,只要有一个元素(Deck)执行结果为true,则直接返回true,否则继续。
6. 返回false。
bool MDeck::isHu(){
//对特殊情况判别
if(length == 14){
e = extractDuizi();
if(e.methodCount == 7){
//对一般情况判别
if(length % 3 != 2){
if(length == 2){
if(isDuizi()){
e = extractKezi();
for(i = 0;i&e.methodCi++){
if(e.remained[i].isHu()){
e = extractShunzi();
for(i = 0;i&e.methodCi++){
if(e.remained[i].isHu()){
然后是基于牌堆分解递归的听牌算法。
所谓听牌,就是手里的牌再加一张就能构成一副和牌。
听牌算法其实可以使用和牌算法和对所有麻将牌的遍历来实现,但是这样极端影响速度,因此最好还是从本源做起。
听牌跟和牌不一样,因为听牌方式有3种,分别是摸一张构成对子(单骑)、刻子(对倒)、顺子(边张或者砍张)。而构成牌的数目也不同,分别是2、3、3,因此,听牌牌堆的长度应该满足2+3N-1或者3+3N-1即被3除余1或者2,因此被3整除的情形是肯定不能算听牌的。
该算法返回牌堆能听的所有牌,大致步骤如下:
1. 初始化返回结果,置为空牌堆。
2. 检查牌堆长度是否被3整除,若是,返回空牌堆,否则继续(如果不对这种情况进行处理,则自然而然就返回空牌堆了,所以这步可以去掉)。
3. 检查牌堆长度是否为13,若是,检查是否有6组对子,如果有,则听剩余的那张牌(七对子的规则),将其加入返回结果;不管有没有6组对子,都继续进行。
4. 检查牌堆长度是否为1,若是,将仅有的这张牌加入返回结果(单骑听牌),然后继续进行。
5. 检查牌堆长度是否为2,若是,分以下情况进行讨论:
a) 两张牌相同,将这张牌加入返回结果(双碰听牌),然后继续进行。
b) 两张牌相邻,将两张牌往两边扩展的那张牌(例如7,8则扩展的牌为6和9,若为8和9则只有7)均加入返回结果(边张听牌),然后继续进行。
c) 两张牌隔一,将这两张牌中间的那张牌(例如6,8则中间那张牌为7)加入返回结果(砍张听牌),然后继续进行。
d) 不满足以上三种情形,则不加入,并继续进行。
6. 检查牌堆长度是否为4,若是,依次对其进行对子提取,刻子提取和顺子提取,并将提取结果的remain数组里的每个元素(Deck)都做听牌算法,并将结果悉数加入返回结果,然后继续进行。
7. 检查牌堆长度是否大于4,若是,依次对其进行刻子提取和顺子提取(同6一样,只不过不做对子提取),并将提取结果的remain数组里的每个元素(Deck)都做听牌算法,并将结果悉数加入返回结果,然后继续进行。
8. 将这个返回结果的非重复牌堆副本作为返回值,OK。
MDeck MDeck::ting(){
//特殊情况
if(length == 13){
dr = removeRepeatedTile();
e = extractDuizi();
if(e.methodCount == 6){
for(i=0;i&dr.i++){
if(countTile(dr.tile[i])==1){
result.pushTile(dr.tile[i]);
if(length == 1){
result.pushTile(tile[0]);
if(length == 2){
if(isDuizi()){
result.pushTile(tile[0]);
if(isShangbianzhang()){
result.pushTile(tile[0]-1);
if(isXiabianzhang()){
result.pushTile(tile[0]+2);
if(isKanzhang()){
result.pushTile(tile[0]+1);
if(isLiangmian()){
result.pushTile(tile[0]-1);
result.pushTile(tile[0]+2);
if(length == 4){
e = extractDuizi();
for(i = 0;i&e.methodCi++){
bineDeck(e.remained[i].ting());
e = extractKezi();
for(i = 0;i&e.methodCi++){
bineDeck(e.remained[i].ting());
e = extractShunzi();
for(i = 0;i&e.methodCi++){
bineDeck(e.remained[i].ting());
if(length & 4){
e = extractKezi();
for(i = 0;i&e.methodCi++){
bineDeck(e.remained[i].ting());
e = extractShunzi();
for(i = 0;i&e.methodCi++){
bineDeck(e.remained[i].ting());
return result.removeRepeatedTile();
最后是基于牌堆分解递归的一向听算法。
所谓一向听,就是牌堆打出一张后进入听牌的状态。例如制作麻将游戏中,玩家摸到牌后,判断听牌按钮是否会亮起就需要该方法的支撑;日本麻将中的能否进行“立直”也需要该算法的支撑。
同听牌算法一样,一向听的算法可以根据听牌算法和对手牌的遍历来实现,但是该方法也是特别耗时……
如果从源头做起的话,一向听的分析方式要比听牌还要复杂……该方法返回所有打出的牌使其成为听牌状态的集合,具体步骤如下:
1. 初始化返回结果,置为空牌堆。
2. 检查牌堆是否已经构成和牌,若是,则返回牌堆本身,否则继续(这步可以省略,某些极端情况可以大量减少运行时间,毕竟玩麻将基本遇不到自己和牌后不宣告和牌反而报听的。如果是检测算法的运算效率,强烈建议不要添加这步)。
3. 检查牌堆长度是否被3除余1,若是,返回空牌堆,否则继续(如果不对这种情况进行处理,则自然而然就返回空牌堆了,所以这步可以去掉)。
4. 检查牌堆长度是否为14,若是,检查是否有7组对子,如果有,则将每一个对子的牌加入返回结果(即七对子已经和牌);若有6组对子,将剩余的两张落单的牌加入返回结果(也就是说,打出任何一张落单的牌都能进入七对子听牌状态),然后继续进行。
5. 检查牌堆长度是否为2,若是,将这两张牌加入返回结果,然后继续进行。
6. 检查牌堆长度是否为3,若是,则:若任意两张牌数值距离&=2,则将第三张牌加入返回结果,然后继续进行。
例如,这三张牌是2,3,5,由于2和3距离位1&2,因此将5加入返回结果;2和5的距离为3&2,PASS;3和5的距离为2,将2加入返回结果,总共就是将2和5加入返回结果。
7. 检查牌堆长度是否为5,若是,依次对其进行对子提取,刻子提取和顺子提取,并将提取结果的remain数组里的每个元素(Deck)都做一向听算法,并将结果悉数加入返回结果,然后继续进行。
8. 检查牌堆长度是否大于5,若是,依次对其进行刻子提取和顺子提取(同7一样,只不过不做对子提取),并将提取结果的remain数组里的每个元素(Deck)都做一向听算法,并将结果悉数加入返回结果,然后继续进行。
9. 将这个返回结果的非重复牌堆副本作为返回值,OK。
MDeck MDeck::xting(){
//特殊情况
if(length == 14){
dr = removeRepeatedTile();
e = extractDuizi();
if(e.methodCount == 7){
for(i=0;i&7;i++){
result.pushTile(tile[i]);
if(e.methodCount == 6){
for(i=0;i&dr.i++){
if(countTile(dr.tile[i]) % 2 ==1){
result.pushTile(dr.tile[i]);
if(length == 2){
result.pushTile(tile[0]);
result.pushTile(tile[1]);
if(length == 3){
if (isDiffBelow2(tile[0], tile[1])) {
result.pushTile(tile[2]);
if (isDiffBelow2(tile[0], tile[2])) {
result.pushTile(tile[1]);
if (isDiffBelow2(tile[1], tile[2])) {
result.pushTile(tile[0]);
if(length == 5){
e = extractDuizi();
for(i = 0;i&e.methodCi++){
bineDeck(e.remained[i].xting());
e = extractKezi();
for(i = 0;i&e.methodCi++){
bineDeck(e.remained[i].xting());
e = extractShunzi();
for(i = 0;i&e.methodCi++){
bineDeck(e.remained[i].xting());
if(length & 5){
e = extractKezi();
for(i = 0;i&e.methodCi++){
bineDeck(e.remained[i].xting());
e = extractShunzi();
for(i = 0;i&e.methodCi++){
bineDeck(e.remained[i].xting());
return result.removeRepeatedTile();
参考知识库
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
访问:506次
排名:千里之外我打麻将只要一听牌 别人每摸一张牌 总感觉很紧张-打麻将为什么我老是输,每次听牌都胡不到牌,还是老是放炮...
你正在浏览:
《我打麻将只要一听牌 别人每摸一张牌 总感觉很紧张 》
我打麻将只要一听牌 别人每摸一张牌 总感觉很紧张
怕别人糊 自己每打一张牌 也是很担心 怕被别人杠 但有时又不得不打 所以总是输钱 怎么解决这问题
“没事没事,当你输过几次麻木了,没怎么输过钱吧心态问题,下次捞回来”,就好了,输了钱一定要安慰自己
游戏就结束要先把心态放轻松。这个问题其实很关键的,自己判断别人要什么牌,因为你一打,一定要注意观察别人的出牌,不听牌都别打。麻将其实属于攻防类游戏。若推断出来别人要胡这张牌,别看输赢,用作娱乐的心情出玩,你听牌也是没用的
我们这打麻将是5元一个子的,广东麻将。有胡十三烂的,有大七对,小七对,单飞,杠上开花,杠上炮,那叫一个刺激,你杠了不能乱打牌,杠了打了一张别人胡了,翻倍。还有豪七,就是手上七对牌,有一个杠在手上胡了的。每人10个子,放炮1个子,自摸每人出2个子,暗杠,每人2个子,明杠每人1个子。我上次胡七对,调西风,自摸了哈哈。我每次打麻将都带300元去输,我打了10多次就赢了一把,我3月份打了一个月,2000多钱输光了。有次杠上开花糊了,放杠的给我5个子,其他人4个子。我那天手气特好,后来换了个人我就输掉了前面赚的钱。那叫一个爽 。
我以前没打过麻将就3月份打了一个月目前不知道还会去打不。不过还是过年根家人打现在 去看看 也不错,我真的是新手我就以前看别人打麻将学会的以前没打过就3月打了一个月
呵呵、、是你的心理问题
你可能感兴趣的内容?

我要回帖

更多关于 每天都很紧张 的文章

 

随机推荐