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(); } }
public static void AiMoves(Dictionary <Square, AiChessPiece> board, Player currentPlayer) { Stopwatch watch = new Stopwatch(); watch.Start(); // Generate new environment for AI to simulate moves in float[,] strength = EvaluationValues.InitStrength(board); Game.AIMoves = null; _totalMovesSearched = 0; _turns = new List <List <ChessMove[]> >(); _ratings = new List <List <float> >(); Player player = new Player(currentPlayer); // Run evaluation EvaluateAiMove(board, strength, currentPlayer); // All combinations are now stored in _turns, and their corresponding ratings are in _ratings // Find the best depending on player float best; int index = 0; // Flatten lists Random random = new Random(); List <ChessMove[]> Moves = new List <ChessMove[]>(); List <float> Ratings = new List <float>(); for (var i = 0; i < _turns.Count; i++) { List <ChessMove[]> moveList = _turns[i]; List <float> ratingList = _ratings[i]; for (int j = 0; j < moveList.Count; j++) { Moves.Add(moveList[j]); Ratings.Add(ratingList[j]); Ratings[Ratings.Count - 1] += random.Next(-Randomness, Randomness); } } // add the ratings of the turns to the total rating for (int i = 0; i < Moves.Count; i++) { ChessMove[] moves = Moves[i]; Ratings[i] = 0; foreach (ChessMove move in moves) { if (move == null) { continue; } Ratings[i] += move.Rating; Ratings[i] += (currentPlayer.Name == "player1" ? 10000 : -10000); } } // find the min or max if (player.Name == "player1") { best = float.MinValue; for (int i = 0; i < Ratings.Count; i++) { if (!(Ratings[i] > best)) { continue; } best = Ratings[i]; index = i; } } else { best = float.MaxValue; for (int i = 0; i < Ratings.Count; i++) { if (!(Ratings[i] < best)) { continue; } best = Ratings[i]; index = i; } } // Set the ai's moves in Game Game.AIMoves = Moves[index]; Game.nextMove = Moves[index][0]; //Debug.Log(watch.ElapsedMilliseconds + " ms - " + _totalMovesSearched + // " moves searched. Best moves yielded eval of: " + Ratings[index]); }
// 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)); }