IEnumerator Minimax(ChessBoardSnapshot boardPosition, int depth) { isRunningMinimax = true; //int highestScore = int.MinValue; //int highestScoreIndex = -1; highestScore = int.MinValue; highestScoreIndex = -1; //getAllBoardPositions returns a list of next possible board positions, the boolean flag is to tell whether the current move is Max or Min ChessBoardSnapshot[] nextBoardPositions = FindPossibleMoves(boardPosition, playerType); totalIterations = nextBoardPositions.Length; for (int i = 0; i < nextBoardPositions.Length; i++) { ChessBoardSnapshot board = nextBoardPositions[i]; int score = Min(board, depth); if (score > highestScore) { highestScore = score; highestScoreIndex = i; } lastScore = score; lastIteration = i; minimaxResult = nextBoardPositions[highestScoreIndex]; yield return(null); } isRunningMinimax = false; }
public int CalculateScore(ChessBoardSnapshot boardSnapshot, ChessPlayerType playerType) { int ret = 0; ChessPieceType[] board = boardSnapshot.board; for (int i = 0; i < board.Length; i++) { if (!board[i].IsValid()) { continue; } if (board[i].IsEmpty()) { continue; } if (board[i].IsSameTeamAs(playerType)) { ret += profilesDict[board[i]].score + 1; } else if (board[i].IsDifferentTeamAs(playerType)) { ret -= profilesDict[board[i]].score + 1; } } return(ret); }
IEnumerator MinimaxAB(ChessBoardSnapshot boardPosition, int depth) { isRunningMinimax = true; //int highestScore = int.MinValue; //int highestScoreIndex = -1; highestScore = int.MinValue; highestScoreIndex = -1; ChessBoardSnapshot[] nextBoardPositions = FindPossibleMoves(boardPosition, playerType); totalIterations = nextBoardPositions.Length; for (int i = 0; i < nextBoardPositions.Length; i++) { int score = AlphaBeta(nextBoardPositions[i], depth, int.MinValue, int.MaxValue, false); if (score > highestScore) { highestScore = score; highestScoreIndex = i; } lastScore = score; lastIteration = i; minimaxResult = nextBoardPositions[highestScoreIndex]; yield return(null); } isRunningMinimax = false; }
/// <summary> /// Generate the next board snapshot /// </summary> /// <param name="boardSnapshot"></param> /// <param name="changed">Changes to the positions</param> /// <returns> /// A new modified snapshot /// </returns> public ChessBoardSnapshot GenNextSnapshot(ChessBoardSnapshot boardSnapshot, params ChessPosition[] changed) { ChessBoardSnapshot newBoard = AdjustBoard(boardSnapshot, "Board #" + snapshots.Count.ToString("0000"), changed); snapshots.Add(newBoard); return(newBoard); }
/// <summary> /// Use a board snapshot to create the board /// </summary> /// <param name="boardSnapshot"></param> public void LoadFromSnapshot(ChessBoardSnapshot boardSnapshot) { List <ChessPosition> boardDict = boardSnapshot.ToList(); if (boardDict.Count <= 0) { return; } foreach (KeyValuePair <int, ChessPieceScript> kvp in piecesDict) { Destroy(kvp.Value.gameObject); } piecesDict.Clear(); for (int i = 0; i < boardDict.Count; i++) { ChessPieceScript newPiece = Instantiate(piecePrefab, chessPiecesParent) .GetComponent <ChessPieceScript>(); newPiece.Position = boardDict[i]; piecesDict.Add(newPiece.Coord.ToArrayCoord(), newPiece); } }
/// <summary> /// Adjust the board snapshot and return it back /// </summary> /// <param name="boardSnapshot"></param> /// <param name="newName"></param> /// <param name="changed">Changes to the positions</param> /// <returns> /// A new modified snapshot /// </returns> public ChessBoardSnapshot AdjustBoard(ChessBoardSnapshot boardSnapshot, string newName = "Board", params ChessPosition[] changed) { //ChessBoardSnapshot newBoard = ScriptableObject.CreateInstance<ChessBoardSnapshot>(); ChessBoardSnapshot newBoard = ScriptableObject.Instantiate <ChessBoardSnapshot>(boardSnapshot); newBoard.name = newName; for (int i = 0; i < changed.Length; i++) { int aCoord = changed[i].coord.ToArrayCoord(); newBoard.board[aCoord] = changed[i].type; newBoard.hasMoved[aCoord] = true; } return(newBoard); }
int Min(ChessBoardSnapshot boardPosition, int depth) { if (depth <= 0 || boardPosition.IsEndGame()) { return(GameManager.Instance.CalculateScore(boardPosition, playerType)); } int lowestScore = int.MaxValue; ChessBoardSnapshot[] nextBoardPositions = FindPossibleMoves(boardPosition, playerType.ToOpposite()); for (int i = 0; i < nextBoardPositions.Length; i++) { ChessBoardSnapshot board = nextBoardPositions[i]; int score = Max(board, depth - 1); if (score < lowestScore) { lowestScore = score; } } return(lowestScore); }
IEnumerator IterativeDeepeningSearch(ChessBoardSnapshot boardPosition) { isRunningItDeep = true; float startTime = Time.unscaledTime; bool isOutTime = false; for (int depth = 1; (depth <= minDepth || depth <= maxDepth && !isOutTime); depth++) { StartCoroutine(MinimaxAB(boardPosition, depth)); hasRunMinimax = true; isRunningMinimax = true; iterativeDepth = depth; yield return(new WaitUntil(() => isRunningMinimax == false)); hasRunMinimax = false; isOutTime = Time.unscaledTime - startTime >= maxTime; } isRunningItDeep = false; }
public ChessBoardSnapshot[] FindPossibleMoves(ChessBoardSnapshot boardSnapshot, ChessPlayerType playerType) { List <ChessBoardSnapshot> ret = new List <ChessBoardSnapshot>(); Dictionary <int, ChessPosition> boardDict = boardSnapshot.ToDictionary(); foreach (KeyValuePair <int, ChessPosition> kvp in boardDict) { ChessPosition position = kvp.Value; if (position.type.IsDifferentTeamAs(playerType)) { continue; } ChessCoordinate from = position.coord; ChessPieceMove[] possibleMoves = GameManager.Instance.profilesDict[position.type].possibleMoves; for (int j = 0; j < possibleMoves.Length; j++) { ChessCoordinate to = from + possibleMoves[j].move; int k = 1; while ( to.IsWithinRange() && (possibleMoves[j].repeatTimes < 0 || k <= possibleMoves[j].repeatTimes) ) { if (GameManager.Instance.IsValidSpecialRule(possibleMoves[j].specialRule, position, from, to)) { // When there are only finite amount of moves, set only when reached destination if (possibleMoves[j].repeatTimes < 0 || k == possibleMoves[j].repeatTimes) { bool isLastMove = false; if (possibleMoves[j].pattern == ChessPieceMovePattern.Normal) { if (boardDict.ContainsKey(to.ToArrayCoord())) { if (boardDict[to.ToArrayCoord()].type.IsSameTeamAs(position.type)) { break; } isLastMove = true; } } else if (possibleMoves[j].pattern == ChessPieceMovePattern.MoveOnly) { if (boardDict.ContainsKey(to.ToArrayCoord())) { break; } } else if (possibleMoves[j].pattern == ChessPieceMovePattern.CaptureOnly) { if (!boardDict.ContainsKey(to.ToArrayCoord())) { break; } if (boardDict[to.ToArrayCoord()].type.IsSameTeamAs(position.type)) { break; } } // Pawn Promotion if (position.type.IsPawn()) { if ( position.type == ChessPieceType.WhitePawn && position.coord.y == 0 ) { position.type = ChessPieceType.WhiteQueen; } else if ( position.type == ChessPieceType.BlackPawn && position.coord.y == 7 ) { position.type = ChessPieceType.BlackQueen; } } ret.Add(GameManager.Instance.AdjustBoard ( boardSnapshot, position.type.ToIcon() + " " + from.x + "," + from.y + "-->" + to.x + "," + to.y, new ChessPosition(ChessPieceType.None, from, true), new ChessPosition(position.type, to, true) )); if (isLastMove) { break; } } else { if (boardDict.ContainsKey(to.ToArrayCoord())) { break; } } } to += possibleMoves[j].move; k++; } } } return(ret.ToArray()); }
int AlphaBeta(ChessBoardSnapshot boardPosition, int depth, int alpha, int beta, bool maximizingPlayer) { int origAlpha = alpha; int origBeta = beta; MinimaxNode node = new MinimaxNode(); // Transposition Table Lookup; node is the lookup key for ttEntry ulong hash = boardPosition.board.ToZobristHash(); if (tTable.ContainsKey(hash)) { node = tTable[hash]; if (node.depth >= depth) { switch (node.flag) { case MinimaxNodeFlag.Exact: return(node.eval); case MinimaxNodeFlag.LowerBound: if (node.eval > alpha) { alpha = node.eval; } break; case MinimaxNodeFlag.UpperBound: if (node.eval < beta) { beta = node.eval; } break; } if (beta <= alpha) { return(node.eval); } } } // Minimax + Alpha Beta Pruning if (depth <= 0 || boardPosition.IsEndGame()) { return(GameManager.Instance.CalculateScore(boardPosition, playerType)); } int val = 0; if (maximizingPlayer) { val = int.MinValue; ChessBoardSnapshot[] nextBoardPositions = FindPossibleMoves(boardPosition, playerType); for (int i = 0; i < nextBoardPositions.Length; i++) { int newValue = AlphaBeta(nextBoardPositions[i], depth - 1, alpha, beta, false); if (newValue > val) { val = newValue; } if (val > alpha) { alpha = val; } if (beta <= alpha) { break; } } } else { val = int.MaxValue; ChessBoardSnapshot[] nextBoardPositions = FindPossibleMoves(boardPosition, playerType.ToOpposite()); for (int i = 0; i < nextBoardPositions.Length; i++) { int newValue = AlphaBeta(nextBoardPositions[i], depth - 1, alpha, beta, true); if (newValue < val) { val = newValue; } if (val < beta) { beta = val; } if (beta <= alpha) { break; } } } // Transposition Table Store; node is the lookup key for ttEntry node.hash = hash; node.eval = val; if (val <= origAlpha) { node.flag = MinimaxNodeFlag.UpperBound; } else if (val >= origBeta) { node.flag = MinimaxNodeFlag.LowerBound; } else { node.flag = MinimaxNodeFlag.Exact; } node.depth = depth; if (tTable.ContainsKey(hash)) { tTable[hash] = node; } else { tTable.Add(hash, node); } return(val); }