ホームページへ戻る

第5章: 2人対戦ゲーム

 2人対戦型のゲームでコンピュータに次の1手を選び出させようとした時に、どのような考え方をすれば良いのか。その最も基本となるアルゴリズムがミニマックス法と呼ばれるものです。

 ミニマックス法

 自分(A)は(A)にとって最も有利になる手を選ぼうとする。(MAXレベル)
 しかし相手(B)も同様に(B)にとって最も有利になる手を選ぼうとする。言い替えると(B)は(A)にとって最も不利になる手を選ぼうとする。(MINレベル)
 MAXレベルとMINレベルが交互に手の選択基準になってゲーム展開の探索を行い、現在の局面における次の最善の1手はどれかを選び出すのがミニマックス法です。

 さて、ここで「深読み名人」と言う名前の大変素晴らしい関数を紹介します。誰が作ったかは知らないが、ミニマックス法の規則に従ってゲームを完全に読み切り、両者が最善を尽した場合の勝敗を正確に判定(予想)してくれます。
 ただ一つだけ欠点があって勝敗の判定はしてくれるが、次にどんな手を打てば勝てるのかは教えてくれません。
 しかし、この深読み名人関数を使えば、次にどんな手を打てば勝てるのかを知るプログラムは簡単に作る事が出来ます。
 総ての手をシラミ潰しに打ってみて対戦相手役の深読み名人に手を渡し、相手が「負けました」と答えた時の手が、こちらにとっては「勝てる手」になる訳です。List.5-1

 深読み名人を使うことによって、こんなに簡単にプログラムが書けてしまいますが、では深読み名人なんて難しそうな関数はどうやって作れば良いのでしょうか?

   実は、もう出来ています?!

 今説明したプログラムそのものが、深読み名人関数と同じものなのです。深読み名人を呼び出す場所で、自分と相手の立場を逆にしてこのプログラムを「再帰呼び出し」すれば良いだけなのです。
 あっけない結末ですが、本当にそうなのです。何故なら、この再帰呼び出しはゲームの勝敗が決するまで深く入り込んで全探索を行ってしまうのです。再帰ってのは本当に魔法のように便利なものですね。List.5-2

 例題6: ロミオゲーム

 二人で遊ぶ「ロミオ」ゲームとは、横1列に11個のマスが並ぶボードとオセロの駒11個を使用する。
 オセロとルールは似ているが、このロミオはボード上のどこに駒を置いても良い。相手の駒をはさんだ時に相手の駒が自分の色に反転するのは同じ。
 ゲームの勝敗は、最後に残った駒の数が、なんと「少ない方が勝ち」となる。
 つまり、相手の駒を出来るだけ反さないようしないといけない。
 実はこのゲーム,後手に必勝法がある。そこで、1手目を人(黒先)とし,コンピュータ(白後)が対戦する必勝プログラムを作ってほしい。
(オリジナルゲーム)

 駒を置いた時の相手駒の反転処理や、またその手を元に戻す処理がちょっと面倒ですが、それ以外は先ほどの基本型に当てはめるだけです。List.5-3


ホームページへ戻る


























List.5-1

//****************************************
// 深読み名人関数を使って次の手を選択する
//****************************************
int 次の手選択()
{
    if (ゲームが終了している) {
        // 勝敗を調べて結果を返す。(-1:負け  0:勝ち)
        if (自分が勝ち) return 0; // 勝ち
    } else {
        for (i=1; i<=可能な次の手の総て; i++) {
            その手iを打つ;
            相手の返事 = 深読み名人関数();
            その手iを戻す;

            // 相手が負けと答えたらこの手(i)で勝てる
            if (相手の返事 == 負け) return i;
        }
    }
    //  総ての手に対して相手が勝ちと答えたら
    // こちらに勝ち目はない。
    return -1; // 負け
}

List.5-2

//****************************************
// 再帰呼出しを使って次の手を選択する
//****************************************
int 次の手選択(相手)
{
    自分 = 相手の反対;

    if (ゲームが終了している) {
        // 勝敗を調べて結果を返す。(-1:負け  0:勝ち)
        if (自分が勝ち) return 0; // 勝ち
    } else {
        for (i=1; i<=可能な次の手の総て; i++) {
            その手iを打つ;
            相手の返事 = 次の手選択(自分); // 再帰呼出し
            その手iを戻す;

            // 相手が負けと答えたらこの手(i)で勝てる
            if (相手の返事 == 負け) return i;
        }
    }
    //  総ての手に対して相手が勝ちと答えたら
    // こちらに勝ち目はない。
    return -1; // 負け
}



List.5-3 例題6: ロミオゲーム

プログラムリスト→ romio.c