/// <summary> /// Runs the minimax algoritm with alpha beta pruning /// </summary> /// <param name="board">The board.</param> /// <param name="depth">The depth.</param> /// <param name="alpha">The alpha.</param> /// <param name="beta">The beta.</param> /// <param name="isMax">if set to <c>true</c> [is maximum].</param> /// <param name="rootPlayer">The root player.</param> /// <returns>Minimax value for this board state</returns> private static int Minimax(CheckerBoard board, int depth, int alpha, int beta, bool isMax, PlayerColor rootPlayer) { List <CheckersMove> possibleMoves = board.GetMovesForPlayer(); if (depth == 0 || possibleMoves.Count == 0) { return(Score(board, rootPlayer)); } int value = 0; if (isMax) { value = int.MinValue; foreach (CheckersMove move in possibleMoves) { CheckersMove moveToMake = move; CheckerBoard boardToMakeMoveOn = board; do { boardToMakeMoveOn = (CheckerBoard)boardToMakeMoveOn.GetMinimaxClone(); boardToMakeMoveOn.MakeMoveOnBoard((CheckersMove)moveToMake.GetMinimaxClone()); moveToMake = moveToMake.NextMove; }while (moveToMake != null); int result = Minimax(boardToMakeMoveOn, depth - 1, alpha, beta, false, rootPlayer); value = Math.Max(result, value); alpha = Math.Max(alpha, value); if (alpha >= beta) { Logger.Debug("Branch was pruned"); break; } } } else { value = int.MaxValue; foreach (CheckersMove move in possibleMoves) { CheckersMove moveToMake = move; CheckerBoard boardToMakeMoveOn = board; do { boardToMakeMoveOn = (CheckerBoard)boardToMakeMoveOn.GetMinimaxClone(); boardToMakeMoveOn.MakeMoveOnBoard((CheckersMove)moveToMake.GetMinimaxClone()); moveToMake = moveToMake.NextMove; }while (moveToMake != null); int result = Minimax(boardToMakeMoveOn, depth - 1, alpha, beta, true, rootPlayer); value = Math.Min(result, value); beta = Math.Min(alpha, value); if (alpha >= beta) { Logger.Debug("Branch was pruned"); break; } } } return(value); }
/// <summary> /// Initial minimax starting method. This method kicks off the algoritm and finds the best move for the current player. /// If two or more moves have the same value, the best move is choosen randomly from the moves /// </summary> /// <param name="board">The board.</param> /// <returns>Best move for the current player</returns> public static CheckersMove MinimaxStart(CheckerBoard board) { int alpha = int.MinValue; int beta = int.MaxValue; thinking = true; List <CheckersMove> possibleMoves = board.GetMovesForPlayer(); List <int> values = new List <int>(); Logger.Info(string.Format("Max is {0}", board.CurrentPlayerTurn)); if (possibleMoves.IsNullOrEmpty()) { return(null); } foreach (CheckersMove move in possibleMoves) { CheckersMove moveToMake = move; CheckerBoard boardToMakeMoveOn = board; do { Logger.Debug("Board Before"); Logger.Debug(boardToMakeMoveOn.ToString()); boardToMakeMoveOn = (CheckerBoard)boardToMakeMoveOn.GetMinimaxClone(); boardToMakeMoveOn.MakeMoveOnBoard((CheckersMove)moveToMake.GetMinimaxClone()); moveToMake = moveToMake.NextMove; Logger.Debug("Board After"); Logger.Debug(boardToMakeMoveOn.ToString()); }while (moveToMake != null); values.Add(Minimax(boardToMakeMoveOn, Settings.AIDepth - 1, alpha, beta, false, board.CurrentPlayerTurn)); } int maxHeuristics = int.MinValue; foreach (int value in values) { if (value >= maxHeuristics) { maxHeuristics = value; } } //filter the list of moves based on max value List <CheckersMove> bestMoves = new List <CheckersMove>(); for (int i = 0; i < values.Count; i++) { if (values[i] == maxHeuristics) { bestMoves.Add(possibleMoves[i]); } } counter = 0; thinking = false; Logger.Info("Node Values: " + string.Join(",", values.Select(x => x.ToString()).ToArray())); return(bestMoves[Rng.Next(bestMoves.Count)]); }
/// <summary> /// Get all of the horizontal moves. The vertical moves are dependant on the vertical modifier. The direction of the hotizontal move is dependant on the modifier /// </summary> /// <param name="currentLocation">The current location.</param> /// <param name="checkerBoard">The checker board.</param> /// <param name="oneAdjacentRow">The one adjacent row.</param> /// <param name="verticalModifier">The vertical modifier.</param> /// <param name="horizontalModifier">The horizontal modifier.</param> /// <returns>List of moves</returns> private List <CheckersMove> ProcessBoardHorizontal(CheckersPoint currentLocation, CheckerBoard checkerBoard, int oneAdjacentRow, int verticalModifier, int horizontalModifier) { List <CheckersMove> list = new List <CheckersMove>(); int adjacentCol = currentLocation.Column + (1 * horizontalModifier); //Check our bounds if (adjacentCol >= 0 && adjacentCol < 8) { CheckerPiece possibleCheckerOnPossiblePoint = checkerBoard.BoardArray[oneAdjacentRow][adjacentCol].CheckersPoint.Checker; if (possibleCheckerOnPossiblePoint == null || possibleCheckerOnPossiblePoint is NullCheckerPiece) { //we can go here list.Add(new CheckersMove(currentLocation, new CheckersPoint(oneAdjacentRow, adjacentCol))); } else { //can we jump this guy? if ((possibleCheckerOnPossiblePoint is IRedPiece && this is IBlackPiece) || (possibleCheckerOnPossiblePoint is IBlackPiece && this is IRedPiece)) { //go another row up and another column to the right int twoAdjacentRow = oneAdjacentRow + (1 * verticalModifier); int twoColAdjacent = adjacentCol + (1 * horizontalModifier); //Check bounds if (twoColAdjacent >= 0 && twoColAdjacent < 8 && twoAdjacentRow >= 0 && twoAdjacentRow < 8) { CheckerPiece possibleCheckerOnPossibleJumpPoint = checkerBoard.BoardArray[twoAdjacentRow][twoColAdjacent].CheckersPoint.Checker; if (possibleCheckerOnPossibleJumpPoint == null || possibleCheckerOnPossibleJumpPoint is NullCheckerPiece) { //we can go here CheckersMove jumpMove = new CheckersMove(currentLocation, new CheckersPoint(twoAdjacentRow, twoColAdjacent), new CheckersPoint(oneAdjacentRow, adjacentCol)); //This is a jump move //Get all possible moves for destination point //For each possible move that is a jump move, make a new move and link it //make the move on a temp clone of the board and pass that to find any more multimoves CheckerBoard clonedBoard = (CheckerBoard)checkerBoard.GetMinimaxClone(); clonedBoard.MakeMoveOnBoard((CheckersMove)jumpMove.GetMinimaxClone(), false); List <CheckersMove> movesAfterJump = this.GetPossibleMoves(jumpMove.DestinationPoint, clonedBoard); List <CheckersMove> processedList = GetJumpMoves(movesAfterJump); if (processedList.Count > 0) { foreach (CheckersMove move in processedList) { CheckersMove clonedMove = (CheckersMove)jumpMove.GetMinimaxClone(); clonedMove.NextMove = move; list.Add(clonedMove); } } else { list.Add(jumpMove); } } } } } } return(list); }