Developer

さくさく理解する Godot 入門(ただし2Dに限る)応用編 テキサスホールデムポーカー【第7回】
2022.08.17
Lv1

さくさく理解する Godot 入門(ただし2Dに限る)応用編 テキサスホールデムポーカー【第7回】

目次

  • アクション処理
  • プレイヤー管理

アクション処理

プレイヤーのアクション(行動)には、フォールド(降りる)、チェック(パス)、コール(レイズと同じ額のチップ出す)、 レイズ(賭けチップを増やす) の4種類がある。
これらは前章のコードで示したようにAIの行動決定後や、人間が Raise ボタンなどを押下したときにコールされる。

それぞれのアクション処理は関数化されているので、以下、順に解説する。 なお、どの関数も引数でプレイヤーインデックスを指定する。

下記はフォールド処理を行う関数だ。

func do_fold(pix):
    nActPlayer -= 1                 # アクティブプレイヤー数デクリメント
    is_folded[pix] = true           # フォールドフラグON
    players_card1[pix].hide()       # カードを非表示に
    players_card2[pix].hide()
    players[pix].set_hand("")       # 手役表示クリア
    set_act_panel_text(pix, "folded", Color.darkgray)   # 行動パネルテキスト更新

アクティブプレイヤー数を1減らし、フォールドフラグをONにする。 そして、プレヤーパネルの手役表示をクリアし、行動パネルテキストを「folded」に設定する。

下記は、チェック処理を行う関数だ。

func do_check(pix):
    set_act_panel_text(pix, "checked", Color.lightgray)

行動パネルテキストを「checked」に設定するだけだ。

下記はコール処理を行う関数だ。

func do_call(pix):
    set_act_panel_text(pix, "called", Color.lightgray)
    players[pix].set_bet_chips(bet_chips)
    # 所持チップ額を考慮して、コールに必要なチップ額を計算
    var cc = min(players[pix].get_chips(), bet_chips - bet_chips_plyr[pix])
    round_bet_chips_plyr[pix] += cc     # 現ラウンドでのチップ額に合算
    cur_sum_bet += cc                   # 現ベット額合計に合算
    players[pix].sub_chips(cc)          # 所持チップ額からコール額を減算
    bet_chips_plyr[pix] = bet_chips     # プレイヤーの現ベット額を更新

行動パネルテキストを「called」に設定し、コールに必要なチップ額を計算し、現ベット額などのデータを更新している。

最後はレイズ処理を行う関数だ。

func do_raise(pix, rc):
    set_act_panel_text(pix, "raised", Color.pink)
    act_panels[pix].show()
    bet_chips += rc         # コール分+レイズ分 が実際に場に出される
    cur_sum_bet += rc
    players[pix].set_bet_chips(bet_chips)
    cur_sum_bet += bet_chips - bet_chips_plyr[pix]
    players[pix].sub_chips(bet_chips - bet_chips_plyr[pix])
    bet_chips_plyr[pix] = bet_chips
    round_bet_chips_plyr[pix] += bet_chips
    n_raised += 1
    $NRaisedLabel.text = "# raised: %d/%d" % [n_raised, MAX_N_RAISES]

処理はコールの場合とほぼ同様だ。 異なるのは、行動パネルテキストに「raised」を設定するのと、レイズ額が引数で指定されるという点だ。

以上で、各行動の処理関数が実装できた。あとは、Fold, Check 等のボタンが押された場合に、 それらをコールする。
以下に、それらのコードを示す。 本アプリでは、Call ボタンと Check ボタンを共有にしているので、 ボタン押下処理関数でレイズされているかどうかを調べ、そうであれば do_call(pix) を、 そうでなければ do_check(pix) をコールしている。
また、レイズの場合は SpinBox に設定されている値をゲットし、それを引数に指定して do_raise(pix, rc) をコールしている。

func _on_FoldButton_pressed():
    do_fold(HUMAN_IX)
    disable_act_buttons()   # アクションボタンをディセーブル
    next_player()           # 次のプレイヤーの手番に
func _on_CheckCallButton_pressed():
    if bet_chips_plyr[HUMAN_IX] < bet_chips:
        do_call(HUMAN_IX)
    else:
        do_check(HUMAN_IX)
    disable_act_buttons()
    next_player()           # 次のプレイヤーの手番に
func _on_RaiseButton_pressed():
    do_raise(HUMAN_IX, $RaiseSpinBox.get_value())
    disable_act_buttons()
    next_player()           # 次のプレイヤーの手番に
func _on_AllInNextButton_pressed():
    if state == SHOW_DOWN || state == ROUND_FINISHED:
        $AllInNextButton.text = "AllIn"
        $AllInNextButton.disabled = true
        next_round()        # ショーダウン時以降は、次のラウンドに遷移
    else:
        var tc = bet_chips - bet_chips_plyr[HUMAN_IX]    # コールに必要なチップ数
        var rc = players[HUMAN_IX].get_chips() - tc      # レイズチップ数
        if rc == 0:     # レイズ不可、コール可能
            do_call(HUMAN_IX)
        elif rc > 0:    # レイズ可能
            do_raise(HUMAN_IX, rc)
        next_player()           # 次のプレイヤーの手番に

最後に、次のプレイヤーに手番を渡す next_player() の実装を示しておく。

func next_player():
    sub_state = READY
    while true:
        nix = (nix + 1) % N_PLAYERS
        if !is_folded[nix]: break
    update_next_player()        # 次の手番強調
    $RaiseSpinBox.set_value(BB_CHIPS)

処理は単純で、プレイヤーインデックスをインクリメントし、 そのプレイヤーがフォールドしている場合は、インクリメントを繰り返すというだけだ。

プレイヤー管理

プレイヤー背景オブジェクトへの参照を players 配列で管理している。[0] が人間プレイヤー、[1] ~[5] がAIプレイヤーだ。 プレイヤー背景オブジェクトは Table/PlayerBG1, 2, … 6 というノード名なので、ループで回してノード名を生成し、 get_node() でノードオブジェクトへの参照を取り出し、players 配列に格納している。

const INIT_CHIPS = 200
const N_PLAYERS = 6
var players = []        # プレイヤーパネル配列、[0] for Human
var is_folded = []      # 各プレイヤーが Fold 済みか?
var act_panels = []         # プレイヤーアクション表示パネル
var bet_chips_plyr = []     # 各プレイヤー現ラウンドのベットチップ数(パネル下部に表示されるチップ数)
func _ready():
    .....
    players = []
    for i in range(N_PLAYERS):
        var pb = get_node("Table/PlayerBG%d" % (i+1))       # プレイヤーパネルノードを取得
        pb.set_hand("")                 # 手役名クリア
        pb.set_chips(INIT_CHIPS)        # テーブル持ち込みチップ額設定
        players.push_back(pb)           # プレイヤーパネルノード(への参照)を配列に追加
        is_folded[i] = false            # フォールドフラグOFF
    .....

エディタでプレイヤー背景を設置せず、スクリプトで生成・設置することも可能なのだが、 配置をエディタで確認できる方がわかりやすいと考えそうしている(下図参照)。

すでにこれまで説明してきたコードに出てきているが、 各プレイヤーがフォールドしたかを示す状態フラグとして is_folded 配列を使用している。

この他にも、各プレイヤーが別途したチップ額の総和:bet_chips_plyr 配列、 各プレイヤーのアクションパネルオブジェクトへの参照 :act_panels 配列なども使用している。

TechProjin Godot入門 関連連載リンク

Godotで学ぶゲーム制作
さくさく理解するGodot入門 連載目次

標準C++ライブラリの活用でコーディング力UP!
「競技プログラミング風」標準C++ライブラリ 連載目次