You are on page 1of 11

15/8/2021 Reversi in C# - CodeProject

14,993,632 members Sign in

articles quick answers discussions features community help Search for articles, questions, tips

Articles / Programming Languages / C#


 

Reversi in C#
BrainJar Rate me: 4.94/5 (188 votes)
26 Sep 2005 13 min read

The game of Reversi in C#.

Download executable - 126 Kb


Download source files - 268 Kb

https://www.codeproject.com/Articles/4672/Reversi-in-C 1/11
15/8/2021 Reversi in C# - CodeProject

Introduction
This is an implementation of the game Reversi, written in C#.

Background
I originally wrote this program as an exercise for learning C# and .NET programming in general. Reversi - or Othello as it is also known - is a popular game that is fun, requires just a
few basic elements and has simple rules. This made it a good choice for learning a new programming environment.

The result was a fairly playable game but it lacked some of the more common features of computer board games, such as the ability to undo moves. So, having gained more
experience with .NET, the time came for an upgrade. The result adds some new features and improves on the original graphics and AI.

Using the Code

https://www.codeproject.com/Articles/4672/Reversi-in-C 2/11
15/8/2021 Reversi in C# - CodeProject

Compile the source files and run the resulting Reversi.exe executable to play the game. You can play around with the various options and settings using the menu or toolbar. Try
resizing the window, changing colors or switching sides with the computer in mid-game.

You may note that the program creates a file called Reversi.xml when you exit it. This file is used to save various settings such as game options, window size, and position and player
statistics which are reloaded when the program is run again.

Help Files
A Windows help file is included with the source code. It can be found in the help files subdirectory within the archive. To make the help file available to the program, simply copy the
Reversi.chm file from there to the directory where the Reversi.exe executable is located. You can run the program without it, but clicking the Help Topics option will display an error
saying the file cannot be found.

All the source files used to create this help file are included in that subdirectory. You can edit and recompile it using the Microsoft HTML Help Workshop.

Points of Interest
The source is pretty well commented but some general overviews are in order.

Game AI
A good portion of the code is related to calculating moves for the computer player so it is worth discussing. The program uses a standard min-max look-ahead algorithm to
determine the best move for the computer. Alpha-beta pruning is used to improve the efficiency of the look-ahead search. If you're not familiar with the min-max algorithm and/or
alpha-beta pruning, a Google search will turn up plenty of information and examples.

Naturally, there are too many possible sequences of moves in the game to do an exhaustive look-ahead search, it would simply take too long to generate all possible move
combinations. The exception is towards the end of a game where there are relatively few empty squares left - around ten or twelve. At this point, a complete search can be done and
it is possible to find the move with the best possible outcome for a given player.

But in most cases, the look-ahead depth must be limited to a certain number of turns (based on the game's difficulty setting). So for each series of possible moves and counter-
moves searched, the resulting game board must be evaluated to determine which player has the best chance of eventually winning the game. This evaluation is done by calculating a
rank using the following criteria:

forfeit - Leaving your opponent with no legal move forces her to forfeit her turn, giving you a big advantage in being able to move two (or more) times in a row.
mobility - This is a measure of how many legal moves you can make vs. how many legal moves your opponent will be left with. Similar to forfeit, the idea is to reduce your
opponent's options while maximizing your own.
frontier - A frontier disc is one that is adjacent to an empty square. Having a large number of frontier discs will, generally speaking, give your opponent greater mobility on
subsequent turns. Conversely, having fewer frontier discs means your opponent will have limited mobility later on. This score reflects the number of your frontier discs vs. your
opponent's.
stability - Corner discs are stable, they can never be outflanked. As a game progresses, other discs will become stable as well. This score reflects the number of your stable
discs vs. your opponent's.
score - This is simply the difference between the number of your discs on the board vs. your opponent's.

https://www.codeproject.com/Articles/4672/Reversi-in-C 3/11
15/8/2021 Reversi in C# - CodeProject

Different weights are assigned to each of these scores (again, based on the game's current difficulty setting). A board is assigned a rank by multiplying each criteria score by its
corresponding weight and adding these values together. A large negative rank represents a board favorable to Black while a large positive rank represents a board favorable to
White. So for a given set of possible moves, the computer will select one that results in the best ranked board for the color being played.

A constant named maxRank is used to indicate an end-game. It is set to the value of System.Int32.MaxValue - 64. This assures that any move resulting in the end of the
game will always have a higher rank (negative or positive) than other moves.

Subtracting 64 from the system's maximum integer value allows us to add the final score to the rank so that a win by 10 discs will rank higher than a win by 2 discs. This causes the
computer player to maximize its score when winning (or to minimize the opponent player's score when it's losing).

The current implementation is no match for the better AI players out there, but it plays pretty tough against a puny human opponent (at least, this puny human opponent). Again, a
Google search will turn up plenty of resources describing strategies and AI approaches to the game.

Game Components

The Board Class

The Board class represents a game board. It uses a two-dimensional array to track the contents of each board square, which can be one of the following constant values defined in
the class:

Black = -1
Empty = 0
White = 1
Two constructors are provided. One creates a new, empty board while the other creates a copy of an existing board.

It provides public methods like MakeMove() which adds a disc to the board, flipping over any outflanked opponent discs. For example, IsValidMove() can be used to
determine if a given move is valid for a given player. HasAnyValidMove() returns false if the given player cannot make any legal moves.

It also tracks disc counts for each player which are used by the computer move AI routine. These counts include the total number of discs, the number of frontier discs and the
number of safe (or unflippable discs) of each color.

Move Structures

In the main ReversiForm class, a couple of structures are defined for storing game moves. Both contain a row and column index pair which corresponds to a particular board
square.

The ComputerMove struct is used for the computer AI. In addition to the move position, it has a rank member. This is used to track how good or bad the move is, as determined
during the look-ahead search.

The MoveRecord struct is used for storing information about each move made during the course of a game. To allow the move undo/redo feature, an array of these is kept to track
the board during each turn. A move record contains a Board representing the game board as it was before the particular move was made, as well as value indicating which player is
to make the next move. An array of these is kept as moves are made by each player, allowing the game to reset to the state it was in at any point in the move history.

https://www.codeproject.com/Articles/4672/Reversi-in-C 4/11
15/8/2021 Reversi in C# - CodeProject

The RestoreGameAt() method handles the resetting of the game to a particular move number. While it potentially allows the game to be restored to any move currently in the
history, the menu and tool bar options on the main form currently only provide for a single move undo/redo at a time or an undo/redo of all moves. A future enhancement might be
to allow the user to click on items in the move list to restore the game to the corresponding move number.

Graphics and the User Interface

The Game Board

Squares on the board are represented by a user control named SquareControl. There is one of these for every square on the game board display. The control contains
information for displaying the square and its contents (empty or a black or white disc) including the animation of discs and any highlighting.

Displaying the Discs

Each disc is drawn dynamically. The basic shape is a circle with some highlighting and a shadow to give it a pseudo-3D appearance. The shapes are scaled in size based on the current
size of the square control. By rendering them this way, instead of using static images, the board may be dynamically resized to fit within the form window.

The Click event on the square controls handled within ReversiForm allows the user to make a move to a particular square (assuming it's a legal move). Likewise, the
MouseMove and MouseLeave events are handled to update the board display to highlight valid moves or preview a move when those options are active.

Animation of Moves

The disc flip animation is done using counters defined within the SquareControl class along with a System.Windows.Forms.Timer. Basically, this is a thread controlled
by the operating system which periodically raises an event that your form application can respond to.

After a move is made, if the move animation option is active, each affected square control has its counter initialized and the timer is activated. The main form's AnimateMove()
method is called each time the timer ticks (see below). This method updates the square counters and redraws their display. The animation basically consists of changing the disc
shape from a circle to ever thinner ellipses, then back to a full circle but in the opposite color. The smoothness and speed of this animation depends on how large the initial counter
value is (set by the constant SquareControl.AnimationStart) and how often the timer ticks (set by the constant animationTimerInterval in the main form).

Game Play
The following variables are used to handle play during a game:

C# Copy Code

// Game parameters.

private GameState gameState;

private int currentColor;

private int moveNumber;

moveNumber should be self-explanatory. currentColor indicates which player has the current turn (Black or White). gameState is set to one of these enumerated values:
https://www.codeproject.com/Articles/4672/Reversi-in-C 5/11
15/8/2021 Reversi in C# - CodeProject

C# Copy Code

// 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).

Most of the game play is event driven, so the use of gameState allows the various event handlers to determine the proper actions to take. For example, when the user clicks on a
board square, SquareControl_Click() is called. If the game state is InPlayerMove, the move will be made on that square. But if the game is in some other state, it's not
the user's turn to move so the click is ignored.

Likewise, if the user clicks the tool bar "Undo Move" button, we want to check the game state to see if any thing needs to be done before resetting the game to the previous move.
For example, if the state is InMoveAnimation, the animation timer needs to be stopped and the square controls need their counters and display reset. Or if the state is
InComputerMove, the program is currently doing a look-ahead search in a separate thread (see below) which will need to be stopped.

Program Flow

The diagram below illustrates the general program flow during the course of a game:

https://www.codeproject.com/Articles/4672/Reversi-in-C 6/11
15/8/2021 Reversi in C# - CodeProject

StartTurn() gets called at the beginning of a game, after a move is made by either player and whenever a move undo or redo is performed. It is responsible for evaluating the
game situation and setting up for the next move.

It first checks to see if the current player can make a legal move. If not, it switches to the other player and checks if that player has any legal move. When neither player can make a
move, by rule, the game is over and it will end the game.

Otherwise, the function will set things up for the current player to make a move. If the current player is under user-control, it simply exits. The user can then make a move by clicking
the mouse pointer on a valid square or by typing in a valid column letter and row number. This results in a call to MakePlayerMove() which does some housekeeping and then
calls MakeMove() to perform the move. If the current player is under computer-control, it starts the look-ahead search to find the best move.

Using a Worker Thread

https://www.codeproject.com/Articles/4672/Reversi-in-C 7/11
15/8/2021 Reversi in C# - CodeProject

Because the look-ahead search is computationally intensive, it is performed in a worker thread. It this were not done, the main form would freeze and become unresponsive during
the time it takes for the computer to calculate its best move. So StartTurn() creates a worker thread to execute the CalculateComputerMove() method and starts it.

A lock is used on the main game board object to prevent race conditions. As an example, both the MakeComputerMove() and the UndoMove() methods change to the
game board. Both methods first attempt to put a lock on it. So if one method happen to be called while the other is in the middle of updating the board, it will be forced to wait until
those changes are completed and the lock is freed.

Once a move has been found, the CalculateComputerMove() method does a callback to run MakeComputerMove() in the main form. This method gets a lock on the
board and calls MakeMove() to perform the move.

Performing a Move

MakeMove() does the actual updating of the board, placing the new disc at the specified location. It also does some maintenance on the undo/redo move history and removes
any square highlighting.

Then, if the move animation option is turned off, it simply calls EndMove() which will switch currentColor and start the next turn with a call to StartTurn().

But if the animate option is on, it will instead initialize the discs to be animated and start the animation timer. As discussed previously, the timer causes AnimateMove() to run
every few milliseconds, updating the display and decrementing the animation counters each time. Eventually, the counters will reach their end and AnimateMove() will then call
EndMove() to complete the move.

Future Enhancements
There is much room for improvement in the computer-player AI. The look-ahead algorithm could be augmented with a book of opening moves or a set of corner and edge patterns
that have been pre-ranked. It could be made to utilize selective deepening, where the search depth could be extended for moves that generally have more impact on the game, such
as near the corners. Another improvement would be to store the look-ahead tree. This would allow it to be searched to a greater depth because the program would not have to
regenerate the same moves each time.

History
August 1, 2003 - version 1.0

Initial version.

September 16, 2005 - version 2.0

Enhanced graphical display.


Added unlimited move undo/redo.
Improved computer player AI.
Corrected threading issues.
Expanded help.

https://www.codeproject.com/Articles/4672/Reversi-in-C 8/11
15/8/2021 Reversi in C# - CodeProject

License
This article has no explicit license attached to it but may contain usage terms in the article text or the download files
themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here

Share

About the Author


BrainJar
Web Developer
United States

No Biography provided

Comments and Discussions


 

https://www.codeproject.com/Articles/4672/Reversi-in-C 9/11
15/8/2021 Reversi in C# - CodeProject

You must Sign In to use this message board.

Search Comments

Spacing Relaxed   Layout Normal   Per page 25     Update

First Prev Next

helpers file Member 7981186 3-Jan-21 4:00 

Wonderful Member 14380612 19-May-19 4:45 

Great functional example -works fine in VS 2015, too! MTEXX.Again 16-Jun-18 16:42 

CRUD in C# using MySQL for newbies Ehtesham Astute 15-Feb-16 17:46 

Reversi in C# Member 11831049 20-Aug-15 19:03 

Message Closed ybonda 18-Jun-12 4:58 

ask blow 'em out 20-Jul-10 5:31 

Brilliant Marcelo Ricardo de 11-Aug-09 18:43 


Oliveira

Awesome Code SwarnenduDas 10-Jun-09 3:45 

Very nice article! Miguel Barros 6-Mar-09 5:58 

Cool one Mushtaque Nizamani 6-Jul-08 23:20 

Very Nice azijoon 21-May-08 6:57 

interesting Ivan Svogor 7-Apr-08 10:41 

wonderful LeThanh125 1-Mar-08 17:19 

[Message Deleted] Danny Rodriguez 27-Jan-08 9:10 

https://www.codeproject.com/Articles/4672/Reversi-in-C 10/11
15/8/2021 Reversi in C# - CodeProject

How to change size of this game ?? trickyboy 9-Apr-07 1:33 

reversi program Prizm07 24-Dec-06 5:27 

Nice. 1 UI thread bug staceyw 7-Jul-05 20:45 

Re: Nice. 1 UI thread bug BrainJar 9-Jul-05 19:36 

Re: Nice. 1 UI thread bug shriop 19-Jul-05 17:31 

Bad use of UserControl Heath Stewart 8-Mar-05 6:17 

Thanks! Good work jeremi.s 22-Nov-04 9:41 

Nice Anonymous 8-Oct-04 8:58 

Difference between Reversi and Othello ydlm 2-Sep-04 13:13 

Re: Difference between Reversi and Othello Prizm07 24-Dec-06 5:38 

Last Visit: 13-Aug-21 22:43     Last Update: 14-Aug-21 22:54 Refresh 1 2 3 Next ᐅ

General    News    Suggestion    Question    Bug    Answer    Joke    Praise    Rant    Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

Permalink
Layout: fixed
|
fluid Article Copyright 2003 by BrainJar

Advertise
Everything else
Copyright © CodeProject, 1999-2021

Privacy

Cookies
Web01 2.8.20210721.1

Terms of Use

https://www.codeproject.com/Articles/4672/Reversi-in-C 11/11

You might also like