using System;
using System.Drawing;
using System.Collections;
using System.ComponentModel;
using System.Text.RegularExpressions;
using System.Threading;
using System.Windows.Forms;
namespace Reversi
{
/// <summary>
/// Summary description for Reversi.
/// </summary>
public class ReversiForm : System.Windows.Forms.Form
{
// Main menu.
private System.Windows.Forms.MainMenu mainMenu;
private System.Windows.Forms.MenuItem gameMenuItem;
private System.Windows.Forms.MenuItem newGameMenuItem;
private System.Windows.Forms.MenuItem resignGameMenuItem;
private System.Windows.Forms.MenuItem gameSeparator1MenuItem;
private System.Windows.Forms.MenuItem optionsMenuItem;
private System.Windows.Forms.MenuItem statisticsMenuItem;
private System.Windows.Forms.MenuItem gameSeparator2MenuItem;
private System.Windows.Forms.MenuItem exitMenuItem;
private System.Windows.Forms.MenuItem moveMenuItem;
private System.Windows.Forms.MenuItem undoMoveMenuItem;
private System.Windows.Forms.MenuItem undoAllMovesMenuItem;
private System.Windows.Forms.MenuItem redoMoveMenuItem;
private System.Windows.Forms.MenuItem redoAllMovesMenuItem;
private System.Windows.Forms.MenuItem moveSeparatorMenuItem;
private System.Windows.Forms.MenuItem resumePlayMenuItem;
private System.Windows.Forms.MenuItem helpMenuItem;
private System.Windows.Forms.MenuItem helpTopicsMenuItem;
private System.Windows.Forms.MenuItem helpSeparatorMenuItem;
private System.Windows.Forms.MenuItem aboutMenuItem;
// Tool bar.
private System.Windows.Forms.ToolBar playToolBar;
private System.Windows.Forms.ImageList playImageList;
private System.Windows.Forms.ToolBarButton newGameToolBarButton;
private System.Windows.Forms.ToolBarButton resignGameToolBarButton;
private System.Windows.Forms.ToolBarButton separatorToolBarButton;
private System.Windows.Forms.ToolBarButton undoAllMovesToolBarButton;
private System.Windows.Forms.ToolBarButton undoMoveToolBarButton;
private System.Windows.Forms.ToolBarButton resumePlayToolBarButton;
private System.Windows.Forms.ToolBarButton redoMoveToolBarButton;
private System.Windows.Forms.ToolBarButton redoAllMovesToolBarButton;
// Board display.
private System.Windows.Forms.Panel boardPanel;
private System.Windows.Forms.Label cornerLabel;
private System.Windows.Forms.Panel squaresPanel;
// Information display.
private System.Windows.Forms.Panel infoPanel;
private System.Windows.Forms.Label whiteTextLabel;
private System.Windows.Forms.Label whiteCountLabel;
private System.Windows.Forms.Label blackTextLabel;
private System.Windows.Forms.Label blackCountLabel;
private System.Windows.Forms.Label currentColorTextLabel;
private System.Windows.Forms.Panel currentColorPanel;
private System.Windows.Forms.ListView moveListView;
private System.Windows.Forms.ColumnHeader moveNullColumn;
private System.Windows.Forms.ColumnHeader moveNumberColumn;
private System.Windows.Forms.ColumnHeader movePlayerColumn;
private System.Windows.Forms.ColumnHeader movePositionColumn;
// Status display.
private System.Windows.Forms.Panel statusPanel;
private System.Windows.Forms.Label statusLabel;
private System.Windows.Forms.ProgressBar statusProgressBar;
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.Container components = null;
// This enumeration should match the button order on the tool bar.
public enum ToolBarButton
{
NewGame,
ResignGame,
Separator,
UndoAllMoves,
UndoMove,
ResumePlay,
RedoMove,
RedoAllMoves
}
// Defines the difficulty settings.
public enum Difficulty
{
Beginner,
Intermediate,
Advanced,
Expert
}
// Defines the game states.
private enum GameState
{
GameOver, // The game is over (also used for the initial state).
InMoveAnimation, // A move has been made and the animation is active.
InPlayerMove, // Waiting for the user to make a move.
InComputerMove, // Waiting for the computer to make a move.
MoveCompleted // A move has been completed (including the animation, if active).
}
// The game board.
private Board board;
private Label[] colLabels, rowLabels;
private SquareControl[,] squareControls;
// Game options.
private Options options = new Options();
// Game statistics.
private Statistics statistics = new Statistics();
// Game parameters.
private GameState gameState;
private int currentColor;
private int moveNumber;
// This timer is used to animate moves during game play.
private System.Windows.Forms.Timer animationTimer = new System.Windows.Forms.Timer();
private static readonly int animationTimerInterval = 50;
// AI parameters.
private int lookAheadDepth;
private int forfeitWeight;
private int frontierWeight;
private int mobilityWeight;
private int stabilityWeight;
// Defines a thread for running the computer move look ahead.
private Thread calculateComputerMoveThread;
// Defines a structure for holding a look ahead move and rank.
private struct ComputerMove
{
public int row;
public int col;
public int rank;
public ComputerMove(int row, int col)
{
this.row = row;
this.col = col;
this.rank = 0;
}
}
// Defines the maximum move rank value (used for ranking an end game).
private static int maxRank = System.Int32.MaxValue - 64;
// For converting column numbers to letters and vice versa.
private static String alpha = "ABCDEFGH";
// For getting moves via the keyboard.
private int keyedColNumber;
private int keyedRowNumber;
// Defines a structure for holding move history data.
private struct MoveRecord
{
public Board board;
public int currentColor;
public ListViewItem moveListItem;
public MoveRecord(Board board, int currentColor, ListViewItem moveListItem)
{
this.board = new Board(board);
this.currentColor = currentColor;
this.moveListItem = moveListItem;
}
}
// Defines an array for storing the move history.
private ArrayList moveHistory;
// Used to track which player made the last move.
private int lastMoveColor;
// Used to suspend computer play while moves are undone/redone.
private bool isComputerPlaySuspended;
// Used to save statistics when undoing moves in a completed game.
private Statistics oldStatistics;
// For tracking the window location and size.
private Rectangle windowSettings;
// For loading and saving program settings.
private ProgramSettings settings;
private static readonly string programSettingsFileName = "Reversi.xml";
// The help file name.
private static readonly string helpFileName = "Reversi.chm";
public ReversiForm()
{
//
// Required for Windows Form Designer support
//
InitializeComponent();
//
// TODO: Add any constructor code after InitializeComponent call
//
// Create the game board.
this.board = new Board();
// Create the controls for each square, add them to the squares
// panel and set up event handling.
this.squareControls = new SquareControl[8, 8];
int i, j;
for (i = 0; i < 8; i++)
for (j = 0; j < 8; j++)
{
// Create it.
this.squareControls[i, j] = new SquareControl(i, j);
// Position it.
this.squareControls[i, j].Left = j * this.squareControls[i, j].Width;
this.squareControls[i, j].Top = i * this.squareControls[i, j].Height;
// Add it.
this.squaresPanel.Controls.Add(this.squareControls[i, j]);
// Set up event handling for it.
this.squareControls[i, j].MouseMove += new MouseEventHandler(this.SquareControl_MouseMove);
this.squareControls[i, j].MouseLeave += new EventHandler(this.SquareControl_MouseLeave);
this.squareControls[i, j].Click += new EventHandler(this.SquareControl_Click);
}
// Create the column and row labels.
this.colLabels = new Label[8];
for (i = 0; i < 8; i++)
{
// Create a column label.
this.colLabels[i] = new Label();
// Set its display properties.
this.colLabels[i].Text = ReversiForm.alpha.Substring(i, 1);
this.colLabels[i].BackColor = this.cornerLabel.BackColor;
this.colLabels[i].ForeColor = this.cornerLabel.ForeColor;
this.colLabels[i].TextAlign = ContentAlignment.MiddleCenter;
// Set its size and position.
this.colLabels[i].Width = this.squareControls[0, 0].Width;
this.colLabels[i].Height = this.cornerLabel.Height;
this.colLabels[i].Left = this.cornerLabel.Width + i * this.colLabels[0].Width;
this.colLabels[i].Top = 0;
// Add it.
this.boardPanel.Controls.Add(this.colLabels[i]);
}
this.rowLabels = new Label[8];
for (i = 0; i < 8; i++)
{
// Create a row label.
this.rowLabels[i] = new Label();
// Set its display properties.
this.rowLabels[i].Text = (i + 1).ToString();
this.rowLabels[i].BackColor = this.cornerLabel.BackColor;
this.rowLabels[i].ForeColor = this.cornerLabel.ForeColor;
this.rowLabels[i].TextAlign = ContentAlignment.MiddleCenter;
// Set its size and position.
this.rowLabels[i].Width = this.cornerLabel.Height;
this.rowLabels[i].Height = this.squareControls[0, 0].Height;
this.rowLabels[i].Left = 0;
this.rowLabels[i].Top = this.cornerLabel.Height + i * this.rowLabels[0].Width;
// Add it.
this.boardPanel.Controls.Add(this.rowLabels[i]);
}
// Initialize the game state.
this.gameState = ReversiForm.GameState.GameOver;
// Initialize the animation timer.
this.animationTimer.Interval = ReversiForm.animationTimerInterval;
this.animationTimer.Tick += new EventHandler(this.AnimateMove);
// Initialize the window settings.
this.windowSettings = new Rectangle(
this.DesktopLocation.X,
this.DesktopLocation.Y,
this.ClientSize.Width,
this.ClientSize.Height);
// Load any saved program settings.
this.settings = new ProgramSettings(ReversiForm.programSettingsFileName);
this.LoadProgramSettings();
}
/// <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.components = new System.ComponentModel.Container();
System.Resources.ResourceManager resources = new System.Resources.ResourceManager(typeof(ReversiForm));
this.infoPanel = new System.Windows.Forms.Panel();
this.currentColorPanel = new System.Windows.Forms.Panel();
this.whiteCountLabel = new System.Windows.Forms.Label();
this.whiteTextLabel = new System.Windows.Forms.Label();
this.currentColorTextLabel = new System.Windows.Forms.Label();
this.blackCountLabel = new System.Windows.Forms.Label();
this.blackTextLabel = new System.Windows.Forms.Label();
this.moveListView = new System.Windows.Forms.ListView();
this.moveNullColumn = new System.Windows.Forms.ColumnHeader();
this.moveNumberColumn = new System.Windows.Forms.ColumnHeader();
this.movePlayerColumn = new System.Windows.Forms.ColumnHeader();
this.movePositionColumn = new System.Windows.Forms.ColumnHeader();
this.squaresPanel = new System.Windows.Forms.Panel();
this.mainMenu = new System.Windows.Forms.MainMenu();
this.gameMenuItem = new System.Windows.Forms.MenuItem();
this.newGameMenuItem = new System.Windows.Forms.MenuItem();
this.resignGameMenuItem = new System.Windows.Forms.MenuItem();
this.gameSeparator1MenuItem = new System.Windows.Forms.MenuItem();
this.optionsMenuItem = new System.Windows.Forms.MenuItem();
this.statisticsMenuItem = new System.Windows.Forms.MenuItem();
this.gameSeparator2MenuItem = new System.Windows.Forms.MenuItem();
this.exitMenuItem = new System.Windows.Forms.MenuItem();
this.moveMenuItem = new System.Windows.Forms.MenuItem();
this.undoMoveMenuItem = new System.Windows.Forms.MenuItem();
this.redoMoveMenuItem = new System.Windows.Forms.MenuItem();
this.undoAllMovesMenuItem = new System.Windows.Forms.MenuItem();
this.redoAllMovesMenuItem = new System.Windows.Forms.MenuItem();
this.moveSeparatorMenuItem = new System.Windows.Forms.MenuItem();
this.resumePlayMenuItem = new System.Windows.Forms.MenuItem();
this.helpMenuItem = new System.Windows.Forms.MenuItem();
this.helpTopicsMenuItem = new System.Windows.Forms.MenuItem();
this.helpSeparatorMenuItem = new System.Windows.Forms.MenuItem();
this.aboutMenuItem = new System.Windows.Forms.MenuItem();
this.boardPanel = new System.Windows.Forms.Panel();
this.cornerLabel = new System.Windows.Forms.Label();
this.statusProgressBar = new System.Windows.Forms.ProgressBar();
this.statusLabel = new System.Windows.Forms.Label();
this.statusPanel = new System.Windows.Forms.Panel();
this.playToolBar = new System.Windows.Forms.ToolBar();
this.newGameToolBarButton = new System.Windows.Forms.ToolBarButton();
this.resignGameToolBarButton = new System.Windows.Forms.ToolBarButton();
this.separatorToolBarButton = new System.Windows.Forms.ToolBarButton();
this.undoAllMovesToolBarButton = new System.Windows.Forms.ToolBarButton();
this.undoMoveToolBarButton = new System.Windows.Forms.ToolBarButton();
this.resumePlayToolBarButton = new System.Windows.Forms.ToolBarButton();
this.redoMoveToolBarButton = new System.Windows.Forms.ToolBarButton();
this.redoAllMovesToolBarButton = new System.Windows.Forms.ToolBarButton();
this.playImageList = new System.Windows.Forms.ImageList(this.components);
this.infoPanel.SuspendLayout();
this.boardPanel.SuspendLayout();
this.statusPanel.SuspendLayout();
this.SuspendLayout();
//
// infoPanel
//
this.infoPanel.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
| System.Windows.Forms.AnchorStyles.Right)));
this.infoPanel.Controls.Add(this.currentColorPanel);
this.infoPanel.Controls.Add(this.whiteCountLabel);
this.infoPanel.Controls.Add(this.whiteTextLabel);
this.infoPanel.Controls.Add(this.currentColorTextLabel);
this.infoPanel.Controls.Add(this.blackCountLabel);
this.infoPanel.Controls.Add(this.blackTextLabel);
this.infoPanel.Controls.Add(this.moveListView);
this.infoPanel.Location = new System.Drawing.Point(296, 32);
this.infoPanel.Name = "infoPanel";
this.infoPanel.Size = new System.Drawing.Size(168, 276);
this.infoPanel.TabIndex = 3;
//
// currentColorPanel
//
this.currentColorPanel.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle;
this.currentColorPanel.Location = new System.Drawing.Point(88, 56);
this.currentColorPanel.Name = "currentColorPanel";
this.currentColorPanel.Size = new System.Drawing.Size(16, 16);
this.currentColorPanel.TabIndex = 5;
this.currentColorPanel.Visible = false;
//
// whiteCountLabel
//
this.whiteCountLabel.Location = new System.Drawing.Point(80, 32);
this.whiteCountLabel.Name = "whiteCountLabel";
this.whiteCountLabel.Size = new System.Drawing.Size(24, 13);
this.whiteCountLabel.TabIndex = 3;
this.whiteCountLabel.Text = "0";
this.whiteCountLabel.TextAlign = System.Drawing.ContentAlignment.MiddleRight;
//
// whiteTextLabel
//
this.whiteTextLabel.AutoSize = true;
this.whiteTextLabel.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((System.Byte)(0)));
this.whiteTextLabel.Location = new System.Drawing.Point(40, 32);
this.whiteTextLabel.Name = "whiteTextLabel";
this.whiteTextLabel.Size = new System.Drawing.Size(39, 16);
this.whiteTextLabel.TabIndex = 2;
this.whiteTextLabel.Text = "White: ";
//
// currentColorTextLabel
//
this.currentColorTextLabel.AutoSize = true;
this.currentColorTextLabel.Location = new System.Drawing.Point(32, 56);
this.currentColorTextLabel.Name = "currentColorTextLabel";
this.currentColorTextLabel.Size = new System.Drawing.Size(48, 16);
this.currentColorTextLabel.TabIndex = 4;
this.currentColorTextLabel.Text = "Current: ";
this.currentColorTextLabel.Visible = false;
//
// blackCountLabel
//
this.blackCountLabel.Location = new System.Drawing.Point(80, 8);
this.blackCountLabel.Name = "blackCountLabel";
this.blackCountLabel.Size = new System.Drawing.Size(24, 13);
this.blackCountLabel.TabIndex = 1;
this.blackCountLabel.Text = "0";
this.blackCountLabel.TextAlign = System.Drawing.ContentAlignment.MiddleRight;
//
// blackTextLabel
//
this.blackTextLabel.AutoSize = true;
this.blackTextLabel.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((System.Byte)(0)));
this.blackTextLabel.Location = new System.Drawing.Point(40, 8);
this.blackTextLabel.Name = "blackTextLabel";
this.blackTextLabel.Size = new System.Drawing.Size(38, 16);
this.blackTextLabel.TabIndex = 0;
this.blackTextLabel.Text = "Black: ";
//
// moveListView
//
this.moveListView.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
| System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right)));
this.moveListView.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle;
this.moveListView.Columns.AddRange(new System.Windows.Forms.ColumnHeader[] {
this.moveNullColumn,
this.moveNumberColumn,
this.movePlayerColumn,
this.movePositionColumn});
this.moveListView.FullRowSelect = true;
this.moveListView.HeaderStyle = System.Windows.Forms.ColumnHeaderStyle.Nonclickable;
this.moveListView.Location = new System.Drawing.Point(2, 88);
this.moveListView.Name = "moveListView";
this.moveListView.Size = new System.Drawing.Size(164, 188);
this.moveListView.TabIndex = 6;
this.moveListView.TabStop = false;
this.moveListView.View = System.Windows.Forms.View.Details;
//
// moveNullColumn
//
this.moveNullColumn.Text = "";
this.moveNullColumn.TextAlign = System.Windows.Forms.HorizontalAlignment.Right;
this.moveNullColumn.Width = 0;
//
// moveNumberColumn
//
this.moveNumberColumn.Text = "#";
this.moveNumberColumn.TextAlign = System.Windows.Forms.HorizontalAlignment.Right;
this.moveNumberColumn.Width = 32;
//
// movePlayerColumn
//
this.movePlayerColumn.Text = "Player";
this.movePlayerColumn.TextAlign = System.Windows.Forms.HorizontalAlignment.Right;
this.movePlayerColumn.Width = 52;
//
// movePositionColumn
//
this.movePositionColumn.Text = "Position";
this.movePositionColumn.TextAlign = System.Windows.Forms.HorizontalAlignment.Center;
this.movePositionColumn.Width = 62;
//
// squaresPanel
//
this.squaresPanel.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
| System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right)));
this.squaresPanel.Location = new System.Drawing.Point(16, 16);
this.squaresPanel.Name = "squaresPanel";
this.squaresPanel.Size = new System.Drawing.Size(256, 256);
this.squaresPanel.TabIndex = 1;
//
// mainMenu
//
this.mainMenu.MenuItems.AddRange(new System.Windows.Forms.MenuItem[] {
this.gameMenuItem,
this.moveMenuItem,
this.helpMenuItem});
//
// gameMenuItem
//
this.gameMenuItem.Index = 0;
this.gameMenuItem.MenuItems.AddRange(new System.Windows.Forms.MenuItem[] {
this.newGameMenuItem,
this.resignGameMenuItem,
this.gameSeparator1MenuItem,
this.optionsMenuItem,
this.statisticsMenuItem,
this.gameSeparator2MenuItem,
this.exitMenuItem});
this.gameMenuItem.ShowShortcut = false;
this.gameMenuItem.Text = "&Game";
//
// newGameMenuItem
//
this.newGameMenuItem.Index = 0;
this.newGameMenuItem.Shortcut = System.Windows.Forms.Shortcut.CtrlN;
this.newGameMenuItem.Text = "&New Game";
this.newGameMenuItem.Click += new System.EventHandler(this.newGameMenuItem_Click);
//
// resignGameMenuItem
//
this.resignGameMenuItem.Enabled = false;
this.resignGameMenuItem.Index = 1;
this.resignGameMenuItem.Shortcut = System.Windows.Forms.Shortcut.CtrlR;
this.resignGameMenuItem.Text = "&Resign Game";
this.resignGameMenuItem.Click += new System.EventHandler(this.resignGameMenuItem_Click);
//
// gameSeparator1MenuItem
//
this.gameSeparator1MenuItem.Index = 2;
this.gameSeparator1MenuItem.Text = "-";
//
// optionsMenuItem
//
this.optionsMenuItem.Index = 3;
this.optionsMenuItem.Shortcut = System.Windows.Forms.Shortcut.CtrlO;
this.optionsMenuItem.Text = "&Options...";
this.optionsMenuItem.Click += new System.EventHandler(this.optionsMenuItem_Click);
//
// statisticsMenuItem
//
this.statisticsMenuItem.Index = 4;
this.statisticsMenuItem.Shortcut = System.Windows.Forms.Shortcut.CtrlS;
this.statisticsMenuItem.Text = "&Statistics...";
this.statisticsMenuItem.Click += new System.EventHandler(this.statisticsMenuItem_Click);
//
// gameSeparator2MenuItem
//
this.gameSeparator2MenuItem.Index = 5;
this.gameSeparator2MenuItem.Text = "-";
//
// exitMenuItem
//
this.exitMenuItem.Index = 6;
this.exitMenuItem.Shortcut = System.Windows.Forms.Shortcut.CtrlX;
this.exitMenuItem.Text = "E&xit";
this.exitMenuItem.Click += new System.EventHandler(this.exitMenuItem_Click);
//
// moveMenuItem
//
this.moveMenuItem.Index = 1;
this.moveMenuItem.MenuItems.AddRange(new System.Windows.Forms.MenuItem[] {
this.undoMoveMenuItem,
this.redoMoveMenuItem,
this.undoAllMovesMenuItem,
this.redoAllMovesMenuItem,
this.moveSeparatorMenuItem,
this.resumePlayMenuItem});
this.moveMenuItem.ShowShortcut = false;
this.moveMenuItem.Text = "&Move";
//
// undoMoveMenuItem
//
this.undoMoveMenuItem.Enabled = false;
this.undoMoveMenuItem.Index = 0;
this.undoMoveMenuItem.Shortcut = System.Windows.Forms.Shortcut.CtrlZ;
this.undoMoveMenuItem.Text = "&Undo Move";
this.undoMoveMenuItem.Click += new System.EventHandler(this.undoMoveMenuItem_Click);
//
// redoMoveMenuItem
//
this.redoMoveMenuItem.Enabled = false;
this.redoMoveMenuItem.Index = 1;
this.redoMoveMenuItem.Shortcut = System.Windows.Forms.Shortcut.CtrlY;
this.redoMoveMenuItem.Text = "&Redo Move";
this.redoMoveMenuItem.Click += new System.EventHandler(this.redoMoveMenuItem_Click);
//
// undoAllMovesMenuItem
//
this.undoAllMovesMenuItem.Enabled = false;
this.undoAllMovesMenuItem.Index = 2;
this.undoAllMovesMenuItem.Shortcut = System.Windows.Forms.Shortcut.CtrlShiftZ;
this.undoAllMovesMenuItem.Text = "U&ndo All Moves";
this.undoAllMovesMenuItem.Click += new System.EventHandler(this.undoAllMovesmenuItem_Click);
//
// redoAllMovesMenuItem
//
this.redoAllMovesMenuItem.Enabled = false;
this.redoAllMovesMenuItem.Index = 3;
this.redoAllMovesMenuItem.Shortcut = System.Windows.Forms.Shortcut.CtrlShiftY;
this.redoAllMovesMenuItem.Text = "Re&do All Moves";
this.redoAllMovesMenuItem.Click += new System.EventHandler(this.redoAllMovesMenuItem_Click);
//
// moveSeparatorMenuItem
//
this.moveSeparatorMenuItem.Index = 4;
this.moveSeparatorMenuItem.Text = "-";
//
// resumePlayMenuItem
//
this.resumePlayMenuItem.Enabled = false;
this.resumePlayMenuItem.Index = 5;
this.resumePlayMenuItem.Shortcut = System.Windows.Forms.Shortcut.CtrlP;
this.resumePlayMenuItem.Text = "Resume &Play";
this.resumePlayMenuItem.Click += new System.EventHandler(this.resumePlayMenuItem_Click);
//
// helpMenuItem
//
this.helpMenuItem.Index = 2;
this.helpMenuItem.MenuItems.AddRange(new System.Windows.Forms.MenuItem[] {
this.helpTopicsMenuItem,
this.helpSeparatorMenuItem,
this.aboutMenuItem});
this.helpMenuItem.ShowShortcut = false;
this.helpMenuItem.Text = "&Help";
//
// helpTopicsMenuItem
//
this.helpTopicsMenuItem.Index = 0;
this.helpTopicsMenuItem.Shortcut = System.Windows.Forms.Shortcut.CtrlH;
this.helpTopicsMenuItem.Text = "&Help Topics";
this.helpTopicsMenuItem.Click += new System.EventHandler(this.helpTopicsMenuItem_Click);
//
// helpSeparatorMenuItem
//
this.helpSeparatorMenuItem.Index = 1;
this.helpSeparatorMenuItem.Text = "-";
//
// aboutMenuItem
//
this.aboutMenuItem.Index = 2;
this.aboutMenuItem.Shortcut = System.Windows.Forms.Shortcut.CtrlA;
this.aboutMenuItem.Text = "&About";
this.aboutMenuItem.Click += new System.EventHandler(this.aboutMenuItem_Click);
//
// boardPanel
//
this.boardPanel.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
| System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right)));
this.boardPanel.BackColor = System.Drawing.SystemColors.Control;
this.boardPanel.Controls.Add(this.cornerLabel);
this.boardPanel.Controls.Add(this.squaresPanel);
this.boardPanel.Location = new System.Drawing.Point(8, 32);
this.boardPanel.Name = "boardPanel";
this.boardPanel.Size = new System.Drawing.Size(272, 272);
this.boardPanel.TabIndex = 2;
//
// cornerLabel
//
this.cornerLabel.BackColor = System.Drawing.SystemColors.ControlDark;
this.cornerLabel.ForeColor = System.Drawing.SystemColors.ControlLightLight;
this.cornerLabel.Location = new System.Drawing.Point(0, 0);
this.cornerLabel.Name = "cornerLabel";
this.cornerLabel.Size = new System.Drawing.Size(16, 16);
this.cornerLabel.TabIndex = 0;
//
// statusProgressBar
//
this.statusProgressBar.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
this.statusProgressBar.BackColor = System.Drawing.SystemColors.ControlLight;
this.statusProgressBar.Location = new System.Drawing.Point(350, 2);
this.statusProgressBar.Name = "statusProgressBar";
this.statusProgressBar.Size = new System.Drawing.Size(104, 16);
this.statusProgressBar.Step = 1;
this.statusProgressBar.TabIndex = 1;
this.statusProgressBar.Visible = false;
//
// statusLabel
//
this.statusLabel.AutoSize = true;
this.statusLabel.Location = new System.Drawing.Point(16, 2);
this.statusLabel.Name = "statusLabel";
this.statusLabel.Size = new System.Drawing.Size(0, 16);
this.statusLabel.TabIndex = 0;
this.statusLabel.TextAlign = System.Drawing.ContentAlignment.MiddleLeft;
//
// statusPanel
//
this.statusPanel.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right)));
this.statusPanel.BorderStyle = System.Windows.Forms.BorderStyle.Fixed3D;
this.statusPanel.Controls.Add(this.statusProgressBar);
this.statusPanel.Controls.Add(this.statusLabel);
this.statusPanel.Location = new System.Drawing.Point(8, 312);
this.statusPanel.Name = "statusPanel";
this.statusPanel.Size = new System.Drawing.Size(456, 24);
this.statusPanel.TabIndex = 4;
//
// playToolBar
//
this.playToolBar.Buttons.AddRange(new System.Windows.Forms.ToolBarButton[] {
this.newGameToolBarButton,
this.resignGameToolBarButton,
this.separatorToolBarButton,
this.undoAllMovesToolBarButton,
this.undoMoveToolBarButton,
this.resumePlayToolBarButton,
this.redoMoveToolBarButton,
this.redoAllMovesToolBarButton});
this.playToolBar.Divider = false;
this.playToolBar.DropDownArrows = true;
this.playToolBar.ImageList = this.playImageList;
this.playToolBar.Location = new System.Drawing.Point(0, 0);
this.playToolBar.Name = "playToolBar";
this.playToolBar.ShowToolTips = true;
this.playToolBar.Size = new System.Drawing.Size(472, 26);
this.playToolBar.TabIndex = 1;
this.playToolBar.ButtonClick += new System.Windows.Forms.ToolBarButtonClickEventHandler(this.playToolBar_ButtonClick);
//
// newGameToolBarButton
//
this.newGameToolBarButton.ImageIndex = 0;
this.newGameToolBarButton.ToolTipText = "New Game";
//
// resignGameToolBarButton
//
this.resignGameToolBarButton.Enabled = false;
this.resignGameToolBarButton.ImageIndex = 1;
this.resignGameToolBarButton.ToolTipText = "Resign Game";
//
// separatorToolBarButton
//
this.separatorToolBarButton.Style = System.Windows.Forms.ToolBarButtonStyle.Separator;
//
// undoAllMovesToolBarButton
//
this.undoAllMovesToolBarButton.Enabled = false;
this.undoAllMovesToolBarButton.ImageIndex = 2;
this.undoAllMovesToolBarButton.ToolTipText = "Undo All Moves";
//
// undoMoveToolBarButton
//
this.undoMoveToolBarButton.Enabled = false;
this.undoMoveToolBarButton.ImageIndex = 3;
this.undoMoveToolBarButton.ToolTipText = "Undo Move";
//
// resumePlayToolBarButton
//
this.resumePlayToolBarButton.Enabled = false;
this.resumePlayToolBarButton.ImageIndex = 4;
this.resumePlayToolBarButton.ToolTipText = "Resume Play";
//
// redoMoveToolBarButton
//
this.redoMoveToolBarButton.Enabled = false;
this.redoMoveToolBarButton.ImageIndex = 5;
this.redoMoveToolBarButton.ToolTipText = "Redo Move";
//
// redoAllMovesToolBarButton
//
this.redoAllMovesToolBarButton.Enabled = false;
this.redoAllMovesToolBarButton.ImageIndex = 6;
this.redoAllMovesToolBarButton.ToolTipText = "Redo All Moves";
//
// playImageList
//
this.playImageList.ImageSize = new System.Drawing.Size(16, 16);
this.playImageList.ImageStream = ((System.Windows.Forms.ImageListStreamer)(resources.GetObject("playImageList.ImageStream")));
this.playImageList.TransparentColor = System.Drawing.Color.Transparent;
//
// ReversiForm
//
this.AutoScaleBaseSize = new System.Drawing.Size(5, 13);
this.ClientSize = new System.Drawing.Size(472, 345);
this.Controls.Add(this.playToolBar);
this.Controls.Add(this.boardPanel);
this.Controls.Add(this.infoPanel);
this.Controls.Add(this.statusPanel);
this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon")));
this.KeyPreview = true;
this.Menu = this.mainMenu;
this.Name = "ReversiForm";
this.Text = "Reversi";
this.Resize += new System.EventHandler(this.ReversiForm_Resize);
this.Closing += new System.ComponentModel.CancelEventHandler(this.ReversiForm_Closing);
this.KeyPress += new System.Windows.Forms.KeyPressEventHandler(this.ReversiForm_KeyPress);
this.Move += new System.EventHandler(this.ReversiForm_Move);
this.Closed += new System.EventHandler(this.ReversiForm_Closed);
this.infoPanel.ResumeLayout(false);
this.boardPanel.ResumeLayout(false);
this.statusPanel.ResumeLayout(false);
this.ResumeLayout(false);
}
#endregion
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main()
{
Application.Run(new ReversiForm());
}
// ===================================================================
// This code handles game play.
// ===================================================================
//
// Starts a new game.
//
private void StartGame()
{
this.StartGame(false);
}
//
// Starts a new game or, optionally, restarts an ended game.
//
private void StartGame(bool isRestart)
{
// Enable/disable the menu items and tool bar buttons as
// appropriate.
this.newGameMenuItem.Enabled = this.playToolBar.Buttons[(int) ReversiForm.ToolBarButton.NewGame].Enabled = false;
this.resignGameMenuItem.Enabled = this.playToolBar.Buttons[(int) ReversiForm.ToolBarButton.ResignGame].Enabled = true;
if (!isRestart)
{
this.undoMoveMenuItem.Enabled =
this.undoAllMovesMenuItem.Enabled =
this.playToolBar.Buttons[(int) ReversiForm.ToolBarButton.UndoMove].Enabled =
this.playToolBar.Buttons[(int) ReversiForm.ToolBarButton.UndoAllMoves].Enabled = false;
this.redoMoveMenuItem.Enabled =
this.redoAllMovesMenuItem.Enabled =
this.playToolBar.Buttons[(int) ReversiForm.ToolBarButton.RedoMove].Enabled =
this.playToolBar.Buttons[(int) ReversiForm.ToolBarButton.RedoAllMoves].Enabled = false;
this.resumePlayMenuItem.Enabled =
this.playToolBar.Buttons[(int) ReversiForm.ToolBarButton.ResumePlay].Enabled = false;
}
// Initialize the information display.
this.currentColorTextLabel.Visible = true;
this.currentColorPanel.Visible = true;
// Initialize a new game.
if (!isRestart)
{
// Initialize the move list.
this.moveNumber = 1;
this.moveListView.Items.Clear();
this.moveListView.Refresh();
// Initialize the move history.
this.moveHistory = new ArrayList(60);
// Initialize the last move color.
this.lastMoveColor = Board.Empty;
// Clear the suspend computer play flag.
this.isComputerPlaySuspended = false;
// Initialize the board.
this.board.SetForNewGame();
this.UpdateBoardDisplay();
// Initialize the status display.
this.statusLabel.Text = "";
this.statusPanel.Refresh();
// Set the first player.
this.currentColor = this.options.FirstMove;
}
// Start the first turn.
this.StartTurn();
}
//
// Ends the current game.
//
private void EndGame()
{
this.EndGame(false);
}
//
// Ends the current game, optionally by player resignation.
//
private void EndGame(bool isResignation)
{
// Set the game state.
this.gameState = ReversiForm.GameState.GameOver;
// Stop the game timer.
this.animationTimer.Stop();
// Enable/disable the menu items and tool bar buttons as
// appropriate.
this.newGameMenuItem.Enabled =
this.playToolBar.Buttons[(int) ReversiForm.ToolBarButton.NewGame].Enabled = true;
this.resignGameMenuItem.Enabled =
this.playToolBar.Buttons[(int) ReversiForm.ToolBarButton.ResignGame].Enabled = false;
this.undoMoveMenuItem.Enabled =
this.undoAllMovesMenuItem.Enabled =
this.playToolBar.Buttons[(int) ReversiForm.ToolBarButton.UndoMove].Enabled =
this.playToolBar.Buttons[(int) ReversiForm.ToolBarButton.UndoAllMoves].Enabled = false;
this.redoMoveMenuItem.Enabled =
this.redoAllMovesMenuItem.Enabled =
this.playToolBar.Buttons[(int) ReversiForm.ToolBarButton.RedoMove].Enabled =
this.playToolBar.Buttons[(int) ReversiForm.ToolBarButton.RedoAllMoves].Enabled = false;
this.resumePlayMenuItem.Enabled =
this.playToolBar.Buttons[(int) ReversiForm.ToolBarButton.ResumePlay].Enabled = false;
// Clear the current player indicator display.
this.currentColorTextLabel.Visible = false;
this.currentColorPanel.BackColor = Color.Empty;
this.currentColorPanel.Visible = false;
// Hide the status progress bar.
this.statusProgressBar.Visible = false;
this.statusPanel.Refresh();
// For a computer vs. user game, determine who played what color.
int computerColor = Board.Empty;
int userColor = Board.Empty;
if (this.IsComputerPlayer(Board.Black) && !this.IsComputerPlayer(Board.White))
{
computerColor = Board.Black;
userColor = Board.White;
}
if (this.IsComputerPlayer(Board.White) && !this.IsComputerPlayer(Board.Black))
{
computerColor = Board.White;
userColor = Board.Black;
}
// Save the current statistics, in case the game is restarted.
this.oldStatistics = new Statistics(this.statistics);
// Handle a resignation.
if (isResignation)
{
// For computer vs. computer game, just update the status
// message.
if (this.IsComputerPlayer(Board.Black) && this.IsComputerPlayer(Board.White))
this.statusLabel.Text = "Game aborted.";
else
{
// Determine which player is resigning. In a computer vs.
// user game, the computer will never resign so it must be
// the user. In a user vs. user game we'll assume it is
// the current player.
int resigningColor = this.currentColor;
if (this.IsComputerPlayer(Board.Black) || this.IsComputerPlayer(Board.White))
resigningColor = userColor;
// Update the status message and record the game as a 64-0
// loss for the resigning player.
if (resigningColor == Board.Black)
{
this.statusLabel.Text = "Black resigns.";
this.statistics.Update(0, 64, computerColor, userColor);
}
else
{
this.statusLabel.Text = "White resigns.";
this.statistics.Update(64, 0, computerColor, userColor);
}
}
}
// Handle an end game.
else
{
// Update the status message.
if (this.board.BlackCount > this.board.WhiteCount)
this.statusLabel.Text = "Black wins.";
else if (this.board.WhiteCount > this.board.BlackCount)
this.statusLabel.Text = "White wins.";
else
this.statusLabel.Text = "Draw.";
// Record the result.
this.statistics.Update(this.board.BlackCount, this.board.WhiteCount, computerColor, userColor);
}
// Update the status display.
this.statusPanel.Refresh();
// Re-enable the undo move-related menu items and tool bar
// buttons.
this.undoMoveMenuItem.Enabled =
this.undoAllMovesMenuItem.Enabled =
this.playToolBar.Buttons[(int) ReversiForm.ToolBarButton.UndoMove].Enabled =
this.undoAllMovesMenuItem.Enabled = this.playToolBar.Buttons[(int) ReversiForm.ToolBarButton.UndoAllMoves].Enabled = true;
}
//
// Sets up for the current player to make a move or ends the game if
// neither player can make a valid move.
//
private void StartTurn()
{
// If the current player cannot make a valid move, forfeit the turn.
if (!this.board.HasAnyValidMove(this.currentColor))
{
// Switch back to the other player.
this.currentColor *= -1;
// If the original player cannot make a valid move either, the game is over.
if (!this.board.HasAnyValidMove(this.currentColor))
{
this.EndGame();
return;
}
}
// Set the player text for the status display.
string playerText = String.Format("{0}'s", (this.currentColor == Board.Black ? "Black" : "White"));
if ((this.options.ComputerPlaysBlack && !this.options.ComputerPlaysWhite) ||
(this.options.ComputerPlaysWhite && !this.options.ComputerPlaysBlack))
playerText = (this.IsComputerPlayer(this.currentColor) ? "My" : "Your");
// Update the turn display.
if (this.currentColor == Board.Black)
this.currentColorPanel.BackColor = Color.Black;
else
this.currentColorPanel.BackColor = Color.White;
this.currentColorPanel.Refresh();
// If the current color is under computer control, set up for a
// computer move.
if (this.IsComputerPlayer(this.currentColor))
{
// Set the game state.
this.gameState = ReversiForm.GameState.InComputerMove;
// Check if computer play is currently suspended.
if (this.isComputerPlaySuspended)
{
// Enable the "Resume Play" menu item and tool bar button
// so that the user can resume the game.
this.resumePlayMenuItem.Enabled =
this.playToolBar.Buttons[(int) ReversiForm.ToolBarButton.ResumePlay].Enabled = true;
// Set the status display.
string shortCutText = this.resumePlayMenuItem.Shortcut.ToString();
System.Text.RegularExpressions.Regex re = new System.Text.RegularExpressions.Regex("(Alt|Ctrl|Shift)", System.Text.RegularExpressions.RegexOptions.IgnoreCase);
shortCutText = re.Replace(shortCutText, "$1+");
this.statusLabel.Text = String.Format("{0} move... (Suspended, press {1} to resume play.)", playerText, shortCutText);
this.statusProgressBar.Visible = false;
}
else
{
// Set the status display.
this.statusLabel.Text = String.Format("{0} move, thinking... ", playerText);
this.statusProgressBar.Minimum = 0;
this.statusProgressBar.Maximum = this.board.GetValidMoveCount(this.currentColor);
this.statusProgressBar.Value = 0;
// Position the progress bar just to the right of the text and make it visible.
this.statusProgressBar.Left = this.statusLabel.Left + this.statusLabel.Width;
this.statusProgressBar.Visible = true;
// Start a separate thread to perform the computer's move.
this.calculateComputerMoveThread = new Thread(new ThreadStart(this.CalculateComputerMove));
this.calculateComputerMoveThread.IsBackground = true;
this.calculateComputerMoveThread.Priority = System.Threading.ThreadPriority.Lowest;
this.calculateComputerMoveThread.Name = "Calculate Computer Move";
this.calculateComputerMoveThread.Start();
}
}
// Otherwise, set up for a user move.
else
{
// Set the game state.
this.gameState = ReversiForm.GameState.InPlayerMove;
// Reset the keyed column and row numbers.
this.keyedColNumber = -1;
this.keyedRowNumber = -1;
// Show valid moves, if that option is active.
if (this.options.ShowValidMoves)
{
this.HighlightValidMoves();
this.squaresPanel.Refresh();
}
// Update the status display.
this.statusLabel.Text = String.Format("{0} move...", playerText);
this.statusProgressBar.Visible = false;
// Set focus on the form so it will receive key presses.
this.Focus();
}
// Update the status display.
this.statusPanel.Refresh();
}
//
// Determines if a given color is being played by the computer.
//
private bool IsComputerPlayer(int color)
{
return ((this.options.ComputerPlaysBlack && color == Board.Black) ||
(this.options.ComputerPlaysWhite && color == Board.White));
}
//
// Makes a move for the current player.
//
private void MakeMove(int row, int col)
{
// Clean up the move history to ensure that it contains only the
// moves made prior to this one.
while (this.moveHistory.Count > this.moveNumber - 1)
this.moveHistory.RemoveAt(this.moveHistory.Count - 1);
// Add the move to the move list.
string color = "Black";
if (this.currentColor == Board.White)
color = "White";
string[] subItems =
{
String.Empty,
this.moveNumber.ToString(),
color,
(alpha[col] + (row + 1).ToString())
};
ListViewItem listItem = new ListViewItem(subItems);
this.moveListView.Items.Add(listItem);
// If necessary, scroll the list to bring the last move into view.
this.moveListView.EnsureVisible(this.moveListView.Items.Count - 1);
// Add this move to the move history.
this.moveHistory.Add(new MoveRecord(this.board, this.currentColor, listItem));
// Enable/disable the move-related menu items and tool bar buttons as
// appropriate.
this.undoMoveMenuItem.Enabled =
this.undoAllMovesMenuItem.Enabled =
this.playToolBar.Buttons[(int) ReversiForm.ToolBarButton.UndoMove].Enabled =
this.playToolBar.Buttons[(int) ReversiForm.ToolBarButton.UndoAllMoves].Enabled = true;
this.redoMoveMenuItem.Enabled =
this.redoAllMovesMenuItem.Enabled =
this.playToolBar.Buttons[(int) ReversiForm.ToolBarButton.RedoMove].Enabled =
this.playToolBar.Buttons[(int) ReversiForm.ToolBarButton.RedoAllMoves].Enabled = false;
// Bump the move number.
this.moveNumber++;
// Update the status display.
this.statusLabel.Text = "";
this.statusProgressBar.Visible = false;
this.statusPanel.Refresh();
// Clear any board square highlighting.
this.UnHighlightSquares();
// Make a copy of the board (for doing move animation).
Board oldBoard = new Board(this.board);
// Make the move on the board.
this.board.MakeMove(this.currentColor, row, col);
// If the animate move option is active, set up animation for the
// affected discs.
if (this.options.AnimateMoves)
{
int i, j;
for (i = 0; i < 8; i++)
for (j = 0; j < 8; j++)
{
// Mark the newly added disc.
if (i == row && j == col)
this.squareControls[i, j].IsNew = true;
else
{
// Initialize animation for the discs that were
// flipped.
if (this.board.GetSquareContents(i, j) != oldBoard.GetSquareContents(i, j))
this.squareControls[i, j].AnimationCounter = SquareControl.AnimationStart;
}
}
}
// Update the display to reflect the board changes.
this.UpdateBoardDisplay();
// Save the player color.
this.lastMoveColor = this.currentColor;
// If the animate moves option is active, start the animation.
if (this.options.AnimateMoves)
{
this.gameState = ReversiForm.GameState.InMoveAnimation;
this.animationTimer.Start();
}
// Otherwise, end the move.
else
this.EndMove();
}
//
// Called when a move has been completed (including any animation) to
// start the next turn.
//
private void EndMove()
{
// Set the game state.
this.gameState = ReversiForm.GameState.MoveCompleted;
// Switch players and start the next turn.
this.currentColor *= -1;
this.StartTurn();
}
//
// Updates the animation of a move.
//
private void AnimateMove(Object sender, EventArgs e)
{
// Lock the board to prevent race conditions.
lock (this.board)
{
// If a move is being animated, advance the animation counters on
// the square controls.
if (this.gameState == ReversiForm.GameState.InMoveAnimation)
{
bool isComplete = true;
int i, j;
for (i = 0; i < 8; i++)
for (j = 0; j < 8; j++)
if (this.squareControls[i, j].AnimationCounter > SquareControl.AnimationStop)
{
this.squareControls[i, j].AnimationCounter--;
isComplete = false;
}
// Refresh the display.
this.squaresPanel.Refresh();
// If the animation is complete, end the move.
if (isComplete)
{
this.StopMoveAnimation();
this.UpdateBoardDisplay();
this.EndMove();
}
}
}
}
//
// Stops animation of a move and resets the squares.
//
private void StopMoveAnimation()
{
// Stop the animation timer.
this.animationTimer.Stop();
// Reset the animation counters and new disc flag on all squares.
SquareControl squareControl;
int i, j;
for (i = 0; i < 8; i++)
for (j = 0; j < 8; j++)
{
squareControl = (SquareControl) this.squaresPanel.Controls[i * 8 + j];
squareControl.AnimationCounter = SquareControl.AnimationStop;
squareControl.IsNew = false;
}
}
//
// Makes a player-controlled move for the current color.
//
private void MakePlayerMove(int row, int col)
{
// Allow the computer to resume play.
this.isComputerPlaySuspended = false;
// Make the move.
this.MakeMove(row, col);
}
// ===================================================================
// Code to handle computer moves.
// ===================================================================
//
// Cancels the computer move thread, if it is active.
//
private void KillComputerMoveThread()
{
if (this.calculateComputerMoveThread == null || this.calculateComputerMoveThread.ThreadState == ThreadState.Stopped)
return;
try
{
this.calculateComputerMoveThread.Abort();
this.calculateComputerMoveThread.Join();
}
catch (Exception ex)
{}
finally
{
this.calculateComputerMoveThread = null;
}
}
//
// Define delegates for callbacks from the worker thread.
//
public delegate void UpdateStatusProgressDelegate();
public delegate void MakeComputerMoveDelegate(int row, int col);
//
// Updates the status progress bar.
// Note: Called from the worker thread.
//
private void UpdateStatusProgress()
{
// Increase the progress bar value by one.
if (this.statusProgressBar.Value < this.statusProgressBar.Maximum)
{
this.statusProgressBar.Value++;
this.statusProgressBar.Refresh();
}
}
//
// Makes a computer-controlled move for the current color.
// Note: Called from the worker thread.
//
private void MakeComputerMove(int row, int col)
{
// Lock the board to prevent a race condition while performing the
// move.
lock (this.board)
{
// Make the move.
this.MakeMove(row, col);
}
}
//
// Calculates a computer move.
// Note: Executed in the worker thread.
//
private void CalculateComputerMove()
{
// Load the AI parameters.
this.SetAIParameters();
// Find the best available move.
ComputerMove move = this.GetBestMove(this.board);
// Perform a callback to make the move.
Object[] args = { move.row, move.col };
MakeComputerMoveDelegate moveDelegate = new MakeComputerMoveDelegate(this.MakeComputerMove);
this.BeginInvoke(moveDelegate, args);
}
// ===================================================================
// Game AI code.
// Note: These are executed in the worker thread.
// ===================================================================
//
// This function starts the look ahead process to find the best move
// for the current player color.
//
private ComputerMove GetBestMove(Board board)
{
// Initialize the alpha-beta cutoff values.
int alpha = ReversiForm.maxRank + 64;
int beta = -alpha;
// Kick off the look ahead.
return this.GetBestMove(board, this.currentColor, 1, alpha, beta);
}
//
// This function uses look ahead to evaluate all valid moves for a
// given player color and returns the best move it can find.
//
private ComputerMove GetBestMove(Board board, int color, int depth, int alpha, int beta)
{
// Initialize the best move.
ComputerMove bestMove = new ComputerMove(-1, -1);
bestMove.rank = -color * ReversiForm.maxRank;
// Find out how many valid moves we have so we can initialize the
// mobility score.
int validMoves = board.GetValidMoveCount(color);
// Start at a random position on the board. This way, if two or
// more moves are equally good, we'll take one of them at random.
Random random = new Random();
int rowStart = random.Next(8);
int colStart = random.Next(8);
// Check all valid moves.
int i, j;
for (i = 0; i < 8; i++)
for (j = 0; j < 8; j++)
{
// Get the row and column.
int row = (rowStart + i) % 8;
int col = (colStart + j) % 8;
if (board.IsValidMove(color, row, col))
{
// Update the progress bar for each move when on the
// first look ahead depth level.
if (depth == 1)
this.BeginInvoke(new UpdateStatusProgressDelegate(this.UpdateStatusProgress));
// Make the move.
ComputerMove testMove = new ComputerMove(row, col);
Board testBoard = new Board(board);
testBoard.MakeMove(color, testMove.row, testMove.col);
int score = testBoard.WhiteCount - testBoard.BlackCount;
// Check the board.
int nextColor = -color;
int forfeit = 0;
bool isEndGame = false;
int opponentValidMoves = testBoard.GetValidMoveCount(nextColor);
if (opponentValidMoves == 0)
{
// The opponent cannot move, count the forfeit.
forfeit = color;
// Switch back to the original color.
nextColor = -nextColor;
// If that player cannot make a move either, the
// game is over.
if (!testBoard.HasAnyValidMove(nextColor))
isEndGame = true;
}
// If we reached the end of the look ahead (end game or
// max depth), evaluate the board and set the move
// rank.
if (isEndGame || depth == this.lookAheadDepth)
{
// For an end game, max the ranking and add on the
// final score.
if (isEndGame)
{
// Negative value for black win.
if (score < 0)
testMove.rank = -ReversiForm.maxRank + score;
// Positive value for white win.
else if (score > 0)
testMove.rank = ReversiForm.maxRank + score;
// Zero for a draw.
else
testMove.rank = 0;
}
// It's not an end game so calculate the move rank.
else
testMove.rank =
this.forfeitWeight * forfeit +
this.frontierWeight * (testBoard.BlackFrontierCount - testBoard.WhiteFrontierCount) +
this.mobilityWeight * color * (validMoves - opponentValidMoves) +
this.stabilityWeight * (testBoard.WhiteSafeCount - testBoard.BlackSafeCount) +
score;
}
// Otherwise, perform a look ahead.
else
{
ComputerMove nextMove = this.GetBestMove(testBoard, nextColor, depth + 1, alpha, beta);
// Pull up the rank.
testMove.rank = nextMove.rank;
// Forfeits are cumulative, so if the move did not
// result in an end game, add any current forfeit
// value to the rank.
if (forfeit != 0 && Math.Abs(testMove.rank) < ReversiForm.maxRank)
testMove.rank += forfeitWeight * forfeit;
// Adjust the alpha and beta values, if necessary.
if (color == Board.White && testMove.rank > beta)
beta = testMove.rank;
if (color == Board.Black && testMove.rank < alpha)
alpha = testMove.rank;
}
// Perform a cutoff if the rank is outside tha alpha-beta range.
if (color == Board.White && testMove.rank > alpha)
{
testMove.rank = alpha;
return testMove;
}
if (color == Board.Black && testMove.rank < beta)
{
testMove.rank = beta;
return testMove;
}
// If this is the first move tested, assume it is the
// best for now.
if (bestMove.row < 0)
bestMove = testMove;
// Otherwise, compare the test move to the current
// best move and take the one that is better for this
// color.
else if (color * testMove.rank > color * bestMove.rank)
bestMove = testMove;
}
}
// Return the best move found.
return bestMove;
}
//
// Sets the AI parameters based on the current difficulty setting.
//
private void SetAIParameters()
{
// Set the AI parameter weights.
switch (this.options.Difficulty)
{
case ReversiForm.Difficulty.Beginner:
this.forfeitWeight = 2;
this.frontierWeight = 1;
this.mobilityWeight = 0;
this.stabilityWeight = 3;
break;
case ReversiForm.Difficulty.Intermediate:
this.forfeitWeight = 3;
this.frontierWeight = 1;
this.mobilityWeight = 0;
this.stabilityWeight = 5;
break;
case ReversiForm.Difficulty.Advanced:
this.forfeitWeight = 7;
this.frontierWeight = 2;
this.mobilityWeight = 1;
this.stabilityWeight = 10;
break;
case ReversiForm.Difficulty.Expert:
this.forfeitWeight = 35;
this.frontierWeight = 10;
this.mobilityWeight = 5;
this.stabilityWeight = 50;
break;
default:
this.forfeitWeight = 0;
this.frontierWeight = 0;
this.mobilityWeight = 0;
this.stabilityWeight = 0;
break;
}
// Set the look-ahead depth.
this.lookAheadDepth = (int) this.options.Difficulty + 3;
// Near the end of the game, when there are relatively few moves
// left, set the look-ahead depth to do an exhaustive search.
if (this.moveNumber >= 55 - (int) this.options.Difficulty)
this.lookAheadDepth = this.board.EmptyCount;
}
// ===================================================================
// Code to handle undo/redo of moves.
// ===================================================================
//
// Restores the game to the state it was in before the specified move
// was made.
//
private void RestoreGameAt(int n)
{
// Get the move record.
MoveRecord item = (MoveRecord) this.moveHistory[n];
// Stop any animation.
this.StopMoveAnimation();
// Clear any board square highlighting.
this.UnHighlightSquares();
// Restore the board and update the display.
this.board = new Board(item.board);
this.UpdateBoardDisplay();
// Restore the current player.
this.currentColor = item.currentColor;
// Restore the move list.
this.moveListView.Items.Clear();
for (int i = 0; i < n; i++)
{
item = (MoveRecord) this.moveHistory[i];
this.moveListView.Items.Add(item.moveListItem);
}
if (this.moveListView.Items.Count > 0)
this.moveListView.EnsureVisible(this.moveListView.Items.Count - 1);
else
this.moveListView.Refresh();
// Set the current move number.
this.moveNumber = n + 1;
// Enable/disable the move-related menu items and tool bar buttons
// as appropriate.
this.undoMoveMenuItem.Enabled =
this.undoAllMovesMenuItem.Enabled =
this.playToolBar.Buttons[(int) ReversiForm.ToolBarButton.UndoMove].Enabled =
this.playToolBar.Buttons[(int) ReversiForm.ToolBarButton.UndoAllMoves].Enabled = (this.moveNumber > 1);
this.redoMoveMenuItem.Enabled =
this.redoAllMovesMenuItem.Enabled =
this.playToolBar.Buttons[(int) ReversiForm.ToolBarButton.RedoMove].Enabled =
this.playToolBar.Buttons[(int) ReversiForm.ToolBarButton.RedoAllMoves].Enabled = (this.moveNumber < this.moveHistory.Count);
this.resumePlayMenuItem.Enabled =
this.playToolBar.Buttons[(int) ReversiForm.ToolBarButton.ResumePlay].Enabled = false;
// Suspend computer play.
this.isComputerPlaySuspended = true;
}
//
// Undoes the previous move or all moves.
//
private void UndoMove(bool undoAll)
{
// Save the current game state so we'll know if we need to perform
// a restart.
GameState oldGameState = this.gameState;
// Stop the computer move thread, if active.
this.KillComputerMoveThread();
// Lock the board to prevent any changes by an active computer
// move.
lock (this.board)
{
// When undoing the last move, we need to save the current
// board and player color in the move history so that it can
// be restored if the move is redone.
if (this.moveHistory.Count < this.moveNumber)
{
// Add the data to the move history.
this.moveHistory.Add(new MoveRecord(this.board, -this.lastMoveColor, new ListViewItem()));
}
// Undo either the previous move or all moves.
this.RestoreGameAt((undoAll ? 0 : this.moveNumber - 2));
}
// If the game was over, restore the statistics and restart it.
if (oldGameState == ReversiForm.GameState.GameOver)
{
this.statistics = new Statistics(this.oldStatistics);
this.StartGame(true);
}
// Otherwise, start play at that move.
else
this.StartTurn();
}
//
// Redoes the next move or all moves.
//
private void RedoMove(bool redoAll)
{
// Redo either the next move or all moves.
this.RestoreGameAt((redoAll ? this.moveHistory.Count - 1 : this.moveNumber));
// Start play at that move.
this.StartTurn();
}
// ===================================================================
// Code to handle the board display.
// ===================================================================
//
// Updates the display to reflect the current game board.
//
private void UpdateBoardDisplay()
{
// Set counts.
this.blackCountLabel.Text = this.board.BlackCount.ToString();
this.blackCountLabel.Refresh();
this.whiteCountLabel.Text = this.board.WhiteCount.ToString();
this.whiteCountLabel.Refresh();
// Map the current game board to the square controls.
SquareControl squareControl;
int i, j;
for (i = 0; i < 8; i++)
for (j = 0; j < 8; j++)
{
squareControl = (SquareControl) this.squaresPanel.Controls[i * 8 + j];
squareControl.Contents = this.board.GetSquareContents(i, j);
squareControl.PreviewContents = Board.Empty;
}
// Redraw the board.
this.squaresPanel.Refresh();
}
//
// Highlights the board squares that represent valid moves for the
// current player.
//
private void HighlightValidMoves()
{
// Check each square.
SquareControl squareControl;
int i, j;
for (i = 0; i < 8; i++)
for (j = 0; j < 8; j++)
{
squareControl = (SquareControl) this.squaresPanel.Controls[i * 8 + j];
if (this.board.IsValidMove(this.currentColor, i, j))
squareControl.IsValid = true;
else
squareControl.IsValid = false;
}
}
//
// Removes any highlighting from all the board squares.
//
private void UnHighlightSquares()
{
// Clear the flags on each square.
SquareControl squareControl;
int i, j;
for (i = 0; i < 8; i++)
for (j = 0; j < 8; j++)
{
squareControl = (SquareControl) this.squaresPanel.Controls[i * 8 + j];
squareControl.IsActive = false;
squareControl.IsValid = false;
squareControl.IsNew = false;
}
}
//
// Sets the board square colors based on the current game options.
//
private void SetSquareControlColors()
{
SquareControl.ActiveSquareBackColor = this.options.ActiveSquareColor;
SquareControl.NormalBackColor = this.options.BoardColor;
SquareControl.MoveIndicatorColor = this.options.MoveIndicatorColor;
SquareControl.ValidMoveBackColor = this.options.ValidMoveColor;
}
//====================================================================
// These functions to handle the loading and saving of the program
// settings.
//====================================================================
//
// Loads any saved program settings.
//
private void LoadProgramSettings()
{
// Load the saved window settings and resize the window.
try
{
// Load the saved window settings.
int left = System.Int32.Parse(this.settings.GetValue("Window", "Left"));
int top = System.Int32.Parse(this.settings.GetValue("Window", "Top"));
int width = System.Int32.Parse(this.settings.GetValue("Window", "Width"));
int height = System.Int32.Parse(this.settings.GetValue("Window", "Height"));
// Reposition and resize the window.
this.StartPosition = System.Windows.Forms.FormStartPosition.Manual;
this.DesktopLocation = new Point(left, top);
this.ClientSize = new Size(width, height);
// Load saved options.
this.options.ShowValidMoves = (bool) System.Boolean.Parse(this.settings.GetValue("Options", "ShowValidMoves"));
this.options.PreviewMoves = (bool) System.Boolean.Parse(this.settings.GetValue("Options", "PreviewMoves"));
this.options.AnimateMoves = (bool) System.Boolean.Parse(this.settings.GetValue("Options", "AnimateMoves"));
this.options.BoardColor = Color.FromArgb(System.Int32.Parse(this.settings.GetValue("Options", "BoardColor")));
this.options.ValidMoveColor = Color.FromArgb(System.Int32.Parse(this.settings.GetValue("Options", "ValidMoveColor")));
this.options.ActiveSquareColor = Color.FromArgb(System.Int32.Parse(this.settings.GetValue("Options", "ActiveSquareColor")));
this.options.MoveIndicatorColor = Color.FromArgb(System.Int32.Parse(this.settings.GetValue("Options", "MoveIndicatorColor")));
this.options.FirstMove = (int) System.Int32.Parse(this.settings.GetValue("Options", "FirstMove"));
this.options.ComputerPlaysBlack = (bool) System.Boolean.Parse(this.settings.GetValue("Options", "ComputerPlaysBlack"));
this.options.ComputerPlaysWhite = (bool) System.Boolean.Parse(this.settings.GetValue("Options", "ComputerPlaysWhite"));
this.options.Difficulty = (ReversiForm.Difficulty) System.Enum.Parse(this.options.Difficulty.GetType(), this.settings.GetValue("Options", "Difficulty"), true);
// Set the square control colors based on options loaded.
this.SetSquareControlColors();
// Load saved statistics.
this.statistics.BlackWins = System.Int32.Parse(settings.GetValue("Statistics", "BlackWins"));
this.statistics.WhiteWins = System.Int32.Parse(settings.GetValue("Statistics", "WhiteWins"));
this.statistics.OverallDraws = System.Int32.Parse(settings.GetValue("Statistics", "OverallDraws"));
this.statistics.BlackTotalScore = System.Int32.Parse(settings.GetValue("Statistics", "BlackTotalScore"));
this.statistics.WhiteTotalScore = System.Int32.Parse(settings.GetValue("Statistics", "WhiteTotalScore"));
this.statistics.ComputerWins = System.Int32.Parse(settings.GetValue("Statistics", "ComputerWins"));
this.statistics.UserWins = System.Int32.Parse(settings.GetValue("Statistics", "UserWins"));
this.statistics.VsComputerDraws = System.Int32.Parse(settings.GetValue("Statistics", "VsComputerDraws"));
this.statistics.ComputerTotalScore = System.Int32.Parse(settings.GetValue("Statistics", "ComputerTotalScore"));
this.statistics.UserTotalScore = System.Int32.Parse(settings.GetValue("Statistics", "UserTotalScore"));
}
catch (Exception ex)
{}
}
//
// Saves the current program settings.
//
private void SaveProgramSettings()
{
// Save window settings.
this.settings.SetValue("Window", "Left", this.windowSettings.Left.ToString());
this.settings.SetValue("Window", "Top", this.windowSettings.Top.ToString());
this.settings.SetValue("Window", "Width", this.windowSettings.Width.ToString());
this.settings.SetValue("Window", "Height", this.windowSettings.Height.ToString());
// Save game options.
this.settings.SetValue("Options", "ShowValidMoves", this.options.ShowValidMoves.ToString());
this.settings.SetValue("Options", "PreviewMoves", this.options.PreviewMoves.ToString());
this.settings.SetValue("Options", "AnimateMoves", this.options.AnimateMoves.ToString());
this.settings.SetValue("Options", "BoardColor", SquareControl.NormalBackColor.ToArgb().ToString());
this.settings.SetValue("Options", "ValidMoveColor", SquareControl.ValidMoveBackColor.ToArgb().ToString());
this.settings.SetValue("Options", "ActiveSquareColor", SquareControl.ActiveSquareBackColor.ToArgb().ToString());
this.settings.SetValue("Options", "MoveIndicatorColor", SquareControl.MoveIndicatorColor.ToArgb().ToString());
this.settings.SetValue("Options", "FirstMove", this.options.FirstMove.ToString());
this.settings.SetValue("Options", "ComputerPlaysBlack", this.options.ComputerPlaysBlack.ToString());
this.settings.SetValue("Options", "ComputerPlaysWhite", this.options.ComputerPlaysWhite.ToString());
this.settings.SetValue("Options", "Difficulty", this.options.Difficulty.ToString());
// Save statistics.
this.settings.SetValue("Statistics", "BlackWins", this.statistics.BlackWins.ToString());
this.settings.SetValue("Statistics", "WhiteWins", this.statistics.WhiteWins.ToString());
this.settings.SetValue("Statistics", "OverallDraws", this.statistics.OverallDraws.ToString());
this.settings.SetValue("Statistics", "BlackTotalScore", this.statistics.BlackTotalScore.ToString());
this.settings.SetValue("Statistics", "WhiteTotalScore", this.statistics.WhiteTotalScore.ToString());
this.settings.SetValue("Statistics", "ComputerWins", this.statistics.ComputerWins.ToString());
this.settings.SetValue("Statistics", "UserWins", this.statistics.UserWins.ToString());
this.settings.SetValue("Statistics", "VsComputerDraws", this.statistics.VsComputerDraws.ToString());
this.settings.SetValue("Statistics", "ComputerTotalScore", this.statistics.ComputerTotalScore.ToString());
this.settings.SetValue("Statistics", "UserTotalScore", this.statistics.UserTotalScore.ToString());
// Save the program settings.
this.settings.Save();
}
//====================================================================
// Event handlers for the form.
//====================================================================
//
// Handles a window close.
//
private void ReversiForm_Closed(object sender, System.EventArgs e)
{
// Stop the computer move thread, if active.
this.KillComputerMoveThread();
// Save the current program settings.
this.SaveProgramSettings();
}
//
// Handles a window close request. If a game is active, it will prompt
// for confirmation first.
//
private void ReversiForm_Closing(object sender, System.ComponentModel.CancelEventArgs e)
{
bool doClose = true;
// Prompt for confirmation if a game is in progress.
if (this.gameState != ReversiForm.GameState.GameOver)
{
// Create and show the confirm dialog, saving the result.
ConfirmDialog dlg = new ConfirmDialog("Exit the program?");
if (dlg.ShowDialog(this) != DialogResult.Yes)
doClose = false;
dlg.Dispose();
}
// Cancel the close request if necessary.
if (!doClose)
e.Cancel = true;
}
//
// Handles a key press.
//
private void ReversiForm_KeyPress(object sender, System.Windows.Forms.KeyPressEventArgs e)
{
// Check the game state to ensure it is the user's turn.
if (this.gameState != ReversiForm.GameState.InPlayerMove)
return;
// Convert the key character to column/row number.
string s = e.KeyChar.ToString().ToUpper();
int col = ReversiForm.alpha.IndexOf(s);
int row;
try
{
row = System.Int32.Parse(s) - 1;
}
catch
{
row = -1;
}
// Is the key a valid column letter?
if (col >= 0 && col < 8)
{
// Yes; store it, clear the keyed row number and exit.
this.keyedColNumber = col;
this.keyedRowNumber = -1;
return;
}
// If we don't have a valid column number yet, exit.
if (this.keyedColNumber < 0 || this.keyedColNumber > 7)
return;
// We have a valid column number. Is the key a valid row number?
if (row < 0 || row > 7)
{
// No, reset both and exit.
this.keyedColNumber = -1;
this.keyedRowNumber = -1;
return;
}
else
this.keyedRowNumber = row;
// The keyed column and row numbers are valid. Is the position a
// valid move?
if (this.board.IsValidMove(this.currentColor, this.keyedRowNumber, this.keyedColNumber))
{
// Yes, make it.
this.MakePlayerMove(this.keyedRowNumber, this.keyedColNumber);
}
else
{
// No, reset both.
this.keyedColNumber = -1;
this.keyedRowNumber = -1;
}
}
//
// Handles a window move.
//
private void ReversiForm_Move(object sender, System.EventArgs e)
{
// If the window has not been minimized or maximized, save its location.
if (this.WindowState == FormWindowState.Normal)
{
this.windowSettings.X = this.DesktopLocation.X;
this.windowSettings.Y = this.DesktopLocation.Y;
}
}
//
// Handles a window resize.
//
private void ReversiForm_Resize(object sender, System.EventArgs e)
{
// Determine the size each square should be within the board.
int l = (int) (Math.Min(this.squaresPanel.Width, this.squaresPanel.Height) / 8);
l = Math.Max(l, 8);
// Resize and reposition each square control.
int i, j;
for (i = 0; i < 8; i++)
for (j = 0; j < 8; j++)
{
this.squareControls[i, j].Width = l;
this.squareControls[i, j].Height = l;
this.squareControls[i, j].Left = j * l;
this.squareControls[i, j].Top = i * l;
}
// Fix the column and row headers.
for (i = 0; i < 8; i++)
{
this.colLabels[i].Width = l;
this.colLabels[i].Left = this.cornerLabel.Width + i * l;
this.rowLabels[i].Height = l;
this.rowLabels[i].Top = this.cornerLabel.Height + i * l;
}
// Fix the info panel height to align it's bottom with the bottom row of squares.
this.infoPanel.Height = 8 * l + this.colLabels[0].Height;
// Position the progress bar just to the right of the satus text.
this.statusProgressBar.Left = this.statusLabel.Left + this.statusLabel.Width;
// If the window has not been minimized or maximized, save it size.
if (this.WindowState == FormWindowState.Normal)
{
this.windowSettings.Width = this.ClientSize.Width;
this.windowSettings.Height = this.ClientSize.Height;
}
}
//====================================================================
// Event handlers for the menu items.
//====================================================================
//
// Handles a "New Game" click.
//
private void newGameMenuItem_Click(object sender, System.EventArgs e)
{
// Start a new game.
this.StartGame();
}
//
// Handles a "Resign Game" click.
//
private void resignGameMenuItem_Click(object sender, System.EventArgs e)
{
bool doEnd = true;
// Prompt for confirmation if a game is in progress.
if (this.gameState != ReversiForm.GameState.GameOver)
{
// Create and show the confirm dialog, saving the result.
ConfirmDialog dlg = new ConfirmDialog("Resign this game?");
if (dlg.ShowDialog(this) != DialogResult.Yes)
doEnd = false;
dlg.Dispose();
}
// End the game if the request was not cancelled.
if (doEnd)
{
// Stop the computer move thread, if active.
this.KillComputerMoveThread();
// Stop any active animation and reset the board display.
this.StopMoveAnimation();
this.UnHighlightSquares();
this.UpdateBoardDisplay();
// End the game with the resignation flag set.
this.EndGame(true);
}
}
//
// Handles an "Options..." click.
//
private void optionsMenuItem_Click(object sender, System.EventArgs e)
{
// Create the options dialog and set the option controls according
// to the current game options.
OptionsDialog dlg = new OptionsDialog(this.options);
// Show the options dialog and if the "OK" button was pressed,
// update the game options.
if (dlg.ShowDialog(this) == DialogResult.OK)
{
// Update the game options that are safe to change using the
// values from the dialog.
this.options.ShowValidMoves = dlg.Options.ShowValidMoves;
this.options.PreviewMoves = dlg.Options.PreviewMoves;
this.options.AnimateMoves = dlg.Options.AnimateMoves;
this.options.BoardColor = dlg.Options.BoardColor;
this.options.ValidMoveColor = dlg.Options.ValidMoveColor;
this.options.ActiveSquareColor = dlg.Options.ActiveSquareColor;
this.options.MoveIndicatorColor = dlg.Options.MoveIndicatorColor;
this.options.Difficulty = dlg.Options.Difficulty;
// Set the square control colors based on the current color options.
this.SetSquareControlColors();
// If a game is currently in progress, special handling is
// needed for changes to the player options.
if (this.gameState != ReversiForm.GameState.GameOver)
{
// Lock the board to prevent race conditions.
lock (this.board)
{
// If a move is currently being animated, complete it
// now.
if (this.gameState == ReversiForm.GameState.InMoveAnimation)
{
// Stop animation and end the move.
this.StopMoveAnimation();
this.UpdateBoardDisplay();
this.EndMove();
}
// Clear any board square highlighting.
this.UnHighlightSquares();
// If the changes to the player options affect the
// current player, restart the current turn.
if ((dlg.Options.FirstMove != this.options.FirstMove && this.lastMoveColor == Board.Empty) ||
dlg.Options.ComputerPlaysBlack != this.options.ComputerPlaysBlack ||
dlg.Options.ComputerPlaysWhite != this.options.ComputerPlaysWhite)
{
// Kill any currently active computer move.
this.KillComputerMoveThread();
// Set the player options.
this.options.FirstMove = dlg.Options.FirstMove;
this.options.ComputerPlaysBlack = dlg.Options.ComputerPlaysBlack;
this.options.ComputerPlaysWhite = dlg.Options.ComputerPlaysWhite;
// If no moves have been made yet, set the current color
// based on the first move option.
if (this.lastMoveColor == Board.Empty)
this.currentColor = this.options.FirstMove;
// Update the board display.
this.squaresPanel.Refresh();
// Restart the current turn.
this.StartTurn();
}
// Otherwise, set the player options and update the
// board display.
else
{
// Set the player options.
this.options.FirstMove = dlg.Options.FirstMove;
this.options.ComputerPlaysBlack = dlg.Options.ComputerPlaysBlack;
this.options.ComputerPlaysWhite = dlg.Options.ComputerPlaysWhite;
// Highlight valid moves, if appropriate.
if (this.options.ShowValidMoves && !this.IsComputerPlayer(this.currentColor))
this.HighlightValidMoves();
// Update the board display.
this.squaresPanel.Refresh();
}
}
}
// A game is not in progress so just set the player options.
else
{
this.options.FirstMove = dlg.Options.FirstMove;
this.options.ComputerPlaysBlack = dlg.Options.ComputerPlaysBlack;
this.options.ComputerPlaysWhite = dlg.Options.ComputerPlaysWhite;
// Update the board display, in case the colors have been
// changed.
this.squaresPanel.Refresh();
}
}
dlg.Dispose();
}
//
// Handles a "Statistics..." click.
//
private void statisticsMenuItem_Click(object sender, System.EventArgs e)
{
// Create and show the statistics dialog.
StatisticsDialog dlg = new StatisticsDialog(this.statistics);
dlg.ShowDialog(this);
dlg.Dispose();
}
//
// Handles an "Exit" click.
//
private void exitMenuItem_Click(object sender, System.EventArgs e)
{
// Close the form.
this.Close();
}
//
// Handles an "Undo Move" click.
//
private void undoMoveMenuItem_Click(object sender, System.EventArgs e)
{
// Undo the previous move.
this.UndoMove(false);
}
//
// Handles a "Redo Move" click.
//
private void redoMoveMenuItem_Click(object sender, System.EventArgs e)
{
// Redo the next move.
this.RedoMove(false);
}
//
// Handles an "Undo All Moves" click.
//
private void undoAllMovesmenuItem_Click(object sender, System.EventArgs e)
{
// Undo all moves.
this.UndoMove(true);
}
//
// Handles a "Redo All Moves" click.
//
private void redoAllMovesMenuItem_Click(object sender, System.EventArgs e)
{
// Redo all moves.
this.RedoMove(true);
}
//
// Handles a "Resume Play" click.
//
private void resumePlayMenuItem_Click(object sender, System.EventArgs e)
{
// Disable the "Redo Move," "Redo All Moves" and "Resume Play"
// menu items and tool bar buttons.
this.redoMoveMenuItem.Enabled =
this.redoAllMovesMenuItem.Enabled =
this.playToolBar.Buttons[(int) ReversiForm.ToolBarButton.RedoMove].Enabled =
this.playToolBar.Buttons[(int) ReversiForm.ToolBarButton.RedoAllMoves].Enabled = false;
this.resumePlayMenuItem.Enabled =
this.playToolBar.Buttons[(int) ReversiForm.ToolBarButton.ResumePlay].Enabled = false;
// Set the game state.
this.gameState = ReversiForm.GameState.MoveCompleted;
// Clear the suspend computer play flag and restart the turn.
this.isComputerPlaySuspended = false;
this.StartTurn();
}
//
// Handles a "Help Topics" click.
//
private void helpTopicsMenuItem_Click(object sender, System.EventArgs e)
{
// If the help file exists, show it. Otherwise, display an error
// message.
System.IO.FileInfo fileInfo = new System.IO.FileInfo(ReversiForm.helpFileName);
if (fileInfo.Exists)
Help.ShowHelp(this, ReversiForm.helpFileName);
else
MessageBox.Show(this,
String.Format("Help file '{0}' not found.", ReversiForm.helpFileName),
"File Not Found",
MessageBoxButtons.OK,
MessageBoxIcon.Error);
}
//
// Handles an "About" click.
//
private void aboutMenuItem_Click(object sender, System.EventArgs e)
{
// Create and show the about dialog.
AboutDialog dlg = new AboutDialog();
dlg.ShowDialog(this);
dlg.Dispose();
}
//====================================================================
// Event handlers for the tool bar.
//====================================================================
//
// Handles a button click on the tool bar.
//
private void playToolBar_ButtonClick(object sender, System.Windows.Forms.ToolBarButtonClickEventArgs e)
{
// Determine which button was clicked and simulate a click on the
// corresponding menu item.
switch(this.playToolBar.Buttons.IndexOf(e.Button))
{
case (int) ReversiForm.ToolBarButton.NewGame:
this.newGameMenuItem.PerformClick();
break;
case (int) ReversiForm.ToolBarButton.ResignGame:
this.resignGameMenuItem.PerformClick();
break;
case (int) ReversiForm.ToolBarButton.UndoAllMoves:
this.undoAllMovesMenuItem.PerformClick();
break;
case (int) ReversiForm.ToolBarButton.UndoMove:
this.undoMoveMenuItem.PerformClick();
break;
case (int) ReversiForm.ToolBarButton.ResumePlay:
this.resumePlayMenuItem.PerformClick();
break;
case (int) ReversiForm.ToolBarButton.RedoMove:
this.redoMoveMenuItem.PerformClick();
break;
case (int) ReversiForm.ToolBarButton.RedoAllMoves:
this.redoAllMovesMenuItem.PerformClick();
break;
default:
break;
}
}
// ===================================================================
// Event handlers for the square controls.
// ===================================================================
//
// Handles a mouse move on a board square.
//
private void SquareControl_MouseMove(object sender, MouseEventArgs e)
{
// Check the game state to ensure that it is the user's turn.
if (this.gameState != ReversiForm.GameState.InPlayerMove)
return;
SquareControl squareControl = (SquareControl) sender;
// If the square is a valid move for the current player,
// indicate it.
if (this.board.IsValidMove(this.currentColor, squareControl.Row, squareControl.Col))
{
//
if (!squareControl.IsActive && squareControl.PreviewContents == Board.Empty)
{
// If the show valid moves option is active, mark the
// square.
if (this.options.ShowValidMoves)
{
squareControl.IsActive = true;
// If the preview moves option is not active, update
// the square display now.
if (!this.options.PreviewMoves)
squareControl.Refresh();
}
// If the preview moves option is active, mark the
// appropriate squares.
if (this.options.PreviewMoves)
{
// Create a temporary board to make the move on.
Board board = new Board(this.board);
board.MakeMove(this.currentColor, squareControl.Row, squareControl.Col);
// Set up the move preview.
for (int i = 0; i < 8; i++)
for (int j = 0; j < 8; j++)
if (board.GetSquareContents(i, j) != this.board.GetSquareContents(i, j))
{
// Set and update the square display.
this.squareControls[i, j].PreviewContents = board.GetSquareContents(i, j);
this.squareControls[i, j].Refresh();
}
}
}
// Change the cursor.
squareControl.Cursor = System.Windows.Forms.Cursors.Hand;
}
}
//
// Handles a mouse leave on a board square.
//
private void SquareControl_MouseLeave(object sender, System.EventArgs e)
{
SquareControl squareControl = (SquareControl) sender;
// If the square is currently active, deactivate it.
if (squareControl.IsActive)
{
squareControl.IsActive = false;
squareControl.Refresh();
}
// If the move is being previewed, clear all affected squares.
if (squareControl.PreviewContents != Board.Empty)
{
// Clear the move preview.
for (int i = 0; i < 8; i++)
for (int j = 0; j < 8; j++)
if (this.squareControls[i, j].PreviewContents != Board.Empty)
{
this.squareControls[i, j].PreviewContents = Board.Empty;
this.squareControls[i, j].Refresh();
}
}
// Restore the cursor.
squareControl.Cursor = System.Windows.Forms.Cursors.Default;
}
//
// Handles a click on a board square.
//
private void SquareControl_Click(object sender, System.EventArgs e)
{
// Check the game state to ensure it's the user's turn.
if (this.gameState != ReversiForm.GameState.InPlayerMove)
return;
SquareControl squareControl = (SquareControl) sender;
// If the move is valid, make it.
if (this.board.IsValidMove(this.currentColor, squareControl.Row, squareControl.Col))
{
// Restore the cursor.
squareControl.Cursor = System.Windows.Forms.Cursors.Default;
// Make the move.
this.MakePlayerMove(squareControl.Row, squareControl.Col);
}
}
}
}