通过创建黑白棋机器人来学习实现 Minimax 算法

本文档介绍了一个黑白棋程序的开发,该程序支持多种模式并允许用户自定义AI。文章详细阐述了程序的文件结构、游戏界面和关键组件,特别是介绍了Minimax算法的工作原理和在游戏中的应用,以及如何配置和优化机器人。此外,还讨论了阿尔法贝塔修剪技术以减少搜索树的节点数量。
摘要由CSDN通过智能技术生成

介绍

黑白棋程序,我选择开发这个程序是因为在寻找黑白棋/黑白棋游戏时找不到具有我需要的功能的程序。

特征

  1. 支持模式 Human vs Human、Human vs Bot、Bot vs Bot
  2. 支持板编辑器
  3. 支持机器人创建者。您可以选择一个图像并自定义将用于评估棋盘的 AI 分数。
  4. 可以显示机器人的最后一步Minimax搜索树
  5. 可以更改个人资料图片Human Player1Human Player2
  6. 可以导航移动
  7. 支持深色模式

程序使用的文件扩展名

  1. .brd用作电路板信息。
  2. .but用作机器人信息。
  3. .rev用作游戏保存信息,游戏信息保留了移动的历史,这是它与棋盘游戏的区别,因此您可以通过导航控件进行导航。

怎么玩

游戏规则与普通黑白棋游戏完全相同。

这个概念

项目的文件结构

在本节中,我想谈谈负责渲染图形的类,但我不会提及与 Board Editor 或 Bot Creator 相关的任何内容。

游戏界面


该板还可以显示数字。

UI\PictureBoxBoard.cs

这是一个继承自PictureBox控件的类。
它负责显示黑白棋盘。此类本身由名为 的控件组成pictureBoxTable。控件将是从方法中呈现表格
的控件。 该方法从该类中读取棋盘信息, 绘制表格和磁盘。pictureBoxTablepictureBoxTable_Paint()
pictureBoxTable_Paint()

这就是画板的逻辑。

  1. 有一个由 64 个矩形组成的数组。我们创建这 64 个矩形来存储单元格在板上的位置。

  2. 此方法将调用:

  • DrawBoard()绘制板表
  • DrawIntersection()顾名思义
  • DrawDisk()只是绘制磁盘
  • DrawRedDot()显示哪个是最后的放置位置
  • DrawDiskBorder()显示可以放置哪个单元格
  • DrawNumber()- 此方法用于当我们需要显示单元格的分数时。
    此方法中的所有绘图仅使用基本的 GDI+ 方法,例如FillEllipse()DrawEllipse(),DrawRectangles()

没有用于电路板绘图的图像。这些是绘制符号的逻辑:

  1. 只需从 0 循环到 7。
  2. 使用DrawString()方法绘制string
    此控件具有PictureBoxBoard_Paint()呈现符号的方法。
C#
缩小▲   
public enum CellImageEnum
{
   WhiteCell,
   BlackCell,
   BlankCell,
   BlankCellWithRed  //To show which is the last cell that the disk was put
}
public enum BoardModeEnum
{
   PlayMode,
   EditMode
}
public Boolean IsSmallBoard { get; set; } = false; // Support display the board
                                                   // as small size
public Boolean IsHideLegent { get; set; } = false;
public Boolean IsDrawNotion { get; set; } = true;  // Can hide the notation (A1-H8)

public event PictureBoxCellClick CellClick;
private void PictureBox1_MouseDown(object sender, MouseEventArgs e)
{
   //Must be a left button
   if (e.Button != MouseButtons.Left)
   {
       return;
   }

   int X = e.X;
   int Y = e.Y;
   int RowClick = Y / CellSize;
   int ColClick = X / CellSize;
   if (RowClick >= NoofRow ||
       ColClick >= NoofRow ||
       RowClick < 0 ||
       ColClick < 0)
   {
       //Row and Column click must be in range.
       return;
   }

   if (CellClick != null)
   {
       //Raise event.
       CellClick(this, new Position(RowClick, ColClick));
   }
}
private void DrawNumber(Graphics g, int Number, Rectangle rec)
{
    /*This method is for drawing a number in case of
      showing a score value from evaluate function.
    */
    Rectangle r = rec;
    r.Height -= 10;
    r.Width -= 10;
    r.X += 10;
    r.Y += 15;

    Font SegoeUIFont = new Font("Segoe UI", 14, FontStyle.Bold);
    Color NumberColor = Color.FromArgb(150, 180, 180);
    String NumberText = "+" + Number;
    if(Number < 0)
    {
      //Negative number does not need "+" prefix.
      NumberText =  Number.ToString ();
      NumberColor = Color.FromArgb(255, 199, 79);
    }
    //Create Brush and other objects
    PointF pointF = new PointF(r.X, r.Y);
    SolidBrush solidBrush = new SolidBrush(NumberColor );
    //Draw text using DrawString
    g.DrawString(NumberText ,
      SegoeUIFont,
      solidBrush, pointF );

    solidBrush.Dispose();
}
private void DrawDisk(Graphics g, CellImageEnum CellImage, Rectangle rec)
{
    if (CellImage == CellImageEnum.BlankCell)
    {
      return;
    }
    Rectangle r = rec;
    //Reduce the size of Rectangle
    r.Height -= 10;
    r.Width -= 10;
    r.X += 5;
    r.Y += 5;
    DrawDiskBorder(g, r);

    Color colorDisk = Color.White;
    if (CellImage != CellImageEnum.WhiteCell)
    {
      colorDisk = Color.Black;
    }

    using (Brush brushDisk = new SolidBrush(colorDisk))
    {
      g.FillEllipse(brushDisk, r);
    }

    Color diskBorderColor = Color.Black;

    //Uncomment this line in case you would like white disk to have white color border
    //diskBorderColor = colorDisk
    using (Pen penDisk = new Pen(new SolidBrush(diskBorderColor)))
    {
     g.DrawEllipse(penDisk, r);
    }
}
private void DrawDiskBorder(Graphics g, Rectangle rec)
{
    //Reduce the size of Rectangle
    Rectangle r = rec;
    r.Height -= 10;
    r.Width -= 10;
    r.X += 5;
    r.Y += 5;
    using (Pen penDisk = new Pen(DiskBorderColor))
    {
      g.DrawEllipse(penDisk, r);
    }
}
private void DrawBoard(Graphics g, Rectangle[] arrRac)
{
    //This method just draw 64 array of Rectangle objects.

    g.Clear(BoardColor);
    g.DrawRectangles(PenBorder, arrRac);
    Rectangle rectangleBorder = new Rectangle(0, 0, CellSize * 8, CellSize * 8);
    using(Pen penBigBorder=new Pen ( Color.Black, 4))
    {
      g.DrawRectangle(penBigBorder, rectangleBorder);
    }
}

棋盘信息

棋盘信息将显示玩家的图片和姓名以及每一方的磁盘数量。它们只是 上的一组控件FormGame,将在ReverseUI课堂上使用。

导航器控件与移动

这些只是FormGame将在ReverseUI 课堂上使用的控件。
Navigator控件由四个按钮组成,移动历史使用了一个容器tableLayoutPanel ,内含Link按钮

Theme.cs

这个类只包含控件颜色信息。

C#
public class Theme
{
    public Color ButtonBackColor { get; set; }
    public Color ButtonForeColor { get; set; }
    public Color LabelForeColor { get; set; }
    public Color FormBackColor { get; set; }
    public Color InputBoxBackColor { get; set; }
    public Color LinkLabelForeColor { get; set; }
    public Color LinkLabelActiveForeColor { get; set; }
    public Boolean IsFormCaptionDarkMode { get; set; } = true;

    public Color MenuBackColor { get; set; }
    public Color MenuHoverBackColor { get; set; }
    public Color MenuForeColor { get; set; }
}

这个程序支持DarkMode,我们使用Global.LightTheme

DarkTheme<br />
Then

,当加载每个表单时,它将访问Global.CurrentTheme,然后用于themeUtil
设置Controls的外观。

这是一个使用themUtil 对象的例子。

C#
themeUtil.SetLabelColor(theme, lblNumberofBlackDisk,
                        lblNumberofWhiteDisk,
                        lblPlayer1Name,
                        lblPlayer2Name)
         .SetButtonColor(theme, btnFirst,
                        btnNext,
                        btnPrevious,
                        btnLast)
         .SetMenu (this.menuStrip1)
         .SetForm(this);

FormGame.cs

这个类是主窗体,它包含移动PictureBoxBoard Navigator 控件和历史记录。

UI.Dialog.cs

显示对话框的所有代码都属于此处。

!!!主程序!!!

AI.Board.cs

这是保存板信息的类。

字段和属性

  • int[,] boardMatrix 我们使用简单的二维数组存储,Black =-1, Blank =0, White =1
  • BoardPhase具有三个阶段:BeginningMiddleEndGame。这个值将被 AI 使用,它将确定它需要哪一组分数值来评估棋盘分数。
  • NumberofLastFlipCell每次我们翻转单元格时,我们都会保留被翻转的单元格的数量
  • CurrentTurn 当前回合
  • generateMoves() 生成可用的移动
  • PutValue()只需输入单元格值
  • IsLegalMove()检查位置是否有效
  • SwitchTurn()只需切换转弯
  • IsTherePlaceToPut()false-如果位置已被占用;否则,它将返回true

这些是Board构造函数。

C#
public Board()
{
    boardMatrix = new int[8, 8];
    SetCell(3, 3, CellValue.White);
    SetCell(3, 4, CellValue.Black);
    SetCell(4, 3, CellValue.Black);
    SetCell(4, 4, CellValue.White);
    listPutPosition.Clear();
}

public Board(Board OriginalBoard)
{
    boardMatrix = new int[8, 8];
    Array.Copy(OriginalBoard.boardMatrix, this.boardMatrix,
               this.boardMatrix.Length);
    this.CurrentTurn = OriginalBoard.CurrentTurn;
    if (OriginalBoard.LastPutPosition != null)
    {
        this.SetLastPutPosition(OriginalBoard.LastPutPosition.Clone());
    }
}

AI.BoardValue.cs

此类包含板信息,当我们想要保存板或创建自定义板时。我们将值存储在此类中,然后将其序列化。

ReverseUI.cs

这个类实现了 IUI 接口,所以它必须有这些方法:

C#
缩小▲   
void RenderHistory();
    void RenderNumberofDisk(int WhiteDisk, int BlackDisk);
    void ShowBoardAtTurn(int NumberofTurn);
    void MoveBoardToNextTurn();
    void MoveBoardToPreviousTurn();
    void RenderBoard();
    void SetGame(KReversiGame game);
    void RemoveGame();
    void BlackPutCellAt(Position position);
    void WhitePutCellAt(Position position);
    void Initial();
    void ReleaseUIResource(); //To make sure there is no event leak
    void InformPlayer1NeedtoPass();
    void InformPlayer2NeedtoPass();
    void InformGameResult(KReversiGame.GameResultEnum result);

// Any event the begin with MoveBoard relate to navigation.
    event EventHandler MoveBoardToNextTurnClick;
    event EventHandler MoveBoardToPreviousTurnClick;
    event EventHandler MoveBoardToFirstTurnClick;
    event EventHandler MoveBoardToLastTurnClick;
    event PictureBoxBoard.PictureBoxCellClick CellClick;
    event EventInt MoveBoardToSpecificTurnClick;
    event EventHandler ContinuePlayingClick;

KReversiGame.cs

此类包含UIU对象和板对象。它充当这两个对象之间的粘合剂。
这意味着当我们点击一​​个单元格时,UI 部分将不会知道任何关于游戏数据的信息。
它不会检查此单元格是否为空或是否为有效位置,它只会通知游戏此特定单元格已被单击,然后游戏对象将决定下一步要做什么。

例如,游戏对象会检查它是否是一个有效的位置,然后它会调用一个棋盘对象来更新值,然后它会通知 UI 让他们知道它需要更新或者它会调用 UI 来更新自己直接。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

StubbornZorro

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值