介绍
黑白棋程序,我选择开发这个程序是因为在寻找黑白棋/黑白棋游戏时找不到具有我需要的功能的程序。
特征
- 支持模式 Human vs Human、Human vs Bot、Bot vs Bot
- 支持板编辑器
- 支持机器人创建者。您可以选择一个图像并自定义将用于评估棋盘的 AI 分数。
- 可以显示机器人的最后一步
Minimax
搜索树 - 可以更改个人资料图片
Human Player1
和Human Player2
- 可以导航移动
- 支持深色模式
程序使用的文件扩展名
- .brd用作电路板信息。
- .but用作机器人信息。
- .rev用作游戏保存信息,游戏信息保留了移动的历史,这是它与棋盘游戏的区别,因此您可以通过导航控件进行导航。
怎么玩
游戏规则与普通黑白棋游戏完全相同。
这个概念
项目的文件结构
在本节中,我想谈谈负责渲染图形的类,但我不会提及与 Board Editor 或 Bot Creator 相关的任何内容。
游戏界面
该板还可以显示数字。
UI\PictureBoxBoard.cs
这是一个继承自PictureBox
控件的类。
它负责显示黑白棋盘。此类本身由名为 的控件组成pictureBoxTable
。控件将是从方法中呈现表格
的控件。 该方法从该类中读取棋盘信息, 绘制表格和磁盘。pictureBoxTable
pictureBoxTable_Paint()
pictureBoxTable_Paint()
这就是画板的逻辑。
-
有一个由 64 个矩形组成的数组。我们创建这 64 个矩形来存储单元格在板上的位置。
-
此方法将调用:
DrawBoard()
绘制板表DrawIntersection()
顾名思义DrawDisk()
只是绘制磁盘DrawRedDot()
显示哪个是最后的放置位置DrawDiskBorder()
显示可以放置哪个单元格DrawNumber()
- 此方法用于当我们需要显示单元格的分数时。
此方法中的所有绘图仅使用基本的 GDI+ 方法,例如FillEllipse()
,DrawEllipse()
,DrawRectangles()
没有用于电路板绘图的图像。这些是绘制符号的逻辑:
- 只需从 0 循环到 7。
- 使用
DrawString()
方法绘制string
此控件具有PictureBoxBoard_Paint()
呈现符号的方法。
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
这个类只包含控件颜色信息。
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
对象的例子。
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
具有三个阶段:Beginning
、Middle
和EndGame
。这个值将被 AI 使用,它将确定它需要哪一组分数值来评估棋盘分数。NumberofLastFlipCell
每次我们翻转单元格时,我们都会保留被翻转的单元格的数量CurrentTurn
当前回合generateMoves()
生成可用的移动PutValue()
只需输入单元格值IsLegalMove()
检查位置是否有效SwitchTurn()
只需切换转弯IsTherePlaceToPut()
false
-如果位置已被占用;否则,它将返回true
。
这些是Board
构造函数。
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 接口,所以它必须有这些方法:
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 来更新自己直接。