斗地主AI算法——第十四章の主动出牌(3)


上一章已经排除了飞机、三带等牌型,那么除去炸弹王炸以外,我们只剩下单牌、对牌、三牌以及单顺、双顺、三顺了。


首先说单牌、对牌、三牌。其逻辑基本一样,只是出牌的个数有差别,即:如果该i牌数量满足这种牌型要求,即先打出,计算其剩余价值。

			//出单牌
			if (clsHandCardData.value_aHandCardList[i] > 0)
			{
				clsHandCardData.value_aHandCardList[i]--;
				clsHandCardData.nHandCardCount--;
				HandCardValue tmpHandCardValue = get_HandCardValue(clsHandCardData);
				clsHandCardData.value_aHandCardList[i]++;
				clsHandCardData.nHandCardCount++;
				if ((BestHandCardValue.SumValue - (BestHandCardValue.NeedRound * 7)) <= (tmpHandCardValue.SumValue - (tmpHandCardValue.NeedRound * 7)))
				{
					BestHandCardValue = tmpHandCardValue;
					BestCardGroup= get_GroupData(cgSINGLE, i, 1);
				}
			}
			//出对牌
			if (clsHandCardData.value_aHandCardList[i] > 1)
			{
				//尝试打出一对牌,估算剩余手牌价值
				clsHandCardData.value_aHandCardList[i] -= 2;
				clsHandCardData.nHandCardCount -= 2;
				HandCardValue tmpHandCardValue = get_HandCardValue(clsHandCardData);
				clsHandCardData.value_aHandCardList[i] += 2;
				clsHandCardData.nHandCardCount += 2;

				//选取总权值-轮次*7值最高的策略  因为我们认为剩余的手牌需要n次控手的机会才能出完,若轮次牌型很大(如炸弹) 则其-7的价值也会为正
				if ((BestHandCardValue.SumValue - (BestHandCardValue.NeedRound * 7)) <= (tmpHandCardValue.SumValue - (tmpHandCardValue.NeedRound * 7)))
				{
					BestHandCardValue = tmpHandCardValue;
					BestCardGroup = get_GroupData(cgDOUBLE, i, 2);
				}
			}
			//出三牌
			if (clsHandCardData.value_aHandCardList[i] > 2)
			{
				clsHandCardData.value_aHandCardList[i] -= 3;
				clsHandCardData.nHandCardCount -= 3;
				HandCardValue tmpHandCardValue = get_HandCardValue(clsHandCardData);
				clsHandCardData.value_aHandCardList[i] += 3;
				clsHandCardData.nHandCardCount += 3;

				//选取总权值-轮次*7值最高的策略  因为我们认为剩余的手牌需要n次控手的机会才能出完,若轮次牌型很大(如炸弹) 则其-7的价值也会为正
				if ((BestHandCardValue.SumValue - (BestHandCardValue.NeedRound * 7)) <= (tmpHandCardValue.SumValue - (tmpHandCardValue.NeedRound * 7)))
				{
					BestHandCardValue = tmpHandCardValue;
					BestCardGroup = get_GroupData(cgTHREE, i, 3);
				}
			}

至于顺子的算法,和被动出牌的有一点点差别,就是因为没有了数量限制,所以需要遍历以i牌为起点可以组成的所有顺子。

//出单顺
			if (clsHandCardData.value_aHandCardList[i] > 0)
			{
				int prov = 0;
				for (int j = i; j < 15; j++)
				{
					if(clsHandCardData.value_aHandCardList[j]>0)
					{
						prov++;
					}
					else
					{
						break;
					}
					if (prov >= 5)
					{

						for (int k = i; k <= j; k++)
						{
							clsHandCardData.value_aHandCardList[k] --;
						}
						clsHandCardData.nHandCardCount -= prov;
						HandCardValue tmpHandCardValue = get_HandCardValue(clsHandCardData);
						for (int k = i; k <= j; k++)
						{
							clsHandCardData.value_aHandCardList[k] ++;
						}
						clsHandCardData.nHandCardCount += prov;

						//选取总权值-轮次*7值最高的策略  因为我们认为剩余的手牌需要n次控手的机会才能出完,若轮次牌型很大(如炸弹) 则其-7的价值也会为正
						if ((BestHandCardValue.SumValue - (BestHandCardValue.NeedRound * 7)) <= (tmpHandCardValue.SumValue - (tmpHandCardValue.NeedRound * 7)))
						{
							BestHandCardValue = tmpHandCardValue;
							BestCardGroup = get_GroupData(cgSINGLE_LINE, j, prov);
						}
					}
				}
				
			}
			//出双顺
			if (clsHandCardData.value_aHandCardList[i] > 1)
			{
				int prov = 0;
				for (int j = i; j < 15; j++)
				{
					if (clsHandCardData.value_aHandCardList[j]>1)
					{
						prov++;
					}
					else
					{
						break;
					}
					if (prov >= 3)
					{

						for (int k = i; k <= j; k++)
						{
							clsHandCardData.value_aHandCardList[k] -=2;
						}
						clsHandCardData.nHandCardCount -= prov*2;
						HandCardValue tmpHandCardValue = get_HandCardValue(clsHandCardData);
						for (int k = i; k <= j; k++)
						{
							clsHandCardData.value_aHandCardList[k] +=2;
						}
						clsHandCardData.nHandCardCount += prov*2;

						//选取总权值-轮次*7值最高的策略  因为我们认为剩余的手牌需要n次控手的机会才能出完,若轮次牌型很大(如炸弹) 则其-7的价值也会为正
						if ((BestHandCardValue.SumValue - (BestHandCardValue.NeedRound * 7)) <= (tmpHandCardValue.SumValue - (tmpHandCardValue.NeedRound * 7)))
						{
							BestHandCardValue = tmpHandCardValue;
							BestCardGroup = get_GroupData(cgDOUBLE_LINE, j, prov*2);
						}
					}
				}
			}
			//出三顺
			if(clsHandCardData.value_aHandCardList[i] > 2)
			{
				int prov = 0;
				for (int j = i; j < 15; j++)
				{
					if (clsHandCardData.value_aHandCardList[j]>2)
					{
						prov++;
					}
					else
					{
						break;
					}
					if (prov >= 2)
					{

						for (int k = i; k <= j; k++)
						{
							clsHandCardData.value_aHandCardList[k] -= 3;
						}
						clsHandCardData.nHandCardCount -= prov * 3;
						HandCardValue tmpHandCardValue = get_HandCardValue(clsHandCardData);
						for (int k = i; k <= j; k++)
						{
							clsHandCardData.value_aHandCardList[k] += 3;
						}
						clsHandCardData.nHandCardCount += prov * 3;

						//选取总权值-轮次*7值最高的策略  因为我们认为剩余的手牌需要n次控手的机会才能出完,若轮次牌型很大(如炸弹) 则其-7的价值也会为正
						if ((BestHandCardValue.SumValue - (BestHandCardValue.NeedRound * 7)) <= (tmpHandCardValue.SumValue - (tmpHandCardValue.NeedRound * 7)))
						{
							BestHandCardValue = tmpHandCardValue;
							BestCardGroup = get_GroupData(cgTHREE_LINE, j, prov * 3);
						}
					}
				}
			}

因为本策略是必须解决掉至少一个i牌的,所以出牌操作放在循环内进行,也就是说,只要你不是炸3,若你手牌有3,在处理3时一定会return  就绝对不会再走到4。


if (BestCardGroup.cgType == cgERROR)
			{

			}
			else if (BestCardGroup.cgType == cgSINGLE)
			{
				clsHandCardData.value_nPutCardList.push_back(BestCardGroup.nMaxCard);
				clsHandCardData.uctPutCardType = BestCardGroup;
			}
			else if (BestCardGroup.cgType == cgDOUBLE)
			{
				clsHandCardData.value_nPutCardList.push_back(BestCardGroup.nMaxCard);
				clsHandCardData.value_nPutCardList.push_back(BestCardGroup.nMaxCard);
				clsHandCardData.uctPutCardType = BestCardGroup;
			}
			else if (BestCardGroup.cgType == cgTHREE)
			{
				clsHandCardData.value_nPutCardList.push_back(BestCardGroup.nMaxCard);
				clsHandCardData.value_nPutCardList.push_back(BestCardGroup.nMaxCard);
				clsHandCardData.value_nPutCardList.push_back(BestCardGroup.nMaxCard);
				clsHandCardData.uctPutCardType = BestCardGroup;
			}
			else if (BestCardGroup.cgType == cgSINGLE_LINE)
			{
				for (int j = BestCardGroup.nMaxCard- BestCardGroup.nCount+1; j <= BestCardGroup.nMaxCard; j++)
				{
					clsHandCardData.value_nPutCardList.push_back(j);
				}
				clsHandCardData.uctPutCardType = BestCardGroup;
			}
			else if (BestCardGroup.cgType == cgDOUBLE_LINE)
			{
				for (int j = BestCardGroup.nMaxCard - (BestCardGroup.nCount/2) + 1; j <= BestCardGroup.nMaxCard; j++)
				{
					clsHandCardData.value_nPutCardList.push_back(j);
					clsHandCardData.value_nPutCardList.push_back(j);
				}
				clsHandCardData.uctPutCardType = BestCardGroup;
			}
			else if (BestCardGroup.cgType == cgTHREE_LINE)
			{
				for (int j = BestCardGroup.nMaxCard - (BestCardGroup.nCount / 3) + 1; j <= BestCardGroup.nMaxCard; j++)
				{
					clsHandCardData.value_nPutCardList.push_back(j);
					clsHandCardData.value_nPutCardList.push_back(j);
					clsHandCardData.value_nPutCardList.push_back(j);
				}
				clsHandCardData.uctPutCardType = BestCardGroup;
			}
			else if (BestCardGroup.cgType == cgTHREE_TAKE_ONE)
			{
				clsHandCardData.value_nPutCardList.push_back(BestCardGroup.nMaxCard);
				clsHandCardData.value_nPutCardList.push_back(BestCardGroup.nMaxCard);
				clsHandCardData.value_nPutCardList.push_back(BestCardGroup.nMaxCard);
				clsHandCardData.value_nPutCardList.push_back(tmp_1);
				clsHandCardData.uctPutCardType = BestCardGroup;
			}
			else if (BestCardGroup.cgType == cgTHREE_TAKE_TWO)
			{
				clsHandCardData.value_nPutCardList.push_back(BestCardGroup.nMaxCard);
				clsHandCardData.value_nPutCardList.push_back(BestCardGroup.nMaxCard);
				clsHandCardData.value_nPutCardList.push_back(BestCardGroup.nMaxCard);
				clsHandCardData.value_nPutCardList.push_back(tmp_1);
				clsHandCardData.value_nPutCardList.push_back(tmp_1);
				clsHandCardData.uctPutCardType = BestCardGroup;
			}
			else if (BestCardGroup.cgType == cgTHREE_TAKE_ONE_LINE)
			{
				for (int j = BestCardGroup.nMaxCard - (BestCardGroup.nCount / 4) + 1; j <= BestCardGroup.nMaxCard; j++)
				{
					clsHandCardData.value_nPutCardList.push_back(j);
					clsHandCardData.value_nPutCardList.push_back(j);
					clsHandCardData.value_nPutCardList.push_back(j);
				}

				if (BestCardGroup.nCount / 4 == 2)
				{
					clsHandCardData.value_nPutCardList.push_back(tmp_1);
					clsHandCardData.value_nPutCardList.push_back(tmp_2);
				}
				if (BestCardGroup.nCount / 4 == 3)
				{
					clsHandCardData.value_nPutCardList.push_back(tmp_1);
					clsHandCardData.value_nPutCardList.push_back(tmp_2);
					clsHandCardData.value_nPutCardList.push_back(tmp_3);
				}
				if (BestCardGroup.nCount / 4 == 4)
				{
					clsHandCardData.value_nPutCardList.push_back(tmp_1);
					clsHandCardData.value_nPutCardList.push_back(tmp_2);
					clsHandCardData.value_nPutCardList.push_back(tmp_3);
					clsHandCardData.value_nPutCardList.push_back(tmp_4);
				}

				clsHandCardData.uctPutCardType = BestCardGroup;
			}
			else if (BestCardGroup.cgType == cgTHREE_TAKE_TWO_LINE)
			{
				for (int j = BestCardGroup.nMaxCard - (BestCardGroup.nCount / 5) + 1; j <= BestCardGroup.nMaxCard; j++)
				{
					clsHandCardData.value_nPutCardList.push_back(j);
					clsHandCardData.value_nPutCardList.push_back(j);
					clsHandCardData.value_nPutCardList.push_back(j);
				}
				if (BestCardGroup.nCount / 5 == 2)
				{
					clsHandCardData.value_nPutCardList.push_back(tmp_1);
					clsHandCardData.value_nPutCardList.push_back(tmp_1);
					clsHandCardData.value_nPutCardList.push_back(tmp_2);
					clsHandCardData.value_nPutCardList.push_back(tmp_2);
				}
				if (BestCardGroup.nCount / 5 == 3)
				{
					clsHandCardData.value_nPutCardList.push_back(tmp_1);
					clsHandCardData.value_nPutCardList.push_back(tmp_1);
					clsHandCardData.value_nPutCardList.push_back(tmp_2);
					clsHandCardData.value_nPutCardList.push_back(tmp_2);
					clsHandCardData.value_nPutCardList.push_back(tmp_3);
					clsHandCardData.value_nPutCardList.push_back(tmp_3);
				}
				clsHandCardData.uctPutCardType = BestCardGroup;
			}
			return;

至此,主动出牌的所有逻辑均已实现,同时整个斗地主算法也基本完成了。接下来我们便可写一些测试模块来进行整合联调。


敬请关注下一章:斗地主AI算法——第十五章の测试模块

评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值