Click here to Skip to main content
Click here to Skip to main content
Add your own
alternative version

A C# implementation of Reversi (Othello) Game for PocketPC and Windows

, 4 Aug 2004
A C# implementation of Reversi (Othello) Game for PocketPC and Windows.
pocketreversi_demo.zip
pocketreversi_setup.zip
pocketreversi_src.zip
gma.Windows.Reversi
gma.Windows.Reversi.csproj.user
Images
0.gif
1.gif
2.gif
3.gif
4.gif
5.gif
Reversi.ico
gma.Mobile.Reversi
gma.Mobile.Reversi.csdproj
gma.Mobile.Reversi.csdproj.user
Images
0.gif
1.gif
2.gif
3.gif
4.gif
5.gif
Reversi.ico
gma.Reversi
gma.Reversi.csproj.user
using System;
using System.Collections;
	
namespace gma.Reversi {

	/// <summary>
	/// Occures when the move was transferred to another player.
	/// </summary>
    public delegate void OnMoveTransferredEvent(int aNextPlayer, int anAvailableMoveCount);
	/// <summary>
	/// Occures after each move or after StartGame.
	/// </summary>
    public delegate void OnNeedRedrawEvent();
	/// <summary>
	/// Occures when Game over conditions are reached.
	/// </summary>
    public delegate void OnGameOverEvent();

	/// <summary>
	/// Identifies a square on the board.
	/// </summary>
    public struct Point
	{
		public int X;
		public int Y;
		public Point(int x, int y)
		{
			X=x;
			Y=y;
		}
		
		/// <summary>
		/// Is used to check if the given point belongs to 8x8 matrix.
		/// </summary>
		/// <returns></returns>
		public bool IsInRange()
		{
			if (X<0 || Y<0 || X>=Board.SIZEX || Y>=Board.SIZEY) return false;
			return true;
		}
	}

	/// <summary>
	/// This structure is used to describe neighboring points.
	/// </summary>
	public struct Direction
	{
		public int dX;
		public int dY;
		public Direction( int dx, int dy)
		{
			dX=Math.Sign(dx);
			dY=Math.Sign(dy);
		}
		
		/// <summary>
		/// Adding a direction to a point gives you a neighbour point in given direction.
		/// </summary>
		/// <param name="aPoint"></param>
		/// <param name="aDirection"></param>
		/// <returns></returns>
		public static Point operator +(Point aPoint, Direction aDirection)
  		{
      		return new Point(aPoint.X + aDirection.dX, aPoint.Y + aDirection.dY );
   		}
   		
		/// <summary>
		/// Every direction has an oposite direction.
		/// </summary>
		/// <param name="aDirection"></param>
		/// <returns></returns>
   		public static Direction Opposite(Direction aDirection)
   		{
   			return new Direction(-aDirection.dX, -aDirection.dY);
   		}
	}

	/// <summary>
	/// This class is a main dataStructure to describe a Board and all actions on it.
	/// </summary>
	public class Board : object {
		
		public const int PLAYER_WHITE = -1;
		public const int PLAYER_RED	= 1;
		
		public const int SQUARE_EMPTY = 0;
		
		public const int SIZEX=8;
		public const int SIZEY=8;
		
		//  NW  N   NE
		//   \  |  /
		//W --    -- E
		//   /  |  \
		// SW   S  SE 

		public static Direction DirN = new Direction(0,-1);
		public static Direction DirNE = new Direction(1,-1);
		public static Direction DirE = new Direction(1,0);
		public static Direction DirSE = new Direction(1,1);
		public static Direction DirS = new Direction(0,1);
		public static Direction DirSW = new Direction(-1,1);
		public static Direction DirW = new Direction(-1,0);
		public static Direction DirNW = new Direction(-1,-1);
		
		public static Direction[] AllDirs = new Direction[8] {DirN, DirNE, DirE, DirSE, DirS, DirSW, DirW, DirNW};
		//initial board placement
		public static int[,] InitBoard= new int[SIZEX, SIZEY] {
						{0,0,0,0,0,0,0,0},
						{0,0,0,0,0,0,0,0},
						{0,0,0,0,0,0,0,0},
						{0,0,0,-1,1,0,0,0},
						{0,0,0,1,-1,0,0,0},
						{0,0,0,0,0,0,0,0},
						{0,0,0,0,0,0,0,0},
						{0,0,0,0,0,0,0,0}};
		
		//
		public static int[,] LocationWeight= new int[SIZEX, SIZEY] {
						{50 , -1,  5,  2 ,  2,  5, -1, 50},
						{-1 ,-10,  1,  1 ,  1,  1,-10, -1},
						{ 5 ,  1,  1,  1 ,  1,  1,  1,  5},
						{ 2 ,  1,  1,  0 ,  0,  1,  1,  2},
						{ 2 ,  1,  1,  0 ,  0,  1,  1,  2},
						{ 5 ,  1,  1,  1 ,  1,  1,  1,  5},
						{-1 ,-10,  1,  1 ,  1,  1,-10, -1},
						{50 , -1,  5,  2 ,  2,  5, -1, 50}};

		public static int[,] Irreversible= new int[SIZEX, SIZEY] {
						{0,0,0,0,0,0,0,0},
						{0,0,0,0,0,0,0,0},
						{0,0,0,0,0,0,0,0},
						{0,0,0,0,0,0,0,0},
						{0,0,0,0,0,0,0,0},
						{0,0,0,0,0,0,0,0},
						{0,0,0,0,0,0,0,0},
						{0,0,0,0,0,0,0,0}};

		public static Point NOMOVE = new Point(-1, -1);

		
		
		public int GetStateAt(Point aPoint)
		{
			return _square[aPoint.X, aPoint.Y];
		}

		public int GetStateAt(int x, int y)
		{
			return _square[x, y];
		}


		protected IList _affectedSquares;
		public IList AffectedSquares
		{
			get 
			{
				if (_affectedSquares==null) 
					_affectedSquares= new ArrayList();
				return _affectedSquares;
			}
		}

        public event OnGameOverEvent OnGameOver;
        public event OnMoveTransferredEvent OnMoveTransferred;
        public event OnNeedRedrawEvent OnNeedRedraw;
		public virtual event OnNeedRedrawEvent OnNeedRedrawAll;

        protected virtual void SetStateAt(Point aPoint, int state)
		{
			//----- Statisticts
			// Decrease
			switch (_square[aPoint.X, aPoint.Y])
			{
				case PLAYER_WHITE:
					_counterWhite--;
					_weightWhite-=LocationWeight[aPoint.X, aPoint.Y];
					break;

				case PLAYER_RED:
					_counterRed--;
					_weightRed-=LocationWeight[aPoint.X, aPoint.Y];
					break;
			}

		
			// Increase
			switch (state)
			{
				case PLAYER_WHITE:
					_counterWhite++;
					_weightWhite+=LocationWeight[aPoint.X, aPoint.Y];
					break;
				case PLAYER_RED:
					_counterRed++;
					_weightRed+=LocationWeight[aPoint.X, aPoint.Y];
					break;
			}
			//-----END Statisticts
			
			_affectedSquares.Add(aPoint);
			_square[aPoint.X, aPoint.Y]=state;
		}
		
		protected int _currentPlayer=PLAYER_RED;
		public int CurrentPlayer
		{
			get {return _currentPlayer;}
		}
	
		protected int[,] _square;
		public int[,] Square
		{
			get {return _square;}
		}
		
		public Board() {
			_square= (int[,])InitBoard.Clone();
		}

		protected Board(Board aParent) {
			_square = (int[,])aParent.Square.Clone();
			_counterWhite = aParent.CounterWhite;
			_counterRed = aParent.CounterRed;
			_weightRed = aParent.WeightRed;
			_weightWhite = aParent.WeightWhite;
            _currentPlayer = aParent.CurrentPlayer;
        }
		
		public Board Clone()
		{
			return new Board(this);
		}


        public virtual void StartGame()
        {
			_square= (int[,])InitBoard.Clone();
			_counterWhite = 2;
			_counterRed = 2;
			_weightRed = 0;
			_weightWhite = 0;
			AffectedSquares.Clear();
			_availableMoves=null;

			if (OnNeedRedrawAll != null) OnNeedRedrawAll();
            // black starts
            _currentPlayer = PLAYER_WHITE;
            // the move will be transfered to black
            TransferMove(0);
        }

		public bool Play(Point aPoint, int aPlayer, bool IsTestOnly)
		{
			if (!IsTestOnly) AffectedSquares.Clear();
			if (!aPoint.IsInRange()) return false;
			if (GetStateAt(aPoint)!=SQUARE_EMPTY) return false;
			bool result = false;
			for (int i=0; i<AllDirs.Length ; i++)
			{
				Point aNeighbourPoint = aPoint + AllDirs[i];
				if (aNeighbourPoint.IsInRange())
				{
					int aNeigbourSquareState = GetStateAt(aNeighbourPoint);
					if (aNeigbourSquareState == -aPlayer) 
					{
						bool doFlip = Flipp(aPoint, AllDirs[i], aPlayer, IsTestOnly);
						result=result || doFlip;
						if (IsTestOnly && result) return true;
					}
				}
			}
            if (result && !IsTestOnly)
            {
				SetStateAt(aPoint,aPlayer);
                TransferMove(0);
            }
			return result;
		}

        protected void TransferMove(int recursionDepth )
        {
            if (recursionDepth == 2)
            {
				if (OnGameOver != null) 
				{
					if (OnNeedRedraw != null) OnNeedRedraw();
					OnGameOver();
				}
                return;
            }

            _currentPlayer = -_currentPlayer;
            _availableMoves = null;
            GetAvailableMoves(_currentPlayer);
            

			if (_availableMoves.Count == 0)
				TransferMove(recursionDepth + 1);
			else
			{
				if (OnNeedRedraw != null) OnNeedRedraw();
				if (OnMoveTransferred != null)
					OnMoveTransferred(_currentPlayer, _availableMoves.Count);
			}
        }

        protected bool Flipp(Point aPoint, Direction aDirection, int aPlayer, bool IsTestOnly)
		{
			Point aNeighbourPoint = aPoint + aDirection;
			if (!aNeighbourPoint.IsInRange()) return false;
			int aNeigbourSquareState = GetStateAt(aNeighbourPoint);
			if (aNeigbourSquareState == aPlayer) return true; 
			if (aNeigbourSquareState == -aPlayer) 
				if (Flipp(aNeighbourPoint, aDirection, aPlayer, IsTestOnly))
				{
					if (!IsTestOnly) SetStateAt(aNeighbourPoint, aPlayer);
					return true;
				}
			return false;
		}
		
		//##################################################################
		//					STATISTICS
		//##################################################################
		private int _counterRed = 2;
		public virtual int CounterRed 
		{
			get { return _counterRed;}
		}
		
		private int _counterWhite = 2;
		public virtual int CounterWhite
		{
			get { return _counterWhite;}
		}
		

		protected int _weightRed = 0;
		public int WeightRed 
		{
			get { return _weightRed;}
		}
		
		protected int _weightWhite = 0;
		public int WeightWhite
		{
			get { return _weightWhite;}
		}

		protected ArrayList _availableMoves = null;
		/// <summary>
		/// Simple brutal Search for available Moves. It was overrided in BoardComplex
		/// </summary>
		/// <param name="aPlayer"></param>
		/// <returns></returns>
        public virtual IList GetAvailableMoves(int aPlayer)
		{
            if (_availableMoves==null)
            {
                _availableMoves = new ArrayList();
                for (int i=0; i<SIZEX; i++)
				    for (int j=0; j<SIZEY; j++)
					    if (Play(new Point(i,j),aPlayer, true)) 
						    _availableMoves.Add(new Point(i,j));
			}
			return _availableMoves;
		}
		
		/// <summary>
		/// Simple brutal chack for evry square. It was overrided in BoardComplex
		/// </summary>
		/// <param name="aPlayer"></param>
		/// <returns></returns>
		public virtual int GetIrreversibleCount(int aPlayer)
		{
			int aPlayerArrayIndex=aPlayer;
			int counter=0;
			if (aPlayer<0) aPlayerArrayIndex=0;
			for (int i=0; i<SIZEX; i++)
				for (int j=0; j<SIZEY; j++)
					if (IsIrreversible(new Point(i,j),aPlayer, 0)) counter++;
			return counter;
		}
		
		protected bool IsIrreversible(Point aPoint, int aPlayer, int recursionDepth)
		{
			if (!aPoint.IsInRange()) return true;
			if (_square[aPoint.X, aPoint.Y]!=aPlayer) return false;
			if (Irreversible[aPoint.X, aPoint.Y]==aPlayer) return true;
			if (recursionDepth==1) return false;
			if (IsLineBlocked(aPoint, DirN, aPlayer, recursionDepth) && 
				IsLineBlocked(aPoint, DirNE, aPlayer, recursionDepth)  &&
				IsLineBlocked(aPoint, DirE, aPlayer, recursionDepth) &&
				IsLineBlocked(aPoint, DirSE, aPlayer, recursionDepth))
				{
					Irreversible[aPoint.X, aPoint.Y]=aPlayer;
					return true;
				}
			return false;
		}
		
		protected bool IsLineBlocked(Point aPoint, Direction lineDirection,int aPlayer, int recursionDepth)
		{
			return (IsIrreversible(aPoint+lineDirection, aPlayer, recursionDepth+1) ||  
			        IsIrreversible(aPoint+Direction.Opposite(lineDirection), aPlayer, recursionDepth+1));
		}

	}
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

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

George Mamaladze
Software Developer
Germany Germany
Tweeter: @gmamaladze
Google+: gmamaladze
Blog: gmamaladze.wordpress.com
Follow on   Twitter

| Advertise | Privacy | Mobile
Web01 | 2.8.140827.1 | Last Updated 5 Aug 2004
Article Copyright 2004 by George Mamaladze
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid