Tic Tac Toe Game In C# For Practice
Introduction
I initiated this project alongside my studies on Pluralsight courses C# - The Big Picture by Mike Woodring and C# - Fundamentals by Gill Cleeren as part of my journey towards ASP.NET Core. My prior experiences had left me with reservations about C# due to its earlier days of limited compatibility and exclusivity. However, after dedicating a week to working with .NET and C#, I was pleasantly surprised by its capabilities and found my perceptions transformed.
Coming from a C++ background, I found C# to be a worthy successor with its intuitive syntax and significant quality-of-life features, including garbage collection. The transition was smooth, and the language's modern features enhanced my productivity. Additionally, using Visual Studio for the first time was a revelation; it offered an ease of use that stood in stark contrast to my experiences with Eclipse for Java development.
I chose to develop a Tic-Tac-Toe program as my initial foray into C# because of its inherent simplicity and potential for demonstrating fundamental and advanced programming concepts. Tic-Tac-Toe, while a straightforward game, offered a perfect canvas for me to explore and implement key features in C#, including object-oriented programming principles and unit testing.
The project allowed me to stretch my legs within the C# ecosystem, enabling me to create a structured, modular codebase that adheres to best practices. I leveraged classes and objects to encapsulate game logic and state, ensuring maintainable and scalable code. Additionally, I implemented comprehensive unit tests to validate the correctness of the game logic, which honed my skills in writing robust, testable code.
By working on this project, I gained hands-on experience with C#'s syntax and libraries, further solidifying my understanding and appreciation of the language's capabilities. This endeavor was instrumental in building a solid foundation for my future projects in C# and ASP.NET Core.
Features
IsWinner Method
The IsWinner
method is a crucial part of my Tic-Tac-Toe program located in the Board class. This function determines whether a player, represented by the character p
, has won the game by forming a straight line of their symbols on the game board. The method systematically checks for winning conditions across horizontal, vertical, and diagonal lines on a 3x3 grid. Below is a detailed explanation of how each part of the method works.
Code Implementation
public bool IsWinner(char p)
{
// Check For Horizontal Wins
if ((this.GetPosition(0, 0) == p && this.GetPosition(1, 0) == p && this.GetPosition(2, 0) == p) ||
(this.GetPosition(0, 1) == p && this.GetPosition(1, 1) == p && this.GetPosition(2, 1) == p) ||
(this.GetPosition(0, 2) == p && this.GetPosition(1, 2) == p && this.GetPosition(2, 2) == p))
{
return true;
}
// Check For Vertical Wins
else if ((this.GetPosition(0, 0) == p && this.GetPosition(0, 1) == p && this.GetPosition(0, 2) == p) ||
(this.GetPosition(1, 0) == p && this.GetPosition(1, 1) == p && this.GetPosition(1, 2) == p) ||
(this.GetPosition(2, 0) == p && this.GetPosition(2, 1) == p && this.GetPosition(2, 2) == p))
{
return true;
}
// Check For Diagonal Wins
else if ((this.GetPosition(0, 0) == p && this.GetPosition(1, 1) == p && this.GetPosition(2, 2) == p) ||
(this.GetPosition(0, 2) == p && this.GetPosition(1, 1) == p && this.GetPosition(2, 0) == p))
{
return true;
}
return false;
}
Detailed Explanation
Horizontal Wins
The first part of the method checks for horizontal wins. It evaluates if the character p
occupies all three positions in any of the three rows. This is accomplished by checking:
- The first row: positions (0,0), (1,0), and (2,0)
- The second row: positions (0,1), (1,1), and (2,1)
- The third row: positions (0,2), (1,2), and (2,2)
If any of these conditions are met, the method returns true
, indicating a horizontal win.
Vertical Wins
Next, the method checks for vertical wins by verifying if the character p
occupies all three positions in any of the three columns:
- The first column: positions (0,0), (0,1), and (0,2)
- The second column: positions (1,0), (1,1), and (1,2)
- The third column: positions (2,0), (2,1), and (2,2)
If any of these vertical checks are satisfied, the method returns true
.
Diagonal Wins
The final part of the method checks for diagonal wins. There are two possible diagonal winning conditions:
- The main diagonal: positions (0,0), (1,1), and (2,2)
- The anti-diagonal: positions (0,2), (1,1), and (2,0)
If the character p
occupies all three positions in either diagonal, the method returns true
.
Method Conclusion
If none of the horizontal, vertical, or diagonal conditions are met, the method concludes that the player has not won the game and returns false
.
Usage
The IsWinner
method is integral to determining the outcome of the Tic-Tac-Toe game. It is called after each player's move to check if they have achieved a winning line, thereby facilitating the progression and resolution of the game.
IsBoardFull Method
The IsBoardFull
method is an essential function within the Board
class of my Tic-Tac-Toe program. This method checks whether the game board is completely filled, meaning all positions on the 3x3 grid have been occupied by either player's symbol. Determining whether the board is full is crucial for identifying if the game has reached a draw. Below is a detailed explanation of how this method works.
Code Implementation
public bool IsBoardFull()
{
for (int i = 0; i < 3; i++)
{
for (int j = 0; j < 3; j++)
{
if (board[i, j] == '\0')
{
return false;
}
}
}
return true;
}
Detailed Explanation
Looping Through the Board
The IsBoardFull
method uses nested for
loops to iterate over each position on the 3x3 game board. The outer loop iterates over the rows, and the inner loop iterates over the columns. This approach ensures that every cell in the grid is examined systematically.
- The outer loop runs three times (
i = 0
to2
), covering each row. - The inner loop also runs three times (
j = 0
to2
), covering each column within the current row.
Checking for Empty Positions
During each iteration, the method checks if the current position (i, j)
on the board contains an empty value, represented by '\0'
. The '\0'
character indicates that a cell is unoccupied. If an empty cell is found, the method immediately returns false
, signifying that the board is not yet full and the game can continue.
if (board[i, j] == '\0')
{
return false;
}
Determining a Full Board
If the loops complete without encountering an empty cell, it means that every position on the board is occupied by either player's symbol. In this case, the method returns true
, indicating that the board is full.
return true;
Method Conclusion
The IsBoardFull
method plays a pivotal role in the game logic by determining if the game board is completely occupied. It helps in identifying draw scenarios where no more moves can be made, and no player has won. This method ensures that the game flow is appropriately managed by detecting when a stalemate has occurred.
Usage
The IsBoardFull
method is typically called after each player's move to assess whether the game should continue or if it has ended in a draw. It complements the IsWinner
method by providing a mechanism to detect draw conditions, thus allowing the game to conclude properly when no winning moves are available.
IsDraw Method
The IsDraw
method is a vital component of the Board
class in my Tic-Tac-Toe program. This method determines if the game has resulted in a draw, meaning the board is full, and neither player has won. It relies on the IsBoardFull
and IsWinner
methods to make this determination. Below is a detailed breakdown of how this method functions and contributes to the game's logic.
Code Implementation
public bool IsDraw()
{
if (IsBoardFull() && !IsWinner('X') && !IsWinner('O'))
{
return true;
}
return false;
}
Detailed Explanation
Checking for a Full Board
The IsDraw
method first calls the IsBoardFull
method to check if all cells on the 3x3 grid are occupied. The IsBoardFull
method returns true
if there are no empty positions ('\0'
) left on the board. This is the first condition that must be satisfied for a draw.
if (IsBoardFull() ...
Verifying No Winners
Once it is confirmed that the board is full, the IsDraw
method then checks if there is no winner. It does this by calling the IsWinner
method twice:
IsWinner('X')
: Checks if player 'X' has formed a winning line.IsWinner('O')
: Checks if player 'O' has formed a winning line.
The method evaluates these conditions using logical NOT operators (!
). If neither IsWinner('X')
nor IsWinner('O')
returns true
, it means that no player has won the game.
... && !IsWinner('X') && !IsWinner('O'))
Returning the Result
If both conditions are met—i.e., the board is full, and neither player 'X' nor player 'O' has won—the method returns true
, indicating a draw.
{
return true;
}
If either condition is not met, the method returns false
, signifying that the game is not a draw, and it can continue.
return false;
Method Conclusion
The IsDraw
method is a critical part of the game's logic, ensuring that the game properly recognizes when a stalemate has occurred. It checks for the fulfillment of conditions necessary for a draw, thereby allowing the game to end appropriately when no further moves can alter the game's outcome.
Usage
The IsDraw
method is typically invoked after each move to determine if the game has reached a draw state. It complements the IsWinner
method by providing a mechanism to detect draw scenarios, ensuring that the game logic accounts for all possible outcomes. This method helps to manage the game flow effectively, ensuring a proper conclusion to each game session.
Code
Program.cs
using TicTacToe;
Game game = new Game();
game.PlayGame();
Game.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace TicTacToe
{
public class Game
{
private Status game_status;
private int current_player = 0;
private Board game_board;
private Player player_1;
private Player player_2;
public enum Status {Win, Draw, InProgress}
public Game()
{
this.game_status = Status.InProgress;
this.current_player = 1;
this.game_board = new Board();
this.player_1 = new Player();
this.player_2 = new Player();
}
public Status GetStatus() => this.game_status;
public void SetStatus(Status status) => this.game_status = status;
public int GetCurrentPlayer() => this.current_player;
public void SetCurrentPlayer(int player) => this.current_player = player;
public void PlayGame()
{
// Print The Title Screen
this.PrintTitleScreen();
// Get Player 1 Username
Console.WriteLine("Player 1: Please Select a Username: ");
string player_1_username = Console.ReadLine();
Console.WriteLine("\n" + player_1_username + ", Please Choose Your Symbol (X or O): ");
string player_1_symbol = Console.ReadLine().ToUpper();
// Get Player 2 Username
Console.WriteLine("\nPlayer 2: Please Select a Username: ");
string player_2_username = Console.ReadLine();
// Set Player 1 symbol
if (player_1_symbol == "X")
{
player_1.SetSymbol('X');
}
else if (player_1_symbol == "O")
{
player_1.SetSymbol('O');
} else
{
player_1.SetSymbol('X');
}
// Give Player 2 The Opposite Symbol
if (player_1.GetSymbol() == 'X')
{
player_2.SetSymbol('O');
}
else
{
player_2.SetSymbol('X');
}
// Set Player Selections in Their Objects
if (player_1_username == "")
{
player_1.SetUsername("Player 1");
} else
{
player_1.SetUsername(player_1_username);
}
if (player_2_username == "")
{
player_2.SetUsername("Player 2");
}
else
{
player_2.SetUsername(player_2_username);
}
// Review Selections
Console.WriteLine("\nPlayer 1: " + player_1.GetUsername() + " - Symbol: " + player_1.GetSymbol());
Console.WriteLine("Player 2: " + player_2.GetUsername() + " - Symbol: " + player_2.GetSymbol());
// Play Game Till a Winner or Draw
while (this.game_status == Status.InProgress)
{
if (this.current_player == 1)
{
Console.WriteLine("\n" + player_1.GetUsername() + " it is your turn...\n");
} else
{
Console.WriteLine("\n" + player_2.GetUsername() + " it is your turn...\n");
}
game_board.PrintBoard();
Console.WriteLine("Select X Coordinate: \n");
string x_axis = Console.ReadLine();
Console.WriteLine("\nSelect Y Coordinate: \n");
string y_axis = Console.ReadLine();
int x = int.Parse(x_axis);
int y = int.Parse(y_axis);
if (this.current_player == 1)
{
game_board.MakeMove(player_1.GetSymbol(), x, y);
} else
{
game_board.MakeMove(player_2.GetSymbol(), x, y);
}
this.SwitchPlayer();
this.UpdateCurrentGameStatus();
}
this.PrintEndGame();
}
public void UpdateCurrentGameStatus()
{
if (game_board.IsDraw())
{
this.game_status = Status.Draw;
}
else if (game_board.IsWinner(player_1.GetSymbol()) || game_board.IsWinner(player_2.GetSymbol()))
{
this.game_status = Status.Win;
}
else
{
this.game_status = Status.InProgress;
}
}
public void SwitchPlayer()
{
if (this.current_player == 1)
{
this.current_player = 2;
}
else
{
this.current_player = 1;
}
}
public void PrintTitleScreen()
{
Console.WriteLine("--------------------------[ TicTacToe ]--------------------------\nProgrammer: Isaac Replogle\nGithub Project: https://github.com/GristleIDR/TicTacToe-CSharp\n\nWelcome...\n\n");
}
public void PrintEndGame()
{
Console.WriteLine("\n--------------------------[ TicTacToe ]--------------------------");
if (this.game_status == Status.Win)
{
if (this.current_player == 1)
{
Console.WriteLine("Congratulations " + player_2.GetUsername() + " You Have Won!\n");
}
else
{
Console.WriteLine("Congratulations " + player_1.GetUsername() + " You Have Won!\n");
}
}
else if (this.game_status == Status.Draw)
{
Console.WriteLine("The Game is a Draw!\n");
}
game_board.PrintBoard();
}
}
}
Board.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace TicTacToe
{
public class Board
{
private char[,] board = { { '\0', '\0', '\0' }, { '\0', '\0', '\0' }, { '\0', '\0', '\0' }, };
public void MakeMove(char p, int x, int y)
{
this.board[x, y] = p;
}
public char GetPosition(int x, int y) => board[x, y];
public void PrintBoard()
{
// Helper method to format the cell content
string FormatCell(char cell)
{
return cell == '\0' ? " " : cell.ToString();
}
// Display the board with formatting adjustments
Console.WriteLine(" Y\n" +
"2 | " + FormatCell(this.board[0, 2]) + " | " + FormatCell(this.board[1, 2]) + " | " + FormatCell(this.board[2, 2]) +
"\n | -----------\n" +
"1 | " + FormatCell(this.board[0, 1]) + " | " + FormatCell(this.board[1, 1]) + " | " + FormatCell(this.board[2, 1]) +
"\n | -----------\n" +
"0 | " + FormatCell(this.board[0, 0]) + " | " + FormatCell(this.board[1, 0]) + " | " + FormatCell(this.board[2, 0]) +
"\n -------------- X\n" +
" 0 1 2");
}
public void ClearBoard()
{
for (int i = 0; i < 3; i++)
{
for (int j = 0; j < 3; j++)
{
board[i, j] = '\0';
}
}
}
public bool IsBoardFull()
{
for (int i = 0; i < 3; i++)
{
for (int j = 0; j < 3; j++)
{
if (board[i, j] == '\0')
{
return false;
}
}
}
return true;
}
public bool IsWinner(char p)
{
// Check For Horizontal Wins
if ((this.GetPosition(0, 0) == p && this.GetPosition(1, 0) == p && this.GetPosition(2, 0) == p) ||
(this.GetPosition(0, 1) == p && this.GetPosition(1, 1) == p && this.GetPosition(2, 1) == p) ||
(this.GetPosition(0, 2) == p && this.GetPosition(1, 2) == p && this.GetPosition(2, 2) == p))
{
return true;
}
// Check For Vertical Wins
else if ((this.GetPosition(0, 0) == p && this.GetPosition(0, 1) == p && this.GetPosition(0, 2) == p) ||
(this.GetPosition(1, 0) == p && this.GetPosition(1, 1) == p && this.GetPosition(1, 2) == p) ||
(this.GetPosition(2, 0) == p && this.GetPosition(2, 1) == p && this.GetPosition(2, 2) == p))
{
return true;
}
// Check For Diagonal Wins
else if ((this.GetPosition(0, 0) == p && this.GetPosition(1, 1) == p && this.GetPosition(2, 2) == p) ||
(this.GetPosition(0, 2) == p && this.GetPosition(1, 1) == p && this.GetPosition(2, 0) == p))
{
return true;
}
return false;
}
public bool IsDraw()
{
if (IsBoardFull() && !IsWinner('X') && !IsWinner('O'))
{
return true;
}
return false;
}
}
}
Player.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Cryptography.X509Certificates;
using System.Text;
using System.Threading.Tasks;
namespace TicTacToe
{
public class Player
{
private string username;
private char symbol;
private int wins;
private int losses;
public Player(string username, char symbol)
{
this.username = username;
this.symbol = symbol;
this.wins = 0;
this.losses = 0;
}
public Player()
{
this.username = "";
this.symbol = '\0';
this.wins = 0;
this.losses = 0;
}
public void SetUsername(string username)
{
this.username = username;
}
public string GetUsername() => this.username;
public void SetSymbol(char symbol)
{
this.symbol = symbol;
}
public char GetSymbol() => this.symbol;
public void IncrementWins()
{
this.wins++;
}
public int GetWins() => this.wins;
public void IncrementLosses()
{
this.losses++;
}
public int GetLosses() => this.losses;
}
}
Tests
GameTest.cs
namespace TicTacToe.Tests
{
public class GameTest
{
[Fact]
public void ConsuctorTest()
{
// Create Test Object
Game game = new Game();
// Assert That Game Status is InProgress and The Current Player is 1
Assert.Equal(Game.Status.InProgress, game.GetStatus());
Assert.Equal(1, game.GetCurrentPlayer());
}
[Fact]
public void SetStatusTest()
{
// Create Test Object
Game game = new Game();
// Set Status to Win
game.SetStatus(Game.Status.Win);
// Assert That Game Status is Win
Assert.Equal(Game.Status.Win, game.GetStatus());
// Set Status to Draw
game.SetStatus(Game.Status.Draw);
// Assert That Game Status is Draw
Assert.Equal(Game.Status.Draw, game.GetStatus());
// Set Status to InProgress
game.SetStatus(Game.Status.InProgress);
// Assert That Game Status is InProgress
Assert.Equal(Game.Status.InProgress, game.GetStatus());
}
[Fact]
public void SetCurrentPlayerTest()
{
// Create Test Object
Game game = new Game();
// Set Current Player to 2
game.SetCurrentPlayer(2);
// Assert That Current Player is 2
Assert.Equal(2, game.GetCurrentPlayer());
// Set Current Player to 1
game.SetCurrentPlayer(1);
// Assert That Current Player is 1
Assert.Equal(1, game.GetCurrentPlayer());
}
[Fact]
public void SwitchPlayerTest()
{
// Create a Test Object
Game game = new Game();
// Assert That Current Player is 1
Assert.Equal(1, game.GetCurrentPlayer());
game.SwitchPlayer();
// Assert That The Current Player is 2
Assert.Equal(2, game.GetCurrentPlayer());
game.SwitchPlayer();
// Assert That The Current Player is Back to 1
Assert.Equal(1, game.GetCurrentPlayer());
}
}
}
BoardTest.cs
namespace TicTacToe.Tests
{
public class BoardTest
{
[Fact]
public void MakeMoveTest()
{
// Create Test Object
Board board = new Board();
// Assert That The Board Is Blank
Assert.Equal('\0', board.GetPosition(0, 0));
Assert.Equal('\0', board.GetPosition(0, 1));
Assert.Equal('\0', board.GetPosition(0, 2));
Assert.Equal('\0', board.GetPosition(1, 0));
Assert.Equal('\0', board.GetPosition(1, 1));
Assert.Equal('\0', board.GetPosition(1, 2));
Assert.Equal('\0', board.GetPosition(2, 0));
Assert.Equal('\0', board.GetPosition(2, 1));
Assert.Equal('\0', board.GetPosition(2, 2));
// Make a Move
board.MakeMove('X', 0, 0);
// Verify That The Move Was Made
Assert.Equal('X', board.GetPosition(0, 0));
Assert.Equal('\0', board.GetPosition(0, 1));
Assert.Equal('\0', board.GetPosition(0, 2));
Assert.Equal('\0', board.GetPosition(1, 0));
Assert.Equal('\0', board.GetPosition(1, 1));
Assert.Equal('\0', board.GetPosition(1, 2));
Assert.Equal('\0', board.GetPosition(2, 0));
Assert.Equal('\0', board.GetPosition(2, 1));
Assert.Equal('\0', board.GetPosition(2, 2));
}
[Fact]
public void ClearBoardTest()
{
// Create Test Board
Board board = new Board();
/* Test Fill The Board
* X | X | X
* -----------
* O | X | O
* -----------
* X | O | O
*/
board.MakeMove('X', 0, 0);
board.MakeMove('O', 0, 1);
board.MakeMove('X', 0, 2);
board.MakeMove('O', 1, 0);
board.MakeMove('X', 1, 1);
board.MakeMove('X', 1, 2);
board.MakeMove('O', 2, 0);
board.MakeMove('O', 2, 1);
board.MakeMove('X', 2, 2);
// Assert That The Board Was Filled
Assert.Equal('X', board.GetPosition(0, 0));
Assert.Equal('O', board.GetPosition(0, 1));
Assert.Equal('X', board.GetPosition(0, 2));
Assert.Equal('O', board.GetPosition(1, 0));
Assert.Equal('X', board.GetPosition(1, 1));
Assert.Equal('X', board.GetPosition(1, 2));
Assert.Equal('O', board.GetPosition(2, 0));
Assert.Equal('O', board.GetPosition(2, 1));
Assert.Equal('X', board.GetPosition(2, 2));
// Clear The Board
/* Expected After Clearing The Board
* | |
* -----------
* | |
* -----------
* | |
*/
board.ClearBoard();
// Verify That The Board Cleared
Assert.Equal('\0', board.GetPosition(0, 0));
Assert.Equal('\0', board.GetPosition(0, 1));
Assert.Equal('\0', board.GetPosition(0, 2));
Assert.Equal('\0', board.GetPosition(1, 0));
Assert.Equal('\0', board.GetPosition(1, 1));
Assert.Equal('\0', board.GetPosition(1, 2));
Assert.Equal('\0', board.GetPosition(2, 0));
Assert.Equal('\0', board.GetPosition(2, 1));
Assert.Equal('\0', board.GetPosition(2, 2));
}
[Fact]
public void IsBoardFullTest()
{
// Create a Test Object
Board board = new Board();
// Verify That The Board Should Not Be Full
Assert.False(board.IsBoardFull());
/* Test Fill The Board
* X | X | X
* -----------
* O | X | O
* -----------
* X | O | O
*/
board.MakeMove('X', 0, 0);
board.MakeMove('O', 0, 1);
board.MakeMove('X', 0, 2);
board.MakeMove('O', 1, 0);
board.MakeMove('X', 1, 1);
board.MakeMove('X', 1, 2);
board.MakeMove('O', 2, 0);
board.MakeMove('O', 2, 1);
board.MakeMove('X', 2, 2);
// Assert That The Board Should Be Full
Assert.True(board.IsBoardFull());
// Clear The Board
board.ClearBoard();
// Assert That The Board Is Not Full
Assert.False(board.IsBoardFull());
/* Partially Fill The Board
* X | X | X
* -----------
* | X | O
* -----------
* X | O | O
*/
board.MakeMove('X', 0, 0);
board.MakeMove('\0', 0, 1);
board.MakeMove('X', 0, 2);
board.MakeMove('O', 1, 0);
board.MakeMove('X', 1, 1);
board.MakeMove('X', 1, 2);
board.MakeMove('O', 2, 0);
board.MakeMove('O', 2, 1);
board.MakeMove('X', 2, 2);
// Assert That The Board Is Not Full
Assert.False(board.IsBoardFull());
}
[Fact]
public void IsWinnerTest()
{
// Create a Test Object
Board board = new Board();
// Test All Possible Winning Combinations
/* ------------------------------------
* Combination 1: Vertical Left Column X
* -------------------------------------
* X | | O
* -----------
* X | O |
* -----------
* X | | O
*/
board.MakeMove('X', 0, 0);
board.MakeMove('X', 0, 1);
board.MakeMove('X', 0, 2);
board.MakeMove('\0', 1, 0);
board.MakeMove('O', 1, 1);
board.MakeMove('\0', 1, 2);
board.MakeMove('O', 2, 0);
board.MakeMove('\0', 2, 1);
board.MakeMove('O', 2, 2);
// Expect That X Is The Winner
Assert.True(board.IsWinner('X'));
// Expect That O Is Not The Winner
Assert.False(board.IsWinner('O'));
// Clear The Board
board.ClearBoard();
/* ------------------------------------
* Combination 2: Vertical Left Column O
* -------------------------------------
* O | | X
* -----------
* O | X |
* -----------
* O | | X
*/
board.MakeMove('O', 0, 0);
board.MakeMove('O', 0, 1);
board.MakeMove('O', 0, 2);
board.MakeMove('\0', 1, 0);
board.MakeMove('X', 1, 1);
board.MakeMove('\0', 1, 2);
board.MakeMove('X', 2, 0);
board.MakeMove('\0', 2, 1);
board.MakeMove('X', 2, 2);
// Expect That O Is The Winner
Assert.True(board.IsWinner('O'));
// Expect That X Is Not The Winner
Assert.False(board.IsWinner('X'));
// Clear The Board
board.ClearBoard();
/*---------------------------------------
* Combination 3: Vertical Middle Column X
* ---------------------------------------
* | X | O
* -----------
* O | X |
* -----------
* | X | O
*/
board.MakeMove('\0', 0, 0);
board.MakeMove('O', 0, 1);
board.MakeMove('\0', 0, 2);
board.MakeMove('X', 1, 0);
board.MakeMove('X', 1, 1);
board.MakeMove('X', 1, 2);
board.MakeMove('O', 2, 0);
board.MakeMove('\0', 2, 1);
board.MakeMove('O', 2, 2);
// Expect That X Is The Winner
Assert.True(board.IsWinner('X'));
// Expect That O Is Not The Winner
Assert.False(board.IsWinner('O'));
// Clear The Board
board.ClearBoard();
/*---------------------------------------
* Combination 4: Vertical Middle Column O
* ---------------------------------------
* | O | X
* -----------
* X | O |
* -----------
* | O | X
*/
board.MakeMove('\0', 0, 0);
board.MakeMove('X', 0, 1);
board.MakeMove('\0', 0, 2);
board.MakeMove('O', 1, 0);
board.MakeMove('O', 1, 1);
board.MakeMove('O', 1, 2);
board.MakeMove('X', 2, 0);
board.MakeMove('\0', 2, 1);
board.MakeMove('X', 2, 2);
// Expect That O Is The Winner
Assert.True(board.IsWinner('O'));
// Expect That X Is Not The Winner
Assert.False(board.IsWinner('X'));
// Clear The Board
board.ClearBoard();
/* -------------------------------------
* Combination 5: Vertical Right Column X
* --------------------------------------
* | O | X
* -----------
* O | | X
* -----------
* | O | X
*/
board.MakeMove('\0', 0, 0);
board.MakeMove('O', 0, 1);
board.MakeMove('\0', 0, 2);
board.MakeMove('O', 1, 0);
board.MakeMove('\0', 1, 1);
board.MakeMove('O', 1, 2);
board.MakeMove('X', 2, 0);
board.MakeMove('X', 2, 1);
board.MakeMove('X', 2, 2);
// Expect That X Is The Winner
Assert.True(board.IsWinner('X'));
// Expect That O Is Not The Winner
Assert.False(board.IsWinner('O'));
// Clear The Board
board.ClearBoard();
/* -------------------------------------
* Combination 6: Vertical Right Column O
* --------------------------------------
* | X | O
* -----------
* X | | O
* -----------
* | X | O
*/
board.MakeMove('\0', 0, 0);
board.MakeMove('X', 0, 1);
board.MakeMove('\0', 0, 2);
board.MakeMove('X', 1, 0);
board.MakeMove('\0', 1, 1);
board.MakeMove('X', 1, 2);
board.MakeMove('O', 2, 0);
board.MakeMove('O', 2, 1);
board.MakeMove('O', 2, 2);
// Expect That O Is The Winner
Assert.True(board.IsWinner('O'));
// Expect That X Is Not The Winner
Assert.False(board.IsWinner('X'));
// Clear The Board
board.ClearBoard();
/* -------------------------------------
* Combination 7: Horizontal Bottom Row X
* --------------------------------------
* O | X | O
* -----------
* O | O | X
* -----------
* X | X | X
*/
board.MakeMove('X', 0, 0);
board.MakeMove('O', 0, 1);
board.MakeMove('O', 0, 2);
board.MakeMove('X', 1, 0);
board.MakeMove('O', 1, 1);
board.MakeMove('X', 1, 2);
board.MakeMove('X', 2, 0);
board.MakeMove('X', 2, 1);
board.MakeMove('O', 2, 2);
// Expect That X Is The Winner
Assert.True(board.IsWinner('X'));
// Expect That O Is Not The Winner
Assert.False(board.IsWinner('O'));
// Clear The Board
board.ClearBoard();
/* -------------------------------------
* Combination 8: Horizontal Bottom Row O
* --------------------------------------
* X | O | X
* -----------
* X | X | O
* -----------
* O | O | O
*/
board.MakeMove('O', 0, 0);
board.MakeMove('X', 0, 1);
board.MakeMove('X', 0, 2);
board.MakeMove('O', 1, 0);
board.MakeMove('X', 1, 1);
board.MakeMove('O', 1, 2);
board.MakeMove('O', 2, 0);
board.MakeMove('O', 2, 1);
board.MakeMove('X', 2, 2);
// Expect That O Is The Winner
Assert.True(board.IsWinner('O'));
// Expect That X Is Not The Winner
Assert.False(board.IsWinner('X'));
// Clear The Board
board.ClearBoard();
/* -------------------------------------
* Combination 9: Horizontal Middle Row X
* --------------------------------------
* O | O | X
* -----------
* X | X | X
* -----------
* O | X | O
*/
board.MakeMove('O', 0, 0);
board.MakeMove('X', 0, 1);
board.MakeMove('O', 0, 2);
board.MakeMove('X', 1, 0);
board.MakeMove('X', 1, 1);
board.MakeMove('O', 1, 2);
board.MakeMove('O', 2, 0);
board.MakeMove('X', 2, 1);
board.MakeMove('X', 2, 2);
// Expect That X Is The Winner
Assert.True(board.IsWinner('X'));
// Expect That O Is Not The Winner
Assert.False(board.IsWinner('O'));
// Clear The Board
board.ClearBoard();
/* --------------------------------------
* Combination 10: Horizontal Middle Row O
* ---------------------------------------
* X | X | O
* -----------
* O | O | O
* -----------
* X | O | X
*/
board.MakeMove('X', 0, 0);
board.MakeMove('O', 0, 1);
board.MakeMove('X', 0, 2);
board.MakeMove('O', 1, 0);
board.MakeMove('O', 1, 1);
board.MakeMove('X', 1, 2);
board.MakeMove('X', 2, 0);
board.MakeMove('O', 2, 1);
board.MakeMove('O', 2, 2);
// Expect That O Is The Winner
Assert.True(board.IsWinner('O'));
// Expect That X Is Not The Winner
Assert.False(board.IsWinner('X'));
// Clear The Board
board.ClearBoard();
/* -----------------------------------
* Combination 11: Horizontal Top Row X
* ------------------------------------
* X | X | X
* -----------
* O | O | X
* -----------
* O | X | O
*/
board.MakeMove('O', 0, 0);
board.MakeMove('O', 0, 1);
board.MakeMove('X', 0, 2);
board.MakeMove('X', 1, 0);
board.MakeMove('O', 1, 1);
board.MakeMove('X', 1, 2);
board.MakeMove('O', 2, 0);
board.MakeMove('X', 2, 1);
board.MakeMove('X', 2, 2);
// Expect That X Is The Winner
Assert.True(board.IsWinner('X'));
// Expect That O Is Not The Winner
Assert.False(board.IsWinner('O'));
// Clear The Board
board.ClearBoard();
/* -----------------------------------
* Combination 12: Horizontal Top Row O
* ------------------------------------
* O | O | O
* -----------
* X | X | O
* -----------
* X | O | X
*/
board.MakeMove('X', 0, 0);
board.MakeMove('X', 0, 1);
board.MakeMove('O', 0, 2);
board.MakeMove('O', 1, 0);
board.MakeMove('X', 1, 1);
board.MakeMove('O', 1, 2);
board.MakeMove('X', 2, 0);
board.MakeMove('O', 2, 1);
board.MakeMove('O', 2, 2);
// Expect That O Is The Winner
Assert.True(board.IsWinner('O'));
// Expect That X Is Not The Winner
Assert.False(board.IsWinner('X'));
// Clear The Board
board.ClearBoard();
/* -----------------------------------
* Combination 13: Diagonal Top Right X
* ------------------------------------
* O | O | X
* -----------
* X | X | O
* -----------
* X | O | X
*/
board.MakeMove('X', 0, 0);
board.MakeMove('X', 0, 1);
board.MakeMove('O', 0, 2);
board.MakeMove('O', 1, 0);
board.MakeMove('X', 1, 1);
board.MakeMove('O', 1, 2);
board.MakeMove('X', 2, 0);
board.MakeMove('O', 2, 1);
board.MakeMove('X', 2, 2);
// Expect That X Is The Winner
Assert.True(board.IsWinner('X'));
// Expect That O Is Not The Winner
Assert.False(board.IsWinner('O'));
// Clear The Board
board.ClearBoard();
/* -----------------------------------
* Combination 14: Diagonal Top Right O
* ------------------------------------
* X | X | O
* -----------
* O | O | X
* -----------
* O | X | O
*/
board.MakeMove('O', 0, 0);
board.MakeMove('O', 0, 1);
board.MakeMove('X', 0, 2);
board.MakeMove('X', 1, 0);
board.MakeMove('O', 1, 1);
board.MakeMove('X', 1, 2);
board.MakeMove('O', 2, 0);
board.MakeMove('X', 2, 1);
board.MakeMove('O', 2, 2);
// Expect That O Is The Winner
Assert.True(board.IsWinner('O'));
// Expect That X Is Not The Winner
Assert.False(board.IsWinner('X'));
// Clear The Board
board.ClearBoard();
/* ----------------------------------
* Combination 15: Diagonal Top Left X
* -----------------------------------
* X | O | X
* -----------
* O | X | O
* -----------
* O | O | X
*/
board.MakeMove('O', 0, 0);
board.MakeMove('O', 0, 1);
board.MakeMove('X', 0, 2);
board.MakeMove('O', 1, 0);
board.MakeMove('X', 1, 1);
board.MakeMove('O', 1, 2);
board.MakeMove('X', 2, 0);
board.MakeMove('O', 2, 1);
board.MakeMove('X', 2, 2);
// Expect That X Is The Winner
Assert.True(board.IsWinner('X'));
// Expect That O Is Not The Winner
Assert.False(board.IsWinner('O'));
// Clear The Board
board.ClearBoard();
/* ----------------------------------
* Combination 16: Diagonal Top Left O
* -----------------------------------
* O | X | O
* -----------
* X | O | X
* -----------
* X | X | O
*/
board.MakeMove('X', 0, 0);
board.MakeMove('X', 0, 1);
board.MakeMove('O', 0, 2);
board.MakeMove('X', 1, 0);
board.MakeMove('O', 1, 1);
board.MakeMove('X', 1, 2);
board.MakeMove('O', 2, 0);
board.MakeMove('X', 2, 1);
board.MakeMove('O', 2, 2);
// Expect That O Is The Winner
Assert.True(board.IsWinner('O'));
// Expect That X Is Not The Winner
Assert.False(board.IsWinner('X'));
// Clear The Board
board.ClearBoard();
}
[Fact]
public void IsDrawTest()
{
// Create a Test Object
Board board = new Board();
// Assert That The Game Is Not A Draw
// The Board Is Empty
/*
* | |
* -----------
* | |
* -----------
* | |
*/
Assert.False(board.IsDraw());
// Board Is Full But No Winner Variation 1
/*
* X | X | O
* -----------
* O | O | X
* -----------
* X | O | O
*/
board.MakeMove('X', 0, 0);
board.MakeMove('O', 0, 1);
board.MakeMove('X', 0, 2);
board.MakeMove('O', 1, 0);
board.MakeMove('O', 1, 1);
board.MakeMove('X', 1, 2);
board.MakeMove('O', 2, 0);
board.MakeMove('X', 2, 1);
board.MakeMove('O', 2, 2);
// Assert That The Game Is A Draw
Assert.True(board.IsDraw());
// Clear The Board
board.ClearBoard();
// Board Is Full But No Winner Variation 2
/*
* X | O | O
* -----------
* O | O | X
* -----------
* X | X | O
*/
board.MakeMove('X', 0, 0);
board.MakeMove('O', 0, 1);
board.MakeMove('X', 0, 2);
board.MakeMove('X', 1, 0);
board.MakeMove('O', 1, 1);
board.MakeMove('O', 1, 2);
board.MakeMove('O', 2, 0);
board.MakeMove('X', 2, 1);
board.MakeMove('O', 2, 2);
// Assert That The Game Is A Draw
Assert.True(board.IsDraw());
// Clear The Board
board.ClearBoard();
// Board Is Full But No Winner Variation 3
/*
* O | X | O
* -----------
* X | X | O
* -----------
* X | O | X
*/
board.MakeMove('X', 0, 0);
board.MakeMove('X', 0, 1);
board.MakeMove('O', 0, 2);
board.MakeMove('O', 1, 0);
board.MakeMove('X', 1, 1);
board.MakeMove('X', 1, 2);
board.MakeMove('X', 2, 0);
board.MakeMove('O', 2, 1);
board.MakeMove('O', 2, 2);
// Assert That The Game Is A Draw
Assert.True(board.IsDraw());
// Clear The Board
board.ClearBoard();
// Board Is Full But There Is a Winner
/*
* X | X | X
* -----------
* O | O | X
* -----------
* X | O | O
*/
board.MakeMove('X', 0, 0);
board.MakeMove('O', 0, 1);
board.MakeMove('X', 0, 2);
board.MakeMove('O', 1, 0);
board.MakeMove('O', 1, 1);
board.MakeMove('X', 1, 2);
board.MakeMove('O', 2, 0);
board.MakeMove('X', 2, 1);
board.MakeMove('X', 2, 2);
// Assert That The Game Is Not A Draw
Assert.False(board.IsDraw());
// Clear The Board
board.ClearBoard();
}
}
}
PlayerTest.cs
namespace TicTacToe.Tests
{
public class PlayerTest
{
[Fact]
public void ConstructorTest()
{
// Create Test Object
Player testPlayer1 = new Player("Bob", 'X');
Player testPlayer2 = new Player();
// Assertions
Assert.True(testPlayer1.GetUsername() == "Bob");
Assert.True(testPlayer1.GetSymbol() == 'X');
Assert.True(testPlayer2.GetUsername() == "");
Assert.True(testPlayer2.GetSymbol() == '\0');
}
[Fact]
public void SetUsernameTest()
{
// Create Test Object
Player testPlayer = new Player();
// Set The Username
testPlayer.SetUsername("test");
// Assertions
Assert.Equal("test", testPlayer.GetUsername());
Assert.Equal('\0', testPlayer.GetSymbol());
}
[Fact]
public void SetSymbolTest()
{
// Create Test Object
Player testPlayer = new Player();
// Set The Symbol
testPlayer.SetSymbol('O');
// Assertions
Assert.Equal("", testPlayer.GetUsername());
Assert.Equal('O', testPlayer.GetSymbol());
}
[Fact]
public void IncrementWinsTest()
{
// Create Test Object
Player player = new Player();
// Assert Default Values
Assert.Equal("", player.GetUsername());
Assert.Equal('\0', player.GetSymbol());
Assert.Equal(0, player.GetWins());
Assert.Equal(0, player.GetLosses());
// Increment Wins
player.IncrementWins();
// Verify That Wins Incremented and Didn't Change Other Values
Assert.Equal("", player.GetUsername());
Assert.Equal('\0', player.GetSymbol());
Assert.Equal(1, player.GetWins());
Assert.Equal(0, player.GetLosses());
}
[Fact]
public void IncrementLossesTest()
{
// Create Test Object
Player player = new Player();
// Assert Default Values
Assert.Equal("", player.GetUsername());
Assert.Equal('\0', player.GetSymbol());
Assert.Equal(0, player.GetWins());
Assert.Equal(0, player.GetLosses());
// Increment Losses
player.IncrementLosses();
// Verify That Wins Incremented and Didn't Change Other Values
Assert.Equal("", player.GetUsername());
Assert.Equal('\0', player.GetSymbol());
Assert.Equal(0, player.GetWins());
Assert.Equal(1, player.GetLosses());
}
}
}