/* * Check if this move puts the king in a less secure position. * Returns 0 if does not involve king, 1000 if move protects king, and -1000 if move leaves open spot next to king. */ private static float EvaluateKingSafety(Dictionary <Square, AiChessPiece> board, Square initial, Square target, string owner) { AiChessPiece initialPiece = board[initial]; if (initialPiece.Type == "king" && initialPiece.Owner.Name == owner) { return(0); } List <AiChessPiece> initialSurroundings = AiMovementUtil.SurroundingPieces(board, initial); List <AiChessPiece> targetSurroundings = AiMovementUtil.SurroundingPieces(board, target); float score = 0; if (initialSurroundings.Any(piece => piece.Type == "king" && piece.Owner.Name == owner)) { score -= 1000; } if (targetSurroundings.Any(piece => piece.Type == "king" && piece.Owner.Name == owner)) { score += 1000; } return(score); }
// Logic for handling a move being made in AI evaluation private static void MakeMove(Dictionary <Square, AiChessPiece> board, ref float[,] strength, Stack <ChessMove> moves, ChessMove move, ref AiChessPiece holder, ref float[,] holderStrength, Player player, ref bool[] commanderState) { holderStrength = strength.Clone() as float[, ]; moves.Push(move); AiChessPiece piece = board[move.InitialSquare]; // make the next move if (move.Attack) { holder = board[move.TargetSquare]; board.Remove(move.TargetSquare); if (!move.AttackOnly) { board.Remove(move.InitialSquare); board.Add(move.TargetSquare, piece); piece.Position = move.TargetSquare; } } else { board.Remove(move.InitialSquare); board.Add(move.TargetSquare, piece); piece.Position = move.TargetSquare; } player.Commanders[piece.GetDivision()].Moved = true; //player.RemainingMoves--; commanderState[0] = player.Commanders["M"].Moved; commanderState[1] = player.Commanders["R"].Moved; commanderState[2] = player.Commanders["L"].Moved; }
public static List <Square> GetPossibleMoves(Dictionary <Square, AiChessPiece> board, AiChessPiece piece, bool updateStrength, float[,] strength) { string owner = piece.Owner.Name; int direction = owner == "player1" ? 1 : -1; List <Square> possibleMoves = null; switch (piece.Type) { case "pawn": case "bishop": { possibleMoves = InfantryMoves(piece.Position, direction, updateStrength, strength); break; } case "king": case "queen": { possibleMoves = RoyaltyMoves(board, piece.Position, 3, owner, updateStrength, strength); break; } case "knight": { possibleMoves = RoyaltyMoves(board, piece.Position, 4, owner, updateStrength, strength); break; } case "rook": { possibleMoves = ArcherMoves(board, piece.Position, owner, updateStrength, strength); break; } } List <Square> cleaned = new List <Square>(); List <Square> surrounding = MovementUtil.SurroundingSquares(piece.Position); foreach (Square s in possibleMoves.Where(s => ChessGrid.ValidPosition(s))) { if (board.ContainsKey(s)) { if (piece.Type != "knight" && !s.AttackOnly && !surrounding.Contains(s)) { // remove this if statement if we want pieces to be able to attack any squares they can reach continue; } AiChessPiece target = board[s]; if (target.Owner == piece.Owner) { continue; } } cleaned.Add(s); } return(cleaned); }
private const int Randomness = 0; // Degree of randomness, random number between -val to +val is added to each rating private static void EvaluateAiMove(Dictionary <Square, AiChessPiece> board, float[,] strength, Player currentPlayer) { List <ChessMove> moves = GetMovesForPlayer(board, strength, currentPlayer); List <Task <float> > threads = new List <Task <float> >(); for (int i = 0; i < NumThreads; i++) { Player threadedPlayer = new Player(currentPlayer); List <ChessMove> threadedMoves = new List <ChessMove>(); int threadIndex = _turns.Count; // split up best moves for (int j = 0; j < moves.Count; j += NumThreads) { if (j + threadIndex >= moves.Count) { continue; } threadedMoves.Add(moves[j + threadIndex]); } // store new board for thread Dictionary <Square, AiChessPiece> threadedBoard = new Dictionary <Square, AiChessPiece>(); foreach (Square s in board.Keys) { AiChessPiece p = board[s]; threadedBoard.Add(s, new AiChessPiece(p.Name, p.Owner, p.Type, p.Position)); } // init new strength for thread float[,] threadedStrength = EvaluationValues.InitStrength(threadedBoard); _turns.Add(new List <ChessMove[]>()); _ratings.Add(new List <float>()); _boards.Add(threadedBoard); Task <float> thread = new Task <float>(() => Minimax(threadedMoves, threadedBoard, threadedStrength, new Stack <ChessMove>(), threadedPlayer, TurnSearchDepth, 3, threadIndex, 0)); threads.Add(thread); thread.Start(); } // Wait for all threads to join foreach (Task <float> t in threads) { t.Wait(); } }
// Movement for the Rook and its Attack range private static List <Square> ArcherMoves(Dictionary <Square, AiChessPiece> board, Square start, string owner, bool updateStrength, float[,] strength) { int direction = owner == "player1" ? 1 : -1; List <Square> moves = MovementUtil.SurroundingSquares(start); foreach (Square s in moves) { if (!board.ContainsKey(s)) { continue; } AiChessPiece target = board[s]; if (target.Owner.Name != owner) { s.AttackOnly = true; } } for (int i = -3; i <= 3; i++) { for (int j = -3; j <= 3; j++) { if (i == 0 && j == 0) { continue; } Square s = new Square(start.X + i, start.Y + j); if (updateStrength && ChessGrid.ValidPosition(s)) { strength[s.X, s.Y] += direction * EvaluationValues.PieceStrength["rook"]; } if (!board.ContainsKey(s)) { continue; } AiChessPiece target = board[s]; if (target.Owner.Name != owner) { moves.Add(new Square(s.X, s.Y, true)); } } } return(moves); }
public float EvaluateChessMove(Dictionary <Square, AiChessPiece> board, float[,] strength, Square initialSquare, Square targetSquare, string owner) { AiChessPiece initialPiece = board[initialSquare]; List <AiChessPiece> initialSurroundings = AiMovementUtil.SurroundingPieces(board, initialSquare); List <AiChessPiece> targetSurroundings = AiMovementUtil.SurroundingPieces(board, targetSquare); float initial = EvaluateChessSquare(strength, initialSquare, owner, initialPiece.Type, initialSurroundings); float target = EvaluateChessSquare(strength, targetSquare, owner, initialPiece.Type, targetSurroundings); var score = target - initial; // take into account if the target square is better than initial square if (targetSurroundings.Any(p => p.Type == "king" && p.Owner.Name != owner)) { score += 30; } if (board.ContainsKey(targetSquare)) { score += 20; // be more aggressive AiChessPiece targetPiece = board[targetSquare]; score += weightEvalPriority * EvaluatePiecePriority(targetPiece); score += weightEvalWinChance * EvaluateWinChance(board, strength, initialPiece, targetPiece); } score += weightEvalKingSafety * EvaluateKingSafety(board, initialSquare, targetSquare, owner); return(score * (owner == "player1" ? 1 : -1)); }
// Logic for handling a move being unmade in AI evaluation private static void UnmakeMove(Dictionary <Square, AiChessPiece> board, ref float[,] strength, Stack <ChessMove> moves, ref AiChessPiece holder, ref float[,] holderStrength, Player player, ref bool[] commanderState) { strength = holderStrength.Clone() as float[, ]; ChessMove move = moves.Pop(); AiChessPiece piece; if (!move.Attack) { piece = board[move.TargetSquare]; board.Remove(move.TargetSquare); board.Add(move.InitialSquare, piece); piece.Position = move.InitialSquare; } else { if (!move.AttackOnly) { piece = board[move.TargetSquare]; board.Remove(move.TargetSquare); board.Add(move.InitialSquare, piece); piece.Position = move.InitialSquare; } board.Add(move.TargetSquare, holder); holder.Position = move.TargetSquare; } //player.RemainingMoves++; player.Commanders["M"].Moved = commanderState[0]; player.Commanders["R"].Moved = commanderState[1]; player.Commanders["L"].Moved = commanderState[2]; }
// Get all possible moves for a piece on the board private static List <ChessMove> GetMovesForPiece(Dictionary <Square, AiChessPiece> board, float[,] strength, AiChessPiece piece) { List <Square> possibleSquares = AiMovementUtil.GetPossibleMoves(board, piece, false, null); List <ChessMove> possibleMoves = new List <ChessMove>(); foreach (Square s in possibleSquares) { if (piece.Position.Equals(s)) { continue; } ChessMove move = new ChessMove(board, piece.Position, s, EvaluateMove.Instance.EvaluateChessMove(board, strength, piece.Position, s, piece.Owner.Name)); move.AttackOnly = s.AttackOnly; possibleMoves.Add(move); } return(possibleMoves); }
// Recursively find best moves private static float Minimax(List <ChessMove> allMoves, Dictionary <Square, AiChessPiece> board, float[,] strength, Stack <ChessMove> moveStack, Player currentPlayer, int turnDepth, int moveDepth, int threadIndex, int turnIndex) { // Base case if (turnDepth == 0) { return(EvaluationValues.BoardEvaluate(board, strength, false)); } // If new turn, switch players if (moveDepth == 0) { Player opposite = currentPlayer.Name == "player1" ? Game.Controller.Player2 : Game.Controller.Player1; opposite.Commanders["M"].Moved = false; opposite.Commanders["R"].Moved = false; opposite.Commanders["L"].Moved = false; _ratings[threadIndex][turnIndex] += Minimax(GetMovesForPlayer(board, strength, opposite), board, strength, moveStack, opposite, turnDepth - 1, 3, threadIndex, turnIndex); } else { int movesSearched = 0; // Iterate over possible moves for (int i = 0; i < allMoves.Count; i++) { if (i >= allMoves.Count || movesSearched >= PossibleMoveDepth) { break; } ChessMove move = allMoves[i]; if (!board.ContainsKey(move.InitialSquare) || move.Attack && !board.ContainsKey(move.TargetSquare)) { continue; } int parentNodes = 3 - moveDepth; // Store results in _turns if (turnDepth == TurnSearchDepth) { turnIndex = GetIndex(_turns[threadIndex], _ratings[threadIndex]); for (int j = 0; j < parentNodes; j++) { _turns[threadIndex][turnIndex][j] = moveStack.Skip(parentNodes - j - 1).First(); _ratings[threadIndex][turnIndex] += _ratings[threadIndex][turnIndex - (j + 1)]; } } movesSearched++; _totalMovesSearched++; if (turnDepth == TurnSearchDepth) { _turns[threadIndex][turnIndex][parentNodes] = move; } // Save state of piece/board before making a move AiChessPiece holder = null; float[,] holderStrength = null; bool[] commanderState = new bool[3]; // Make a move MakeMove(board, ref strength, moveStack, move, ref holder, ref holderStrength, currentPlayer, ref commanderState); // Determine next best move _ratings[threadIndex][turnIndex] += Minimax(GetMovesForPlayer(board, strength, currentPlayer), board, strength, moveStack, currentPlayer, turnDepth, moveDepth - 1, threadIndex, turnIndex); // Unmake move UnmakeMove(board, ref strength, moveStack, ref holder, ref holderStrength, currentPlayer, ref commanderState); } } return(EvaluationValues.BoardEvaluate(board, strength, false)); }
/** * Evaluate the win change against an enemy piece. */ private static float EvaluateWinChance(Dictionary <Square, AiChessPiece> board, float[,] strength, AiChessPiece initial, AiChessPiece target) { List <AiChessPiece> surroundings = AiMovementUtil.SurroundingPieces(board, initial.Position); bool addOne = false; switch (initial.Type) { case "knight": { if (!surroundings.Contains(target)) { addOne = true; } break; } case "rook": return(10 * EvaluationValues.PieceValues[target.Type]); } float minRoll = CaptureMatrix.GetMin(initial.Type, target.Type, addOne); if (minRoll > 6) { return(-10000); } return(minRoll); }
/** * Evaluate the priority of an enemy piece. */ private static float EvaluatePiecePriority(AiChessPiece target) { return(EvaluationValues.PieceValues[target.Type]); }