public static OthelloGame GetReflectedAcrossA1H8(OthelloGame game) { OthelloGame newGame = game.DeepCopy(); for (int i = 0; i <= game.GetMovesMade(); i++) { for (int j = 0; j < BOARD_SIZE; j++) { for (int k = 0; k < BOARD_SIZE; k++) { if (game.BoardHistory[i] != null) { newGame.BoardHistory[i][j, k] = game.BoardHistory[i][k, j]; } } } } for (int i = 0; i < BOARD_SIZE; i++) { for (int j = 0; j < BOARD_SIZE; j++) { newGame.Board[i, j] = game.Board[j, i]; } } return(newGame); }
public static OthelloGame GetInverseGame(OthelloGame game) { OthelloGame newGame = game.DeepCopy(); for (int i = 0; i <= game.GetMovesMade(); i++) { for (int j = 0; j < BOARD_SIZE; j++) { for (int k = 0; k < BOARD_SIZE; k++) { if (game.BoardHistory[i] != null) { newGame.BoardHistory[i][j, k] = OpposingPlayer(game.BoardHistory[i][j, k]); } } } } for (int i = 0; i < BOARD_SIZE; i++) { for (int j = 0; j < BOARD_SIZE; j++) { newGame.Board[i, j] = OpposingPlayer(game.Board[i, j]); } } return(newGame); }
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 HeuristicEval(BoardStates player, OthelloGame game) { //Based of features of the board that humans have identified. //Hints of evaluation from any source I could find //idealy these could me optimized using a genetic algorithm, //but that is a different project const int searchableDepthOverride = 2; //override min-max in favor of complete evaluation const int endGame = 20; //<20 moves is endgame const int midGame = 40; // 20 moves in is midgame double value = 0; int empty = game.GetPieceCount(BoardStates.empty); if (game.GameComplete) { return(CompleteEval(player, game)); } else if (empty < searchableDepthOverride) { return(MinimaxAlphaBeta(game, searchableDepthOverride, int.MinValue, int.MaxValue, player)); } value += coinDiffWeight * Math.Pow((game.GetPieceCount(player) - game.GetPieceCount(~player) + empty - coinDiffOffset), coinDiffPower); value += cornerDiffWeight * Math.Pow((game.GetCornerCount(player) - game.GetCornerCount(~player) + empty - cornerDiffOffset), cornerDiffPower); value += nearCornerDiffWeight * Math.Pow((game.GetAdjCornerCount(player) - game.GetAdjCornerCount(~player) + empty - nearCornerDiffOffset), nearCornerDiffPower); value += avalibleMoveDiffWeight * Math.Pow((game.GetPossiblePlayList(player).Count() - game.GetPossiblePlayList(~player).Count() + empty - avalibleMoveDiffOffset), avalibleMoveDiffPower); value += nonTurnableCoinDiffWeight * Math.Pow((game.GetSafePeiceCountEstimation(player) - game.GetSafePeiceCountEstimation(~player) + empty - nonTurnableCoinDiffOffset), nonTurnableCoinDiffPower); value += ControlledCornerDiffWeight * Math.Pow((game.GetControlledCorners(player) - game.GetControlledCorners(~player) + empty - ControlledCornerDiffOffset), ControlledCornerDiffPower); return(value); }
public static OthelloGame GetHalfPiRotation(OthelloGame game) { OthelloGame newGame = game.DeepCopy(); for (int i = 0; i <= game.GetMovesMade(); i++) { for (int j = 0; j < BOARD_SIZE; j++) { for (int k = 0; k < BOARD_SIZE; k++) { if (game.BoardHistory[i] != null) { newGame.BoardHistory[i][j, k] = game.BoardHistory[i][BOARD_SIZE - k - 1, j]; } } } } for (int i = 0; i < BOARD_SIZE; i++) { for (int j = 0; j < BOARD_SIZE; j++) { newGame.Board[i, j] = game.Board[BOARD_SIZE - j - 1, i]; } } return(newGame); }
public static BoardStates GetCurrentLeader(OthelloGame game) { BoardStates leader = BoardStates.empty; leader = game.GetPieceCount(BoardStates.white) > game.GetPieceCount(BoardStates.black) ? BoardStates.white : BoardStates.black; return(leader); }
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); }
public OthelloGame DeepCopy() { //return ObjectCopier.Clone<OthelloGame>(this); OthelloGame game = (OthelloGame)this.MemberwiseClone(); game.Board = GetBoardCopy(this.Board); game.BoardHistory = GetBoardHistoryCopy(this.BoardHistory); return(game); }
public static List <OthelloGame> ReadAllGames3(string path) {//Parallel ForEach for files & games, all at once, outdated List <OthelloGame> gameRepo = new List <OthelloGame>(); List <string> files = GetFiles(path); object gameTransferLock = new object(); List <Task> gameTransferTask = new List <Task>(); Parallel.ForEach(files, new ParallelOptions { MaxDegreeOfParallelism = Environment.ProcessorCount }, (file) => //For each file, read games { object fileGameLock = new object(); List <OthelloGame> fileGameRepo = new List <OthelloGame>(); var stopwatch = System.Diagnostics.Stopwatch.StartNew(); List <ThorGame> games = FileIO.ReadThorFile(file); //Parallel.ForEach(games, (tgame) => //for each game, transfer to OthelloGame //{ foreach (ThorGame tgame in games) { OthelloGame oGame; try { oGame = new OthelloGame(tgame); lock (fileGameLock) { fileGameRepo = fileGameRepo.Concat(OthelloGame.GetAllGameRotations(oGame)).ToList(); } } catch (Exception) { System.Diagnostics.Debug.WriteLine("failed a THOR->OthelloGame Transformation"); } } //}); lock (gameTransferLock) { gameRepo = gameRepo.Concat(fileGameRepo).ToList(); } if (8 * games.Count != fileGameRepo.Count) { Console.WriteLine("Games have been lost, " + fileGameRepo.Count + " / " + (8 * games.Count) + " games transferred : " + ((double)fileGameRepo.Count / (8 * games.Count)) + " %"); } stopwatch.Stop(); Console.WriteLine("Elapsed time for transferring " + file + " info= {0}", stopwatch.Elapsed); }); return(gameRepo); }
private static int CompleteEval(BoardStates player, OthelloGame game) { if (game.FinalWinner == player) { return(int.MaxValue); } else { return(int.MinValue); } }
public static List <OthelloGame> ReadAllGames(string path) {//Task per game in a given File, tackles one file at a time List <OthelloGame> gameRepo = new List <OthelloGame>(); List <string> files = GetFiles(path); object gameTransferLock = new object(); foreach (var file in files) //For each file, read games { object fileGameLock = new object(); List <OthelloGame> fileGameRepo = new List <OthelloGame>(); List <Task> fileTransferTask = new List <Task>(); var stopwatch = System.Diagnostics.Stopwatch.StartNew(); List <ThorGame> games = FileIO.ReadThorFile(file); foreach (ThorGame tgame in games) //for each game, transfer to OthelloGame { OthelloGame oGame; fileTransferTask.Add(Task.Run(() => { try { oGame = new OthelloGame(tgame); lock (fileGameLock) { fileGameRepo = fileGameRepo.Concat(OthelloGame.GetAllGameRotations(oGame)).ToList(); } } catch (Exception) { System.Diagnostics.Debug.WriteLine("failed a THOR->OthelloGame Transformation"); } })); } Task.WaitAll(fileTransferTask.ToArray()); foreach (Task t in fileTransferTask) { t.Dispose(); } gameRepo = gameRepo.Concat(fileGameRepo).ToList(); if (8 * games.Count != fileGameRepo.Count) { Console.WriteLine("Games have been lost, " + fileGameRepo.Count + " / " + (8 * games.Count) + " games transferred : " + ((double)fileGameRepo.Count / (8 * games.Count)) + " %"); } stopwatch.Stop(); Console.WriteLine("Elapsed time for transferring " + file + " info= {0}", stopwatch.Elapsed); } return(gameRepo); }
private static byte[] OpeningMove(BoardStates player, OthelloGame game) {//avoid computation for first move - only one symmetric option //randomly select perpendicular or diagonal for second move - parallel //has been shown to be much worse //SPECIFIC TO 8x8 BOARDS byte[][] firstMoves = new byte[4][] { new byte[] { 2, 3 }, new byte[] { 3, 2 }, new byte[] { 4, 5 }, new byte[] { 5, 4 } }; if (game.GetPieceCount(BoardStates.empty) == 60) { Random rndGen = new Random(); int rand = (int)Math.Ceiling(rndGen.NextDouble() * 4); switch (rand) { case 1: return(firstMoves[0]); case 2: return(firstMoves[1]); case 3: return(firstMoves[2]); case 4: return(firstMoves[3]); default: throw new Exception("OpeningMove has faulted with random number generation"); } } if (game.GetPieceCount(BoardStates.empty) == 59) { List <byte[]> moves = game.GetPossiblePlayList(); Random rndGen = new Random(); byte rand = (byte)Math.Ceiling(rndGen.NextDouble() * 2); switch (rand) { case 1: //diagonal return(moves[0]); case 2: //perpendicular return(moves[0]); default: throw new Exception("Opening move has faulted with random number generation"); } } return(new byte[] { byte.MaxValue, byte.MaxValue }); }
public static List <OthelloGame> ReadAllGames2(string path) {//Thread per File, tackles all at once, outdated List <OthelloGame> gameRepo = new List <OthelloGame>(); List <Thread> fileIOThreadList = new List <Thread>(); List <string> files = GetFiles(path); foreach (var file in files) { fileIOThreadList.Add(new Thread(() => { List <OthelloGame> fileGameRepo = new List <OthelloGame>(); var stopwatch = System.Diagnostics.Stopwatch.StartNew(); List <ThorGame> games = ReadThorFile(file); foreach (ThorGame tgame in games) { OthelloGame oGame; try { oGame = new OthelloGame(tgame); fileGameRepo = fileGameRepo.Concat(OthelloGame.GetAllGameRotations(oGame)).ToList(); } catch (Exception) { System.Diagnostics.Debug.WriteLine("failed a THOR->OthelloGame Transformation"); } } gameRepo = gameRepo.Concat(fileGameRepo).ToList(); stopwatch.Stop(); Console.WriteLine("Elapsed time for transferring " + file + " info= {0}", stopwatch.Elapsed); })); } foreach (Thread t in fileIOThreadList) { t.Start(); } foreach (Thread t in fileIOThreadList) { t.Join(); } return(gameRepo); }
public static List <OthelloGame> GetAllGameRotations(OthelloGame game) { List <OthelloGame> gameList = new List <OthelloGame>(); gameList.Add(game); gameList.Add(GetReflectedAcrossA1H8(game)); gameList.Add(GetReflectedAcrossA8H1(game)); gameList.Add(GetPiRotation(game)); OthelloGame invGame = GetHalfPiRotation(GetInverseGame(game)); gameList.Add(invGame); gameList.Add(GetReflectedAcrossA1H8(game)); gameList.Add(GetReflectedAcrossA8H1(game)); gameList.Add(GetPiRotation(game)); return(gameList); }
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); }
public byte[] MakeMove(int searchDepth, OthelloGame game, BoardStates player) { return(PredictBestMove(searchDepth, game, player)); }
public override byte[] MakeMove(OthelloGame game, BoardStates player) { return(PredictBestMove(SEARCH_DEPTH, game, player)); }
public override double EvaluateBoard(OthelloGame game, BoardStates player) { throw new NotImplementedException(); }
/// <summary> /// Returns Agent's move of choice /// </summary> /// <param name="game"></param> /// <param name="player"></param> /// <returns>Location of choice move</returns> public abstract byte[] MakeMove(OthelloGame game, BoardStates player);
public static OthelloGame GetPiRotation(OthelloGame game) { OthelloGame newGame = GetHalfPiRotation(GetHalfPiRotation(game)); return(game); }