Click here to Skip to main content
15,880,967 members
Articles / Programming Languages / C#

Simple AI for the Game of Breakthrough

Rate me:
Please Sign up or sign in to vote.
5.00/5 (11 votes)
5 Jun 2009LGPL39 min read 65.4K   3.7K   39  
This article presents an implementation of a simple alpha-beta player for the board game of Breakthrough.
using System;
using System.Collections.Generic;

namespace Breakthrough.Game.Engine
{
	internal static class BoardEvaluation
	{
        internal const int WinValue = 500000;
        internal const short PieceAlmostWinValue = 10000;
        internal const short PieceValue = 1300;
        internal const short PieceDangerValue = 10;
        internal const short PieceHighDangerValue = 100;
        internal const short PieceAttackValue = 50;
        internal const short PieceProtectionValue = 65;
        internal const short PieceConnectionHValue = 35;
        internal const short PieceConnectionVValue = 15;
        internal const short PieceColumnHoleValue = 20;
        internal const short PieceHomeGroundValue = 10;
		
        internal static void GetValue(Board board, GamePieceColor ColorMoving)
        {
	        board.Value = 0;
        	
	        // evaluate remaining pieces and the state of the game
            int RemainingWhitePieces = 0;
            int RemainingBlackPieces = 0;
        	
	        // scan all squares
            for (byte column = 0; column < 8; column++)
            {
                int BlackPiecesOnColumn = 0;
                int WhitePiecesOnColumn = 0;

                for (byte row = 0; row < 8; row++)
                {

                    int Position = GetPosition(column, row);
                    BoardSquare square = board.BoardSquares[Position];

                    if (square.CurrentPiece == null)
                        continue;

                    if (square.CurrentPiece.PieceColor == GamePieceColor.White)
                    {
                        RemainingWhitePieces++;
                        WhitePiecesOnColumn++;
                        board.Value += GetPieceValue(square, column, row);
                        if (row == 7)
                        { // winning position
                            board.WhiteWins = true;
                        }
                        else if (row == 6)
                        { // check almost win
                            bool ThreatA = false;
                            bool ThreatB = false;
                            if (column > 0) ThreatA = (board.BoardSquares[GetPosition(column - 1, 7)].CurrentPiece == null);
                            if (column < 7) ThreatB = (board.BoardSquares[GetPosition(column + 1, 7)].CurrentPiece == null);
                            if (!(ThreatA && ThreatB)) // almost win
                                board.Value += PieceAlmostWinValue;
                        }
                        else if (row == 0)
                        { // home ground
                            board.Value += PieceHomeGroundValue;
                        }
                    }
                    else
                    {
                        RemainingBlackPieces++;
                        BlackPiecesOnColumn++;
                        board.Value -= GetPieceValue(square, column, row);
                        if (row == 0)
                        { // winning position
                            board.BlackWins = true;
                        }
                        else if (row == 1)
                        { // check almost win
                            bool ThreatA = false;
                            bool ThreatB = false;
                            if (column > 0) ThreatA = (board.BoardSquares[GetPosition(column - 1, 0)].CurrentPiece == null);
                            if (column < 7) ThreatB = (board.BoardSquares[GetPosition(column + 1, 0)].CurrentPiece == null);
                            if(!(ThreatA && ThreatB)) // almost win
                                board.Value -= PieceAlmostWinValue;
                        }
                        else if (row == 7)
                        { // home ground
                            board.Value -= PieceHomeGroundValue;
                        }
                    }
                }

                // Row hole feature
                if (WhitePiecesOnColumn == 0) board.Value -= PieceColumnHoleValue;
                if (BlackPiecesOnColumn == 0) board.Value += PieceColumnHoleValue;

	        }

            // if no more material available
            if (RemainingWhitePieces == 0) board.BlackWins = true; 
            if (RemainingBlackPieces == 0) board.WhiteWins = true;

            // winning positions
            if (board.WhiteWins) board.Value += WinValue;
            if (board.BlackWins) board.Value -= WinValue;

            // invert Value for Negamax
            if (GetOppositeColor(ColorMoving) == GamePieceColor.Black)
                board.Value = -board.Value;
        }


		/// <summary>
		/// Evaluates current piece value
		/// </summary>
		private static int GetPieceValue(BoardSquare square, byte Column, byte Row)
		{
            int Value = PieceValue;
			var Piece = square.CurrentPiece;
			
            // add connections value
            if (Piece.ConnectedH) Value += PieceConnectionHValue;
            if (Piece.ConnectedV) Value += PieceConnectionVValue;

			// add to the value the protected value
			Value += Piece.ProtectedValue;
			
			// evaluate attack
			if (Piece.AttackedValue > 0)
            {
                Value -= Piece.AttackedValue;
				if (Piece.ProtectedValue == 0)
					Value -= Piece.AttackedValue;
			}else{
				if (Piece.ProtectedValue != 0)
				{ 
					// pawns at the end that are not attacked are worth more points
					if (Piece.PieceColor == GamePieceColor.White)
					{
                        if (Row == 5) Value += PieceDangerValue;
                        else if (Row == 6) Value += PieceHighDangerValue;
					}
					else
					{
                        if (Row == 2) Value += PieceDangerValue;
                        else if (Row == 1) Value += PieceHighDangerValue;
					}
				}
			}

            // danger value
            if (Piece.PieceColor == GamePieceColor.White)
                Value += Row * PieceDangerValue;
            else
                Value += (8-Row) * PieceDangerValue;

			
			// mobility feature
			Value += Piece.ValidMoves.Count;
			
			return Value;
		}


        #region Helper Methods
        private static GamePieceColor GetOppositeColor(GamePieceColor color){ if (color == GamePieceColor.Black) return GamePieceColor.White; else return GamePieceColor.Black; }
        private static byte ModifyDepth(byte depth, int PossibleMoves){if (PossibleMoves < 9) depth = (byte)(depth + 2); return depth;}
		private static int Sort(Board board1, Board board2){ return board1.Value - board2.Value; }
        private static byte GetRow(byte position) { return (byte)(7 - (int)(position / 8)); }
        private static byte GetColumn(byte position) { return (byte)(position % 8); }
        private static int GetPosition(int column, int row) { return (7 - row) * 8 + column; }

        #endregion


	}
}

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, along with any associated source code and files, is licensed under The GNU Lesser General Public License (LGPLv3)


Written By
Chief Technology Officer Misakai Ltd.
Ireland Ireland
Roman Atachiants, Ph.D. is the architect behind emitter.io service, a real-time, low-latency publish/subscribe service for IoT, Gaming. He is a software engineer and scientist with extensive experience in different computer science domains, programming languages/principles/patterns & frameworks.

His main expertise consists of C# and .NET platform, game technologies, cloud, human-computer interaction, big data and artificial intelligence. He has an extensive programming knowledge and R&D expertise.



Comments and Discussions