斗地主项目总结一,设计过程1,由于我没有玩过斗地主,设计初期,没有一点思路。
先写了一个界面,完成了洗牌,分牌,出牌功能。
牌值数组:i nt Pai[54] = {1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,6,6,6,6,7,7,7,7,8,8,8,8,9,9,9,9,10,10,10,10,11,11,11,11,12,12,12,12,13,13,13,13,14,15};在程序内部,操作的是1-15之间的牌值。
之后在显示的时候才通过转换函数:char paiChange (int n)将其转换为‘J’‘Q’之类的字符,并显示到屏幕上。
并通过一个保存三个玩家牌值数组名的指针数组实现三人轮流出牌:int *WanJia[] = {JiaPai, YiPai, BingPai};//玩家名称数组while(1){…………………………………………..// DoPai为出牌函数if(DoPai (WanJia[i % 3], WanJiaPaiShu[i % 3], i % 3, &ComPai, nBool)){break;}nPaiAay[i % 3] = ComPai;i++;}这样就实现了三人轮流出牌。
然后,用一个临时数组保存玩家出的牌:for (i = 0; ZhuoMian[i]; i++);//找到最后一个位置ZhuoMian[i] = ary[nPaiIndex - 1];//从玩家牌值数组中拷贝ZhuoMianCount ++;//临时数组长度加1ary[nPaiIndex - 1] = 30;//把玩家牌值数组中相应的牌值赋为30(*nCount) --;//把玩家牌值数组长度减1…………………………………………..关键:每次操作后,都要进行排序2,增加了判定牌型的函数。
int ComparePai (int ZhuoMian[], int ZhuoMianCount, char **cPaiType);完成单牌,对子,三张,顺子,双顺子,三顺子,三带一. . . . . .等的判定。
难点:牌之间的比较。
起初是直接用两个玩家的牌进行比较,后来发现这样很容易出错。
重写了。
后来,我想到了为每个牌型规定一定比较数,这个数的结构是这样的:例:三带一的比较数都是300开始的。
‘8、8、8、4’的比较数是306.最高位是权限位,后面的是用于比较大小的牌面值。
在比较的时候,通过//实现权限比对,10 ~ 10000之间的权限一样,10000以上的权限最高//例如:103可以和106比较,但是禁止和504比较,10000以上的可以通吃if ((nCompZM > *ComPai && nCompZM / 100 == *ComPai / 100 &&nCompZM > 100)|| nCompZM / 10000 > *ComPai / 10000 ||(*ComPai == 0 && nCompZM >100))进行比较。
然后个玩家出牌后,都调用比较函数ComparePai进行比较,并且返回一个比较数,如果比上家大,就和上家的比较数进行交换,大不过,就保持不变。
这样就实现了牌的比较。
3,增加了出完牌后,两家都不要的处理方式。
思路是:用一个三元素数组保存三个玩家出牌后,返回的比较数。
如果三次都不变,就让第一个出牌的人,继续条件出牌(不用继续比较)。
具体实现:int nPaiAay[3] = {0};nPaiAay[i % 3] = ComPai;if (i >= j) //实现其余两家不要,本家继续随便出牌{for (j = i; j - i < 3; j++){if (nPaiAay[j % 3] != nPaiAay[j % 3 +1] || nPaiAay[j % 3] == 10){break;}}if (j - i >= 3 && ComPai != 0 ){ComPai = 0;}}4,增加了对输入错误和Pass等的判定功能。
难点:由于我用的是动态出牌,也就是输入牌值对应的下标后,相应的牌就会从数组中消失(其实不是消失,只是没赋值为30,数组长度减1)。
于是在输入错误之后,要把牌重新返回到玩家手中。
for (i = 0; ZhuoMian[i]; i++){(*nCount) ++;//现把数组长度加一ary[(*nCount) - 1] = ZhuoMian[i]; //把牌重新拷贝回去}以上:初期设计大体结束。
二,AI设计1,牌面分析由于原计划没有想过弄这个东西,后来时间比较充足就搞了。
所以没有什么计划,想到一点写一点。
结构也比较乱,代码比较长。
思路是:在机器人出牌之前,现把它手中的牌进行分析,把结果保存到一个结构体中,然后根据上家出牌返回的比较数,来选择其要出的牌。
首先写了一个用于保存牌面分析结果的结构体:牌型大体分为,单牌,对子,三张,顺子,双顺子,炸弹,对王。
通过以上牌型的组合,可实现斗地主中所有的牌型。
typedef struct{DANPAI danpai[20];//单牌DUIZI duizi[10];//对子SANBUDAI sanbudai[7];//以下略。
ZHADAN zhadan[4];WUSHUNZI wushunzi[5];LIUSHUNZI liushunzi[6];QISHUNZI qishunzi[7];DUIW ANG duiwang[1];SANDAIYI sandaiyi;SANDAIER sandaier;SIDAIER sidaier;SANSHUANGSHUNZI sanshuangshunzi[3];SISHUANGSHUNZI sishuangshunzi[2];BASHUNZI bashunzi[2];JIUSHUNZI jiushunzi[2];}PAIXING;然后开始写牌面分析函数//AI判断对子int AnalyseDUIZI (PAIXING *paixing, int PaiDemo[], int *PaiCount);//AI判断顺子int AnalyseSHUNZI (PAIXING *paixing, int PaiDemo[], int *PaiCount, int n);//AI判断三张int AnalyseSANZHANG (PAIXING *paixing, int PaiDemo[], int *PaiCount);//AI判断炸弹int AnalyseZHADAN (PAIXING *paixing, int PaiDemo[], int *PaiCount);//AI判断单牌int AnalyseDANPAI (PAIXING *paixing, int PaiDemo[], int *PaiCount);//AI判断对王int AnalyseDUIW ANG (PAIXING *paixing, int PaiDemo[], int *PaiCount);//判断双顺子int AnalyseSHUANGSHUNZI (PAIXING *paixing, int PaiDemo[],int *PaiCount, int n);通过比较函数把牌进行分类后,按类型放入牌型结构体相应的位置中。
由于分析函数的先后顺序不同,产生的牌型排列组合也不同。
这里我用到了钱老师周五讲到的函数指针数组(思路来源于肖航同学)。
思路是:按照牌面分析函数的分析顺序,写了七个函数,用函数指针依次按照这七个函数进行分析归类,然后统计每种分析方式分析后,牌型结构体中的单牌数,对牌数,和三张牌数。
用一个结构体保存每种分析之后的“优化数”:typedef struct{int sum; //函数序号int fnIndex;//“优化数”}STSORT;优化数= 单牌数+ 对牌数–三张牌数,这个数越小,牌就相对越容易出去。
这样就可以优化牌面分析,实现:在三张牌不是很少的基础上,让单牌和小对子尽量少。
另外,也可以实现顺子重叠时,的最优处理方式。
例如:3,4,5,5,6,6,7,7,8,9,10 就可以分为两个五顺子。
STSORT StSort[7] = {0};int (*pfnAnalyse[7])(PAIXING *, int *, int, int) = {0};//函数指针数组//以下为各种分析函数的同顺序的组合分析函数pfnAnalyse[0] = AnalysePaiXing;pfnAnalyse[1] = AnalysePaiXingEX;pfnAnalyse[2] = AnalysePaiXingEX1;pfnAnalyse[3] = AnalysePaiXingEX2;pfnAnalyse[4] = AnalysePaiXingEX3;pfnAnalyse[5] = AnalysePaiXingEX4;pfnAnalyse[6] = AnalysePaiXingEX5;for (i = 0; i < 7; i++){memset(&paixing, 0, sizeof(PAIXING));//每次都要清零pfnAnalyse[i](&paixing, ary, *nCount, 0);//依次执行这七个函数for (j = 0; paixing.duizi[j + 1].Pai[0] &&paixing.duizi[j + 1].Pai[0] < 8; j++);StSort[i].sum += j; //以下为统计单牌,对牌,和三张for (j = 0; paixing.danpai[j + 1].Pai[0]; j++);StSort[i].sum += j;for (j = 0; paixing.sanbudai[j + 1].Pai[0]; j++);StSort[i].sum -= j;StSort[i].fnIndex = i;}MySTSort(StSort, 7); //对其次的“优化数”进行排序memset(&paixing, 0, sizeof(PAIXING));pfnAnalyse[StSort[0].fnIndex](&paixing, ary, *nCount, 0);// 选择“优化数”数值最小的分析函数,完成分析。
以上实现了机器人牌的优化。
2.实现机器人自动出牌我为机器人写了一个专门的出牌函数int AIPai (int ary[], int *nCount, int nIndex, int *ComPai,char *szName, int nBool);在总出牌函数中调用这个函数,如果是机器人,就直接进入这个函数,完成出牌。