tictactoe : tictactoe game

Download tictactoe.zip

Synopsis:

TTTModel.cs
Players.cs
Player.cs
HumanPlayer.cs
MachinePlayer_FirstFit.cs
MachinePlayer_HardCoded.cs
MachinePlayer_HardCoded2.cs
MachinePlayer_Random.cs
MainWindow.cs
StatusDetector.cs
StatusDetector_Hardcoded.cs
TTTBoard.cs
TTTBoardGeometry.cs
TTTBoardGrid.cs
GridCoordinate.cs
GridEnumerator.cs
GridTriplet.cs
TripletEnumerator.cs


TTTModel.cs

Synopsis
using System;
using System.Collections;
using System.Drawing;

namespace tictactoe_basic
{
  /// <summary>
  /// Summary description for TTTModel.
  /// </summary>
  public class TTTModel
  {
    private Players mPlayers = new Players();
    private StatusDetector mStatus = new StatusDetector_Hardcoded();
    private TTTBoard mBoard = new TTTBoard();

    public TTTModel()
    {
      mBoard.NewGame();
    }
    public delegate void UpdatedModel();
    public event UpdatedModel Updated;
    public StatusDetector.ResultEnum Result
    {
      get { return mStatus.GetStatus(mBoard); }
    }
    public bool MyTurn
    {
      get { return mPlayers.MyTurn; }
    }
    public void SetPlayerType(Player p)
    {
      mPlayers.SetPlayerType(p);
    }
    public void MakeMove(GridCoordinate gc)
    {
      if (Result != StatusDetector.ResultEnum.NotDone) return;
      if (mBoard.CellOccupied(gc)) return;

      mPlayers.Move(mBoard, gc);
      mPlayers.NextPlayer();
      NotifyOfUpdate();

      if (Result != StatusDetector.ResultEnum.NotDone) return;
      if (!mPlayers.CurrentPlayerIsHuman())
      {
        mPlayers.Move(mBoard, mPlayers.CurrentPlayersMove(mBoard));
        mPlayers.NextPlayer();
        NotifyOfUpdate();
      }
    }
    public bool CellIsX(GridCoordinate gc)
    {
      return mBoard.CellIsX(gc);
    }
    public bool CellIsO(GridCoordinate gc)
    {
      return mBoard.CellIsO(gc);
    }
    public void NewGame()
    {
      mPlayers.NewGame();
      mStatus.NewGame();
      mBoard.NewGame();
      NotifyOfUpdate();
    }
    public GridCoordinate GetWinnerSlashStart
    {
      get {return mStatus.GetWinnerSlashStart; }
    }
    public GridCoordinate GetWinnerSlashEnd
    {
      get {return mStatus.GetWinnerSlashEnd; }
    }
    private void NotifyOfUpdate() 
    {
      if (Updated != null)
        Updated();
    }
  }
}

Players.cs

Synopsis
using System;

namespace tictactoe_basic
{
  class Players
  {
    private Player mMyPlayer;
    private Player mYourPlayer;
    private Player mPlayer;
    private bool mMyTurn = false;

    public Players()
    {
      mMyPlayer = new HumanPlayer();
      mYourPlayer = new HumanPlayer();
      SetPlayer();
    }
    public bool MyTurn
    {
      get { return mMyTurn; }
    }
    public void NewGame()
    {
      mMyTurn = false;
      SetPlayer();
    }
    public void Move(TTTBoard board, GridCoordinate gc)
    {
      if (mMyTurn)
        board.SetCellToO(gc);
      else
        board.SetCellToX(gc);
    }
    public void NextPlayer()
    {
      mMyTurn = !mMyTurn;
      SetPlayer();
    }
    public bool CurrentPlayerIsHuman()
    {
      return mPlayer.IsHuman();
    }
    public GridCoordinate CurrentPlayersMove(TTTBoard board)
    {
      return mPlayer.Calculate(board);
    }
    public void SetPlayerType(Player p)
    {
      mMyPlayer = p;
    }
    private void SetPlayer()
    {
      if (MyTurn)
        mPlayer = mMyPlayer;
      else
        mPlayer = mYourPlayer;
    }
  }
}

Player.cs

Synopsis
using System;

namespace tictactoe_basic
{
	/// <summary>
	/// a Player takes a turn.
	/// </summary>
  abstract public class Player
  {
    public virtual bool IsHuman()
    {
      return false;
    }
    public abstract GridCoordinate Calculate(TTTBoard board);
  }
}

HumanPlayer.cs

Synopsis
using System;

namespace tictactoe_basic
{
	/// <summary>
	/// HumanPlayer uses the mouse to select a move
	/// Therefore Calculate() returns bogus coordinate
	/// </summary>
  class HumanPlayer : Player
  {
    public override bool IsHuman()
    {
      return true;
    }
    public override GridCoordinate Calculate(TTTBoard board)
    {
      return new GridCoordinate(-1, -1);
    }
  }
}

MachinePlayer_FirstFit.cs

Synopsis
using System;

namespace tictactoe_basic
{
	/// <summary>
	/// This player finds the first unoccupied square.
	/// </summary>
  class MachinePlayer_FirstFit : Player
  {
    public override GridCoordinate Calculate(TTTBoard board)
    {
      return board.GetNextFreeCell();
    }
  }
}

MachinePlayer_HardCoded.cs

Synopsis
using System;

namespace tictactoe_basic
{
  /// <summary>
  /// This player tries some hard-coded strategies:
  /// 1) finding two squares ready for a third
  /// 2) blocking the other guy
  /// not necessarily in that order; 
  /// if not found return a random free square
  /// Assume: we are 'O' the human is 'X'
  /// </summary>
  class MachinePlayer_HardCoded : Player
  {
    public override GridCoordinate Calculate(TTTBoard board)
    {
      GridCoordinate gc = CheckForPossibleMove(board);
      if (gc != null) return gc;

      return new MachinePlayer_Random().Calculate(board);
    }
    private GridCoordinate CheckForPossibleMove(TTTBoard board)
    {
      GridCoordinate gc;
      foreach (GridTriplet triplet in new TTTBoardGeometry())
      {
        gc = CheckAxis(board, triplet);
        if (gc != null) return gc;
      }
      return null;
    }
    private GridCoordinate CheckAxis(TTTBoard board, GridTriplet triplet)
    {
      GridCoordinate gc;
      foreach(GridTriplet t in triplet)
      {
        gc = CheckTriplet(board, t);
        if (gc != null) return gc;
      }
      return null;
    }
    private GridCoordinate CheckTriplet(TTTBoard board, GridTriplet triplet)
    {
      if (!board.CellOccupied(triplet.Cell0) && 
        board.CellOccupied(triplet.Cell1) &&
        board.CellsMatch(triplet.Cell1, triplet.Cell2))
        return triplet.Cell0;
      return null;
    }
  }
}

MachinePlayer_HardCoded2.cs

Synopsis
using System;

namespace tictactoe_basic
{
  /// <summary>
  /// This player tries some hard-coded strategies:
  /// 1) finding two squares ready for a third
  /// 2) blocking the other guy
  /// 3) a random free square
  /// in that order!
  /// (Only traps/pins/forks are not recognized.)
  /// Assume: we are 'O' the human is 'X'
  /// </summary>
  class MachinePlayer_HardCoded2 : Player
  {
    private delegate GridCoordinate CheckTripletFunc(TTTBoard board, GridTriplet triplet);
    private delegate GridCoordinate CheckAxisFunc(CheckTripletFunc trip, TTTBoard board, int i);

    public override GridCoordinate Calculate(TTTBoard board)
    {
      GridCoordinate gc;
      gc = CheckForMove(new CheckTripletFunc(CheckTripletForWin), board);
      if (gc != null) return gc;

      gc = CheckForMove(new CheckTripletFunc(CheckTripletForBlock), board);
      if (gc != null) return gc;

      return new MachinePlayer_Random().Calculate(board);
    }
    private GridCoordinate CheckForMove(CheckTripletFunc trip, TTTBoard board)
    {
      GridCoordinate gc;
      foreach (GridTriplet triplet in new TTTBoardGeometry())
      {
        gc = CheckAxis(trip, board, triplet);
        if (gc != null) return gc;
      }
      return null;
    }
    private GridCoordinate CheckAxis(CheckTripletFunc trip, TTTBoard board, GridTriplet triplet)
    {
      GridCoordinate gc;
      foreach(GridTriplet t in triplet)
      {
        gc = trip(board, t);
        if (gc != null) return gc;
      }
      return null;
    }
    private GridCoordinate CheckTripletForWin(TTTBoard board, GridTriplet triplet)
    {
      if (!board.CellOccupied(triplet.Cell0) && 
        board.CellsMatch(triplet.Cell1, triplet.Cell2) && board.CellIsO(triplet.Cell1))
        return triplet.Cell0;
      return null;
    }
    private GridCoordinate CheckTripletForBlock(TTTBoard board, GridTriplet triplet)
    {
      if (!board.CellOccupied(triplet.Cell0) && 
        board.CellsMatch(triplet.Cell1, triplet.Cell2) && board.CellIsX(triplet.Cell1))
        return triplet.Cell0;
      return null;
    }
  }
}

MachinePlayer_Random.cs

Synopsis
using System;
using System.Collections;

namespace tictactoe_basic
{
  /// <summary>
  /// This player selects a random cell
  /// from the unoccupied cells
  /// </summary>
  class MachinePlayer_Random : Player
  {
    ArrayList mFreeCells = new ArrayList();
    public override GridCoordinate Calculate(TTTBoard board)
    {
      mFreeCells = board.GetFreeCells();
      if (mFreeCells.Count == 0) return null;
      return RandomCell();
    }
    private GridCoordinate RandomCell()
    {
      return (GridCoordinate) mFreeCells[RandomIndex()];
    }
    private int RandomIndex()
    {
      return new Random().Next(mFreeCells.Count);
    }
  }
}

MainWindow.cs

Synopsis
using System;
using System.Drawing;
using System.Collections;
using System.ComponentModel;
using System.Windows.Forms;
using System.Data;

namespace tictactoe_basic
{
  /// <summary>
  /// Summary description for MainWindow.
  /// </summary>
  public class MainWindow : System.Windows.Forms.Form
  {
    private TTTModel mModel = new TTTModel();

    private TTTBoardGrid mGrid;
    private System.Windows.Forms.Button mResultMistake;
    private System.Windows.Forms.GroupBox groupBox1;
    private System.Windows.Forms.RadioButton mNotDone;
    private System.Windows.Forms.RadioButton mIWin;
    private System.Windows.Forms.RadioButton mYouWin;
    private System.Windows.Forms.RadioButton mATie;
    private System.Windows.Forms.Button mNewGame;
    private System.Windows.Forms.Label mMyTurn;
    private System.Windows.Forms.Label mYourTurn;
    private System.Windows.Forms.ListBox mMessages;
    private System.Windows.Forms.MainMenu mainMenu1;
    private System.Windows.Forms.MenuItem menuItem1;
    private System.Windows.Forms.MenuItem mMenuPlayerHuman;
    private System.Windows.Forms.MenuItem menuItem7;
    private System.Windows.Forms.MenuItem mMenuPlayerFirstFit;
    private System.Windows.Forms.MenuItem mMenuPlayerRandom;
    private System.Windows.Forms.MenuItem mMenuPlayerHardCoded;
    private System.Windows.Forms.MenuItem mMenuPlayerHardCoded2;

    private MenuItem mCurrentPlayer;
    /// <summary>
    /// Required designer variable.
    /// </summary>
    private System.ComponentModel.Container components = null;

    public MainWindow()
    {
      this.mGrid = new TTTBoardGrid(mModel);
      InitializeComponent();
      mModel.Updated += new TTTModel.UpdatedModel(OnModelUpdated);
      this.Controls.Add(mGrid);
      mModel.NewGame();
      mCurrentPlayer = mMenuPlayerHuman;
      mMenuPlayerHuman.Checked = true;
    }

    /// <summary>
    /// Clean up any resources being used.
    /// </summary>
    protected override void Dispose( bool disposing )
    {
      if( disposing )
      {
        if (components != null) 
        {
          components.Dispose();
        }
      }
      base.Dispose( disposing );
    }

		#region Windows Form Designer generated code
    /// <summary>
    /// Required method for Designer support - do not modify
    /// the contents of this method with the code editor.
    /// </summary>
    private void InitializeComponent()
    {
      this.mNotDone = new System.Windows.Forms.RadioButton();
      this.mIWin = new System.Windows.Forms.RadioButton();
      this.mResultMistake = new System.Windows.Forms.Button();
      this.groupBox1 = new System.Windows.Forms.GroupBox();
      this.mATie = new System.Windows.Forms.RadioButton();
      this.mYouWin = new System.Windows.Forms.RadioButton();
      this.mNewGame = new System.Windows.Forms.Button();
      this.mMyTurn = new System.Windows.Forms.Label();
      this.mYourTurn = new System.Windows.Forms.Label();
      this.mMessages = new System.Windows.Forms.ListBox();
      this.mainMenu1 = new System.Windows.Forms.MainMenu();
      this.menuItem1 = new System.Windows.Forms.MenuItem();
      this.mMenuPlayerHuman = new System.Windows.Forms.MenuItem();
      this.mMenuPlayerFirstFit = new System.Windows.Forms.MenuItem();
      this.mMenuPlayerRandom = new System.Windows.Forms.MenuItem();
      this.mMenuPlayerHardCoded = new System.Windows.Forms.MenuItem();
      this.mMenuPlayerHardCoded2 = new System.Windows.Forms.MenuItem();
      this.menuItem7 = new System.Windows.Forms.MenuItem();
      this.groupBox1.SuspendLayout();
      this.SuspendLayout();
      // 
      // mNotDone
      // 
      this.mNotDone.Enabled = false;
      this.mNotDone.Location = new System.Drawing.Point(8, 13);
      this.mNotDone.Name = "mNotDone";
      this.mNotDone.Size = new System.Drawing.Size(72, 24);
      this.mNotDone.TabIndex = 1;
      this.mNotDone.Text = "Not Done";
      // 
      // mIWin
      // 
      this.mIWin.Enabled = false;
      this.mIWin.Location = new System.Drawing.Point(88, 13);
      this.mIWin.Name = "mIWin";
      this.mIWin.Size = new System.Drawing.Size(56, 24);
      this.mIWin.TabIndex = 2;
      this.mIWin.Text = "I Win!";
      // 
      // mResultMistake
      // 
      this.mResultMistake.Location = new System.Drawing.Point(272, 13);
      this.mResultMistake.Name = "mResultMistake";
      this.mResultMistake.Size = new System.Drawing.Size(88, 23);
      this.mResultMistake.TabIndex = 5;
      this.mResultMistake.Text = "That\'s Wrong!";
      this.mResultMistake.Visible = false;
      // 
      // groupBox1
      // 
      this.groupBox1.Controls.AddRange(new System.Windows.Forms.Control[] {
                                                                            this.mATie,
                                                                            this.mYouWin,
                                                                            this.mNotDone,
                                                                            this.mIWin,
                                                                            this.mResultMistake});
      this.groupBox1.Location = new System.Drawing.Point(144, 352);
      this.groupBox1.Name = "groupBox1";
      this.groupBox1.Size = new System.Drawing.Size(368, 48);
      this.groupBox1.TabIndex = 6;
      this.groupBox1.TabStop = false;
      this.groupBox1.Text = "The Result";
      // 
      // mATie
      // 
      this.mATie.Enabled = false;
      this.mATie.Location = new System.Drawing.Point(216, 12);
      this.mATie.Name = "mATie";
      this.mATie.Size = new System.Drawing.Size(56, 24);
      this.mATie.TabIndex = 5;
      this.mATie.Text = "A Tie!";
      // 
      // mYouWin
      // 
      this.mYouWin.Enabled = false;
      this.mYouWin.Location = new System.Drawing.Point(144, 12);
      this.mYouWin.Name = "mYouWin";
      this.mYouWin.Size = new System.Drawing.Size(72, 24);
      this.mYouWin.TabIndex = 4;
      this.mYouWin.Text = "You Win!";
      // 
      // mNewGame
      // 
      this.mNewGame.Location = new System.Drawing.Point(24, 32);
      this.mNewGame.Name = "mNewGame";
      this.mNewGame.TabIndex = 7;
      this.mNewGame.Text = "New Game";
      this.mNewGame.Click += new System.EventHandler(this.mNewGame_Click);
      // 
      // mMyTurn
      // 
      this.mMyTurn.Location = new System.Drawing.Point(24, 88);
      this.mMyTurn.Name = "mMyTurn";
      this.mMyTurn.Size = new System.Drawing.Size(64, 16);
      this.mMyTurn.TabIndex = 8;
      this.mMyTurn.Text = "My Turn!";
      // 
      // mYourTurn
      // 
      this.mYourTurn.Location = new System.Drawing.Point(24, 104);
      this.mYourTurn.Name = "mYourTurn";
      this.mYourTurn.Size = new System.Drawing.Size(64, 16);
      this.mYourTurn.TabIndex = 9;
      this.mYourTurn.Text = "Your Turn!";
      // 
      // mMessages
      // 
      this.mMessages.Location = new System.Drawing.Point(0, 144);
      this.mMessages.Name = "mMessages";
      this.mMessages.Size = new System.Drawing.Size(136, 251);
      this.mMessages.TabIndex = 10;
      // 
      // mainMenu1
      // 
      this.mainMenu1.MenuItems.AddRange(new System.Windows.Forms.MenuItem[] {
                                                                              this.menuItem1});
      // 
      // menuItem1
      // 
      this.menuItem1.Index = 0;
      this.menuItem1.MenuItems.AddRange(new System.Windows.Forms.MenuItem[] {
                                                                              this.mMenuPlayerHuman,
                                                                              this.mMenuPlayerFirstFit,
                                                                              this.mMenuPlayerRandom,
                                                                              this.mMenuPlayerHardCoded,
                                                                              this.mMenuPlayerHardCoded2,
                                                                              this.menuItem7});
      this.menuItem1.Text = "Player Type";
      // 
      // mMenuPlayerHuman
      // 
      this.mMenuPlayerHuman.Index = 0;
      this.mMenuPlayerHuman.Text = "Human";
      this.mMenuPlayerHuman.Click += new System.EventHandler(this.mMenuPlayerHuman_Click);
      // 
      // mMenuPlayerFirstFit
      // 
      this.mMenuPlayerFirstFit.Index = 1;
      this.mMenuPlayerFirstFit.Text = "First Fit";
      this.mMenuPlayerFirstFit.Click += new System.EventHandler(this.mMenuPlayerFirstFit_Click);
      // 
      // mMenuPlayerRandom
      // 
      this.mMenuPlayerRandom.Index = 2;
      this.mMenuPlayerRandom.Text = "Random";
      this.mMenuPlayerRandom.Click += new System.EventHandler(this.mMenuPlayerRandom_Click);
      // 
      // mMenuPlayerHardCoded
      // 
      this.mMenuPlayerHardCoded.Index = 3;
      this.mMenuPlayerHardCoded.Text = "Hard Coded #1";
      this.mMenuPlayerHardCoded.Click += new System.EventHandler(this.mMenuPlayerHardCoded_Click);
      // 
      // mMenuPlayerHardCoded2
      // 
      this.mMenuPlayerHardCoded2.Index = 4;
      this.mMenuPlayerHardCoded2.Text = "Hard Coded #2";
      this.mMenuPlayerHardCoded2.Click += new System.EventHandler(this.mMenuPlayerHardCoded2_Click);
      // 
      // menuItem7
      // 
      this.menuItem7.Enabled = false;
      this.menuItem7.Index = 5;
      this.menuItem7.Text = "Neural Net";
      // 
      // MainWindow
      // 
      this.AutoScaleBaseSize = new System.Drawing.Size(5, 13);
      this.ClientSize = new System.Drawing.Size(520, 405);
      this.Controls.AddRange(new System.Windows.Forms.Control[] {
                                                                  this.mMessages,
                                                                  this.mYourTurn,
                                                                  this.mMyTurn,
                                                                  this.mNewGame,
                                                                  this.groupBox1});
      this.MaximizeBox = false;
      this.Menu = this.mainMenu1;
      this.MinimizeBox = false;
      this.Name = "MainWindow";
      this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen;
      this.Text = "Tic-Tac-Toe Basic";
      this.groupBox1.ResumeLayout(false);
      this.ResumeLayout(false);

    }
		#endregion

    /// <summary>
    /// The main entry point for the application.
    /// </summary>
    [STAThread]
    static void Main() 
    {
      Application.Run(new MainWindow());
    }
    public void OnModelUpdated()
    {
      UpdateResult();
      UpdateTurn();
      mGrid.Invalidate();
    }
    private void mNewGame_Click(object sender, System.EventArgs e)
    {
      mModel.NewGame();
    }
    private void UpdateTurn()
    {
      if (mModel.MyTurn)
      {
        this.mMyTurn.BackColor = Color.Azure;
        this.mYourTurn.BackColor = Color.LightGray;
      }
      else
      {
        this.mMyTurn.BackColor = Color.LightGray;
        this.mYourTurn.BackColor = Color.Azure;
      }
    }
    private void UpdateResult()
    {
      this.mNotDone.BackColor = Color.LightGray;
      this.mATie.BackColor = Color.LightGray;
      this.mIWin.BackColor = Color.LightGray;
      this.mYouWin.BackColor = Color.LightGray;
      RadioButton btn = null;
      switch(mModel.Result)
      {
        case StatusDetector.ResultEnum.NotDone: btn = this.mNotDone; break;
        case StatusDetector.ResultEnum.Tie: btn = this.mATie; break;
        case StatusDetector.ResultEnum.IWin: btn = this.mIWin; break;
        case StatusDetector.ResultEnum.YouWin: btn = this.mYouWin; break;
      }
      btn.Checked = true;
      btn.BackColor = Color.Azure;
    }
    private void mMenuPlayerFirstFit_Click(object sender, System.EventArgs e)
    {
      mModel.SetPlayerType(new MachinePlayer_FirstFit());
      SelectPlayerItem(mMenuPlayerFirstFit);
    }
    private void mMenuPlayerHardCoded_Click(object sender, System.EventArgs e)
    {
      mModel.SetPlayerType(new MachinePlayer_HardCoded());
      SelectPlayerItem(mMenuPlayerHardCoded);
    }
    private void mMenuPlayerHardCoded2_Click(object sender, System.EventArgs e)
    {
      mModel.SetPlayerType(new MachinePlayer_HardCoded2());
      SelectPlayerItem(mMenuPlayerHardCoded2);
    }
    private void mMenuPlayerHuman_Click(object sender, System.EventArgs e)
    {
      mModel.SetPlayerType(new HumanPlayer());
      SelectPlayerItem(mMenuPlayerHuman);
    }
    private void mMenuPlayerRandom_Click(object sender, System.EventArgs e)
    {
      mModel.SetPlayerType(new MachinePlayer_Random());
      SelectPlayerItem(mMenuPlayerRandom);
    }
    private void SelectPlayerItem(MenuItem newitem)
    {
      mCurrentPlayer.Checked = false;
      newitem.Checked = true;
      mCurrentPlayer = newitem;
    }
  }
}

StatusDetector.cs

Synopsis
using System;

namespace tictactoe_basic
{
  /// <summary>
  /// base class for all Status Detectors
  /// </summary>
  public abstract class StatusDetector
  {
    public enum ResultEnum {NotDone, IWin, YouWin, Tie};
    protected GridCoordinate mResultWinnerStart = new GridCoordinate(-1, -1);
    protected GridCoordinate mResultWinnerEnd = new GridCoordinate(-1, -1);

    public void NewGame()
    {
      mResultWinnerStart = new GridCoordinate(-1, -1);
      mResultWinnerEnd = new GridCoordinate(-1, -1);
    }
    public GridCoordinate GetWinnerSlashStart
    {
      get {return mResultWinnerStart; }
    }
    public GridCoordinate GetWinnerSlashEnd
    {
      get {return mResultWinnerEnd; }
    }

    public abstract ResultEnum GetStatus(TTTBoard board);

    protected void SetWinner(GridCoordinate start, GridCoordinate end)
    {
      mResultWinnerStart = start;
      mResultWinnerEnd = end;
    }
  }
}

StatusDetector_Hardcoded.cs

Synopsis
using System;

namespace tictactoe_basic
{
  /// <summary>
  /// Just try all the possible combinations...
  /// </summary>
  public class StatusDetector_Hardcoded : StatusDetector
  {
    public override ResultEnum GetStatus(TTTBoard board)
    {
      ResultEnum res;
      for (int row = 0; row < 3; ++row)
      {
        res = RowStatus(board, row);
        if (res != ResultEnum.NotDone) return res;
      }
      for (int col = 0; col < 3; ++col)
      {
        res = ColStatus(board, col);
        if (res != ResultEnum.NotDone) return res;
      }

      res = Diag1Status(board);
      if (res != ResultEnum.NotDone) return res;

      res = Diag2Status(board);
      if (res != ResultEnum.NotDone) return res;

      for (int row = 0; row < 3; ++row)
        for (int col = 0; col< 3; ++col)
          if (!board.CellOccupied(new GridCoordinate(row, col)))
            return ResultEnum.NotDone;
      return ResultEnum.Tie;
    }

    private ResultEnum RowStatus(TTTBoard board, int row)
    {
      GridTriplet triplet = new GridTriplet(new GridCoordinate(row, 0), new GridCoordinate(row, 1), new GridCoordinate(row, 2));
      return LineStatus(board, triplet);
    }
    private ResultEnum ColStatus(TTTBoard board, int col)
    {
      GridTriplet triplet = new GridTriplet(new GridCoordinate(0, col), new GridCoordinate(1, col), new GridCoordinate(2, col));
      return LineStatus(board, triplet );
    }
    private ResultEnum Diag1Status(TTTBoard board)
    {
      GridTriplet triplet = new GridTriplet(new GridCoordinate(0, 0), new GridCoordinate(1, 1), new GridCoordinate(2, 2));
      return LineStatus(board, triplet);
    }
    private ResultEnum Diag2Status(TTTBoard board)
    {
      GridTriplet triplet = new GridTriplet(new GridCoordinate(2, 0), new GridCoordinate(1, 1), new GridCoordinate(0, 2));
      return LineStatus(board, triplet);
    }
    private ResultEnum LineStatus(TTTBoard board, GridTriplet triplet)
    {
      if (!LineFilled(board, triplet)) return ResultEnum.NotDone;
      SetWinner(triplet.Cell0, triplet.Cell2);
      return StatusOf(board, triplet.Cell0);
    }
    private bool LineFilled(TTTBoard board, GridTriplet triplet)
    {
      return board.CellsMatch(triplet.Cell0, triplet.Cell1) && 
        board.CellsMatch(triplet.Cell0, triplet.Cell2) &&
        board.CellOccupied(triplet.Cell0);
    }
    private ResultEnum StatusOf(TTTBoard board, GridCoordinate gc)
    {
      if (board.CellIsX(gc))
        return ResultEnum.YouWin;
      return ResultEnum.IWin;
    }
  }
}

TTTBoard.cs

Synopsis
using System;
using System.Collections;

namespace tictactoe_basic
{
	/// <summary>
	/// Summary description for TTTBoard.
	/// </summary>
	public class TTTBoard
	{
    private Cell[,] mBoard = new Cell[3, 3];
    public enum Cell{X, O, None};
    public TTTBoard()
		{
      NewGame();
		}
    public void SetCellToX(GridCoordinate gc)
    {
      mBoard[gc.Row, gc.Col] = Cell.X;
    }
    public void SetCellToO(GridCoordinate gc)
    {
      mBoard[gc.Row, gc.Col] = Cell.O;
    }
    public bool CellIsX(GridCoordinate gc)
    {
      return mBoard[gc.Row, gc.Col] == Cell.X;
    }
    public bool CellIsO(GridCoordinate gc)
    {
      return mBoard[gc.Row, gc.Col] == Cell.O;
    }
    public bool CellOccupied(GridCoordinate gc)
    {
      return CellIsX(gc) || CellIsO(gc);
    }
    public GridCoordinate GetNextFreeCell()
    {
      for (int row = 0; row < 3; ++row)
        for(int col = 0; col < 3; ++col)
          if (mBoard[row, col] == Cell.None)
            return new GridCoordinate(row, col);
      return null;
    }
    public ArrayList GetFreeCells()
    {
      ArrayList list = new ArrayList();
      for (int row = 0; row < 3; ++row)
        for(int col = 0; col < 3; ++col)
          if (mBoard[row, col] == Cell.None)
            list.Add(new GridCoordinate(row, col));
      return list;
    }
    public void NewGame()
    {
      for(int row = 0; row < 3; ++row)
        for(int col = 0; col < 3; ++col)
          mBoard[row,col] = Cell.None;
    }
    public bool CellsMatch(GridCoordinate gc1, GridCoordinate gc2)
    {
       return mBoard[gc1.Row, gc1.Col] == mBoard[gc2.Row, gc2.Col];
    }
  }
}

TTTBoardGeometry.cs

Synopsis
using System;
using System.Collections;
using System.Drawing;

namespace tictactoe_basic
{
  class TTTBoardGeometry : IEnumerable
  {
    private const int cHotGap = 10;
    private const int cLineWidth = 3;
    private const int cGap = 5;
    private Rectangle[,] mRects = new Rectangle[3,3];
    private int mColWidth = -1;
    private int mRowHeight = -1;
    private int mWidth = -1;
    private int mHeight = -1;
     
    public IEnumerator GetEnumerator()
    {
      return new GridEnumerator();
    }
    public void Init(int h, int w)
    {
      mHeight = h - 2 * cGap;
      mWidth = w - 2 * cGap;
      mColWidth = (mWidth - 2 * cLineWidth) / 3;
      mRowHeight = (mHeight - 2 * cLineWidth) / 3;
      int colwidth = mColWidth - (2 * cHotGap);
      int rowheight = mRowHeight - (2 * cHotGap);

      mRects[0,0].X = cGap + cHotGap;
      mRects[0,0].Y = cGap + cHotGap;
      mRects[0,0].Width = colwidth;
      mRects[0,0].Height = rowheight;

      mRects[0,1].X = mRects[0,0].X + mRects[0,0].Width + cHotGap + cLineWidth + cHotGap;
      mRects[0,1].Y = cGap + cHotGap;
      mRects[0,1].Width = colwidth;
      mRects[0,1].Height = rowheight;

      mRects[0,2].X = mRects[0,1].X + mRects[0,1].Width + cHotGap + cLineWidth + cHotGap;
      mRects[0,2].Y = cGap + cHotGap;
      mRects[0,2].Width = colwidth;
      mRects[0,2].Height = rowheight;

      mRects[1,0].X = cGap + cHotGap;
      mRects[1,0].Y = mRects[0,0].Y + mRects[0,0].Height + cHotGap + cLineWidth + cHotGap;
      mRects[1,0].Width = colwidth;
      mRects[1,0].Height = rowheight;

      mRects[1,1].X = mRects[1,0].X + mRects[1,0].Width + cHotGap + cLineWidth + cHotGap;
      mRects[1,1].Y = mRects[0,1].Y + mRects[0,1].Height + cHotGap + cLineWidth + cHotGap;
      mRects[1,1].Width = colwidth;
      mRects[1,1].Height = rowheight;

      mRects[1,2].X = mRects[1,1].X + mRects[1,1].Width + cHotGap + cLineWidth + cHotGap;
      mRects[1,2].Y = mRects[0,2].Y + mRects[0,2].Height + cHotGap + cLineWidth + cHotGap;
      mRects[1,2].Width = colwidth;
      mRects[1,2].Height = rowheight;

      mRects[2,0].X = cGap + cHotGap;
      mRects[2,0].Y = mRects[1,0].Y + mRects[1,0].Height + cHotGap + cLineWidth + cHotGap;
      mRects[2,0].Width = colwidth;
      mRects[2,0].Height = rowheight;

      mRects[2,1].X = mRects[2,0].X + mRects[2,0].Width + cHotGap + cLineWidth + cHotGap;
      mRects[2,1].Y = mRects[1,1].Y + mRects[1,1].Height + cHotGap + cLineWidth + cHotGap;
      mRects[2,1].Width = colwidth;
      mRects[2,1].Height = rowheight;

      mRects[2,2].X = mRects[2,1].X + mRects[2,1].Width + cHotGap + cLineWidth + cHotGap;
      mRects[2,2].Y = mRects[1,2].Y + mRects[1,2].Height + cHotGap + cLineWidth + cHotGap;
      mRects[2,2].Width = colwidth;
      mRects[2,2].Height = rowheight;
    }
    public int IndexAtPoint(int x, int y)
    {
      for(int row = 0; row < 3; ++row)
        for(int col = 0; col < 3; ++col)
          if (mRects[row,col].Contains(x, y))
            return row*3 + col;
      return -1;
    }
    public Rectangle this [GridCoordinate gc]
    {
      get { return mRects[gc.Row, gc.Col]; }
    }
    public Point TopLeft(GridCoordinate gc)
    {
      return new Point(mRects[gc.Row, gc.Col].X, mRects[gc.Row, gc.Col].Y);
    }
    public Point TopRight(GridCoordinate gc)
    {
      return new Point(mRects[gc.Row, gc.Col].X + mRects[gc.Row, gc.Col].Width, mRects[gc.Row, gc.Col].Y);
    }
    public Point BottomLeft(GridCoordinate gc)
    {
      return new Point(mRects[gc.Row, gc.Col].X, mRects[gc.Row, gc.Col].Y + mRects[gc.Row, gc.Col].Height);
    }
    public Point BottomRight(GridCoordinate gc)
    {
      return new Point(mRects[gc.Row, gc.Col].X + mRects[gc.Row, gc.Col].Width, mRects[gc.Row, gc.Col].Y + mRects[gc.Row, gc.Col].Height);
    }
    public Point GridVerticalLine1Top
    {
      get { return new Point(cGap + mColWidth, cGap); }
    }
    public Point GridVerticalLine1Bottom
    {
      get { return new Point(cGap + mColWidth, mHeight); }
    }
    public Point GridVerticalLine2Top
    {
      get { return new Point(cGap + mColWidth + cLineWidth + mColWidth, cGap); }
    }
    public Point GridVerticalLine2Bottom
    {
      get { return new Point(cGap + mColWidth + cLineWidth + mColWidth, mHeight); }
    }
    public Point GridHorizontalLine1Left
    {
      get { return new Point(cGap, cGap + mRowHeight); }
    }
    public Point GridHorizontalLine1Right
    {
      get { return new Point(mWidth, cGap + mRowHeight); }
    }
    public Point GridHorizontalLine2Left
    {
      get { return new Point(cGap, cGap + mRowHeight + cLineWidth + mRowHeight); }
    }
    public Point GridHorizontalLine2Right
    {
      get { return new Point(mWidth, cGap + mRowHeight + cLineWidth + mRowHeight); }
    }

    private static Pen cGridPen = new Pen(Color.Blue, cLineWidth);
    public Pen GridPen
    {
      get { return cGridPen; }
    }
    private static Pen cXPen = new Pen(Color.Red, cLineWidth);
    public Pen XPen
    {
      get { return cXPen; }
    }
    private static Pen cOPen = new Pen(Color.Green, cLineWidth);
    public Pen OPen
    {
      get { return cOPen; }
    }

    private static SolidBrush cBackColor = new SolidBrush(Color.White);
    public SolidBrush BackColor
    {
      get { return cBackColor; }
    }
  }
}

TTTBoardGrid.cs

Synopsis
using System;
using System.Drawing;
using System.Windows.Forms;

namespace tictactoe_basic
{
  class TTTBoardGrid : Panel
  {
    private TTTBoardGeometry mGeometry = new TTTBoardGeometry();
    private TTTModel mModel;

    public TTTBoardGrid(TTTModel model)
    {
      mModel = model;
      this.BackColor = Color.White;
      this.MouseDown += new MouseEventHandler(Grid_MouseDown);
      this.Paint += new PaintEventHandler(PaintHandler);
      this.Location = new System.Drawing.Point(144, 8);
      this.Name = "mGrid";
      this.Size = new System.Drawing.Size(368, 336);

      mGeometry.Init(this.Height, this.Width);
    }
    private void Grid_MouseDown(object sender, MouseEventArgs e)
    {
      int i = mGeometry.IndexAtPoint(e.X, e.Y);
      if (i < 0) return;
      mModel.MakeMove(new GridCoordinate(i / 3, i % 3));
    }
    private void PaintHandler(object sender, PaintEventArgs e)
    {
      Draw(e.Graphics);
    }
    public void Draw(Graphics g)
    {
      DrawBackground(g);
      DrawGrid(g);
      DrawCells(g);
    }
    private void DrawBackground(Graphics g)
    {
      g.FillRectangle(mGeometry.BackColor, 0, 0, this.Width, this.Height);
    }
    private void DrawGrid(Graphics g)
    {
      g.DrawLine(mGeometry.GridPen, mGeometry.GridVerticalLine1Top, mGeometry.GridVerticalLine1Bottom);
      g.DrawLine(mGeometry.GridPen, mGeometry.GridVerticalLine2Top, mGeometry.GridVerticalLine2Bottom);
      g.DrawLine(mGeometry.GridPen, mGeometry.GridHorizontalLine1Left, mGeometry.GridHorizontalLine1Right);
      g.DrawLine(mGeometry.GridPen, mGeometry.GridHorizontalLine2Left, mGeometry.GridHorizontalLine2Right);
    }
    private void DrawCells(Graphics g)
    {
      for(int row = 0; row < 3; ++row)
        for(int col = 0; col < 3; ++col)
        {
          GridCoordinate gc = new GridCoordinate(row, col);
          DrawHighlightedBackground(g, gc);
          if (mModel.CellIsX(gc))
            DrawXAt(g, gc);
          else if (mModel.CellIsO(gc))
            DrawOAt(g, gc);
        }
    }
    private void DrawXAt(Graphics g, GridCoordinate gc)
    {
      g.DrawLine(mGeometry.XPen, mGeometry.TopLeft(gc), mGeometry.BottomRight(gc));
      g.DrawLine(mGeometry.XPen, mGeometry.TopRight(gc), mGeometry.BottomLeft(gc));
    }
    private void DrawOAt(Graphics g, GridCoordinate gc)
    {
      g.DrawEllipse(mGeometry.OPen, mGeometry[gc]);
    }
    private void DrawHighlightedBackground(Graphics g, GridCoordinate gc)
    {
      GridCoordinate  start = mModel.GetWinnerSlashStart;
      GridCoordinate end = mModel.GetWinnerSlashEnd;
      if ((start.Col == gc.Col && start.Row == gc.Row) ||
        ((start.Col + end.Col) / 2 == gc.Col && (start.Row + end.Row) / 2 == gc.Row) ||
        (end.Col == gc.Col && end.Row == gc.Row))
        g.FillRectangle(new SolidBrush(Color.LightGray), mGeometry[gc]);
    }
  }
}

GridCoordinate.cs

Synopsis
using System;

namespace tictactoe_basic
{
	/// <summary>
	/// Summary description for Coordinate.
	/// </summary>
	public class GridCoordinate
	{
    private int mRow;
    private int mCol;
		public GridCoordinate(int row, int col)
		{
      mRow = row;
      mCol = col;
		}
    public int Row
    {
      get { return mRow; }
    }
    public int Col
    {
      get { return mCol; }
    }
  }
}

GridEnumerator.cs

Synopsis
using System;
using System.Collections;

namespace tictactoe_basic
{
  /// <summary>
  /// Knows how to return all the possible rows, cols and diagonals
  /// </summary>
  public class GridEnumerator : IEnumerator
  {
    private int mCurrent = -1;
    public GridEnumerator()
    {
      Reset();
    }
    public object Current
    {
      get
      {
        switch (mCurrent)
        {
            //rows
          case 0 : return new GridTriplet(new GridCoordinate(0, 0), new GridCoordinate(0, 1), new GridCoordinate(0, 2));
          case 1 : return new GridTriplet(new GridCoordinate(1, 0), new GridCoordinate(1, 1), new GridCoordinate(1, 2));
          case 2 : return new GridTriplet(new GridCoordinate(2, 0), new GridCoordinate(2, 1), new GridCoordinate(2, 2));

            //cols
          case 3 : return new GridTriplet(new GridCoordinate(0, 0), new GridCoordinate(1, 0), new GridCoordinate(2, 0));
          case 4 : return new GridTriplet(new GridCoordinate(0, 1), new GridCoordinate(1, 1), new GridCoordinate(2, 1));
          case 5 : return new GridTriplet(new GridCoordinate(0, 2), new GridCoordinate(1, 2), new GridCoordinate(2, 2));

            //diags
          case 6 : return new GridTriplet(new GridCoordinate(0, 0), new GridCoordinate(1, 1), new GridCoordinate(2, 2));
          case 7 : return new GridTriplet(new GridCoordinate(0, 2), new GridCoordinate(1, 1), new GridCoordinate(2, 0));

          default:
            throw new InvalidOperationException("bug in GridEnumerator");
        }
      }
    }
    public bool MoveNext()
    {
      if (mCurrent >= 7) return false;
      mCurrent++;
      return true;
    }
    public void Reset()
    {
      mCurrent = -1;
    }
  }
}

GridTriplet.cs

Synopsis
using System;
using System.Collections;

namespace tictactoe_basic
{
	/// <summary>
	/// A FlyWeight to contain a row, col, or diagonal of cells
	/// </summary>
	public class GridTriplet
	{
    GridCoordinate mCell0;
    GridCoordinate mCell1;
    GridCoordinate mCell2;
		public GridTriplet(GridCoordinate cell0, GridCoordinate cell1, GridCoordinate cell2)
		{
      mCell0 = cell0;
      mCell1 = cell1;
      mCell2 = cell2;
		}
    public GridCoordinate Cell0 { get { return mCell0; } }
    public GridCoordinate Cell1 { get { return mCell1; } }
    public GridCoordinate Cell2 { get { return mCell2; } }
  
    public TripletEnumerator GetEnumerator()
    {
      return new TripletEnumerator(this);
    }
  }
}

TripletEnumerator.cs

Synopsis
using System;
using System.Collections;

namespace tictactoe_basic
{
	/// <summary>
	/// Knows how to permute the cells in a GridTriplet
	/// as required by the MachinePlayers
	/// </summary>
  public class TripletEnumerator : IEnumerator
  {
    private int mCurrent = -1;
    private GridTriplet mOriginal;
    public TripletEnumerator(GridTriplet triplet)
    {
      mOriginal = triplet;
      Reset();
    }
    public object Current
    {
      get
      {
        switch (mCurrent)
        {
          case 0 : return new GridTriplet(mOriginal.Cell0, mOriginal.Cell1, mOriginal.Cell2); 
          case 1 : return new GridTriplet(mOriginal.Cell1, mOriginal.Cell0, mOriginal.Cell2);
          case 2 : return new GridTriplet(mOriginal.Cell2, mOriginal.Cell1, mOriginal.Cell0);
          default:
            throw new InvalidOperationException("bug in TripletEnumerator");
        }
      }
    }
    public bool MoveNext()
    {
      if (mCurrent >= 2) return false;
      mCurrent++;
      return true;
    }
    public void Reset()
    {
      mCurrent = -1;
    }
  }
}






Contact me about content on this page using john_web-at-arrizza-dot-com
For Web Master or site problems contact: webadmin-at-arrizza-dot-com
Copyright John Arrizza (c) 2001-2010