Beispiel #1
0
        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);
        }
Beispiel #2
0
    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);
    }
Beispiel #3
0
    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++;
    }
Beispiel #4
0
 void OnCollisionEnter2D(Collision2D collision)
 {
     if (collision.gameObject.GetComponent <Wall>() != null)
     {
         ms = MoveScore.DIE;
         GameMaster.GetInstance().gameOver = true;
     }
 }
Beispiel #5
0
    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);
    }
Beispiel #6
0
        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);
        }
Beispiel #7
0
        public int AddScore(MoveScore score)
        {
            if (score == MoveScore.One)
            {
                m_Score++;
            }
            else if (score == MoveScore.Two)
            {
                m_Score += 2;
            }

            return(m_Score);
        }
Beispiel #8
0
        public int AddScore(MoveScore score)
        {
            if(score == MoveScore.One)
            {
                m_Score++;
            }
            else if(score == MoveScore.Two)
            {
                m_Score += 2;
            }

            return m_Score;
        }
Beispiel #9
0
        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());
        }
Beispiel #10
0
        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);
            }
        }
Beispiel #11
0
        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);
        }
Beispiel #12
0
    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;
    }
Beispiel #13
0
 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);
        }
Beispiel #15
0
    //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();
        }
    }