C语言实现简单的ai麻将对局(较大工程,持续更新ing)

最近雀魂玩的很多,于是决定自己写一个立直麻将的程序,进行机器学习生成ai。

立直麻将的规则太繁琐,我先实现了简单麻将的功能,包括:

1.建立牌海,实现随机洗牌

2.生成四位玩家,分发初始手牌

3.实现按序抓牌、打牌

4.实现胡牌检测,包括自摸和点炮

5.编写简单的切牌算法,为手中每张牌加权,每次切除最不需要的牌

其中1~4已经较完善,5还只是最无脑的算法,按牌型加权。

并且还不能吃碰杠。

后面可能会做一下图形化界面、人机交互。重头还是切牌算法,看能不能让深度学习一下。

2022/7/27更新

贴一下程序结果

发牌:

 模切:

对局结果:自摸

对局结果:点炮

 对局结果:荒牌流局

 还不能读牌河、考虑番数等等,后面会做。

下面是代码

​
#include<stdio.h> 
#include<stdlib.h>
#include<sys/time.h>
#include<malloc.h> 

//设置全局变量——玩家手牌数组
static int player1[15];
static int player2[15];
static int player3[15];
static int player4[15];
static int base[136] = {  
				   11,12, 13, 14, 15, 16, 17, 18, 19,
				   11,12, 13, 14, 15, 16, 17, 18, 19,
				   11,12, 13, 14, 15, 16, 17, 18, 19,
				   11,12, 13, 14, 15, 16, 17, 18, 19,
				   
				   31,32, 33, 34, 35, 36, 37, 38, 39, 
				   31,32, 33, 34, 35, 36, 37, 38, 39, 
				   31,32, 33, 34, 35, 36, 37, 38, 39, 
				   31,32, 33, 34, 35, 36, 37, 38, 39, 
				   
				   51,52, 53, 54, 55, 56, 57, 58, 59, 
				   51,52, 53, 54, 55, 56, 57, 58, 59,
				   51,52, 53, 54, 55, 56, 57, 58, 59, 
				   51,52, 53, 54, 55, 56, 57, 58, 59,
				   
				   71,72, 73, 74, 75, 76, 77, 
				   71,72, 73, 74, 75, 76, 77, 
				   71,72, 73, 74, 75, 76, 77, 
				   71,72, 73, 74, 75, 76, 77
				};
 
void MakeRand(int base[], int count)/*洗牌程序*/ 
{
	int i;
	srand((unsigned int)time(NULL));
	for( i = 0; i < count-1 ; i++)
	{
		int num = i + rand()%(count -1-i);
		int temp = base[i];
		base[i] = base[num];
		base[num] = temp;
	}
	//return base[146];
}



void decode(int num)/*单张牌面翻译打印程序*/ 
{
	int typeword = (num-num%10)/10;
	if(typeword == 1) 
		{printf(" %d", num%10);
		printf("萬  ") ;} 
	else if(typeword == 3)
		{printf(" %d", num%10);
		printf("饼  ") ;} 
	else if(typeword == 5)
		{printf(" %d", num%10);
		printf("索  ") ; } 
	else 
		switch(num%10)
		{
			case 1:printf("  東  ");break;
			case 2:printf("  南  ");break;
			case 3:printf("  西  ");break;
			case 4:printf("  北  ");break;
			case 5:printf("  白  ");break;
			case 6:printf("  發  ");break;
			case 7:printf("  中  ");break;
		}
}



void AotoCollation(int nums[],int count)/*自动理牌程序*/ 
{
	int i, j, temp, isSorted;  
for(i=0; i<count-1; i++)
{
	isSorted = 1;  //假设剩下的元素已经排序好了
	for(j=0; j<count-1-i; j++)
	{
		if(nums[j] > nums[j+1])
		{
			temp = nums[j];
			nums[j] = nums[j+1];
			nums[j+1] = temp;
			isSorted = 0;  
		}
	}
if(isSorted) break; 
}
}


void getcard(int hand[14], int playernum, int nowget )/*抓第nextcard张牌*/ 
{
	int k;
	hand[13]=nowget;
	printf("\n\n手牌 for Player%d:                                                          摸到↓\n",playernum);
	int i;
	for ( i = 0; i < 14; i++)
	decode(hand[i]);
		if(playernum==1)
		{
			for(k=0;k<14;k++){player1[k]=hand[k];} 
		}
		if(playernum==2)
		{
			for(k=0;k<14;k++){player2[k]=hand[k];}
		}
		if(playernum==3)
		{
			for(k=0;k<14;k++){player3[k]=hand[k];} 
		}
		if(playernum==4)
		{
			for(k=0;k<14;k++){player4[k]=hand[k];}
		}
}

int putcard(int hand[],int playernum, int putnum) /*打出选中的牌,然后自动理牌*/ 
{
	int k;
	int temp = 0;
	temp = hand[putnum-1];
	hand[putnum-1] = 99;
	printf("  打出--->");
	decode(temp);
	AotoCollation(hand, 14);
	if(playernum==1)
	{
		for(k=0;k<13;k++){player1[k]=hand[k];}
	}
	if(playernum==2)
	{
		for(k=0;k<13;k++){player2[k]=hand[k];}
	}
	if(playernum==3)
	{
		for(k=0;k<13;k++){player3[k]=hand[k];}
	}
	if(playernum==4)
	{
		for(k=0;k<13;k++){player4[k]=hand[k];}
	}
	return temp;
}


int hupai(int hand[15])/*判断14张牌,胡返回1,否则返回0*/ 
{
	AotoCollation(hand,14);
	int temphand[15];
	int temphandnew[15];
	int i,j,m,n=0,x,y;
	int temp = 0, addup = 0 ;
	int count1 = 0, count2 = 0, rest = 12;
	
	int grouphead;
	
	/*for(i=0;i<14;i++)
	{
		scanf("%d",&hand[i]);
	}*/ 
	
	
	for(i=0;i<13;i++)
	{
		temp = hand[i];
		for(j=i+1;j<i+2;j++)
		{
			for(x=0;x<14;x++)
				{
					temphand[x] = hand[x];
				}
				
				
			if(hand[j]==temp)//找到了head 
			{
				count1++;
				temphand[i] = 0; temphand[j] = 0;//去掉head继续判断 
				for(x=i+2;x<15;x++)
				{
					temphand[x-2]=temphand[x];
				}
				
				rest=12; 
				for(m=0;m<12;m++)
				{
					if(temphand[m]>0)
					{
						grouphead = temphand[m];
						
						count2=0;
						if(temphand[m+1]==grouphead)//刻子 
						{
							count2++;
							if(temphand[m+2]==grouphead)
							{
								count2++;
								if(count2==2)
								{
									rest=rest-3;
									temphand[m]=0;
									temphand[m+1]=0;
									temphand[m+2]=0;
									continue;
								}
							}
						}
					
						count2=0;
						for(x=m+1;x<15;x++)
						{
							if(m-(m%10)/10==7)continue;
							if(temphand[x]==grouphead+1)//顺子 
							{
								count2++;
								for(y=x+1;y<15;y++)
								{
									if(temphand[y]==grouphead+2)
									{
										count2++;
										if(count2==2)
										{
											rest=rest-3;
											temphand[m]=0;
											temphand[x]=0;
											temphand[y]=0;
										
										}
									}
								}	
							}
						}
						/*for(x=0;x<13;x++)
						{
							printf("%d ",temphand[x]);
						}
						printf("\n");*/
					}	
				}
				//printf("%d",rest);
				if(rest==0) 
				{
					//printf("\n荣胡!!!");
					return 1;

				}
			}
		}
	}
		//printf("\nNot ting...");
		return 0;
	
}

int value(int inthand[])
{
	//判断14张手牌每张牌价值,并按价值排序
	//价值取决于形成雀头或面子的难易度
			    /*万11,12, 13, 14, 15, 16, 17, 18, 19,
				   11,12, 13, 14, 15, 16, 17, 18, 19,
				   11,12, 13, 14, 15, 16, 17, 18, 19,
				   11,12, 13, 14, 15, 16, 17, 18, 19,
				   
				   饼31,32, 33, 34, 35, 36, 37, 38, 39, 
				   31,32, 33, 34, 35, 36, 37, 38, 39, 
				   31,32, 33, 34, 35, 36, 37, 38, 39, 
				   31,32, 33, 34, 35, 36, 37, 38, 39, 
				   
				   索51,52, 53, 54, 55, 56, 57, 58, 59, 
				   51,52, 53, 54, 55, 56, 57, 58, 59,
				   51,52, 53, 54, 55, 56, 57, 58, 59, 
				   51,52, 53, 54, 55, 56, 57, 58, 59,
				   
				   字71,72, 73, 74, 75, 76, 77, 
				   71,72, 73, 74, 75, 76, 77, 
				   71,72, 73, 74, 75, 76, 77, 
				   71,72, 73, 74, 75, 76, 77*/ 
	int i,j,m,n,x,y;
	int hand[20]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
	int hand2[20]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};//便于顺子判断 
	int value2[15]={0,1,2,3,4,5,6,7,8,9,10,11,12,13,14};
	
	/*for(n=0;n<14;n++)
	{
		scanf("%d",&inthand[n]);
	}*/
	
	for(i=0;i<14;i++)
	{
		hand[i]=inthand[i]; 
		hand2[i]=inthand[i]; 
	}
	int value[20]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
	
	int now;
	//基本价值 
	for(i=0;i<14;i++)
	{
		j=i; 
		if(hand[j]==11||hand[j]==19||hand[j]==31||hand[j]==39||hand[j]==51||hand[j]==59) {value[j]=value[j]+5;}
		if(hand[j]==12||hand[j]==18||hand[j]==32||hand[j]==38||hand[j]==52||hand[j]==58) {value[j]=value[j]+10;}
		if(hand[j]==13||hand[j]==17||hand[j]==33||hand[j]==37||hand[j]==53||hand[j]==57) {value[j]=value[j]+20;}
		if(hand[j]==14||hand[j]==15||hand[j]==16||hand[j]==34||hand[j]==35||hand[j]==36||hand[j]==54||hand[j]==55||hand[j]==56) {value[j]=value[j]+30;}
		if(hand[j]==71||hand[j]==72||hand[j]==73||hand[j]==74) {value[j]=value[j]+0;}
		if(hand[j]==75||hand[j]==76||hand[j]==77) {value[j]=value[j]+5;}
	} 
	//对子
	for(x=0;x<7;x++)
	{
		for(i=0;i<13;i++)
	{
		if(hand2[i]==hand2[i+1] && hand2[i+1]!=hand2[i+2] && hand2[i]!=hand2[i-1]
			&& (hand2[i]==hand2[i+2]-1 && hand[i]==hand2[i+3]-2)!=1//11 11 12 13
			&& (hand2[i]==hand2[i-1]+1 && hand[i]==hand2[i-2]+2)!=1)//11 12 13 13
		{
			if(hand[i]==hand[i+1] && hand[i+2]==hand[i+3] && hand[i+4]==hand[i+5])//一杯口 
			{
				value[i]=value[i]+80;
				value[i+1]=value[i+1]+80;
				value[i+2]=value[i+2]+80;
				value[i+3]=value[i+3]+80;
				value[i+4]=value[i+4]+80;
				value[i+5]=value[i+5]+80;
				for(j=0;j<8-i;j++) //去掉一杯口
				{
					hand2[j+i]=hand2[j+i+6];
					value2[j+i]=value2[j+i+6];
				}
			}
			if(hand2[i]==71,72,73,74,75,76,77) 
			{
				value[value2[i]]=value[value2[i]]+35;
				value[value2[i+1]]=value[value2[i+1]]+35;
			}//白 白
			else 
			{
				value[value2[i]]=value[value2[i]]+30;
				value[value2[i+1]]=value[value2[i+1]]+30;
			} 
			
			if(hand2[i]==hand2[i-1]+1 && hand2[i+1]==hand2[i+2]-1) //11 12 12 13 去掉一个 12 
			{
				for(j=1;j<14-i;j++) 
				{
					hand2[j+i]=hand2[j+i+1];
					value2[j+i]=value2[j+i+1];
				}
			}
			else for(j=0;j<14-i;j++) //去掉孤立的对子 
			{
				hand2[j+i]=hand2[j+i+2];
				value2[j+i]=value2[j+i+2];
			}
		}
	}
	}
	

	
	//刻子11 11 11, 杠暂定无价值 
	for(x=0;x<3;x++)
	{
		for(i=0;i<12;i++)
		{
			if(hand2[i-1]!=hand2[i] && hand2[i]==hand2[i+1] && hand2[i+1]==hand2[i+2] && hand2[i]!=0)
			{
				value[value2[i]]=value[value2[i]]+60;
				value[value2[i+1]]=value[value2[i+1]]+60;
				value[value2[i+2]]=value[value2[i+2]]+60;
				if(hand2[i]==hand2[i-1]+1 && hand2[i+2]==hand2[i+3]-1)//11 12 12 12 13 给12 12对子的价值然后去掉12 12 
				{
					value[value2[i]]=value[value2[i]]-60+30;
					value[value2[i+1]]=value[value2[i+1]]-60+30;
					value[value2[i+2]]=value[value2[i+2]]-60;
						
					for(j=1;j<12-i;j++) 
					{
						hand2[j+i]=hand2[j+i+2];
						value2[j+i]=value2[j+i+2];
					}
				}
			
				else for(j=0;j<14-i;j++) 
				{
					hand2[j+i]=hand2[j+i+3];
					value2[j+i]=value2[j+i+3];
				}
			}
		}
	}
	
	for(i=0;i<14;i++)//字牌的顺子不算顺子 
	{
		if(hand2[i]-(hand2[i]%10)==70) 
		{
			hand2[i]=0;
		}
	}
	//顺子诸多长度  
	//顺子在理牌后容易被分割,对子刻子部分已经去掉形如“11 12 12 12 13”结构中的多余对子
	for(i=0;i<12;i++)
	{
		if(hand2[i]+1==hand2[i+1] && hand2[i+1]+1==hand2[i+2] && hand2[i]!=hand2[i-1]+1)//确保hand[i]是顺子的头部 
		{
			value[value2[i]] = value[value2[i]]+45;
			value[value2[i+1]] = value[value2[i+1]]+45;
			value[value2[i+2]] = value[value2[i+2]]+45;
			for(j=0;j<14-i;j++) 
				{
					hand2[j+i]=hand2[j+i+3];
					value2[j+i]=value2[j+i+3];
				}
		}
	}
	
	//14 15
	for(i=0;i<12;i++)
	{
		if(hand2[i]==hand2[i+1]-1 && hand2[i]!=0)
		{
			value[value2[i]]=value[value2[i]]+30;
			value[value2[i+1]]=value[value2[i+1]]+30;
		}
	}
	
	//14 16
	for(i=0;i<11;i++)
	{
		if(hand2[i]==hand2[i+1]-2 && hand2[i+1]!=0)
		{
			value[value2[i]]=value[value2[i]]+20;
			value[value2[i+1]]=value[value2[i+1]]+20;
		}
	}
	

	
	
	
	printf("\n");
	for(n=0;n<14;n++)
	{
		if(value[n]<10)
		{
			printf("   %d  ",value[n]);
		}
		else if(value[n]>99)
		{
			printf(" %d  ",value[n]);
		}
		else
			printf("  %d  ",value[n]);
	}
	printf("<---Value");
	int cardnum[14]={0,1,2,3,4,5,6,7,8,9,10,11,12,13};
	
	for(x=0;x<13;x++)
	{
		if(value[x]<value[x+1])
		{
			value[x+1]=value[x];
			cardnum[x+1]=cardnum[x];
		}
	}
	//printf("cardnum[13]=%d",cardnum[13]);
	return cardnum[13]+1;
}


int drive(int lun)//四人游戏驱动程序 
{
	printf("\n\n----第%d轮摸切开始----\n\n",(lun+1));
	int nextcard = 52+4*lun;
	int waiter,i,j,m,nowplayer;
	int output;
	int win=0;
	for(i=1;i<=4;i++)
	{
		//printf("P");
		if(nextcard+i==123)//荒牌流局 
		{
			win=2;
			return win;
		}
		if(i==1){getcard(player1, 1, base[nextcard+1]); nowplayer=hupai(player1);}
		if(i==2){getcard(player2, 2, base[nextcard+2]); nowplayer=hupai(player2);}
		if(i==3){getcard(player3, 3, base[nextcard+3]); nowplayer=hupai(player3);}
		if(i==4){getcard(player4, 4, base[nextcard+4]); nowplayer=hupai(player4);}
		
		printf("胡牌状态:");
		if(nowplayer==1)
		{
			printf("自摸!!!");
			win=1;
			return win;
		}
		else {printf("没胡...");}
		//scanf("%d",&waiter);
			if(i==1){
				AotoCollation(player1,14);
				printf("\n");
				for (j = 0; j < 14; j++)
				decode(player1[j]);
				printf("<---摸牌后自动理牌");
				output=putcard(player1, 1, value(player1));//暂时固定打value算法认定的最无价值的一张牌 
				if(dianpao(1,output)==1){win=1;return win;}//判断是否放铳 
	 			printf("\n");
 				for ( m = 0; m < 13; m++)
				{
					decode(player1[m]);
				}
				printf("<---回合结束");
				printf("\n\n");
				}
			if(i==2)
			{
				AotoCollation(player2,14);
				printf("\n");
				for (j = 0; j < 14; j++)
				decode(player2[j]);
				printf("<---摸牌后自动理牌");
				output=putcard(player2, 2, value(player2));
				if(dianpao(2,output)==1){win=1;return win;}
	 			printf("\n");
 				for ( m = 0; m < 13; m++)
				{
					decode(player2[m]);
				}
				printf("<---回合结束");
				printf("\n\n");
			}
			if(i==3)
			{
				AotoCollation(player3,14);
				printf("\n");
				for (j = 0; j < 14; j++)
				decode(player3[j]);
				printf("<---摸牌后自动理牌");
				output=putcard(player3, 3, value(player3));
				if(dianpao(3,output)==1){win=1;return win;}
	 			printf("\n");
 				for ( m = 0; m < 13; m++)
				{
					decode(player3[m]);
				}
				printf("<---回合结束");
				printf("\n\n");
			}
			if(i==4)
			{
				AotoCollation(player4,14);
				printf("\n");
				for (j = 0; j < 14; j++)
				decode(player4[j]);
				printf("<---摸牌后自动理牌");
				output=putcard(player4, 4, value(player4));
				if(dianpao(4,output)==1){win=1;return win;}
	 			printf("\n");
 				for ( m = 0; m < 13; m++)
				{
					decode(player4[m]);
				}
				printf("<---回合结束");
				printf("\n\n");
			}
	}
}

int dianpao(int playernum, int dianpaopai)
{
	int i,m;
	int win1,win2,win3,win4;
	int temp_player1[14];
	int temp_player2[14];
	int temp_player3[14];
	int temp_player4[14];
	for(i=0;i<14;i++)
	{
		temp_player1[i]=player1[i];
	}
	temp_player1[13]=dianpaopai;
	
	for(i=0;i<14;i++)
	{
		temp_player2[i]=player2[i];
	}
	temp_player2[13]=dianpaopai;
	
	for(i=0;i<14;i++)
	{
		temp_player3[i]=player3[i];
	}
	temp_player3[13]=dianpaopai;
	

	for(i=0;i<14;i++)
	{
		temp_player4[i]=player4[i];
	}
	temp_player4[13]=dianpaopai;
	
	if(playernum==1)
	{
		win2=hupai(temp_player2);
		win3=hupai(temp_player3);
		win4=hupai(temp_player4);
	}
	if(playernum==2)
	{
		win1=hupai(temp_player1);
		win3=hupai(temp_player3);
		win4=hupai(temp_player4);
	}
	if(playernum==3)
	{
		win1=hupai(temp_player1);
		win2=hupai(temp_player2);
		win4=hupai(temp_player4);
	}
	if(playernum==4)
	{
		win1=hupai(temp_player1);
		win2=hupai(temp_player2);
		win3=hupai(temp_player3);
	}
	
	if(win1==1||win2==1||win3==1||win4==1)
	{
		
		printf("\n放铳!!Player%d--->",playernum);
			if(win1==1)
			{
				printf("Player1!!\n胡牌牌型为:");
				for ( m = 0; m < 13; m++)
				{
					decode(player1[m]);
				}
				printf("+");
			}
			if(win2==1)
			{
				printf("Player2!!\n胡牌牌型为:");
				for ( m = 0; m < 13; m++)
				{
					decode(player2[m]);
				}
				printf("+");
			}
			if(win3==1)
			{
				printf("Player3!!\n胡牌牌型为:");
				for ( m = 0; m < 13; m++)
				{
					decode(player3[m]);
				}
				printf("+");
			}
			if(win4==1)
			{
				printf("Player4!!\n胡牌牌型为:");
				for ( m = 0; m < 13; m++)
				{
					decode(player4[m]);
				}
				printf("+");
			}
			decode(dianpaopai);
		return 1;
	}
	else return 0;
}


主函数部分 
int main()
{
int i=0;
int ii=0;
srand((unsigned)time(NULL));

/*整理全部牌*/
/*int base[136] = {  
				   11,12, 13, 14, 15, 16, 17, 18, 19,
				   11,12, 13, 14, 15, 16, 17, 18, 19,
				   11,12, 13, 14, 15, 16, 17, 18, 19,
				   11,12, 13, 14, 15, 16, 17, 18, 19,
				   
				   31,32, 33, 34, 35, 36, 37, 38, 39, 
				   31,32, 33, 34, 35, 36, 37, 38, 39, 
				   31,32, 33, 34, 35, 36, 37, 38, 39, 
				   31,32, 33, 34, 35, 36, 37, 38, 39, 
				   
				   51,52, 53, 54, 55, 56, 57, 58, 59, 
				   51,52, 53, 54, 55, 56, 57, 58, 59,
				   51,52, 53, 54, 55, 56, 57, 58, 59, 
				   51,52, 53, 54, 55, 56, 57, 58, 59,
				   
				   71,72, 73, 74, 75, 76, 77, 
				   71,72, 73, 74, 75, 76, 77, 
				   71,72, 73, 74, 75, 76, 77, 
				   71,72, 73, 74, 75, 76, 77
				};综合考虑,将牌库设为了全局变量*/

/*4个玩家*/

//int player1[14], player2[14], player3[14], player4[14];

MakeRand (base,136);
/*发牌*/ 
for (i = 0; i<13;i++)
{
	player1[i] = base[ii];
	ii++;
}
for (i = 0; i<13;i++)
{
	player2[i] = base[ii];
	ii++;
}
for (i = 0; i<13;i++)
{
	player3[i] = base[ii];
	ii++;
}
for (i = 0; i<13;i++)
{
	player4[i] = base[ii];
	ii++;
}

AotoCollation (player1,13);
AotoCollation (player2,13);
AotoCollation (player3,13);
AotoCollation (player4,13);

printf("手牌 for Player1:\n");
for (i = 0; i < 13; i++)
decode(player1[i]);
printf("\n\n");

printf("手牌 for Player2:\n");
for ( i = 0; i < 13; i++)
decode(player2[i]);
printf("\n\n");

printf("手牌 for Player3:\n");
for ( i = 0; i < 13; i++)
decode(player3[i]);
printf("\n\n");

printf("手牌 for Player4:\n");
for ( i = 0; i < 13; i++)
decode(player4[i]);

int dora1 = base[126];
printf("\n\nDORA1指示牌  :");
decode(dora1);

int gameresult;

for(i=0;i<30;i++)
{
	gameresult=drive(i);
	if(gameresult==2){printf("\n荒牌流局!"); return 0;}
	if(gameresult==1){return 0;}
} 

}


​

  • 6
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值