/// <summary> /// Check GameState /// </summary> /// <param name="player"></param> /// <param name="board"></param> /// <returns></returns> public GameState GetGameState(Player player, Board board) { var rule = new Rules(); var numberplayerPieces = rule.NumberOfPlayerPieces(board, player); var isplayerBlocked = rule.IsPlayerBlocked(board, player); if (numberplayerPieces == 0 || isplayerBlocked) { return GameState.Lost; } var numberopponentPieces = rule.NumberOfPlayerPieces(board, board.GetOpponent(player)); var isopponentBlocked = rule.IsPlayerBlocked(board, board.GetOpponent(player)); if (numberopponentPieces == 0 || isopponentBlocked) { return GameState.Won; } else { return GameState.Undetermined; } }
/// <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> /// 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> /// alpha beta algorithm /// boardcoordlist- each item in this map contains a new board (representing a new state) and a list of 2 coords representing the move that brought to this new state /// mapBoardSrcDestCap- each item in this map contains the new board and source- dest coords (like above) and a list of captured coordinate (in case the new board is a result of a capture) /// </summary> /// <param name="board"></param> /// <param name="depth"></param> /// <param name="alpha"></param> /// <param name="beta"></param> /// <param name="player"></param> /// <param name="maxplayer"></param> /// <param name="srcCoord"></param> /// <param name="destCoord"></param> /// <param name="updateBoard"></param> /// <param name="captures"></param> /// <returns></returns> public int AlphaBeta(Board board, int depth, int alpha, int beta, Player player, bool maxplayer, ref Coordinate srcCoord, ref Coordinate destCoord, ref Board updateBoard, ref IList<Coordinate> captures) { var robj = new Rules(); if (depth == 0 || rule.IsBoardLeaf(player, board)) // is node a terminal node { var obj = new HeuristicFunction(); return obj.Evaluate(board, player); //return Heurisitic value of node } //Finds if there are any captures IDictionary<IList<Coordinate>, IList<Coordinate>> capturesAvailable = robj.FindCaptures(board, player); IDictionary<Board, IList<Coordinate>> boardCoordsList; //Calculating board according to current state (with or without captures) if (capturesAvailable.Count > 0) { boardCoordsList = robj.CreateNewBoards(board, capturesAvailable); } else { boardCoordsList = robj.CalculateNewBoardsFromCoordinates(board, player); } //values which save capture list , source, detination on the chosen move var minsrcCoord = new Coordinate(); var mindestCoord = new Coordinate(); var maxsrcCoord = new Coordinate(); var maxdestCoord = new Coordinate(); var maxCapturesList = new List<Coordinate>(); var minCapturesList = new List<Coordinate>(); var minBoard = new Board(); var maxBoard = new Board(); //player if (maxplayer) { foreach (var newState in boardCoordsList) { Coordinate newSrcCoord = newState.Value[0]; Coordinate newDestCoord = newState.Value[1]; IList<Coordinate> capturesList = robj.MapContainsCoords(capturesAvailable, newSrcCoord, newDestCoord); IList<Coordinate> tempCapList = new List<Coordinate>(); int res = AlphaBeta(newState.Key, depth - 1, alpha, beta, board.GetOpponent(player), !maxplayer, ref newSrcCoord, ref newDestCoord, ref updateBoard, ref tempCapList); if (res > alpha) { alpha = res; maxsrcCoord = newState.Value[0]; maxdestCoord = newState.Value[1]; maxBoard = newState.Key.Copy(); maxCapturesList = new List<Coordinate>(capturesList); } if (beta <= alpha) { break; } if (capturesList.Count > 0) { capturesAvailable.Remove(capturesList); } } srcCoord = maxsrcCoord; destCoord = maxdestCoord; updateBoard = maxBoard.Copy(); captures = maxCapturesList; return alpha; } //opponent else { foreach (var newState in boardCoordsList) { Coordinate newSrcCoord = newState.Value[0]; Coordinate newDestCoord = newState.Value[1]; IList<Coordinate> capturesList = robj.MapContainsCoords(capturesAvailable, newSrcCoord, newDestCoord); IList<Coordinate> tempCapList = new List<Coordinate>(); int res = AlphaBeta(newState.Key, depth - 1, alpha, beta, board.GetOpponent(player), !maxplayer, ref newSrcCoord, ref newDestCoord, ref updateBoard, ref tempCapList); if (res < beta) { beta = res; minsrcCoord = newState.Value[0]; mindestCoord = newState.Value[1]; minBoard = newState.Key.Copy(); minCapturesList = new List<Coordinate>(capturesList); } if (beta <= alpha) { break; } if (capturesList.Count > 0) { capturesAvailable.Remove(capturesList); } } srcCoord = minsrcCoord; destCoord = mindestCoord; updateBoard = minBoard.Copy(); captures = minCapturesList; return beta; } }
/// <summary> /// checks if the game is already determined (one of the oppenents) /// </summary> /// <param name="player"></param> /// <param name="srcBoard"></param> /// <returns></returns> public bool IsBoardLeaf(Player player, Board srcBoard) { if (DidPlayerLost(player, srcBoard) || (DidPlayerLost(srcBoard.GetOpponent(player), srcBoard))) 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; }