//
// Written by Grant Jenks, Copyright 2010 - All rights reserved.
//
// Interesting ideas worth considering:
// 1. Write a nearly identical version of this that uses a loop instead of
// recursion. Add comments to that and put it on the wiki.
// 2. Make the loop-based solution generic such that separate games may be
// made for Mancala and Othello. I think this sounds the most interesting.
// It would probably require separate classes: Graphics, Board, Player,
// Game... ?
//
using System;
using System.Drawing;
using System.Windows.Forms;
enum Square { X = -1, Empty = 0, O = 1 };
enum Turn { X = -1, O = 1 };
enum Outcome { Lose = -1, Tie = 0, Win = 1 };
class TicTacToeForm : Form
{
bool HumanFirst = true;
Turn CurrentTurn = Turn.X;
Square[,] Squares = new Square[3, 3];
Pen BlackPen = new Pen(Color.Black, 5);
Pen RedPen = new Pen(Color.Red, 3);
Pen GreenPen = new Pen(Color.Green, 3);
Random random = new Random();
void TicTacToeForm_MouseClick(object sender, MouseEventArgs e)
{
if (CountEmptySquares(Squares) == 0 || (int)Winner(Squares) != 0)
{
Clear();
HumanFirst = !HumanFirst;
if (!HumanFirst)
GoComputer();
this.Refresh();
}
else if (Squares[e.X/100, e.Y/100] == Square.Empty)
{
Squares[e.X/100, e.Y/100] = (Square)(int)CurrentTurn;
CurrentTurn = (Turn)(-1 * (int)CurrentTurn);
this.Refresh();
GoComputer();
this.Refresh();
}
}
void GoComputer()
{
int row = -1, col = -1;
if (CountEmptySquares(Squares) == 9)
{
row = random.Next(3);
col = random.Next(3);
}
else
FindBestMove(CurrentTurn, Squares, ref row, ref col);
if (row != -1 && col != -1)
Squares[row,col] = (Square)(int)CurrentTurn;
CurrentTurn = (Turn)(-1 * (int)CurrentTurn);
}
Outcome FindBestMove(Turn cur, Square[,] squares, ref int row,
ref int col)
{
int winner = (int)Winner(squares);
if (winner != 0)
return (Outcome)(winner * (int)cur);
if (CountEmptySquares(squares) == 0)
return Outcome.Tie;
Outcome bestOutcome = Outcome.Lose;
int bestRow = -1, bestCol = -1;
for (int i = 0; i < 3; i++)
for (int j = 0; j < 3; j++)
if (squares[i,j] == Square.Empty)
{
Square[,] local = new Square[3,3];
int dummyRow = -1, dummyCol = -1;
Array.Copy(squares, local, squares.Length);
local[i,j] = (Square)(int)cur;
Outcome counterMove = FindBestMove((Turn)(-1*(int)cur), local,
ref dummyRow, ref dummyCol);
if (-1*(int)counterMove >= (int)bestOutcome)
{
bestRow = i;
bestCol = j;
bestOutcome = (Outcome)(-1*(int)counterMove);
}
}
row = bestRow;
col = bestCol;
return bestOutcome;
}
void TicTacToeForm_Paint(object sender, PaintEventArgs e)
{
Graphics g = e.Graphics;
g.DrawLine(BlackPen, 5, 100, 295, 100);
g.DrawLine(BlackPen, 5, 200, 295, 200);
g.DrawLine(BlackPen, 100, 5, 100, 295);
g.DrawLine(BlackPen, 200, 5, 200, 295);
for (int i = 0; i < 3; i++)
for (int j = 0; j < 3; j++)
if (Squares[i, j] == Square.X)
{
g.DrawLine(RedPen, i*100+10, j*100+10, (i+1)*100-10, (j+1)*100-10);
g.DrawLine(RedPen, i*100+10, (j+1)*100-10, (i+1)*100-10, j*100+10);
}
else if (Squares[i, j] == Square.O)
g.DrawEllipse(GreenPen, i*100+10, j*100+10, 80, 80);
}
Square Winner(Square[,] squares)
{
for (int i = 0; i < 3; i++)
if (squares[i,0] == squares[i,1] && squares[i,1] == squares[i,2])
return squares[i,0];
else if (squares[0,i] == squares[1,i] && squares[1,i] == squares[2,i])
return squares[0,i];
if (squares[0,0] == squares[1,1] && squares[1,1] == squares[2,2])
return squares[1,1];
if (squares[2,0] == squares[1,1] && squares[1,1] == squares[0,2])
return squares[1,1];
return Square.Empty;
}
void InitializeComponent()
{
this.SuspendLayout();
this.MaximizeBox = false;
this.MinimizeBox = false;
this.Text = "Tic Tac Toe";
this.BackColor = Color.White;
this.ClientSize = new Size(300, 300);
this.FormBorderStyle = FormBorderStyle.FixedSingle;
this.Paint += new PaintEventHandler(this.TicTacToeForm_Paint);
this.MouseClick += new MouseEventHandler(this.TicTacToeForm_MouseClick);
this.ResumeLayout(false);
}
int CountEmptySquares(Square[,] squares)
{
int count = 0;
for (int i = 0; i < 3; i++)
for (int j = 0; j < 3; j++)
if (squares[i,j] == Square.Empty)
count++;
return count;
}
void Clear()
{
for (int i = 0; i < 3; i++)
for (int j = 0; j < 3; j++)
Squares[i, j] = Square.Empty;
}
TicTacToeForm()
{
Clear();
InitializeComponent();
}
[STAThread]
static void Main()
{
Application.Run(new TicTacToeForm());
}
}