上一章我們已經搭好了被動出牌的基本架子,本章我們主要說明一下被動出牌演算法的基本步驟。
我把出牌邏輯分為四個階段,也就是策略的優先順序。分別是:【直接打光手牌】→【同型別牌壓制】→【炸彈王炸壓制】→【不出】
第一階段【直接打光手牌】就是說如果我們可以一次性把手牌打出,那就不用考慮接下來價值之類的問題了,因為已經贏了。這種情況可能是對方打出的牌型和你一樣且你比他大,或者你剩的牌是炸彈王炸。
我們先以單牌為例:
//剪枝:如果能出去最後一手牌直接出
cardgroupdata surcardgroupdata = ins_surcardstype(clshandcarddata.value_ahandcardlist);
if (surcardgroupdata.cgtype != cgerror)
else
if (surcardgroupdata.cgtype == cgbomb_card|| surcardgroupdata.cgtype == cgking_card)
}
也就是通過呼叫第六章實現的判定是否為一手牌函式,如果返回的型別與當前牌型相同且nmaxcard值更大一些,或者是炸彈王炸。那麼直接打光所有手牌。
put_all_surcards打光所有手牌實現方法:
/*封裝好的 將所有的牌都打出*/
void
put_all_surcards
(gamesituation &clsgamesituation, handcarddata &clshandcarddata, cardgroupdata surcardgroupdata)
void
put_all_surcards
( handcarddata &clshandcarddata, cardgroupdata surcardgroupdata)
第二階段【同型別牌壓制】就是需要遍歷當前手牌滿足可以管上的組合,然後選出最優解。我們先做一些準備工作,因為要考慮出牌和不出牌收益情況,所以我們先計算出當前手牌的價值,之所以把原始牌型輪數+1也是為了在這裡若能搶占一輪盡量出牌管上。當然,若管完之後的剩餘價值損失的太大就只能算了。
還需要設定暫存最佳牌號的變數、是否出牌的標誌。
//暫存最佳的價值
handcardvalue besthandcardvalue = get_handcardvalue(clshandcarddata);
//我們認為不出牌的話會讓對手乙個輪次,即加一輪(權值減少7)便於後續的對比參考。
besthandcardvalue.needround +=
1;//暫存最佳的牌號
int bestmaxcard=
0;//是否出牌的標誌
bool putcards =
false;
然後就是迴圈遍歷滿足條件的若干個選擇,選出最優的解決方案。還是以單牌為例:
for (
int i = clsgamesituation.uctnowcardgroup.nmaxcard +
1; i <
18; i++)}}
if (putcards)
按照之前價值的定義,我們對比的公式為總價值-輪數*7。
第三階段【炸彈王炸壓制】的策略與上文邏輯類似,唯一的區別就是加了乙個手牌剩餘價值的判定,就是如果我出完炸剩餘手牌價值還蠻可觀的話,我們就可以任性的炸出,
畢竟此時我們獲勝的機率很大。
for (
int i =
3; i <
16; i++)}}
if (putcards)
//王炸
if (clshandcarddata.value_ahandcardlist[
17] >
0 && clshandcarddata.value_ahandcardlist[
16] > 0)}
若以上三個階段都沒有return的話,就進入我們第四階段了。
第四階段【不出】
//管不上
clshandcarddata.uctputcardtype = get_groupdata(cgzero,
0, 0);
return;
這也就是我們被動出牌演算法的基本步驟,本章是以單牌為例,後續文章會進而補充其他的牌型。
上一章我們已經搭好了被動出牌的基本架子,本章我們主要說明一下被動出牌演算法的基本步驟。
我把出牌邏輯分為四個階段,也就是策略的優先順序。分別是:【直接打光手牌】→【同型別牌壓制】→【炸彈王炸壓制】→【不出】
第一階段【直接打光手牌】就是說如果我們可以一次性把手牌打出,那就不用考慮接下來價值之類的問題了,因為已經贏了。這種情況可能是對方打出的牌型和你一樣且你比他大,或者你剩的牌是炸彈王炸。
我們先以單牌為例:
//剪枝:如果能出去最後一手牌直接出
cardgroupdata surcardgroupdata = ins_surcardstype(clshandcarddata.value_ahandcardlist);
if (surcardgroupdata.cgtype != cgerror)
else
if (surcardgroupdata.cgtype == cgbomb_card|| surcardgroupdata.cgtype == cgking_card)
}
也就是通過呼叫第六章實現的判定是否為一手牌函式,如果返回的型別與當前牌型相同且nmaxcard值更大一些,或者是炸彈王炸。那麼直接打光所有手牌。
put_all_surcards打光所有手牌實現方法:
/*封裝好的 將所有的牌都打出*/
void
put_all_surcards
(gamesituation &clsgamesituation, handcarddata &clshandcarddata, cardgroupdata surcardgroupdata)
void
put_all_surcards
( handcarddata &clshandcarddata, cardgroupdata surcardgroupdata)
第二階段【同型別牌壓制】就是需要遍歷當前手牌滿足可以管上的組合,然後選出最優解。我們先做一些準備工作,因為要考慮出牌和不出牌收益情況,所以我們先計算出當前手牌的價值,之所以把原始牌型輪數+1也是為了在這裡若能搶占一輪盡量出牌管上。當然,若管完之後的剩餘價值損失的太大就只能算了。
還需要設定暫存最佳牌號的變數、是否出牌的標誌。
//暫存最佳的價值
handcardvalue besthandcardvalue = get_handcardvalue(clshandcarddata);
//我們認為不出牌的話會讓對手乙個輪次,即加一輪(權值減少7)便於後續的對比參考。
besthandcardvalue.needround +=
1;//暫存最佳的牌號
int bestmaxcard=
0;//是否出牌的標誌
bool putcards =
false;
然後就是迴圈遍歷滿足條件的若干個選擇,選出最優的解決方案。還是以單牌為例:
for (
int i = clsgamesituation.uctnowcardgroup.nmaxcard +
1; i <
18; i++)}}
if (putcards)
按照之前價值的定義,我們對比的公式為總價值-輪數*7。
第三階段【炸彈王炸壓制】的策略與上文邏輯類似,唯一的區別就是加了乙個手牌剩餘價值的判定,就是如果我出完炸剩餘手牌價值還蠻可觀的話,我們就可以任性的炸出,
畢竟此時我們獲勝的機率很大。
for (
int i =
3; i <
16; i++)}}
if (putcards)
//王炸
if (clshandcarddata.value_ahandcardlist[
17] >
0 && clshandcarddata.value_ahandcardlist[
16] > 0)}
若以上三個階段都沒有return的話,就進入我們第四階段了。
第四階段【不出】
//管不上
clshandcarddata.uctputcardtype = get_groupdata(cgzero,
0, 0);
return;
這也就是我們被動出牌演算法的基本步驟,本章是以單牌為例,後續文章會進而補充其他的牌型。
鬥地主AI演算法 第八章 被動出牌 2
上一章我們已經搭好了被動出牌的基本架子,本章我們主要說明一下被動出牌演算法的基本步驟。我把出牌邏輯分為四個階段,也就是策略的優先順序。分別是 直接打光手牌 同型別牌壓制 炸彈王炸壓制 不出 第一階段 直接打光手牌 就是說如果我們可以一次性把手牌打出,那就不用考慮接下來價值之類的問題了,因為已經贏了。...
鬥地主AI演算法 第七章 被動出牌 1
哎,之前扯了那麼多蛋,終於講出牌了!本章開始講被動出牌的邏輯演算法。首先我們先把架子搭起來,被動出牌我們肯定是要知道場上目前打出的是什麼牌型。在第二章資料結構裡我們定義過,遊戲全域性類裡面有乙個存放當前牌型結構的成員,即 當前打出牌的型別資料,被動出牌時玩家根據這裡做出篩選 cardgroupdat...
鬥地主AI演算法 第七章 被動出牌 1
哎,之前扯了那麼多蛋,終於講出牌了!本章開始講被動出牌的邏輯演算法。首先我們先把架子搭起來,被動出牌我們肯定是要知道場上目前打出的是什麼牌型。在第二章資料結構裡我們定義過,遊戲全域性類裡面有乙個存放當前牌型結構的成員,即 當前打出牌的型別資料,被動出牌時玩家根據這裡做出篩選 cardgroupdat...