井字棋是一个经典的简单游戏,通常作为学生游戏开发者的第一个项目。如果不熟悉规则,可以访问。本文旨在提供一个简单而完整的解决方案示例,包括单元测试,以帮助开发者更好地理解如何构建和测试游戏逻辑。
井字棋游戏的实现通常包括三个主要部分:游戏逻辑、单元测试和图形用户界面(GUI)。本文的项目包括三个项目:TicTacToeLib(主要项目,包含所有游戏逻辑)、TicTacToeLibTests(测试项目)和TicTacToe(Windows Forms项目,用于展示游戏,但可以轻松替换为其他类型的GUI项目,如WPF、Web等)。
游戏逻辑由两个类处理:Board和Field。Board类是Field的容器,而Field类的唯一目的是保持其状态信息。Field的状态可以是EMPTY(默认值),或者是PLAYER1/PLAYER2。每当状态改变时,FieldStatusChanged事件会被触发。
public class Field
{
private FIELD_STATUS _fieldStatus;
public event EventHandler FieldStatusChanged;
// 省略了一些代码
public FIELD_STATUS FieldStatus
{
get { return _fieldStatus; }
set
{
if (value != _fieldStatus)
{
_fieldStatus = value;
OnFieldStatusChanged();
}
}
}
}
Board类监听其Fields集合中的FieldStatusChanged事件,并检查游戏结束条件。为Board类中的每个字段创建事件处理程序。
private void AddFieldEventListeners()
{
for (int i = 0; i < _fields.GetLength(0); i++)
{
for (int j = 0; j < _fields.GetLength(1); j++)
{
_fields[i, j].FieldStatusChanged += Board_FieldStatusChanged;
}
}
}
根据游戏规则,可以定义五种游戏结束条件:
每当任何字段的状态发生变化时,Board类中的CheckWinCondition方法被调用。如果适用任何胜利条件或平局,Board会触发GameEnd事件。事件以GameStatus类作为参数发送,该类是一个简单的两个枚举集合,包含有关获胜玩家和胜利条件的信息。调用者应该适当处理它 - 在这个例子中,Windows Form会禁用所有用于显示字段的控件,显示游戏结果,并突出显示获胜的行、列或对角线。
由于Field和GameStatus类很简单,只有Board类被测试覆盖。BoardTests类中有九个测试。前三个测试检查Board类是否在构造函数参数错误时抛出异常。
[TestMethod]
[ExpectedException(typeof(ArgumentNullException))]
public void TestConstructorFieldNull()
{
Board board = new Board(null);
}
第四个测试检查Fields集合是否成功设置为类的Fields属性。
[TestMethod]
public void TestConstructorRegularCase()
{
Field[,] fields = new Field[3, 3];
// 初始化字段...
Board board = new Board(fields);
Assert.AreEqual(fields.GetLength(0), board.Fields.GetLength(0));
Assert.AreEqual(fields.GetLength(1), board.Fields.GetLength(1));
}
最后五个测试模拟并测试胜利条件。所有测试都有三个部分:首先是Board对象的构造,然后是为Board GameEnd事件创建事件处理程序。当事件被触发时,它将断言板返回了正确的胜利参数。最后,通过更改板字段状态来模拟游戏。