public MoveScore MakeMove(IMove move, Player p) { List <Box> results = SpeculateMove(move, p); bool foundSlot = false; foreach (Move m in AvailableMoves) { if (move.CompareTo(m) == 0) { m.SetPlayer(p); AvailableMoves.Remove(m); Moves.Add(m); foundSlot = true; break; } } System.Diagnostics.Debug.Assert(foundSlot); MoveScore score = MoveScore.Zero; if (results.Count > 0) { foreach (Box b in results) { m_Boxes.Add(b); score = BumpScore(score); } p.AddScore(results.Count); } return(score); }
public override void GameStep() { switch (compas) { case Compas.NORTH: dest.y += 1; break; case Compas.EAST: dest.x += 1; break; case Compas.SOUTH: dest.y -= 1; break; case Compas.WEST: dest.x -= 1; break; } steps++; stepsWithoutFood++; if (stepsWithoutFood > 1000 && PlayerPrefs.GetInt("isbot") == 1) { ms = MoveScore.DIE; killed = 1; GameMaster.GetInstance().gameOver = true; } sbSnakeHead.MoveTo(dest); GameMaster.Register(sbSnakeHead); }
public void Eat() { ms = MoveScore.EAT; stepsWithoutFood = 0; GameObject go = Instantiate(goBodyPrefab, sbSnakeTail.transform.position, Quaternion.identity); SnakeBody sb = go.GetComponent <SnakeBody>(); SnakeId.GetInstance().SetHighScore(apples + 1); GameMaster.AddScore(1); if (length == 1) { sbSnakeHead.back = sb; sb.front = sbSnakeHead; sbSnakeTail = sb; } else { sbSnakeTail.back = sb; sb.front = sbSnakeTail; sbSnakeTail = sb; } apples++; length++; }
void OnCollisionEnter2D(Collision2D collision) { if (collision.gameObject.GetComponent <Wall>() != null) { ms = MoveScore.DIE; GameMaster.GetInstance().gameOver = true; } }
public override IEnumerator GetDecision(TTTBoard board) { currentTime = System.DateTime.Now; isDoneMoving = false; //Reset previous data anticipatedResult = new MoveScore(0, 0); statesSampled = 0; culledStates = 0; moveChoices.Clear(); viableChoices.Clear(); //Copy the board to avoid affecting the real one TTTBoard testBoard = new TTTBoard(board); //Get all free squares - the player may cheat, but the AI does not testBoard.PopulateMoveQueue(moveChoices); //Error checking for if we try to edit a board that's complete if (moveChoices.Count > 0) { chosenMove = moveChoices.Peek(); } else { UnityEngine.Debug.LogError("TRYING TO PLAY ON ALREADY COMPLETE BOARD"); isDoneMoving = true; chosenMove = Vector2Int.one * -1; } //Allows us to check whether or not the following coroutine has finished, by saving a reference to a bool BoolCheck newBc = new BoolCheck(); StartCoroutine(GetBestMovesRecursive(testBoard, playingAs, 0, anticipatedResult, newBc, 2)); //Keep waiting for previous functions to complete before moving on while (!newBc.complete) { yield return(new WaitForEndOfFrame()); } //Debug info Debug.Log("Computer player " + playingAs.ToString() + " processed " + statesSampled + " unique states, " + "best case scenario: " + anticipatedResult.outcome + " after " + anticipatedResult.depth + " turns"); string tot = ""; for (int i = 0; i < viableChoices.Count; ++i) { Vector2Int vChoice = viableChoices.Dequeue(); viableChoices.Enqueue(vChoice); tot += vChoice; tot += " "; } Debug.Log("Choices: " + tot); }
MoveScore MiniMax(IState <MoveType> s, int alpha, int beta) { MoveScore best = new MoveScore(); List <MoveScore> scores = new List <MoveScore> (); foreach (var move in s.AllMoves) { var newState = s.Pick(move); var score = MiniMax(newState, move, alpha, beta); var moveScore = new MoveScore(move, score.Score); if (best.Move == null) { best = moveScore; } if (!s.Min) { if (best.Score < moveScore.Score) { best = moveScore; } if (alpha < best.Score) { alpha = best.Score; } if (beta <= alpha) { break; } } else { if (best.Score > moveScore.Score) { best = moveScore; } if (beta > best.Score) { beta = best.Score; } if (beta <= alpha) { break; } } } return(best); }
public int AddScore(MoveScore score) { if (score == MoveScore.One) { m_Score++; } else if (score == MoveScore.Two) { m_Score += 2; } return(m_Score); }
public int AddScore(MoveScore score) { if(score == MoveScore.One) { m_Score++; } else if(score == MoveScore.Two) { m_Score += 2; } return m_Score; }
public void Start(IGraphicProvider gfx) { m_GameBoard.Render(gfx); int playerMovesRemaining = m_MovesPerTurn; while (m_GameBoard.MovesRemaining > 0) { IMove move = null; while (true) { gfx.ShowUserTurn(CurrentPlayer); move = CurrentPlayer.GetNextMove(m_GameBoard); if (m_GameBoard.IsAvailableMove(move)) { break; } } MoveScore s = m_GameBoard.MakeMove(move, CurrentPlayer); playerMovesRemaining--; if (FreeMoveOnScore) { if (s == MoveScore.One || s == MoveScore.Two) { playerMovesRemaining += m_RewardLines; } } m_GameBoard.Render(gfx); if (playerMovesRemaining == 0) { NextPlayer(); playerMovesRemaining = m_MovesPerTurn; } } StringBuilder sb = new StringBuilder(); foreach (Player p in m_Players) { sb.AppendFormat("{0}: {1}\n", p.DisplayName, p.Score); } System.Windows.Forms.MessageBox.Show(sb.ToString()); }
static MoveScore BumpScore(MoveScore score) { switch (score) { case MoveScore.NotAllowed: return(MoveScore.One); case MoveScore.Zero: return(MoveScore.One); case MoveScore.One: return(MoveScore.Two); default: return(MoveScore.NotAllowed); } }
static string BestDFSMove(SnakeBoard board, List <string> moves) { Console.WriteLine("Doing DFS..."); // Order such that direction away from player is searched first moves = moves.OrderByDescending(x => NextSquareDistanceFromEnemy( board.GetPlayerPosition(), board.GetEnemyPosition(), x, board.GetEnemyDirection())).ToList(); MoveScore bestMove = new MoveScore(moves[0], DFSScore(game.SimulatePlayerMove(board, moves[0]))); for (int i = 1; i < moves.Count; i++) { int score = DFSScore(game.SimulatePlayerMove(board, moves[i])); if (score > bestMove.score) { bestMove.dir = moves[i]; bestMove.score = score; } } Console.WriteLine("Done DFS! Max depth: " + bestMove.score); return(bestMove.dir); }
public override void PreGameStep() { ms = MoveScore.MOVE; switch (dir) { case Turning.FORWARD: break; case Turning.LEFT: compas = (Compas)Mod((int)compas - 1, 4); turns++; break; case Turning.RIGHT: compas = (Compas)Mod((int)compas + 1, 4); turns++; break; } dir = Turning.FORWARD; }
void Awake() { _moveScore = FindObjectOfType <MoveScore>(); _pausePopup = FindObjectOfType <PausePopup>(); _winPopup = FindObjectOfType <WinPopup>(); }
public Move chooseMove(Game game, Player player) { Dictionary <simpleMove, MoveScore> moveResults = new Dictionary <simpleMove, MoveScore>(); // have to treat this player as if they will play random in these sub-games, not as monte carlo var currentController = player.Controller; player.Controller = new ControllerRandom(); var currentLogSettings = game.rules.LoggingSettings; object scorelock = new object(); // for monte carlo we should play out n random games and track how our next move might win or lose for (int i = 0; i < GamesToRun; i++) //Parallel.For(0, GamesToRun, (i) => { System.Diagnostics.Debug.Indent(); if (game.rules.LoggingSettings.showEachPlayersPlanning || ShowSubResults) { Console.WriteLine("Running sub-game"); } // create a copy of the game and play it through with what we know so far Game testgame = new Game(game); // becomes an infinitely recursive problem since every sub-game is also monte carlo testgame.rules.MaxPhysicalTurns = MaxSubGameDepth; if (!ShowSubGames) { testgame.rules.LoggingSettings = new GameRules.LogSettings() { logTime = false, showEachPlayersPlanning = false, showStatePerTurn = false, pausePerMove = false, winLossReasons = false, debugJumpchecks = false, showMovePerTurn = false, listMoveSeqenceAtEnd = false, showBombDefusals = false, showHiddenPieces = false, }; } // randomize any unknown pieces on the board if (testgame.CurrentBoard.PieceSet.Any(x => x.IsRevealed == false && x.Owner != player)) { List <CoordAbs> availableSpaces = new List <CoordAbs>(); // randomize any unknown piece and play through foreach (Piece unknownPiece in testgame.CurrentBoard.PieceSet.Where( x => x.IsRevealed == false && x.Owner != player)) { availableSpaces.Add(unknownPiece.pos); unknownPiece.turnRevealed = -1; // place holder to indicate piece is being moved testgame.CurrentBoard.PiecesLayout[unknownPiece.pos.X, unknownPiece.pos.Y] = null; } Random rand = new Random(); foreach (Piece unknownPiece in testgame.CurrentBoard.PieceSet.Where(x => x.turnRevealed == -1)) { // hide the piece again unknownPiece.turnRevealed = null; // assign it a random location from the spots available int ran = rand.Next(0, availableSpaces.Count); unknownPiece.pos = availableSpaces[ran]; availableSpaces.RemoveAt(ran); testgame.CurrentBoard.PiecesLayout[unknownPiece.pos.X, unknownPiece.pos.Y] = unknownPiece; } } // DIFFERENCE FROM MONTE CARLO - // weigh the state based on number of pieces we own instead of win/lose var results = testgame.Run(); int score = 0; if (results.Winners.Count == 1 && results.Winners.Contains(player)) // WIN { score = 100; } else if (results.Winners.Count == 1 && results.Winners.Contains(player)) // LOSS { score = -100; } else { score = testgame.CurrentBoard.PieceSet.Count(x => x.Owner == player); } // lookup via string instead of object Move firstMove = testgame.MoveSequence.FirstOrDefault().Value; // remember move sequence is <turn, move> if (firstMove == null) { continue; } simpleMove simple = new simpleMove() { from = firstMove.FromCoord, to = firstMove.ToCoord, }; MoveScore mscore = new MoveScore(); lock (scorelock) { if (moveResults.Keys.Contains(simple)) { mscore = moveResults[simple]; mscore.occurances++; mscore.totalScore += score; moveResults[simple] = mscore; } else { mscore.totalScore = score; mscore.occurances = 1; moveResults.Add(simple, mscore); } } if (game.rules.LoggingSettings.showEachPlayersPlanning || ShowSubResults) { Console.WriteLine($"{firstMove} ... => {score}, x{moveResults[simple].occurances} Total = {(moveResults[simple].totalScore / moveResults[simple].occurances)}"); } System.Diagnostics.Debug.Unindent(); } ; // reset back to original settings player.Controller = currentController; game.rules.LoggingSettings = currentLogSettings; // do the move that has the most win options // Note: the liklihood of making the same move, with 1000 randomized boards seems infinitely tiny, let alone useful var bestMoveStats = moveResults.OrderByDescending(x => x.Value.totalScore / x.Value.occurances).FirstOrDefault(); if (game.rules.LoggingSettings.showEachPlayersPlanning || ShowSubResults) { Console.WriteLine("Best move from subgames: " + bestMoveStats.Key.from + " to " + bestMoveStats.Key.to + " w/ avg score " + (bestMoveStats.Value.totalScore / bestMoveStats.Value.occurances)); } var bestMove = bestMoveStats.Key; if (bestMove.from == null) // if there is no moves available then return null and be removed from game { return(null); } // convert the sub-game move into a move that is applicable to the current game var relevantMove = new Move( game.CurrentBoard.GetPieceAtCoord(bestMove.from), bestMove.to, game.CurrentBoard, game.rules ); return(relevantMove); }
//Takes in the board, the type of symbol (X, O) to simulate being placed, the level of recursion, // a reference to the outcome to return, a check to say if it completed or was interrupted, and a previous best for AlphaBeta pruning private IEnumerator GetBestMovesRecursive(TTTBoard board, SquareState toPlace, int functionDepth, MoveScore returnOutcome, BoolCheck bc, int previousBest) { statesSampled += 1; //Outcome determines if the end result is a win, loss or tie. //It gets set to be the state we expect to get overriden, with 1 meaning a win and -1 meaning a loss //Because we expect the AI to only make moves that improve its odds of winning, we initialize it to -2 if it's the AI's turn //That way, it will get overriden immediately //The opposite is true for if it's the player's turn int outcome = ((toPlace == playingAs) ? -2 : 2); //Estimate of how long before the end of the game int movesUntilEndgame = 0; //Used to determine if the rest of the loop is garbage, due to alpha-beta pruning //The reason this is used as opposed to break; is to ensure that the list of move choices is kept consistent //The queue should be unaltered once the function passes to a higher recursion bool skipRestOfLoop = false; for (int i = 0; i < moveChoices.Count; ++i) { //For if alpha-beta determines all proceeding tests to be obsolete if (skipRestOfLoop) { //cycles through the queue until it returns to its starting arrangement moveChoices.Enqueue(moveChoices.Dequeue()); continue; } //Take a move from the queue to test Vector2Int testMove = moveChoices.Dequeue(); //Checks if the square is worth testing according to symmetry pruning - if it's not, skip the square and continue if (SymmetryPrune) { if (board.board[testMove.x][testMove.y].CheckIfCulled()) { moveChoices.Enqueue(testMove); culledStates += 1; continue; } } //Sets the square of the simulated board board.SetSquareState(testMove, toPlace); MoveScore moveScore = new MoveScore(0, 0); SquareState isVictory = board.CheckWin(); if (isVictory == SquareState.Empty) { //Nobody one, continue simulation if possible if (moveChoices.Count > 0) { //Set move score, which indicates (GameResult, GameLength). //Passes the current best outcome to conduct AlphaBeta BoolCheck newBc = new BoolCheck(); IEnumerator getReturn = GetBestMovesRecursive(board, (toPlace == SquareState.O) ? SquareState.X : SquareState.O, functionDepth + 1, moveScore, newBc, outcome); StartCoroutine(getReturn); //Do not continue until the inner function has succeeded while (!newBc.complete) { yield return(new WaitForEndOfFrame()); } //If moveScore is marked as invalid, it means to prune it //Move to the next iteration if (moveScore.invalid) { moveChoices.Enqueue(testMove); board.SetSquareState(testMove, SquareState.Empty); continue; } //Keeps track of how long the game will last, with the leaf node starting at 1 and counting up moveScore.depth += 1; } else { //Tie moveScore = new MoveScore(0, 1); } } else if (isVictory == playingAs) { //you won moveScore = new MoveScore(1, 1); } else { //Opponent won moveScore = new MoveScore(-1, 1); } moveChoices.Enqueue(testMove); board.SetSquareState(testMove, SquareState.Empty); if (AlphaBetaPrune && functionDepth > 0) { //AlphaBeta //The root function should not alpha-beta prune, only receive a series of possible moves if (playingAs == toPlace) { //If it's simulating your move and you can do better than the current best, your opponent will move to stop it //Mark the return as invalid to tell the outer function to skip it if (previousBest < moveScore.outcome) { culledStates += 1; returnOutcome.invalid = true; skipRestOfLoop = true; continue; } } else { //Opposite logic for opponent's turn if (previousBest > moveScore.outcome) { culledStates += 1; returnOutcome.invalid = true; skipRestOfLoop = true; continue; } } } if (playingAs == toPlace) { //If the move proposes a better result, then we overwrite our existing best move and options //If the result is net positive, we want a smaller MovesUntilEndgame. If net negative, we'll take the bigger. //If it's a tie, we don't care due to the fact that all tie games take the same amount of moves if (moveScore.outcome > outcome) { outcome = moveScore.outcome; movesUntilEndgame = moveScore.depth; if (functionDepth == 0) { viableChoices.Clear(); Debug.Log("VIABLE CHOICE: " + testMove + " AT DEPTH " + moveScore.depth + " RESULTING IN BETTER SCORE OF " + moveScore.outcome); viableChoices.Enqueue(testMove); } } else if (moveScore.outcome == outcome) { //Delay the game if losing, quicken the game if winning if (outcome > 0) { if (moveScore.depth < movesUntilEndgame) { movesUntilEndgame = moveScore.depth; if (functionDepth == 0) { viableChoices.Clear(); Debug.Log("VIABLE CHOICE: " + testMove + " AT DEPTH " + moveScore.depth + " RESULTING IN SCORE OF " + moveScore.outcome); viableChoices.Enqueue(testMove); } } else if (moveScore.depth == movesUntilEndgame) { if (functionDepth == 0) { Debug.Log("VIABLE CHOICE: " + testMove + " AT DEPTH " + moveScore.depth + " RESULTING IN SCORE OF " + moveScore.outcome); viableChoices.Enqueue(testMove); } } } else { if (moveScore.depth > movesUntilEndgame) { movesUntilEndgame = moveScore.depth; if (functionDepth == 0) { viableChoices.Clear(); Debug.Log("VIABLE CHOICE: " + testMove + " AT DEPTH " + moveScore.depth + " RESULTING IN SCORE OF " + moveScore.outcome); viableChoices.Enqueue(testMove); } } else if (moveScore.depth == movesUntilEndgame) { if (functionDepth == 0) { Debug.Log("VIABLE CHOICE: " + testMove + " AT DEPTH " + moveScore.depth + " RESULTING IN SCORE OF " + moveScore.outcome); viableChoices.Enqueue(testMove); } } } } } else { //Invert the checks during the Opponent's turn //The recursive function always starts on the AI's turn, so we can remove the isRoot checks during the opponent's logic if (moveScore.outcome < outcome) { outcome = moveScore.outcome; movesUntilEndgame = moveScore.depth; } else if (moveScore.outcome == outcome) { //Delay the game if losing, quicken the game if winning - but for the Opponent if (outcome < 0) { if (moveScore.depth < movesUntilEndgame) { movesUntilEndgame = moveScore.depth; } } else { if (moveScore.depth > movesUntilEndgame) { movesUntilEndgame = moveScore.depth; } } } } } //Return the best options we saved returnOutcome.outcome = outcome; returnOutcome.depth = movesUntilEndgame; //forces the program to stop for now, if the coroutine is taking too long to respond if (NonBlocking && (System.DateTime.Now - currentTime).Milliseconds >= millisecondsPerCycle) { yield return(new WaitForEndOfFrame()); currentTime = System.DateTime.Now; } //Tell the outer function that you have finished bc.complete = true; //Choose a square from the viable options once all the functions have completed if (functionDepth == 0) { isDoneMoving = true; currentTime = System.DateTime.Now; int toChoose = Random.Range(0, viableChoices.Count); for (int i = 0; i < toChoose; ++i) { viableChoices.Enqueue(viableChoices.Dequeue()); } chosenMove = viableChoices.Peek(); } }