井字对电脑

tick tac toe against computer

所以我编写了一个井字游戏。此时此刻是人对人的游戏。现在我希望计算机是一个人,所以 "person vs computer" 我已经对计算机进行了编程,让它应该回答游戏,但它仍然是人对人。我怎样才能改变我的程序,让计算机响应?

井字游戏的代码是这样的:

bool turn = true;
int tic_counter = 0;
public Baden()
{
    InitializeComponent();
}

private void button_click(object sender, MouseEventArgs e)
{
    Button b = (Button)sender;
    if (turn)
        b.Text = "X";
    else
        b.Text = "O";
        computer_make_move();

    turn = !turn;
    b.Enabled = false;
    tic_counter++;

    CheckForWinner();    
}

private void CheckForWinner()
{
    bool there_is_a_winner = false;

    if ((A1.Text == A2.Text) && (A2.Text == A3.Text) && (!A1.Enabled))
        there_is_a_winner = true;
    else if ((B1.Text == B2.Text) && (B2.Text == B3.Text) && (!B1.Enabled))
        there_is_a_winner = true;
    else if ((C1.Text == C2.Text) && (C2.Text == C3.Text) && (!C1.Enabled))
        there_is_a_winner = true;



    if ((A1.Text == B1.Text) && (B1.Text == C1.Text) && (!A1.Enabled))
        there_is_a_winner = true;
    else if ((B2.Text == A2.Text) && (A2.Text == C2.Text) && (!A2.Enabled))
        there_is_a_winner = true;
    else if ((C3.Text == A3.Text) && (A3.Text == B3.Text) && (!A3.Enabled))
        there_is_a_winner = true;


    if ((A1.Text == B2.Text) && (B2.Text == C3.Text) && (!A1.Enabled))
        there_is_a_winner = true;
    else if ((B2.Text == A3.Text) && (A3.Text == C1.Text) && (!B2.Enabled))
        there_is_a_winner = true;


    if (there_is_a_winner)
    {
        disableButtons();
        String winner = "";
        if (turn)
            winner = "O";
        else
            winner = "X";

        MessageBox.Show("Der gewinner ist: " + winner);
        this.Close();
    }
    else
    {
        if(tic_counter == 9)
        {
            MessageBox.Show("Unentschieden!");
            this.Close();
        }
    }
}

电脑是这样的:

private void computer_make_move()
{
    //priority 1:  get tick tac toe
    //priority 2:  block x tic tac toe
    //priority 3:  go for corner space
    //priority 4:  pick open space

    Button move = null;

    //look for tic tac toe opportunities
    move = look_for_win_or_block("O"); //look for win
    if (move == null)
    {
        move = look_for_win_or_block("X"); //look for block
        if (move == null)
        {
            move = look_for_corner();
            if (move == null)
            {
                move = look_for_open_space();
            }
        }

    }
    move.PerformClick();

}

private Button look_for_open_space()
{
    Console.WriteLine("Looking for open space");
    Button b = null;
    foreach (Control c in Controls)
    {
        b = c as Button;
        if (b != null)
        {
            if (b.Text == "")
                return b;
        }//end if
    }//end if

    return null;
}

private Button look_for_corner()
{
    Console.WriteLine("Looking for corner");
    if (A1.Text == "O")
    {
        if (A3.Text == "")
            return A3;
        if (C3.Text == "")
            return C3;
        if (C1.Text == "")
            return C1;
    }

    if (A3.Text == "O")
    {
        if (A1.Text == "")
            return A1;
        if (C3.Text == "")
            return C3;
        if (C1.Text == "")
            return C1;
    }

    if (C3.Text == "O")
    {
        if (A1.Text == "")
            return A3;
        if (A3.Text == "")
            return A3;
        if (C1.Text == "")
            return C1;
    }

    if (C1.Text == "O")
    {
        if (A1.Text == "")
            return A3;
        if (A3.Text == "")
            return A3;
        if (C3.Text == "")
            return C3;
    }

    if (A1.Text == "")
        return A1;
    if (A3.Text == "")
        return A3;
    if (C1.Text == "")
        return C1;
    if (C3.Text == "")
        return C3;

    return null;
}

private Button look_for_win_or_block(string mark)
{
    Console.WriteLine("Looking for win or block:  " + mark);
    //HORIZONTAL TESTS
    if ((A1.Text == mark) && (A2.Text == mark) && (A3.Text == ""))
        return A3;
    if ((A2.Text == mark) && (A3.Text == mark) && (A1.Text == ""))
        return A1;
    if ((A1.Text == mark) && (A3.Text == mark) && (A2.Text == ""))
        return A2;

    if ((B1.Text == mark) && (B2.Text == mark) && (B3.Text == ""))
        return B3;
    if ((B2.Text == mark) && (B3.Text == mark) && (B1.Text == ""))
        return B1;
    if ((B1.Text == mark) && (B3.Text == mark) && (B2.Text == ""))
        return B2;

    if ((C1.Text == mark) && (C2.Text == mark) && (C3.Text == ""))
        return C3;
    if ((C2.Text == mark) && (C3.Text == mark) && (C1.Text == ""))
        return C1;
    if ((C1.Text == mark) && (C3.Text == mark) && (C2.Text == ""))
        return C2;

    //VERTICAL TESTS
    if ((A1.Text == mark) && (B1.Text == mark) && (C1.Text == ""))
        return C1;
    if ((B1.Text == mark) && (C1.Text == mark) && (A1.Text == ""))
        return A1;
    if ((A1.Text == mark) && (C1.Text == mark) && (B1.Text == ""))
        return B1;

    if ((A2.Text == mark) && (B2.Text == mark) && (C2.Text == ""))
        return C2;
    if ((B2.Text == mark) && (C2.Text == mark) && (A2.Text == ""))
        return A2;
    if ((A2.Text == mark) && (C2.Text == mark) && (B2.Text == ""))
        return B2;

    if ((A3.Text == mark) && (B3.Text == mark) && (C3.Text == ""))
        return C3;
    if ((B3.Text == mark) && (C3.Text == mark) && (A3.Text == ""))
        return A3;
    if ((A3.Text == mark) && (C3.Text == mark) && (B3.Text == ""))
        return B3;

    //DIAGONAL TESTS
    if ((A1.Text == mark) && (B2.Text == mark) && (C3.Text == ""))
        return C3;
    if ((B2.Text == mark) && (C3.Text == mark) && (A1.Text == ""))
        return A1;
    if ((A1.Text == mark) && (C3.Text == mark) && (B2.Text == ""))
        return B2;

    if ((A3.Text == mark) && (B2.Text == mark) && (C1.Text == ""))
        return C1;
    if ((B2.Text == mark) && (C1.Text == mark) && (A3.Text == ""))
        return A3;
    if ((A3.Text == mark) && (C1.Text == mark) && (B2.Text == ""))
        return B2;

    return null;
}

那么我怎样才能更改计算机正在应答的程序呢?

如果你想让它基于你的点击事件,最简单的方法是将按钮点击事件更改为如下所示: private void button_click(object sender, MouseEventArgs e) {

    Button b = (Button)sender;
    if (turn)
        b.Text = "X";
    else
        b.Text = "O";

    b.Enabled = false;
    tic_counter++;
    turn = !turn;
    CheckForWinner();

    if (!turn)
        computer_make_move();
}

现在每次轮到玩家时都会直接进行电脑走棋。将 computer_make_move 保留在 else 路径中意味着,您必须单击一个按钮来执行计算机代码,然后再单击一次。

试一试:

private void button_click(object sender, MouseEventArgs e)
{
    var oldTurn = turn;

    Button b = (Button)sender;
    if (turn)
        b.Text = "X";
    else
        b.Text = "O";

    turn = !turn;

    //try to remove this line since it will block PerformClick in computer_make_move()
    //don't know for sure if that is the button that gets "clicked" in Computer
    //b.Enabled = false;

    tic_counter++;
    CheckForWinner();

    if (oldTurn)
        computer_make_move();
}

如果您需要禁用该按钮,您可以将代码提取到不同的方法中并调用它而不是 PerformClick

private void button_click(object sender, MouseEventArgs e)
{
     ExecuteButtonCode(sender);
}

private void ExecuteButtonCode(object sender)
{
    var oldTurn = turn;

    Button b = (Button)sender;
    if (turn)
        b.Text = "X";
    else
        b.Text = "O";

    turn = !turn;

    b.Enabled = false;

    tic_counter++;
    CheckForWinner();

    if (oldTurn)
        computer_make_move();
}

在你的 computer_make_move 中这样调用:

ExecuteButtonCode(move);

而不是

move.PerformClick();

如果游戏结束,您需要阻止 computer_make_move(); 的执行。

将您的 CheckForWinner 方法更改为此 -(删除了一些代码,以便更容易看到我放置代码的位置,在我删除代码的位置放置注释):

private bool gameIsOver = false; //boolean to track if the game is over or not
private void CheckForWinner()
{
    bool there_is_a_winner = false;

    //[RemovedCode] - Evaluation of your buttons...

    if (there_is_a_winner)
    {
        gameIsOver = true;
        //[RemovedCode] - winning code...
    }
    else
    {
        if(tic_counter == 9)
        {
            gameIsOver = true;
            //[RemovedCode] - losing code...
        }
    }
}

现在将检查游戏是否结束添加到您的方法中:

private void ExecuteButtonCode(object sender)
{
    if (gameIsOver)
        return; //return (exit) the method if the game is over and just do nothing in that case

    var oldTurn = turn;

    Button b = (Button)sender;
    if (b == null)
        return; //return (exit) the method if b is null to prevent the NullReferenceException from occuring

    if (turn)
        b.Text = "X";
    else
        b.Text = "O";

    turn = !turn;

    b.Enabled = false;

    tic_counter++;
    CheckForWinner();

    if (gameIsOver)
        return; //return (exit) the method if the game is over, to prevent computer from making a move

    if (oldTurn)
        computer_make_move();
}

对像这样的编程有很大帮​​助的技巧之一是:不要在事件函数中放置大量代码。

这是您的一般代码结构:

mouseClickEvent()
{
    // lines of code to handle stuff
    // lines of code to check for a winner
    // lines of code to have the computer decide a move
    // calling mouseClickEvent() for the computer.
}

...您 运行 遇到了问题,因为 mouseClickEvent() 是从两个不同的来源调用的:用户单击按钮,以及从代码内部调用。

好吧,有一个相对简单的修复方法:

void mouseClickEvent(...)
{
    HandlePlayerSelectingSquare(playerNum: 1, source: sender);
}
void HandlePlayerSelectingSquare(int playerNum, Object source)
{
    // misc stuff
    // if playerNum == 0, return - we don't need to continue on
    // figure out a computer move
    // call HandlePlayerSelectingSquare(2, computersButtonChoice)
}

...有意义吗?现在你有一个简单的方法来判断是玩家还是计算机正在做出他们的选择:通过将 playerNum 传递给函数(计算机永远不会调用该事件。)