private void backgroundWorker_DoWork(object sender, DoWorkEventArgs e) { var game = (GameState)e.Argument; var move = _ai.FindGoodMove(game); move.MakeMove(game); game.Ply++; e.Result = game; }
static void GameLoop() { int MCTSWins = 0; int ABWins = 0; int gameIterator = 0; Random r = new Random(); var fullauto = new bool[] { false, false }; fullauto[1] = true; var game = GameState.NewGame(5); MCTree tree = new MCTree(game); var ai = new TakAI_V4(game.Size, maxDepth: 3); var evaluator = ai.Evaluator; bool gameOver; int eval; var recentMoves = new Stack <IMove>(); var recentStates = new Stack <GameState>(); while (true) { // print board PrintBoard(game, previous: recentStates.Count > 0 ? recentStates.Peek() : null); evaluator.Evaluate(game, out eval, out gameOver); if (gameOver) { Console.Write("Game over, "); if (eval == 0) { Console.WriteLine("Tie"); } else if (eval > 0) { MCTSWins++; } else { ABWins++; } for (int i = 0; i < game.Size; i++) { gameIterator++; } //records results of game and resets board if (gameIterator < gameNum) { game = GameState.NewGame(game.Size); } } Console.Write("[T{0}, {1}]: ", game.Ply, (game.Ply & 1) == 0 ? 'X' : 'O'); string cmd; if (fullauto[game.Ply & 1] && !gameOver) { ABTree ABTree = new ABTree(game); cmd = ABTree.AB(1, 15F); /* * cmd = "ai"; * Console.WriteLine(cmd);*/ } else { //cmd = Console.ReadLine().Trim(); var legalMoves = new List <IMove>(); Helper.EnumerateMoves(legalMoves, game, ai.RandomPositions); int index = r.Next(legalMoves.Count); evaluator.Evaluate(game, out eval, out gameOver); if (!gameOver) { //check if current state exists in MCT. If so, move root node to maintain exploration records if (!tree.changeRoot(game)) { tree = new MCTree(game); } tree.evaluate(tree.root); cmd = tree.MCTS(15); /*ABTree tree = new ABTree(game); * cmd = tree.AB(1);*/ //Console.WriteLine("\nMCTree:" + cmd); } else { cmd = ""; } } if (string.IsNullOrEmpty(cmd) || cmd == "q") { break; } else if (cmd == "help") { Console.ForegroundColor = ConsoleColor.Cyan; Console.WriteLine("==Global commands"); Console.ForegroundColor = ConsoleColor.Gray; Console.WriteLine("ai AI will choose a move for this player"); Console.WriteLine("ai on This player will become completely controlled by the AI"); Console.WriteLine("ai N Set AI difficulty to N [2-9], default is 3."); Console.WriteLine("ai off Disable AI control for all players"); Console.WriteLine("undo Undo last move, including the AI's response (if any)"); Console.WriteLine("list List all legal moves in the current board position"); Console.WriteLine(" Warning: level N+1 is roughly 30 to 50 times slower than N!"); Console.ForegroundColor = ConsoleColor.Cyan; Console.WriteLine("==Move notation"); Console.ForegroundColor = ConsoleColor.Gray; Console.WriteLine(" Use Portable Tak Notation (PTN)"); Console.WriteLine(" https://www.reddit.com/r/Tak/comments/3o2omm/tak_game_notation/"); Console.Write("<Press any key to continue>"); Console.ReadKey(); Console.WriteLine(); } else if (cmd == "undo") { while (recentMoves.Count > 0) { var undoing = recentMoves.Pop(); undoing.TakeBackMove(game); game.Ply--; recentStates.Pop(); if (!fullauto[game.Ply & 1]) { break; } } } else if (cmd == "ai off") { fullauto[0] = fullauto[1] = false; } else if (cmd.StartsWith("ai ") && cmd.Length > 3 && Char.IsDigit(cmd[3])) { int diff = 0; if (!int.TryParse(cmd.Substring(3), out diff)) { diff = 0; } if (diff < 2 || diff > 9) { Console.ForegroundColor = ConsoleColor.Red; Console.WriteLine("Invalid difficulty level. Legal values are 2 thru 9."); Console.ForegroundColor = ConsoleColor.Gray; } else { if (diff == ai.MaxDepth) { Console.ForegroundColor = ConsoleColor.Red; Console.WriteLine("AI difficulty is already set to " + diff.ToString()); } else { Console.ForegroundColor = ConsoleColor.Cyan; ai.MaxDepth = diff; Console.WriteLine("AI difficulty set to " + diff.ToString()); Console.ForegroundColor = ConsoleColor.Gray; } } } else { if (gameOver) { Console.WriteLine("Invalid command"); } else if (cmd == "ai on" || cmd == "ai") { if (cmd == "ai on") { fullauto[game.Ply & 1] = true; } var move = ai.FindGoodMove(game); var restoreColor = Console.ForegroundColor; Console.ForegroundColor = ConsoleColor.DarkGray; Console.WriteLine("ai move => {0}", move.Notate()); Console.ForegroundColor = restoreColor; recentStates.Push(game.DeepCopy()); recentMoves.Push(move); move.MakeMove(game); game.Ply++; } else if (cmd == "list") { var legalMoves = new List <IMove>(); Helper.EnumerateMoves(legalMoves, game, ai.RandomPositions); foreach (var move in legalMoves) { Console.WriteLine(move.Notate()); } } else { TakEngine.Notation.MoveNotation notated; if (!TakEngine.Notation.MoveNotation.TryParse(cmd, out notated)) { Console.ForegroundColor = ConsoleColor.Red; Console.WriteLine("UNRECOGNIZED MOVE NOTATION. Type 'help' for more information."); Console.ForegroundColor = ConsoleColor.Gray; continue; } var legalMoves = new List <IMove>(); Helper.EnumerateMoves(legalMoves, game, ai.RandomPositions); var match = notated.MatchLegalMove(legalMoves); if (match == null) { Console.ForegroundColor = ConsoleColor.Red; Console.WriteLine("ILLEGAL MOVE. Type 'help' for more information."); Console.ForegroundColor = ConsoleColor.Gray; } else { recentStates.Push(game.DeepCopy()); recentMoves.Push(match); match.MakeMove(game); game.Ply++; } } } } File.AppendAllText("Records/records.csv", MCTSWins.ToString() + "," + ABWins.ToString() + Environment.NewLine); Console.WriteLine("Exiting"); }
static void Analysis() { while (true) { GetPath: Console.Write("PTN file? "); var path = Console.ReadLine(); if (string.IsNullOrEmpty(path)) { break; } if (!System.IO.File.Exists(path)) { Console.WriteLine(); Console.WriteLine("File does not exist"); goto GetPath; } TakEngine.Notation.GameRecord gameRecord; int size; try { var database = TakEngine.Notation.TakPGN.LoadFromFile(path); if (database.Games.Count != 1) { Console.WriteLine("Database does not contain exactly 1 game"); goto GetPath; } gameRecord = database.Games[0]; size = gameRecord.BoardSize; } catch (Exception ex) { Console.WriteLine(ex.Message); goto GetPath; } var ai = new TakAI_V4(size); while (true) { Console.Write("Level [{0}]? ", ai.MaxDepth); var response = Console.ReadLine(); if (string.IsNullOrEmpty(response)) { break; } int level; if (int.TryParse(response.Trim(), out level) && level >= 2 && level < 10) { ai.MaxDepth = level; break; } Console.WriteLine("Invalid response"); } var game = GameState.NewGame(size); var legalMoves = new List <IMove>(); var evaluator = ai.Evaluator; foreach (var notated in gameRecord.MoveNotations) { int turn = (game.Ply >> 1) + 1; int player = (game.Ply & 1) + 1; string header = string.Format("#{0}, {1}.{2}: {3}", game.Ply + 1, turn, player, notated.Text); Console.Write("{0,-30}", header); legalMoves.Clear(); Helper.EnumerateMoves(legalMoves, game, ai.NormalPositions); var move = notated.MatchLegalMove(legalMoves); if (move == null) { Console.WriteLine("Illegal move?"); break; } move.MakeMove(game); game.Ply++; int unused; bool gameOver; evaluator.Evaluate(game, out unused, out gameOver); if (gameOver) { break; } ai.MaxDepth--; ai.FindGoodMove(game); var myeval = ai.LastEvaluation * -1; ai.MaxDepth++; move.TakeBackMove(game); game.Ply--; var aimove = ai.FindGoodMove(game); { int eval = myeval; if (0 != (game.Ply & 1)) { eval *= -1; } Console.WriteLine("Eval: {0}", eval); } if (Math.Abs(myeval - ai.LastEvaluation) > 100) { if (ai.LastEvaluation > 1000) { Console.WriteLine(); Console.WriteLine("Missed opportunity, AI sees certain victory:"); Console.WriteLine(" " + aimove.Notate()); Console.WriteLine(); } if (myeval < -1000) { Console.WriteLine(); Console.WriteLine("Blunder, defeat is now certain!"); Console.WriteLine(" AI suggests {0}, with expected outcome of {1}", aimove.Notate(), ai.LastEvaluation); } } else if (Math.Abs(myeval - ai.LastEvaluation) >= 20) { Console.WriteLine(); Console.WriteLine("Poor move? AI suggests {0}, with expected outcome of {1}", aimove.Notate(), ai.LastEvaluation); } move.MakeMove(game); game.Ply++; } } }