7

この質問(または同様の質問)が数回尋ねられていることがわかります.Googleでよく検索して理解しようとしましたが、間違いなく行き詰まっています.

私の仕事は、「良い」変数を使用して、コンピューターが実行できる最善の動きを決定する再帰関数を使用することです。これを支援するためのドキュメントもありますが、私の人生では、わかりません。

誰かが私を助けたり、実際に行う必要があることを分解したりするのに時間がかかる場合は、非常に感謝しています。これまでに持っているコードを以下にリンクしますが、これは割り当てであるため、直接の回答よりもガイダンスが望ましいです。私は MinMax ソリューションを見てきましたが、それは間違いなく私の理解を超えているようです。私はプログラミングに非常に慣れていないため (特に C# では数か月の経験しかありません)、簡単に行ってください!

これが私が従うことを意図した提案された解決策です:

http://erwnerve.tripod.com/prog/recursion/tictctoe.htm

public partial class Form1 : Form
{
    public static string[,] Board = new string[3, 3] { { "1", "2", "3" }, { "4", "5", "6" }, { "7", "8", "9" } };
    public bool Winner = false;
    public string WinState;

    private void Reset()
    {
        WinState = "";
        Winner = false;
        Board[0, 0] = "1";
        Board[0, 1] = "2";
        Board[0, 2] = "3";
        Board[1, 0] = "4";
        Board[1, 1] = "5";
        Board[1, 2] = "6";
        Board[2, 0] = "7";
        Board[2, 1] = "8";
        Board[2, 2] = "9";
        btn1.Text = "";
        btn2.Text = "";
        btn3.Text = "";
        btn4.Text = "";
        btn5.Text = "";
        btn6.Text = "";
        btn7.Text = "";
        btn8.Text = "";
        btn9.Text = "";
    }

    private void checkWinner()
    {
        // Top Row
        if (Board[0, 0].Equals(Board[0, 1]) && Board[0, 1].Equals(Board[0, 2]))
        {
            Winner = true;
            WinState = Board[0, 0];
        }
        // Middle Row
        if (Board[1, 0].Equals(Board[1, 1]) && Board[1, 1].Equals(Board[1, 2]))
        {
            Winner = true;
            WinState = Board[1, 0];
        }
        // Bottom Row
        if (Board[2, 0].Equals(Board[2, 1]) && Board[2, 1].Equals(Board[2, 2]))
        {
            Winner = true;
            WinState = Board[2, 0];
        }
        // Left column
        if (Board[0, 0].Equals(Board[1, 0]) && Board[1, 0].Equals(Board[2, 0]))
        {
            Winner = true;
            WinState = Board[0, 0];
        }
        // Middle column
        if (Board[0, 1].Equals(Board[1, 1]) && Board[1, 1].Equals(Board[2, 1]))
        {
            Winner = true;
            WinState = Board[0, 1];
        }
        // Right column
        if (Board[0, 2].Equals(Board[1, 2]) && Board[1, 2].Equals(Board[2, 2]))
        {
            Winner = true;
            WinState = Board[0, 2];
        }
        // Diagonal 1
        if (Board[0, 0].Equals(Board[1, 1]) && Board[1, 1].Equals(Board[2, 2]))
        {
            Winner = true;
            WinState = Board[0, 0];
        }
        // Diagonal 2
        if (Board[2, 0].Equals(Board[1, 1]) && Board[1, 1].Equals(Board[0, 2]))
        {
            Winner = true;
            WinState = Board[2, 0];
        }

        if (Winner == true)
        {
            if (WinState == "X")
            {
                MessageBox.Show("Congratulations you win!");
                Reset();
            }
            else if (WinState == "O")
            {
                MessageBox.Show("Sorry you lose!");
                Reset();
            }
        }
    }

    private void btn1_Click(object sender, EventArgs e)
    {
        btn1.Text = "X";
        Board[0, 0] = "X";
        checkWinner();
    }

    private void btn2_Click(object sender, EventArgs e)
    {
        btn2.Text = "X";
        Board[0, 1] = "X";
        checkWinner();
    }

    private void btn3_Click(object sender, EventArgs e)
    {
        btn3.Text = "X";
        Board[0, 2] = "X";
        checkWinner();
    }

    private void btn4_Click(object sender, EventArgs e)
    {
        btn4.Text = "X";
        Board[1, 0] = "X";
        checkWinner();
    }

    private void btn5_Click(object sender, EventArgs e)
    {
        btn5.Text = "X";
        Board[1, 1] = "X";
        checkWinner();
    }

    private void btn6_Click(object sender, EventArgs e)
    {
        btn6.Text = "X";
        Board[1, 2] = "X";
        checkWinner();
    }

    private void btn7_Click(object sender, EventArgs e)
    {
        btn7.Text = "X";
        Board[2, 0] = "X";
        checkWinner();
    }

    private void btn8_Click(object sender, EventArgs e)
    {
        btn8.Text = "X";
        Board[2, 1] = "X";
        checkWinner();
    }

    private void btn9_Click(object sender, EventArgs e)
    {
        btn9.Text = "X";
        Board[2, 2] = "X";
        checkWinner();
    }
}
4

1 に答える 1

12

そのドキュメントを読んだ結果として再帰を理解していなくても、それほど悪くはありません。再帰を説明するのはあまりうまくいきません。(それは難しい概念です-私もおそらくそれをうまくやらないでしょう)。最終的に、あなたがしなければならないことは、あなたのプログラムにあなたがするであろうことをさせようとすることです。その観点から説明していきたいと思います。

再帰は、ソリューションのステップを(1回)コーディングし、計算されたばかりの結果を入力として使用してそのステップを繰り返すことができるため、便利です。任意の良さのアルゴリズムではなく、自分の視点から問題を見てみてください。あなたはおそらく、論文からアルゴリズムを理解するのに一生懸命努力しているでしょう。

次のように考えてみてください。ゲームの開始時に、プレーヤー1がプレイします。プログラムはプレーヤー2の動きを選択する必要がありますが、プレーヤー2はゲームの残りの部分について考える必要があります(可能な動きごとに)。

  1. プレイヤー2は8つの可能な動きから選択できます。
  2. プレイヤー1は7から選択できます
  3. プレイヤー2は6から選択できます
  4. プレイヤー1は5から選択できます
  5. プレイヤー2は4から選択できます
  6. プレイヤー1は3から選択できます
  7. プレイヤー2は2から選択できます
  8. プレーヤー1が最後の正方形を取ります。

これを次のように言い換えることができます。
現在のプレーヤーは2であり、現在のプレーヤーの残りのすべての選択肢に重みを付けます
現在のプレーヤーは1であり、現在のプレーヤーの残りのすべての選択肢に重みを付けます
現在のプレーヤーは2であり、現在のプレーヤーの残りのすべての選択肢に重みを付けます
現在のプレーヤーは1であり、現在のプレーヤーの残りのすべての選択肢に重みを付けます
現在のプレーヤーは2であり、現在のプレーヤーの残りのすべての選択肢に重みを付けます
現在のプレーヤーは1であり、現在のプレーヤーの残りのすべての選択肢に重みを付けます
現在のプレーヤーは2であり、現在のプレーヤーの残りのすべての選択肢に重みを付けます
現在のプレーヤーは1であり、現在のプレーヤーの残りのすべての選択肢に重みを付けます

これを次のように言い換えることができます:現在のプレーヤーを指定し、プレーヤーを切り替えて、現在のプレーヤーのすべての可能な選択肢に重みを付けます
選択肢がなくなるまで繰り返します

これを次のように言い換えることができます:現在のプレーヤーを指定し、プレーヤーを切り替え、CheckGoodness()これ以上選択肢がなくなるまで繰り返します

だから、あなたの記事に戻ってください。作者は1&-1のプレイヤーを使用しています。なんで?ムーブをどんどん深く通過するにつれて、プレーヤーを入れ替える必要があり、このようにレベルを下げるときにプレーヤーを切り替えるのは非常に簡単です(ここではプレーヤーについてのみ話します:

public int CheckGoodness(bool playerID)
{
    playerID = -playerID;
    if (!endConditionMet)
    {
        goodness = CheckGoodness(playerID);
    }
    return goodness;
}

プレイヤーと一緒に、残っているすべての可能な動きの状態を表す何かを渡す必要があります。問題は、参照として渡すものを渡すと、行った変更が元のデータに反映されることです。それが起こっていないことを確認してください。@CodeInChaosがクローンを提案したのはおそらくそのためです。

再帰では、常に呼び出しシーケンスを終了する方法があることを確認する必要があることに注意してください。終了条件が依存しているものは何でも変更する必要があります。この場合、可能な移動の数は減少しています。そうしないと、永久に呼び出してメモリが不足します。

編集:ボードクラスの説明が追加されました。

全体像から考えてみてください。クラスは、実世界のもの(オブジェクトなど)の表現です。モノにはそれを説明する属性があります。これらはクラスのデータです。物事も行動を起こします。これらはメソッドです。私が聞いたクラスのもう1つの定義は、クラスはデータとそのデータに対する操作です。

ゲームにあるオブジェクトについて考えてください。2人のプレーヤーと1つのボード。他にはあまりありません。

プレイヤーは移動でき、一意の識別子(この場合は「X」または「O」)を持ち、人間またはAIのいずれかです。現時点では(重要なことは)他に何も考えられませんが、通常はそこにある可能性はあるものの、プログラムに実際には影響を与えないもの(目の色など)は他にもあります。これは、継承を使用できる場所でもあります。抽象moveメソッドを持つプレーヤークラスがあります。UIからの入力を受け入れるオーバーライド移動メソッドを使用してプレーヤーから継承する人間のクラス、プレーヤーから継承し、良さの評価で移動を計算することによって移動メソッドをオーバーライドするコンピューター/AIクラス。

ボードにはデータがあります:

  • 可能なプレイロケーションの3x3グリッド(ちなみに、これはロケーションオブジェクトのコレクションでもあります)
    • プレーヤー1と2の表現が必要な場合があります

取締役会の行動は次のようになります。

  • プレイヤー(人間またはAI)の動きを受け入れ、有効な場合はそれを記録し、勝利を決定し、良い動き、悪い動き、ゲームオーバー、または勝利のインジケーターを返します
  • 現在のゲームの勝者を返す方法があるかもしれません
  • おそらくリセット方法が必要です
  • 移動履歴がある可能性があります

データがなく、単一のメソッドを持つ静的なGoodNessクラス(より適切な名前が必要な場合があります)を持つことができます(または、これはボードクラスの別のメソッドである可能性があります:

  • ボードを受け入れるか、良さの配列を計算して返すか、または単に最良の動きを返す

AIは、移動する前にGoodnessGetBestMoveメソッドを呼び出すことができます。
再帰は、そのGetBestMoveメソッドに分離されます。

これらのどれも石に設定されていないことに注意してください。クラスは、その中にあるべきだと思うものによって定義されます。それはすべて、問題を解決するための最良の方法であるとあなたがどのように認識しているかに基づいています。それでも問題が解決しない場合は、機能させようとしたコードで質問を更新してください。コードのレイアウトを開始するときに、図を描くのに非常に役立ちます。

幸運を祈ります。これがお役に立てば幸いです。StackOverflowの通知をより適切に監視できるように努めます。

于 2012-01-17T02:50:02.153 に答える