/// <summary> /// Evalution function which counts number of pieces per player and calculates if player can be captured /// </summary> /// <param name="board"></param> /// <param name="player"></param> /// <returns></returns> public int Evaluate(Board board, Player player) { const int kingConstant = 120; const int soldierConstant = 100; int score = 0; int blackCaptures = 0; int whiteCaptures = 0; int numWhiteKings = board.NumberOfWhiteKings; int numBlackKings = board.NumberOfBlackKings; int numOfWhiteSold = board.NumberOfWhitePieces; int numOfBlackSold = board.NumberOfBlackPieces; int numOfSoldOnBoard = numWhiteKings + numBlackKings + numOfWhiteSold + numOfBlackSold; if (numOfSoldOnBoard <= 7) return Evaluate2(board, player); int blackScore = (numOfBlackSold*soldierConstant) + (numBlackKings*kingConstant); int whiteScore = (numOfWhiteSold*soldierConstant) + (numWhiteKings*kingConstant); score += ((blackScore - whiteScore)*200)/(blackScore + whiteScore); score += blackScore - whiteScore; for (int i = 1; i <= 32; i++) { if (board.GetPlayer(board[i]) == Player.Black) { blackCaptures += (CanCapture(board, board[i], Player.Black)/(numOfBlackSold + numBlackKings))*30; } else if (board.GetPlayer(board[i]) == Player.White) { whiteCaptures += (CanCapture(board, board[i], Player.White)/(numOfWhiteSold + numWhiteKings))*30; } } score += (blackCaptures - whiteCaptures); return (player == Player.Black) ? score : -score; }
/// <summary> /// returns the number of ways in which soldier on coord can be captured; /// </summary> /// <param name="board"></param> /// <param name="coord"></param> /// <param name="player"></param> /// <returns></returns> public int CanBeCaptured(Board board, Coordinate coord, Player player) { int num = 0; Rules rule = new Rules(); IList<Coordinate> optionalCoords = rule.OptionalMoves(board, coord, player); IList<Coordinate> coordsInDir = rule.GetMovesInDirection(board, coord, player); //collect all coords behind coord IList<Coordinate> coordsfrombehind = optionalCoords.Where(opCor => !coordsInDir.Contains(opCor)).ToList(); foreach (var cid in coordsInDir) { if (board.GetPlayer(board[cid.X, cid.Y]) == board.GetOpponent(player) && rule.CoordsToCaptureAndDest(board, cid, coord, board.GetOpponent(player)).Count > 0) num++; } foreach (var cfb in coordsfrombehind) { if (board.GetPlayer(board[cfb.X, cfb.Y]) == board.GetOpponent(player) && board.IsKing(coord) && rule.CoordsToCaptureAndDest(board, cfb, coord, board.GetOpponent(player)).Count > 0) num++; } return num; }
/// <summary> /// Pc VS Pc /// </summary> /// <returns> </returns> public void StartGameWithPc(Board board) { int countmovesforDraw = 0; var rule = new Rules(); var print = new PrintBoardState(); int depth; Coordinate srcCoord = new Coordinate(); Coordinate destCoord = new Coordinate(); //Create the file engine who Read\write to file all moves FileEngine file = new FileEngine(); //define that file will be created in the root folder under the name sync.txt string path = "sync.txt"; //define colors. Console.WriteLine("Opponent color is white? [Yes/No]"); Opponet: string opponentColor = Console.ReadLine(); if (!(opponentColor == "Yes" || opponentColor == "yes" || opponentColor == "No" || opponentColor == "no")) { Console.WriteLine("Invalid input,please try again"); goto Opponet; } Player oppColor = (opponentColor == "Yes" || opponentColor == "yes") ? Player.White : Player.Black; Player pcColor = oppColor == Player.White ? Player.Black : Player.White; //define who starts. Console.WriteLine("Opponent Starts? [Yes/No]"); Start: string opponentStarts = Console.ReadLine(); if ( !(opponentStarts == "Yes" || opponentStarts == "yes" || opponentStarts == "No" || opponentStarts == "no")) { Console.WriteLine("Invalid input,please try again"); goto Start; } if (opponentStarts == "Yes" || opponentStarts == "yes") goto OppTurn; goto MyTurn; //oppoenent tuen OppTurn: try { //open file in path with read permision and no sharing using (var stream = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.None)) { ShowPlayerChange(oppColor); Player color = Player.None; bool capMove = false; IList<Coordinate> oppMove = new List<Coordinate>(); //read moves and captures (if exists) while (oppMove.Count == 0) { oppMove = file.ReadFromFile(stream, board, path, out color); } //Source srcCoord = oppMove.First(); oppMove.RemoveAt(0); //Destination destCoord = oppMove[0]; oppMove.RemoveAt(0); //Captures list var capturesOppdid = oppMove; //if move is not oppoent move or source piece is not opponenet color return to read file if ((color != oppColor) || (board.GetPlayer(srcCoord) != oppColor)) { goto OppTurn; } //Find captures IDictionary<IList<Coordinate>, IList<Coordinate>> capturesAvailable = rule.FindCaptures(board, oppColor); if (capturesAvailable.Count > 0) { var boolean = rule.MapContainsCoordsOfCaptures(capturesAvailable, srcCoord, destCoord, capturesOppdid); if (!boolean) { Console.WriteLine("You must capture maximum opponent soldiers on board"); goto OppTurn; } else { foreach (var coordinate in capturesOppdid) { board[coordinate.X, coordinate.Y].Status = Piece.None; board.UpdateCapturedSoldiers(coordinate, pcColor); capMove = true; } } } if (capMove || rule.IsValidMove(board, srcCoord, destCoord, oppColor)) { board.UpdateBoard(srcCoord, destCoord); rule.IsBecameAKing(board, board[destCoord.X, destCoord.Y]); print.DrawBoard(board); } else { Console.WriteLine("This is not a valid move, please enter again"); goto OppTurn; } //check if game has been determined GameState game = GetGameState(oppColor, board); if (GameState.Draw == CheckDraw(board, board[destCoord.X, destCoord.Y], capMove, ref countmovesforDraw)) { Console.WriteLine("Draw"); return; } if (game == GameState.Lost) { Console.WriteLine("{0} Lost the game and {1} won", oppColor.ToString(), pcColor.ToString()); return; } if (game == GameState.Won) { Console.WriteLine("{0} Won", oppColor.ToString()); return; } } } catch (Exception) { goto OppTurn; } //local turn MyTurn: try { using (var stream = new FileStream(path, FileMode.Create, FileAccess.ReadWrite, FileShare.None)) { ShowPlayerChange(pcColor); var alphaBeta = new Alphabeta(); Board temp = new Board(); IList<Coordinate> tempCaptures = new List<Coordinate>(); depth = rule.DefineDepth(board); alphaBeta.AlphaBeta(board, depth, Int32.MinValue, Int32.MaxValue, pcColor, true, ref srcCoord, ref destCoord, ref temp, ref tempCaptures); if ((rule.InBounds(board, srcCoord.X, srcCoord.Y)) && (rule.InBounds(board, destCoord.X, destCoord.Y))) { board = temp.Copy(); print.DrawBoard(board); } //write move to file file.WriteToFile(stream, srcCoord, destCoord, tempCaptures, path, pcColor); //check if game has been determined bool pcCaptured = tempCaptures.Count > 0; GameState game = GetGameState(oppColor, board); if (GameState.Draw == CheckDraw(board, board[destCoord.X, destCoord.Y], pcCaptured, ref countmovesforDraw)) { Console.WriteLine("Draw"); return; } if (game == GameState.Lost) { Console.WriteLine("{0} Lost the game and {1} won", oppColor.ToString(), pcColor.ToString()); return; } if (game == GameState.Won) { Console.WriteLine("{0} Won", oppColor.ToString()); return; } } Thread.Sleep(5000); goto OppTurn; } catch (Exception) { goto MyTurn; } }
/// <summary> /// Human VS PC /// </summary> /// <param name="board"></param> public void StartGameWithHuman(Board board) { int countmovesforDraw = 0; var humanColor = Player.None; var pcColor = Player.None; bool firstTurn = true; var rule = new Rules(); var print = new PrintBoardState(); while (true) { //first turn is always the human's Console.WriteLine("Your turn, please enter a move in format 'from cell number' 'to cell number'"); HumanTurn: bool capMove = false; var input = Console.ReadLine(); IList<Coordinate> coords = ParseStrToCoords(input, board); //If there is no input if (coords == null) { Console.WriteLine("Wrong Input, please try again"); goto HumanTurn; } var srcCoord = coords.First(); var destCoord = coords.Last(); if (firstTurn) { humanColor = board.GetPlayer(srcCoord); pcColor = board.GetOpponent(humanColor); firstTurn = false; } //if select coordinate is not your coordinate then show error message else if (humanColor != board.GetPlayer(srcCoord)) { Console.WriteLine( "This is not your piece , please enter cell number which allocated with your piece color"); goto HumanTurn; } //Fins captures IDictionary<IList<Coordinate>, IList<Coordinate>> capturesAvailable = rule.FindCaptures(board, humanColor); //if there are any captures if (capturesAvailable.Count > 0) { //check if they are contained in captured dictionary by looking on source and destination IList<Coordinate> captures = rule.MapContainsCoords(capturesAvailable, srcCoord, destCoord); //id they are not contained show error message if (captures.Count == 0) { Console.WriteLine("You must capture maximum opponent soldiers on board"); goto HumanTurn; } else { //if there are captures and source and destination are corrrect, update board with move and captures foreach (var coordinate in captures) { board[coordinate.X, coordinate.Y].Status = Piece.None; board.UpdateCapturedSoldiers(coordinate, pcColor); capMove = true; } } } //update board if (capMove || rule.IsValidMove(board, srcCoord, destCoord, humanColor)) { board.UpdateBoard(srcCoord, destCoord); rule.IsBecameAKing(board, board[destCoord.X, destCoord.Y]); print.DrawBoard(board); } else { Console.WriteLine("This is not a valid move, please enter again"); goto HumanTurn; } //check if game is finish by checking if players draw,lost or won GameState game = GetGameState(humanColor, board); if (GameState.Draw == CheckDraw(board, board[destCoord.X, destCoord.Y], capMove, ref countmovesforDraw)) { Console.WriteLine("Draw"); break; } if (game == GameState.Lost) { Console.WriteLine("{0} Lost the game and {1} won", humanColor.ToString(), pcColor.ToString()); break; } if (game == GameState.Won) { Console.WriteLine("{0} Won", humanColor.ToString()); break; } //switch to opponent (PC) ShowPlayerChange(pcColor); var alphaBeta = new Alphabeta(); Board temp = new Board(); IList<Coordinate> tempCaptures = new List<Coordinate>(); //Define depth int depth = rule.DefineDepth(board); //Calling alpha-beta algorithm which return the best move in addition, return source,destination coordinates and a list of captures alphaBeta.AlphaBeta(board, depth, Int32.MinValue, Int32.MaxValue, pcColor, true, ref srcCoord, ref destCoord, ref temp, ref tempCaptures); //Verify the move is in bounds, if yes update the board with it if ((rule.InBounds(board, srcCoord.X, srcCoord.Y)) && (rule.InBounds(board, destCoord.X, destCoord.Y))) { board = temp.Copy(); print.DrawBoard(board); } // check if there were any captures. if yes check for draw bool pcCaptured = tempCaptures.Count > 0; //check if game was determined game = GetGameState(humanColor, board); if (GameState.Draw == CheckDraw(board, board[destCoord.X, destCoord.Y], pcCaptured, ref countmovesforDraw)) { Console.WriteLine("Draw"); break; } if (game == GameState.Lost) { Console.WriteLine("{0} Lost the game and {1} won", humanColor.ToString(), pcColor.ToString()); break; } if (game == GameState.Won) { Console.WriteLine("{0} Won", humanColor.ToString()); break; } } }
/// <summary> /// Is opponet won the game by blocking player /// </summary> /// <param name="board"></param> /// <param name="player"></param> /// <returns></returns> public bool IsPlayerBlocked(Board board, Player player) { for (int i = 1; i <= 32; i++) { if (board.GetPlayer(board[i]) == player) { if (!IsCoordinateBlocked(board, board[i], player)) { return false; } } } return true; }
/// <summary> /// Checks if a coordinate can't move (capture or just move) /// </summary> /// <param name="srcBoard"></param> /// <param name="coordinate"></param> /// <param name="player"></param> /// <returns></returns> public bool IsCoordinateBlocked(Board srcBoard, Coordinate coordinate, Player player) { int count = 0; IList<Coordinate> moves = GetMovesInDirection(srcBoard, coordinate, player); if (moves.Count == 0) { return true; } foreach (var move in moves) { if (move.Status == Piece.None) { return false; } if (srcBoard.GetPlayer(move) == player) { count++; if (moves.Count == count) { return true; } } else if (srcBoard.GetPlayer(move) != player) { IDictionary<IList<Coordinate>, Coordinate> captures = CoordsToCaptureAndDest(srcBoard, coordinate, move, player); if (captures.Count > 0) { return false; } } } return true; }
/// <summary> /// check if piece is in player color and valid /// </summary> /// <param name="board"></param> /// <param name="x"></param> /// <param name="y"></param> /// <param name="player"></param> /// <returns></returns> private bool CheckValidPieceColor(Board board, int x, int y, Player player) { var srcCoord = board[x, y]; if (srcCoord != null && board.GetPlayer(srcCoord) == player) return true; return false; }
/// <summary> /// Check the safeness of a coordinate (can be captured). if the sold is on boundary than safe (4) else return the /// delta between number of player soldiers and number of Opp soldiers around the coord. /// </summary> /// <param name="board"></param> /// <param name="coord"></param> /// <param name="player"></param> /// <returns></returns> private int Safeness(Board board, Coordinate coord, Player player) { int playerCoords = 0; int opponentCoords = 0; Rules rule = new Rules(); if (coord.X == 1 || coord.X == 8 || coord.Y == 1 || coord.Y == 8) { return 4; } IList<Coordinate> coordinates = rule.OptionalMoves(board, coord, player); foreach (var coordinate in coordinates) { if (player == board.GetPlayer(coordinate)) { playerCoords++; } else if (board.GetOpponent(player) == board.GetPlayer(coordinate)) { opponentCoords++; } } return playerCoords - opponentCoords; }
/// <summary> /// An old revion of evalution function - our evaluation got better and better when playing thus we upgraded to the new one /// </summary> /// <param name="board"></param> /// <param name="player"></param> /// <returns></returns> public int EvaluateOld(Board board, Player player) { int score = 0; int blackscloseToKing = 0; int whitescloseToKing = 0; int whiteCanBeCaptured = 0; int blackCanBeCaptured = 0; int blackCaptures = 0; int whiteCaptures = 0; const int kingConstant = 130; const int soldierConstant = 100; int numWhiteKings = board.NumberOfWhiteKings; int numBlackKings = board.NumberOfBlackKings; int numOfWhiteSold = board.NumberOfWhitePieces; int numOfBlackSold = board.NumberOfBlackPieces; int blackScore = (numOfBlackSold*soldierConstant) + (numBlackKings*kingConstant); int whiteScore = (numOfWhiteSold*soldierConstant) + (numWhiteKings*kingConstant); score += ((blackScore - whiteScore)*200)/(blackScore + whiteScore); score += blackScore - whiteScore; for (int i = 1; i <= 32; i++) { if (board.GetPlayer(board[i]) == Player.Black) { blackscloseToKing += CloseToBecomeAKing(board[i])*5; blackCanBeCaptured += CanBeCaptured(board, board[i], Player.Black)*60; blackCaptures += CanCapture(board, board[i], Player.Black)*30; } else if (board.GetPlayer(board[i]) == Player.White) { whitescloseToKing += CloseToBecomeAKing(board[i])*5; whiteCanBeCaptured += CanBeCaptured(board, board[i], Player.White)*60; whiteCaptures += CanCapture(board, board[i], Player.White)*30; } } int closeToking = blackscloseToKing - whitescloseToKing; int canBeCapture = blackCanBeCaptured - whiteCanBeCaptured; int captures = blackCaptures - whiteCaptures; score += closeToking; score += canBeCapture; score += captures; const int randomizerMin = -10; const int randomizerMax = 10; return (player == Player.Black) ? score : -score + random.Next(randomizerMin, randomizerMax); }