public static void TestMinMax(OthelloGame _myGame, int minimaxDepth = 3) { const int testCount = 100; object wonGamesLock = new object(); int wonGames = 0; object tieGamesLock = new object(); int tieGames = 0; var stopwatch = Stopwatch.StartNew(); //Parallel.For(0, testCount, new ParallelOptions { MaxDegreeOfParallelism = Environment.ProcessorCount },index => //{ for (int index = 0; index < testCount; index++) {//non-parallel for loop to debug BoardStates player = (index % 2 == 0) ? BoardStates.black : BoardStates.white; OthelloGame testGame = new OthelloGame(); MinMaxAgent othelloAgent = new MinMaxAgent(2); RandomAgent randAgent = new RandomAgent(); while (!testGame.GameComplete) { if (testGame.WhosTurn == player) { testGame.MakeMove(othelloAgent.MakeMove(testGame, player)); } else { testGame.MakeMove(randAgent.MakeMove(testGame, ~player)); } } if (testGame.GameComplete)//just gotta check { if (testGame.FinalWinner == player) { lock (wonGamesLock) { wonGames++; } } else if (testGame.FinalWinner == BoardStates.empty) { lock (tieGamesLock) { tieGames++; } } Console.WriteLine("Finished Game " + index + ", " + testGame.FinalWinner.ToString() + " won " + testGame.GetPieceCount(testGame.FinalWinner) + " to " + testGame.GetPieceCount(OthelloGame.OpposingPlayer(testGame.FinalWinner)));; } else { throw new Exception("MiniMax Testing didn't complete a game"); } } //}); stopwatch.Stop(); Console.WriteLine("Won " + wonGames + " / " + testCount + " games, " + ((double)wonGames / testCount) * 100 + " %"); Console.WriteLine("Tied " + tieGames + " / " + testCount + " games, " + ((double)tieGames / testCount) * 100 + " %"); Console.WriteLine("Lost " + (testCount - wonGames - tieGames) + " / " + testCount + " games, " + ((double)(testCount - wonGames - tieGames) / testCount) * 100 + " %"); Console.WriteLine("Elapsed time for games : {0}", stopwatch.Elapsed); }
private byte[] PredictBestMove(int depth, OthelloGame game, BoardStates player) { byte[] bestMove = new byte[] { byte.MaxValue, byte.MaxValue }; List <byte[]> moves = game.GetPossiblePlayList(); double bestScore = int.MinValue + 1; if (game.GetPieceCount(BoardStates.empty) > 58)//first two moves, don't compute { return(OpeningMove(player, game)); } else if (moves.Count == 1) //don't compute if there is only 1 move { return(moves[0]); } foreach (byte[] move in moves) { OthelloGame testGame = game.DeepCopy(); testGame.MakeMove(move); double thisScore = MinimaxAlphaBeta(testGame, depth - 1, double.MinValue, double.MaxValue, player); if (thisScore > bestScore) { bestScore = thisScore; bestMove = move; } } return(bestMove); }
private double MinimaxAlphaBeta(OthelloGame board, int depth, double a, double b, BoardStates player)// bool isMaxPlayer) { // The heart of our AI. Minimax algorithm with alpha-beta pruning to speed up computation. // Higher search depths = greater difficulty. //from oliverzh200/reversi https://github.com/oliverzh2000/reversi if (depth == 0 || board.GameComplete) { return(HeuristicEval(player, board)); } double bestScore = double.MinValue; List <byte[]> validMoves = board.GetPossiblePlayList(); if (validMoves.Count > 0) { foreach (byte[] move in validMoves) { OthelloGame childBoard = board.DeepCopy(); childBoard.MakeMove(move); double nodeScore = MinimaxAlphaBeta(childBoard, depth - 1, a, b, player); bestScore = Math.Max(bestScore, nodeScore); a = Math.Max(bestScore, a); if (b <= a) //Prune { break; } } } else { return(MinimaxAlphaBeta(board, depth, a, b, player)); } return(bestScore); }
private byte[] PredictBestMove(int depth, OthelloGame game, BoardStates player) { byte[] bestMove = new byte[] { byte.MaxValue, byte.MaxValue }; List <byte[]> moves = game.GetPossiblePlayList(); double bestScore = int.MinValue + 1; foreach (byte[] move in moves) { OthelloGame testGame = game.DeepCopy(); testGame.MakeMove(move); double thisScore = MinimaxAlphaBeta(testGame, depth - 1, double.MinValue, double.MaxValue, player); if (thisScore > bestScore) { bestScore = thisScore; bestMove = move; } } if ((bestMove[0] == byte.MaxValue || bestMove[1] == byte.MaxValue) && moves.Count > 0) {//All moves are valued at -inf, return one of em return(moves[0]); } return(bestMove); }