在命令行玩麻将!使用C89标准编写的双人麻将

闲来无事,发现命令行上的游戏较少,便想模仿FC双人麻将,写一个麻将游戏,使用键盘输入数字控制出牌,如第一张就输入1,电脑和玩家可以捉炮和自摸,可以胡n×AAA + m×ABC + DD和七对,没有吃碰杠,玩家打出了30张牌就流局。

运行效果(DOS):

代码如下

#include <stdio.h>
#include <stdlib.h>
#include <time.h>
int paishan[10][5];
char echo_hua[6]={' ','W','T','S','Z'};		/*万,筒,条,字*/
char echo_num[11]={' ','1','2','3','4','5','6','7','8','9'};
int she_pc_num = 1;
int she_me_num = 1;
struct me{
    int hua; 
    int num;  
} me[15];

struct pc{
    int hua; 
    int num;  
} pc[15];

struct she_pc{
    int hua; 
    int num;  
} she_pc[31];

struct she_me{
    int hua; 
    int num;  
} she_me[31];
void all_init(void)
{
    int i,j;
    for(i=1;i<5;i++)
    {
        for(j=1;j<10;j++)
        {
            paishan[j][i]=4;
        }
    }
    paishan[8][4]=0;
    paishan[9][4]=0;
}

int rand_range(int min, int max)
{
    return min + rand() % (max - min + 1);
}

void fapai_pc(void)
{
	int i,j,tmp_hua,tmp_num;
	for(i=1;i<11;i=i+3)
	{
		
		switch(rand_range(1,2))		/*电脑组建手牌,1为顺,2为刻,听牌雀头*/ 
		{
			case 1:
				shun:
					tmp_hua=rand_range(1,3);
	    			tmp_num=rand_range(1,7);
	    		for(j=0;j<3;j++)
	    		{
	    			if(paishan[tmp_num + j][tmp_hua] < 1)
	    			{
	    				goto shun;	
					}
				}
				for(j=0;j<3;j++)
				{
					pc[i+j].hua=tmp_hua;
					pc[i+j].num=tmp_num+j;
					paishan[tmp_num+j][tmp_hua]--;
				}
				break;
			case 2:
				tmp_hua=rand_range(1,4);
	    		tmp_num=rand_range(1,9);
				while(paishan[tmp_num][tmp_hua]<3)
				{
					tmp_hua=rand_range(1,4);
	    			tmp_num=rand_range(1,9);
				}
				for(j=0;j<3;j++)
				{
					pc[i+j].hua=tmp_hua;
					pc[i+j].num=tmp_num;
					paishan[tmp_num][tmp_hua]--;
				}					
		}
	}
	tmp_hua=rand_range(1,4);
	tmp_num=rand_range(1,9);
	while(paishan[tmp_num][tmp_hua]<1)
	{
		tmp_hua=rand_range(1,4);
		tmp_num=rand_range(1,9);
	}
	pc[13].hua=tmp_hua;
	pc[13].num=tmp_num;
	paishan[tmp_num][tmp_hua]--;
} 
int fapai(int a,int b)
{
    int tmp_hua,tmp_num,i,j;
    tmp_hua=rand_range(1,4);
    tmp_num=rand_range(1,9);
    while(tmp_hua==4 && tmp_num>7)	/*字牌只有7种*/ 
    {
        tmp_hua=rand_range(1,4);
        tmp_num=rand_range(1,9);   
    }
    if(paishan[tmp_num][tmp_hua]>0)
    {
        switch(a)
        {
            case 1:		/*玩家摸牌*/
                me[b].hua=tmp_hua;
                me[b].num=tmp_num;
                return 0;
                break;
            case 2:		/*PC不允许胡牌*/
                she_pc[she_pc_num].hua=tmp_hua;
                she_pc[she_pc_num].num=tmp_num;
                return 0;
                break;
            case 3:		/*PC允许胡牌*/
            	if(pc[13].hua==tmp_hua && pc[13].num==tmp_num)
            	{
            		pc[14].hua=tmp_hua;
					pc[14].num=tmp_num;
					printf("%s\n", "You lose");
					fflush(stdout);
					for(i=1;i<15;i++)
	    			{
	    				printf("%c", echo_num[pc[i].num]);
	     				printf("%c", echo_hua[pc[i].hua]);
	   				  	printf("%c", ' ');
					}
					exit(0);
				}
				else 
				{
					she_pc[she_pc_num].hua=tmp_hua;
                	she_pc[she_pc_num].num=tmp_num;	
                	return 0;
				}
				break;
        }
    }
    else
    {
        return 1;
    }
    
}

void sort_hand(void)	/*冒泡排序*/
{
	int i,j,tmp;
    for(i = 1; i < 14; i++)
    {
        for(j = 1; j < 15 - i; j++)
        {

            if( me[j].hua > me[j+1].hua || (me[j].hua == me[j+1].hua && me[j].num > me[j+1].num) )
            {
                tmp = me[j].hua;
                me[j].hua = me[j+1].hua;
                me[j+1].hua = tmp;
                
                tmp = me[j].num;
                me[j].num = me[j+1].num;
                me[j+1].num = tmp;
                
            }
        }
    }
}

void read_int(int *out)
{
    int ret;
    while(1)
    {
        ret = scanf("%d", out);
        if(ret == 1)
        {
            while(getchar() != '\n');
            break;
        }
        while(getchar() != '\n');
        printf("no:");
    }
}

void screen(void)
{
	int i,j;
	for(i = 0; i < 40; i++)
    {
        printf("\n");
    }
  	/*
	for(i=1;i<15;i++)
    {
    	printf("%c", echo_num[pc[i].num]);
     	printf("%c", echo_hua[pc[i].hua]);
     	printf("%c", ' ');
	} 
	printf("\n");
	*/
 	/*电脑手牌打印*/
	printf("%s\n", "================================");
	printf("%s\n", "PC:");
   	for(i=1;i<4;i++)
   	{
   		for(j=1;j<11;j++)
  		{
		    int tmp_num = (i-1)*10 + j;
   			printf("%c", echo_num[she_pc[tmp_num].num]);
	     	printf("%c", echo_hua[she_pc[tmp_num].hua]);
	     	printf("%c", ' ');	
		}
		printf("\n");
	}
	printf("%s\n", "================================");
	printf("%s\n", "YOUR:");
    for(i=1;i<4;i++)
    {
    	for(j=1;j<11;j++)
    	{
    		int tmp_num = (i-1)*10 + j;
    		printf("%c", echo_num[she_me[tmp_num].num]);
	     	printf("%c", echo_hua[she_me[tmp_num].hua]);
	     	printf("%c", ' ');	
		}
		printf("\n");
	}
	printf("%s\n", "================================");
   	for(i=1;i<15;i++)
	{
	   	printf("%c", echo_num[me[i].num]);
	   	printf("%c", echo_hua[me[i].hua]);
	   	printf("%c", ' ');
	}
	printf("\n");
	printf("%s\n", "01 02 03 04 05 06 07 08 09 10 11 12 13 14");	
}

int check_qidui(int check[10][5])
{
    int i,j;
    int dui_cnt = 0;

    for(i = 1; i < 5; i++)
    {
        for(j = 1; j < 10; j++)
        {
            int c = check[j][i];
            if(c == 0)
                continue;
            if(c == 2)
            {
                dui_cnt ++;
            }
            else
            {
                /*出现1、3、4,七对不成立*/
                return 0;
            }
        }
    }
    if(dui_cnt ==7)
    {
        printf("%s\n", "You win");
        fflush(stdout);
		exit(0);
    }
    return 0;
}
int check_mianzi(int cnt[10][5])
{
	int i,j;
	for(i=1;i<5;i++)
	{
		for(j=1;j<10;j++)
		{
			if(cnt[j][i]>0)
			{
				if(cnt[j][i]>=3)	/*检测刻子*/
				{
					cnt[j][i]=cnt[j][i]-3;
					if(check_mianzi(cnt))
					{
						return 1;
					}
					cnt[j][i]=cnt[j][i]+3;
					
				}
				if(i!=4 && j<=7 && cnt[j][i]>0 && cnt[j+1][i]>0 && cnt[j+2][i]>0)	/*检测顺子*/
				{
					cnt[j][i]=cnt[j][i]-1;
					cnt[j+1][i]=cnt[j+1][i]-1;
					cnt[j+2][i]=cnt[j+2][i]-1;
					if(check_mianzi(cnt))
					{
						return 1;
					}
					cnt[j][i]=cnt[j][i]+1;
					cnt[j+1][i]=cnt[j+1][i]+1;
					cnt[j+2][i]=cnt[j+2][i]+1;
					
				}
				return 0;
			}	
		}
	}
	return 1;
}
int check_hupai(int a)
{
	int check[10][5];
	int tmp_check[10][5];
	int i,j,m,n;
	switch(a)
	{
		case 1:		/*捉炮*/
			for(i=1;i<5;i++)
			{
				for(j=1;j<10;j++)
				{
					check[j][i]=0;
				}
			}
			check[she_pc[she_pc_num-1].num][she_pc[she_pc_num-1].hua]++;
			for(i=2;i<15;i++)
			{
				check[me[i].num][me[i].hua]++;
			}
			break; 
		case 2:		/*自摸*/
			for(i=1;i<5;i++)
			{
				for(j=1;j<10;j++)
				{
					check[j][i]=0;
				}
			}
			for(i=1;i<15;i++)
			{
				check[me[i].num][me[i].hua]++;
			}
			break; 		
	}	
	check_qidui(check);
	for(m=1;m<5;m++)
	{
		for(n=1;n<10;n++)
		{
			tmp_check[n][m]=check[n][m];
		}
	}
	for(i=1;i<5;i++)
	{
		for(j=1;j<10;j++)
		{
			if(check[j][i]>=2)
			{
				for(m=1;m<5;m++)
				{
					for(n=1;n<10;n++)
					{
						tmp_check[n][m]=check[n][m];
					}
				}
				tmp_check[j][i]=tmp_check[j][i]-2;
				if(check_mianzi(tmp_check))
				{
					printf("%s\n", "You win");
					fflush(stdout);
					exit(0);	
				}
			}	
		}	
	}	
}

int main(void)
{
    int i,j;
    int chose = 0;
    int state = 1;
    int pc_hu_time;
    srand((unsigned)time(NULL));
    all_init();
    fapai_pc();
    for(i=1;i<14;i++)
    {
        while(fapai(1,i));
    }
    sort_hand();
    pc_hu_time = rand_range(1,20);
    while (she_me_num <= 30)
    {
    	switch (state)
    	{
    		case 1:		/*PC*/
    			if(she_me_num < pc_hu_time)
    			{
    				while(fapai(2,0));	/*直接舍牌*/
					she_pc_num++;	
				}
				else
				{
					while(fapai(3,0));	/*允许胡牌*/
					she_pc_num++;	
				}
				check_hupai(1);
				state=2;
				break;
			case 2:		/*玩家*/
				while(fapai(1,1));
				screen();				
				check_hupai(2);
				
				read_int(&chose);
				she_me[she_me_num].hua = me[chose].hua;
				she_me[she_me_num].num = me[chose].num;	
				me[chose].hua = 0;
				me[chose].num = 0;
					
				if(pc[13].hua==she_me[she_me_num].hua && pc[13].num==she_me[she_me_num].num && she_me_num > pc_hu_time)		/*电脑捉炮*/
			    {
			       	pc[14].hua=she_me[she_me_num].hua;
					pc[14].num=she_me[she_me_num].num;
					printf("%s\n", "You lose");
					fflush(stdout);
					for(i=1;i<15;i++)
				   	{
				   		printf("%c", echo_num[pc[i].num]);
				   		printf("%c", echo_hua[pc[i].hua]);
				   		printf("%c", ' ');
					}
					exit(0);
				}
				else 
				{
					sort_hand();
					she_me_num++;
					state=1;
				}
				break;
		}
	}
	printf("%s\n", "Draw!");	/*流局*/
	fflush(stdout);
}

机制讲解

使用W代表万,T代表筒,S代表条,Z是字牌(东南西北中发白)。电脑随机组建n×AAA + m×ABC + D的手牌,听牌D,之后随机一个胡牌时间,当玩家舍牌达到预定个数时,电脑就允许胡牌(需要自摸或放炮,不是直接胡牌),在此之前,电脑直接舍牌。电脑胡牌时,程序会显示“You lose”,下一行显示电脑手牌。玩家胡牌时,程序显示“You win”。

胡牌判断

先判断七对,就是判断手牌中是否有7个相同的对子。

n×AAA + m×ABC + DD的判断较为复杂,首先将手牌中每一种大于两张的牌尝试设为雀头,剩下的牌通过递归,看剩下能不能全部拆成面子,如果可以就胡牌。

递归算法完整流程

双重循环找到第一张还存在的牌 cnt[j][i]>0

从最小的牌开始处理,不做排列组合,避免重复搜索

尝试刻子:如果 >=3 张,扣掉 3 张,递归继续拆解剩下;如果递归返回 1;失败:回溯,把 3 张加回来,恢复数组状态,尝试下一种可能性。

尝试顺子:字牌 (i=4) 不能组成顺子;数字 j 不能大于 7,防止 j+2 越界;需要同时有 j、j+1、j+2 三张同花色;各扣掉一张,递归;失败就回溯,三张加回来。

如果刻子、顺子两种路径全部失败 → 当前分支无解,return 0。

递归出口:循环走完,没有任何牌cnt>0 → 所有牌拆解完毕 return 1

编译

Linux(openwrt_mipsel):

mipsel-openwrt-linux-musl-gcc -O3 maj.c -o maj -I. -lm

DOS(使用Turbo C):

1.打开tc.exe,载入maj.c

2.按F10,用键盘将光标移动到Compile,回车,移动到Make EXE file,回车

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值