斗地主AI算法——第十五章の测试模块

举报
九日王朝 发表于 2019/01/22 12:03:29 2019/01/22
【摘要】 前面几章已经把整个斗地主AI算法工程完成的差不多了,接下来进入整合联调以及模拟测试模块。测试模块主要任务就是代替服务器给出我们需要的数据。因为我们本来的计划是封装成类库通过服务器调用获取,其调用的接口无非就是叫分、被动出牌、主动出牌。被动出牌和主动出牌我们已经完成,叫分我们已经实现了权值的获取,只需要在外面加一个区间划分即可:/*获取叫分函数*/ int LandScore(GameSitu...

前面几章已经把整个斗地主AI算法工程完成的差不多了,接下来进入整合联调以及模拟测试模块。


测试模块主要任务就是代替服务器给出我们需要的数据。因为我们本来的计划是封装成类库通过服务器调用获取,其调用的接口无非就是叫分、被动出牌、主动出牌。


被动出牌和主动出牌我们已经完成,叫分我们已经实现了权值的获取,只需要在外面加一个区间划分即可:


/*

获取叫分函数

*/

 

int LandScore(GameSituation &clsGameSituation, HandCardData &clsHandCardData)

{

int SumValue = 0;

 

clsHandCardData.uctHandCardValue=get_HandCardValue(clsHandCardData);

 

SumValue = clsHandCardData.uctHandCardValue.SumValue;

cout << "SumValue is :" << SumValue << ",";

 

cout << "NeedRound is :" << clsHandCardData.uctHandCardValue.NeedRound << endl;

 

if (SumValue<10) 

{

return 0;

}

else if (SumValue < 15)

{

return 1;

}

else if (SumValue < 20)

{

return 2;

}

else 

{

return 3;

}

}


接下来就是模拟数据了,首先完成洗牌,即初始化牌值与随机打乱。

//洗牌

void InitCards(vector <int> &Cards)

{

//先清空Cards

Cards.clear();

 

vector <int> tmpCards;

int i;

 

//大王56,小王52,没有53,54,55号牌

for (i = 0; i < 53; i++) {

tmpCards.push_back(i);

}

tmpCards.push_back(56);

 

 

//顺序随机打乱

for (i = tmpCards.size(); i>0; i--) {

srand(unsigned(time(NULL)));

// 选中的随机下标

int index = rand() % i;

Cards.push_back(tmpCards[index]);

tmpCards.erase(tmpCards.begin() + index);

}

 

}


同时为了方便测试,我也做了一个指定牌型的函数。

//洗牌(指定牌型,用于测试)

void InitCards_Appoint(vector <int> &Cards)

{

//先清空Cards

Cards.clear();

 

/***********飞机与炸弹连续拆分逻辑测试**********/

Cards.push_back(48); Cards.push_back(50); Cards.push_back(49);

Cards.push_back(44); Cards.push_back(47); Cards.push_back(35);

Cards.push_back(40); Cards.push_back(46); Cards.push_back(34);

Cards.push_back(36); Cards.push_back(45); Cards.push_back(33);

Cards.push_back(23); Cards.push_back(43); Cards.push_back(31);

     Cards.push_back(22); Cards.push_back(42); Cards.push_back(30);

Cards.push_back(21); Cards.push_back(41); Cards.push_back(29);

Cards.push_back(19); Cards.push_back(39); Cards.push_back(27);

Cards.push_back(18); Cards.push_back(38); Cards.push_back(26);

Cards.push_back(17); Cards.push_back(37); Cards.push_back(25);

     Cards.push_back(15); Cards.push_back(32); Cards.push_back(20);

Cards.push_back(14); Cards.push_back(28); Cards.push_back(16);

Cards.push_back(13); Cards.push_back(24); Cards.push_back(12);

Cards.push_back(11); Cards.push_back(3); Cards.push_back(7);

Cards.push_back(10); Cards.push_back(2); Cards.push_back(6);

Cards.push_back(9); Cards.push_back(1); Cards.push_back(5);

Cards.push_back(8); Cards.push_back(0); Cards.push_back(4);

Cards.push_back(51); Cards.push_back(52); Cards.push_back(56);

 

}



洗完牌就是发牌了,发牌这里我们需要定义一个包含三个人手牌的结构,因为作为正常调用来说我们是不应该有这样的数据的。

//下发到三名玩家的手牌序列,此数据只用于测试,作为AI时不会获取

 

struct ALLCardsList

{

vector <int>  arrCardsList[3];

};



然后依次发送到玩家对应的手牌数组里,最后三张为底牌。

//发牌

void SendCards(GameSituation & clsGameSituation, ALLCardsList &uctALLCardsList)

{

//洗牌

vector <int> Cards;

InitCards(Cards);

//InitCards_Appoint(Cards);

int i, j, k;

j = 0;

for (k = 0; k < 17; k++) {

for (i = 0; i < 3; i++,j++)

{

uctALLCardsList.arrCardsList[i].push_back(Cards[j]);

}

}

 

//三张底牌

clsGameSituation.DiPai[0] = Cards[j];

clsGameSituation.DiPai[1] = Cards[j+1];

clsGameSituation.DiPai[2] = Cards[j+2];

return;

}


再然后就是模拟游戏过程,首先定义游戏全局类,与三名玩家的手牌信息类。调用发牌函数完成发牌环节,可以用手牌信息类里面的PrintAll输出你想要的数据信息。

GameSituation clsGameSituation;

 

ALLCardsList  uctALLCardsList;

 

//发牌

SendCards(clsGameSituation, uctALLCardsList);

 

HandCardData arrHandCardData[3];

 

arrHandCardData[0].color_nHandCardList = uctALLCardsList.arrCardsList[0];

arrHandCardData[1].color_nHandCardList = uctALLCardsList.arrCardsList[1];

arrHandCardData[2].color_nHandCardList = uctALLCardsList.arrCardsList[2];

 

for (int i = 0; i < 3; i++)

{

arrHandCardData[i].Init();

arrHandCardData[i].nOwnIndex = i;

}

 

cout << "0号玩家牌为:" << endl;

arrHandCardData[0].PrintAll();

cout << "1号玩家牌为:" << endl;

arrHandCardData[1].PrintAll();

cout << "2号玩家牌为:" << endl;

arrHandCardData[2].PrintAll();

 

cout << "底牌为:" << endl;

cout << get_CardsName(clsGameSituation.DiPai[0]) << ','

<< get_CardsName(clsGameSituation.DiPai[1]) << ','

<< get_CardsName(clsGameSituation.DiPai[2]) << endl;

 

cout << endl;


发完牌后开始叫地主,调用LandScore函数返回其叫的分值,只有比当前已叫的分值更高才可以刷新叫地主记录。若无人叫地主重新开一局,否则将三张底牌给地主,同时刷新地主手牌,且将地主设置成将要出牌的玩家


for (int i = 0; i < 3; i++)

{

int  tmpLandScore = LandScore(clsGameSituation, arrHandCardData[i]);

if (tmpLandScore > clsGameSituation.nNowLandScore)

{

clsGameSituation.nNowLandScore = tmpLandScore;

clsGameSituation.nNowDiZhuID = i;

}

}

 

if (clsGameSituation.nNowDiZhuID == -1)

{

cout << "无人叫地主" << endl;

return;

}

 

cout << clsGameSituation.nNowDiZhuID << "号玩家是地主,叫分为:" << clsGameSituation.nNowLandScore << endl;

clsGameSituation.nDiZhuID=clsGameSituation.nNowDiZhuID;

clsGameSituation.nLandScore =clsGameSituation.nNowLandScore;

 

 

//将三张底牌给地主

arrHandCardData[clsGameSituation.nDiZhuID].color_nHandCardList.push_back(clsGameSituation.DiPai[0]);

arrHandCardData[clsGameSituation.nDiZhuID].color_nHandCardList.push_back(clsGameSituation.DiPai[1]);

arrHandCardData[clsGameSituation.nDiZhuID].color_nHandCardList.push_back(clsGameSituation.DiPai[2]);

 

//地主手牌刷新

arrHandCardData[clsGameSituation.nDiZhuID].Init();

 

//出牌玩家ID

int indexID= clsGameSituation.nDiZhuID;

 

cout << endl;

 

 

cout << "0号玩家牌为:" << endl;

arrHandCardData[0].PrintAll();

cout << "1号玩家牌为:" << endl;

arrHandCardData[1].PrintAll();

cout << "2号玩家牌为:" << endl;

arrHandCardData[2].PrintAll();

//当前控手玩家先为地主

clsGameSituation.nCardDroit = indexID;


接下来就是循环进行出牌了。在游戏全局类里我们设置了一个标志是否结束的变量,可以用于控制循环。出牌时我们只需调用get_PutCardList出牌函数即可。若某个玩家出完牌后手牌为0,则游戏结束。若玩家出过牌,则刷新游戏全局类里面当前牌型信息。



while (!clsGameSituation.Over)

{

get_PutCardList_2(clsGameSituation, arrHandCardData[indexID]);//获取出牌序列

arrHandCardData[indexID].PutCards();

cout << indexID << "号玩家出牌:" << endl;

for (vector<int>::iterator iter = arrHandCardData[indexID].color_nPutCardList.begin();

   iter != arrHandCardData[indexID].color_nPutCardList.end(); iter++)

cout << get_CardsName(*iter) << (iter == arrHandCardData[indexID].color_nPutCardList.end() - 1 ? '\n' : ',');

cout << endl;

 

if (arrHandCardData[indexID].nHandCardCount == 0)

{

clsGameSituation.Over = true;

 

if (indexID == clsGameSituation.nDiZhuID)

{

cout << "地主" << indexID << "号玩家获胜" << endl;

}

else

{

for (int i = 0; i < 3; i++) {

if (i != clsGameSituation.nDiZhuID)

{

cout << "农民" << i << "号玩家获胜" << endl;

}

}

}

}

        

if (arrHandCardData[indexID].uctPutCardType.cgType != cgZERO)

{

clsGameSituation.nCardDroit = indexID;

clsGameSituation.uctNowCardGroup = arrHandCardData[indexID].uctPutCardType;

}

 

indexID == 2 ? indexID = 0 : indexID++;

 

}


get_PutCardList函数做了一个分支,通过nCardDroit当前控手对象判断是主动出牌还是被动出牌

/*

2.0版本策略  根据场上形势决定当前预打出的手牌——分支处理

*/

void get_PutCardList_2(GameSituation &clsGameSituation, HandCardData &clsHandCardData)

{

if (clsGameSituation.nCardDroit == clsHandCardData.nOwnIndex)

{

get_PutCardList_2_unlimit(clsGameSituation, clsHandCardData);

}

else

{

get_PutCardList_2_limit(clsGameSituation, clsHandCardData);

}

return;

}



完成测试模块后,我们就可以调试程序了。

20170427174224031

20170427174559114

20170427174435878



那么现在我们就可以愉快的玩耍了,下一章我们将观察几次对局情况进行样例的分析。




【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。