C#: tictactoe
Overview
Tictactoe (also known as noughts and crosses) is a two player game, in this case you versus your computer. In this version there are no levels of difficulty, and it is very tough to beat.
I originally wrote this game in Java, but I decided to convert it to C# and then to VB.Net, to share it here on MSDN. The Java code is very similar to very simple C#, so it was fairly easily converted, but it is simple C# (and VB), containing no LINQ, and as such it will, within my knowledge, work in any version of VS from 2005-13 (and probably earlier too).
To play, you just click a cell. When it is the computer's turn to play, the game grid is strategically assessed and each cell is assigned a priority value according to its strategic value in the game. After rating all of the cells, the computer plays either the highest rated cell, or in the case of jointly rated cells, a random cell chosen from the joint highest rated cells. The used cells will never be chosen in this decision making algorithm as they are always assigned a rating of -1 which will always make them the lowest rated cells.
This has been a fun program to write, and converting between languages always leads to greater understanding and programming knowledge.
The code -The Form
The form code is very simple. Just a Button_Click event is handled.
using System.Windows.Forms;
namespace tictactoe_cs
{
public partial class game : Form
{
public game()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
grid1.newGame();
}
}
}
The code - cell Class
The cell Class is again very simple, just holding 3 member variables.
using System;
using System.Drawing;
namespace tictactoe_cs
{
class cell
{
public char display = ' ';
public Color color = Color.Black;
public int priority = -1;
}
}
The code - grid Class
The grid Class inherits from Panel - it is an extended Panel. It holds some Class level variables that are used throughout the Class during game play.
cell[][] matrix = new cell[3][];
char first = 'X';
char current = 'X';
bool gameOver = false;
int moveCounter = 0;
Sub new sets the size of the Panel and initializes the matrix array.
public grid()
{
this.Size = new Size(210, 210);
for (int r = 0; r < 3; r++)
{
matrix[r] = new cell[3];
}
for (int r = 0; r < 3; r++)
{
for (int c = 0; c < 3; c++)
{
matrix[r][c] = new cell();
matrix[r][c].display = ' ';
matrix[r][c].color = Color.Black;
}
}
}
There are two overridden events handled in the extended Panel Class. These are the MouseClick event and the Paint event.
OnMouseClick
protected override void OnMouseClick(System.Windows.Forms.MouseEventArgs e)
{
if (!gameOver)
{
int r = (int)Math.Floor((double)e.Y / 70);
int c = (int)Math.Floor((double)e.X / 70);
if (matrix[r][c].display == ' ')
{
matrix[r][c].display = current;
current = '0';
this.Invalidate();
moveCounter++;
gameOver = checkLines();
if (!gameOver && moveCounter < 9)
{
playMove();
moveCounter++;
}
}
}
base.OnMouseClick(e);
}
OnPaint
protected override void OnPaint(System.Windows.Forms.PaintEventArgs e)
{
base.OnPaint(e);
e.Graphics.DrawLine(Pens.Black,70, 0, 70, 210);
e.Graphics.DrawLine(Pens.Black, 140, 0, 140, 210);
e.Graphics.DrawLine(Pens.Black, 0, 70, 210, 70);
e.Graphics.DrawLine(Pens.Black, 0, 140, 210, 140);
for (int r = 0; r < 3; r++)
{
for (int c = 0; c < 3; c++)
{
if (matrix[r][c].display != ' ')
{
e.Graphics.DrawString(matrix[r][c].display.ToString(), new Font("Times New Roman", 20, FontStyle.Bold), new SolidBrush(matrix[r][c].color), (float)((c * 70) + 25), (float)((r * 70) + 25));
}
}
}
}
newGame
The newGame procedure resets the Class level variables for a new game.
public void newGame()
{
gameOver = false;
for (int r = 0; r < 3; r++)
{
for (int c = 0; c < 3; c++)
{
matrix[r][c].display = ' ';
matrix[r][c].color = Color.Black;
}
}
first = (first == 'X') ? '0' : 'X';
current = first;
this.Invalidate();
moveCounter = 0;
if (current == '0')
{
playMove();
}
}
playMove
The playMove procedure is the AI part of this program, where the computer chooses its move.
public void playMove()
{
for (int r = 0; r < 3; r++)
{
for (int c = 0; c < 3; c++)
{
if (matrix[r][c].display == ' ')
{
matrix[r][c].priority = 0;
}
else
{
matrix[r][c].priority = -1;
}
}
}
if (matrix[1][1].display == ' ')
{
matrix[1][1].priority = 2;
}
if (matrix[0][0].display == ' ')
{
matrix[0][0].priority = 1;
}
if (matrix[0][2].display == ' ')
{
matrix[0][2].priority = 1;
}
if (matrix[2][0].display == ' ')
{
matrix[2][0].priority = 1;
}
if (matrix[2][2].display == ' ')
{
matrix[2][2].priority = 1;
}
for (int r = 0; r < 3; r++)
{
if (matrix[r][0].display != ' ' && matrix[r][0].display == matrix[r][1].display && matrix[r][2].display == ' ')
{
matrix[r][2].priority += 3;
}
if (matrix[r][0].display != ' ' && matrix[r][0].display == matrix[r][2].display && matrix[r][1].display == ' ')
{
matrix[r][1].priority += 3;
}
if (matrix[r][1].display != ' ' && matrix[r][1].display == matrix[r][2].display && matrix[r][0].display == ' ')
{
matrix[r][0].priority += 3;
}
}
for (int c = 0; c < 3; c++)
{
if (matrix[0][c].display != ' ' && matrix[0][c].display == matrix[1][c].display && matrix[2][c].display == ' ')
{
matrix[2][c].priority += 3;
}
if (matrix[0][c].display != ' ' && matrix[0][c].display == matrix[2][c].display && matrix[1][c].display == ' ')
{
matrix[1][c].priority += 3;
}
if (matrix[1][c].display != ' ' && matrix[1][c].display == matrix[2][c].display && matrix[0][c].display == ' ')
{
matrix[0][c].priority += 3;
}
}
if (matrix[0][0].display != ' ' && matrix[0][0].display == matrix[1][1].display && matrix[2][2].display == ' ')
{
matrix[2][2].priority += 3;
}
if (matrix[0][0].display != ' ' && matrix[0][0].display == matrix[2][2].display && matrix[1][1].display == ' ')
{
matrix[1][1].priority += 3;
}
if (matrix[1][1].display != ' ' && matrix[1][1].display == matrix[2][2].display && matrix[0][0].display == ' ')
{
matrix[0][0].priority += 3;
}
if (matrix[0][2].display != ' ' && matrix[0][2].display == matrix[1][1].display && matrix[2][0].display == ' ')
{
matrix[2][0].priority += 3;
}
if (matrix[0][2].display != ' ' && matrix[0][2].display == matrix[2][0].display && matrix[1][1].display == ' ')
{
matrix[1][1].priority += 3;
}
if (matrix[1][1].display != ' ' && matrix[1][1].display == matrix[2][0].display && matrix[0][2].display == ' ')
{
matrix[0][2].priority += 3;
}
//int maxC=-1;
List<int> maxC = new List<int>();
//int maxR=-1;
List<int> maxR = new List<int>();
int maxPriority = -1;
for (int r = 0; r < 3; r++)
{
for (int c = 0; c < 3; c++)
{
if (matrix[r][c].priority > maxPriority)
{
maxPriority = matrix[r][c].priority;
}
}
}
for (int r = 0; r < 3; r++)
{
for (int c = 0; c < 3; c++)
{
if (matrix[r][c].priority == maxPriority)
{
maxC.Add(c);
maxR.Add(r);
}
}
}
Random rnd = new Random();
int index = rnd.Next(maxC.Count());
matrix[maxR[index]][maxC[index]].display = '0';
current = 'X';
gameOver = checkLines();
this.Invalidate();
}
Finally the checkLines Function, which checks for a win in any of the eight possible lines.
This returns a Boolean True or False depending on the state of play. Also any winning line is highlighted with red text.
checkLines
public bool checkLines()
{
for (int r = 0; r < 3; r++)
{
if (matrix[r][0].display != ' ' && matrix[r][0].display == matrix[r][1].display && matrix[r][0].display == matrix[r][2].display)
{
matrix[r][0].color = matrix[r][1].color = matrix[r][2].color = Color.Red;
return true;
}
}
for (int c = 0; c < 3; c++)
{
if (matrix[0][c].display != ' ' && matrix[0][c].display == matrix[1][c].display && matrix[0][c].display == matrix[2][c].display)
{
matrix[0][c].color = matrix[1][c].color = matrix[2][c].color = Color.Red;
return true;
}
}
if (matrix[0][0].display != ' ' && matrix[0][0].display == matrix[1][1].display && matrix[0][0].display == matrix[2][2].display)
{
matrix[0][0].color = matrix[1][1].color = matrix[2][2].color = Color.Red;
return true;
}
if (matrix[0][2].display != ' ' && matrix[0][2].display == matrix[1][1].display && matrix[0][2].display == matrix[2][0].display)
{
matrix[0][2].color = matrix[1][1].color = matrix[2][0].color = Color.Red;
return true;
}
return false;
}
See Also
Download: here
Articles related to game programming
VB.Net - WordSearch
VB.Net - Vertex
VB.Net - Perspective
VB.Net - MasterMind
VB.Net - OOP BlackJack
VB.Net - Numbers Game
VB.Net - HangMan
Console BlackJack - VB.Net | C#
OOP Conway's Game of Life - VB.Net | C#
OOP Sudoku - VB.Net | C#
OctoWords VB.Net | C#
OOP Buttons Guessing Game VB.Net | C#
OOP Tangram Shapes Game VB.Net | C#
VB.Net - Three-card Monte
VB.Net - Split Decisions
VB.Net - Pascal's Pyramid
VB.Net - Random Maze Games
(Office) Wordsearch Creator
VB.Net - Event Driven Programming - LockWords Game
C# - Crack the Lock
VB.Net - Totris