65.9K
CodeProject 正在变化。 阅读更多。
Home

具有 AI 和网络支持的 C# TicTacToe

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.54/5 (23投票s)

2004年3月31日

CPOL

2分钟阅读

viewsIcon

150852

downloadIcon

6843

用 C# 编写的带有 AI 和网络支持的井字游戏

TicTacToe

引言

这是一个用C#编写的井字游戏示例,具有AI和网络支持。可以与电脑对战,也可以与另一名玩家对战。与电脑对战时有三个难度级别。与另一名玩家对战时,可以本地对战,也可以通过局域网或互联网对战(它使用TCP套接字进行通信)。控制方式可以是鼠标或键盘,使用数字键盘或QWE、ASD、ZXC键。

AI支持

我尽力以最好的方式实现AI,即使代码很长。它首先尝试赢得游戏,阻止其他玩家获胜,然后尝试进行移动,避免“陷阱”并将两个电脑棋子排成一行。如果以上都不可能,则进行随机移动,首先尝试角落,然后尝试侧面。我通过在难度设置为简单或中等时,并非每次都处理防御移动例程来实现难度级别。

网络支持

使用TcpListenerTcpClient类实现网络对战,用于TCP套接字通信。通信协议非常简单,一个两字节的数据包:第一个字节指示行,第二个字节指示列。还有一个控制数据包,第一个字节为'R',用于控制游戏重启。客户端和服务器可以随时重启游戏。

这是服务器端接收数据线程的代码片段

private void ThreadReceivingServer()
{
    //________________________________________________________________________
    //
    // Thread for receiving packets from client
    //________________________________________________________________________

    try 
    {
        byte[] buf = new byte[512];
        IPHostEntry localHostEntry = Dns.GetHostByName(Dns.GetHostName());
        int bytesReceived=0;

        tcpListener = new TcpListener(localHostEntry.AddressList[0],SERVERPORT);

        tcpListener.Start();

        //____________________________________________________________________
        //
        // Thread is blocked until it gets a connection from client
        //____________________________________________________________________

        soTcpServer = tcpListener.AcceptSocket();

        serverSockStream = new NetworkStream(soTcpServer);

        objTicTacToe.RestartGame();
        objTicTacToe.SetStatusMessage("Connected!");

        wReceivingServer=true;

        while (wReceivingServer)
        {

            //________________________________________________________________
            //
            // Thread is blocked until receives data
            //________________________________________________________________

            try 
            {
                bytesReceived=serverSockStream.Read(buf,0,2);
            }
            catch
            {
                return;
            }

            //________________________________________________________________
            //
            // Processes network packet
            //________________________________________________________________

            if (bytesReceived>0) 
            {

                //____________________________________________________________
                //
                // Control packet for game restart
                //____________________________________________________________

                if (buf[0]==byte.Parse(Asc("R").ToString()))
                {
                    objTicTacToe.RestartGame();
                    continue;
                }

                //____________________________________________________________
                //
                // Packet indicating a game move
                //____________________________________________________________

                int wRow=int.Parse(Convert.ToChar(buf[0]).ToString());
                int wColumn=int.Parse(Convert.ToChar(buf[1]).ToString());

                if ((wRow>0 && wRow<4) && (wColumn>0 && wColumn<4))
                {
                    objTicTacToe.wNetworkPlay=true;
                    objTicTacToe.MakeMove(wRow,wColumn);
                }

            }    //if (bytesReceived>0) 
    
        }    //while (wReceivingServer)
    }
    catch (ThreadAbortException) {}
    catch (Exception ex)
    {
        MessageBox.Show("An error ocurred: " + ex.Message + "\n" + ex.StackTrace);
        objTicTacToe.mnDisconnect_Click(null,null);
        return;
    }
}

这是通过网络发送游戏移动的代码片段

public void SendMove(int wRow,int wColumn)
{
    //________________________________________________________________________
    //
    // Sends packet that shows move position
    //________________________________________________________________________

    byte[] buf = new byte[2];
    buf[0]=byte.Parse(Asc(wRow.ToString()).ToString());
    buf[1]=byte.Parse(Asc(wColumn.ToString()).ToString());

    SendPacketTCP(buf);

}

public void SendPacketTCP(Byte[] pDados)
{
    //_________________________________________________________________________
    //
    // Sends a packet via TCP
    //_________________________________________________________________________

    try
    {
        if (objTicTacToe.wClient==true)
        {
            if (clientSockStream==null)
                return;

            if (clientSockStream.CanWrite)
            {
                clientSockStream.Write(pDados, 0, 2);
                clientSockStream.Flush();
            }
        }
        else
        {
            if (serverSockStream==null)
                return;

            if (serverSockStream.CanWrite)
            {
                serverSockStream.Write(pDados,0, 2);
                serverSockStream.Flush();
            }
        }
    }
    catch (Exception ex)
    {
        MessageBox.Show("An error ocurred: " + ex.Message + "\n" + ex.StackTrace);
        objTicTacToe.mnDisconnect_Click(null,null);
        return;
    }

}

关注点

我在做这个项目时遇到了一些问题。第一个问题是绘制显示获胜者的简单线条。我尝试在游戏窗体中绘制一条线,但是PictureBoxes在绘制线条后会被重新绘制。我尝试在PictureBoxes中绘制,但是代码太复杂,结果也不好。所以,我有了使用GDI32.dll BitBlt函数捕获游戏屏幕,将图像放在一个大的PictureBox中,然后在该PictureBox上绘制线条的想法。

另一个问题,我花了一些时间才解决。我让客户端和服务器线程在有获胜者时显示大的PictureBox。但是当我使用picWinner.Visible=True时,程序变得不稳定并冻结。我进行了大量研究,发现只有窗体的线程才能更新UI,而其他线程不能。因此,我必须使用Control.Invoke来更新PictureBox的可见性。

//_____________________________________________________________________________
//
// Show the picure using Invoke because of socket thread 
// (locks the game if you set picWinner.Visible=True)
//_____________________________________________________________________________

object[] p = new object[1];
p[0] = picWinner;
picWinner.Invoke(new MakeVisibleHandler(MakeVisible), p);

public delegate void MakeVisibleHandler(Control control);

public void MakeVisible(Control control)
{
    //_________________________________________________________________________
    //
    // Make changes to UI using Invoke because of socket thread
    //_________________________________________________________________________

    control.Visible = true;
}
© . All rights reserved.