本章開始,我們介紹主動出牌的演算法,和被動出牌類似,我們第一步把主要架子搭起來。
首先清空出牌序列
clshandcarddata.clearputcardlist();
主動出牌的策略按照優先順序大體可以分為三類:
【一】能直接一手牌出去,優先出。
【二】兩手牌出去且有絕對大牌,先出絕對大牌。
【三】出一手牌使得接下來自己手牌價值最大化。
//剪枝:如果能出去最後一手牌直接出
cardgroupdata surcardgroupdata = ins_surcardstype(clshandcarddata.value_ahandcardlist);
//如果能一次性出去且沒有炸彈,因為有炸彈的話權值可能會更大
if (surcardgroupdata.cgtype != cgerror&&!hasboom(clshandcarddata.value_ahandcardlist))
接下來就是第三步, 就是我們選擇打出一手牌盡量使得接下來自己手牌價值最大化。/*王炸——當前策略只處理王炸作為倒數第二手的優先出牌邏輯,後續版本會在此基礎上優化*/
if (clshandcarddata.value_ahandcardlist[
17] >
0 && clshandcarddata.value_ahandcardlist[
16] > 0)}
與被動出牌不一樣的是,主動出牌我們沒有限制條件,我也嘗試過全部列舉,不過時間消耗肯定是**的。於是我定製了乙個基本的出牌優先順序策略:
①三帶一、飛機等牌優先打出,因為這種牌型可以把小牌帶出。其實這裡對比較大的三帶一不是很公平,後續版本可以在此處做分支處理,比如說三帶一的話只迴圈到10,j以上先不著急打出。飛機倒還好說,那玩意基本管不到別人,所以出了就出了。至於四帶二嘛。。。四帶二是個什麼東西?我不知道,我眼裡只有炸彈。
所以,這部分的架子應該是這樣的。
若可以出這幾種牌型,選擇一種價值最高的打出。因為要列舉所有的牌,所以在迴圈外根據最佳策略進行出牌處理。
②沒有上述牌型後,優先處理當前最小的一張牌。若是該牌有四張,先不處理。
這裡出牌處理就放在迴圈內了,因為當確定了這個i值後無論如何都是要打出一手牌的,且打完牌就可以return了。
③如果沒有從3到2的非炸牌,那麼看看有沒有單王,如果有,可以出。
④單王也沒有,出炸彈。//如果沒有3-2的非炸牌,則看看有沒有單王
if (clshandcarddata.value_ahandcardlist[
16] ==
1 && clshandcarddata.value_ahandcardlist[
17] ==
0)if (clshandcarddata.value_ahandcardlist[
16] ==
0 && clshandcarddata.value_ahandcardlist[
17] ==
1)
這裡可能有人會想,需不需要再加上炸彈也沒有,出王炸呢?其實不存在的,因為如果你真的沒牌打了就剩王炸了,早在前面剪枝部分就處理了。//單王也沒有,出炸彈
for (
int i =
3; i <
16; i++)
}
所以如果走到這裡都沒有返回的話,肯定是出現錯誤了。
把上述的各個模組連線起來,即構成主動出牌的基本架子:
至此主動出牌的架子就搭好了,且除了三帶牌型出牌策略及解決最小值牌出牌策略這兩個大部分,其他部分**本章均已給出,下一章我們開始實現三帶牌型的出牌策略。void
get_putcardlist_2
(handcarddata &clshandcarddata)
/*王炸——當前策略只處理王炸作為倒數第二手的優先出牌邏輯,後續版本會在此基礎上優化*/
if (clshandcarddata.value_ahandcardlist[
17] >
0 && clshandcarddata.value_ahandcardlist[
16] >
0)//暫存最佳的價值
handcardvalue besthandcardvalue;
besthandcardvalue.needround =
20;besthandcardvalue.sumvalue = mincardsvalue;
//我們認為不出牌的話會讓對手乙個輪次,即加一輪(權值減少7)便於後續的對比參考。
besthandcardvalue.needround +=
1;//暫存最佳的組合
cardgroupdata bestcardgroup;
//帶出去的牌
int tmp_1 =
0;int tmp_2 =
0;int tmp_3 =
0;int tmp_4 =
0;//優先處理三牌、飛機等牌
for (
int i =
3; i <
16; i++)
//這部分出牌處理放到迴圈外
if (bestcardgroup.cgtype == cgthree_take_one)
else
if (bestcardgroup.cgtype == cgthree_take_two)
else
if (bestcardgroup.cgtype == cgthree_take_one_line)
else
if (bestcardgroup.cgtype == cgthree_take_two_line)
//次之處理當前價值最低的牌,現在不必再考慮這張牌可能被三牌帶出等情況
for (
int i =
3; i <
16; i++)
//如果沒有3-2的非炸牌,則看看有沒有單王
if (clshandcarddata.value_ahandcardlist[
16] ==
1 && clshandcarddata.value_ahandcardlist[
17] ==
0)if (clshandcarddata.value_ahandcardlist[
16] ==
0 && clshandcarddata.value_ahandcardlist[
17] ==
1)//單王也沒有,出炸彈
for (
int i =
3; i <
16; i++)
//異常錯誤
clshandcarddata.uctputcardtype = get_groupdata(cgerror,
0, 0);
return;
}
鬥地主AI演算法 第十二章 主動出牌 1
本章開始,我們介紹主動出牌的演算法,和被動出牌類似,我們第一步把主要架子搭起來。首先清空出牌序列 cpp view plain copy clshandcarddata.clearputcardlist 主動出牌的策略按照優先順序大體可以分為三類 一 能直接一手牌出去,優先出。二 兩手牌出去且有絕對...
鬥地主AI演算法 第十三章 主動出牌 2
上一章我們已經搭好了出牌演算法的基本框架,本章主要實現優先處理的三帶 飛機等牌型。首先定義一些基本變數 cpp view plain copy 暫存最佳的價值 handcardvalue besthandcardvalue besthandcardvalue.needround 20 besthan...
鬥地主AI演算法 第十四章 主動出牌 3
上一章已經排除了飛機 三帶等牌型,那麼除去炸彈王炸以外,我們只剩下單牌 對牌 三牌以及單順 雙順 三順了。首先說單牌 對牌 三牌。其邏輯基本一樣,只是出牌的個數有差別,即 如果該i牌數量滿足這種牌型要求,即先打出,計算其剩餘價值。出單牌 if clshandcarddata.value ahandc...