Пример #1
0
        // runs an iterative deepening minimax search limited by the given timeLimit
        public Move IterativeDeepening(State state, double timeLimit)
        {
            int depth = 1;
            Stopwatch timer = new Stopwatch();
            Move bestMove = null;
            // start the search
            timer.Start();

            while (true)
            {
                if (timer.ElapsedMilliseconds > timeLimit)
                {
                    if (bestMove == null) // workaround to overcome problem with timer running out too fast with low limits
                    {
                        timeLimit += 10;
                        timer.Reset();
                        timer.Start();
                    }
                    else
                    {
                        return bestMove;
                    }
                }
                Tuple<Move, Boolean> result = IterativeDeepeningAlphaBeta(state, depth, Double.MinValue, Double.MaxValue, timeLimit, timer);
                if (result.Item2) bestMove = result.Item1; // only update bestMove if full recursion
                depth++;
            }
        }
Пример #2
0
        public void changeState(State newState)
        {
            currentState.Exit(this);

            currentState = newState;

            currentState.Enter(this);
        }
Пример #3
0
 public Node(Move move, Node parent, State state)
 {
     this.random = new Random();
     this.state = state;
     this.generatingMove = move;
     this.parent = parent;
     this.results = 0;
     this.visits = 0;
     this.children = new List<Node>();
     this.untriedMoves = state.GetMoves();
 }
Пример #4
0
 void Start()
 {
     audio.volume = Data.SfxVol%.75f;
     state = State.waiting;
     currentText = new System.Text.StringBuilder();
     currentPage = 0;
     currentLetter = 0;
     pageStrings = new string[pages.Length];
     for (int i = 0; i < pages.Length;i++ )
     {
         pageStrings[i] = pages[i].text;
     }
 }
Пример #5
0
        // Runs a Monte Carlo Tree Search limited by a given time limit
        public Node TimeLimited(State rootState, int timeLimit, Stopwatch timer)
        {
            Node rootNode = new Node(null, null, rootState);
            while (true)
            {
                if (timer.ElapsedMilliseconds > timeLimit)
                {
                    if (FindBestChild(rootNode.Children) == null && !rootNode.state.IsGameOver())
                    {
                        timeLimit += 10;
                        timer.Reset();
                        timer.Start();
                    }
                    else
                    {
                        return rootNode;
                    }

                }
                Node node = rootNode;
                State state = rootState.Clone();

                // 1: Select
                while (node.UntriedMoves.Count == 0 && node.Children.Count != 0)
                {
                    node = node.SelectChild();
                    state = state.ApplyMove(node.GeneratingMove);
                }

                // 2: Expand
                if (node.UntriedMoves.Count != 0)
                {
                    Move randomMove = node.UntriedMoves[random.Next(0, node.UntriedMoves.Count)];
                    state = state.ApplyMove(randomMove);
                    node = node.AddChild(randomMove, state);
                }

                // 3: Simulation
                while (state.GetMoves().Count != 0)
                {
                    state = state.ApplyMove(state.GetRandomMove());
                }

                // 4: Backpropagation
                while (node != null)
                {
                    node.Update(state.GetResult());
                    node = node.Parent;
                }
            }
        }
Пример #6
0
        // Starts the time limited Monte Carlo Tree Search and returns the best child node
        // resulting from the search
        public Node TimeLimitedMCTS(State rootState, int timeLimit)
        {
            Stopwatch timer = new Stopwatch();
            Node bestNode = null;
            while (bestNode == null && !rootState.IsGameOver())
            {
                timer.Start();
                Node rootNode = TimeLimited(rootState, timeLimit, timer);
                bestNode = FindBestChild(rootNode.Children);
                timeLimit += 10;
                timer.Reset();
            }

            return bestNode;
        }
Пример #7
0
 // Iterative Deepening Expectimax search
 public Move IterativeDeepening(State state, double timeLimit)
 {
     int depth = 1;
     Stopwatch timer = new Stopwatch();
     Move bestMove = null;
     // start the search
     timer.Start();
     while (timer.ElapsedMilliseconds < timeLimit)
     {
         Tuple<Move, Boolean> result = IterativeDeepeningExpectimax(state, depth, timeLimit, timer);
         if (result.Item2) bestMove = result.Item1; // only update bestMove if full recursion
         depth++;
     }
     return bestMove;
 }
Пример #8
0
        // recursive part of the minimax algorithm when used in iterative deepening search
        // checks at each recursion if timeLimit has been reached
        // if is has, it cuts of the search and returns the best move found so far, along with a boolean indicating that the search was not fully completed
        private Tuple<Move, Boolean> IterativeDeepeningAlphaBeta(State state, int depth, double alpha, double beta, double timeLimit, Stopwatch timer)
        {
            Move bestMove;

            if (depth == 0 || state.IsGameOver())
            {
                if (state.Player == GameEngine.PLAYER)
                {
                    bestMove = new PlayerMove(); // dummy action, as there will be no valid move
                    bestMove.Score = AI.Evaluate(state);
                    return new Tuple<Move, Boolean>(bestMove, true);
                }
                else if (state.Player == GameEngine.COMPUTER)
                {
                    bestMove = new ComputerMove(); // dummy action, as there will be no valid move
                    bestMove.Score = AI.Evaluate(state);
                    return new Tuple<Move, Boolean>(bestMove, true);
                }
                else throw new Exception();
            }
            if (state.Player == GameEngine.PLAYER) // AI's turn
            {
                bestMove = new PlayerMove();

                double highestScore = Double.MinValue, currentScore = Double.MinValue;

                List<Move> moves = state.GetMoves();

                foreach (Move move in moves)
                {
                    State resultingState = state.ApplyMove(move);
                    currentScore = IterativeDeepeningAlphaBeta(resultingState, depth - 1, alpha, beta, timeLimit, timer).Item1.Score;

                    if (currentScore > highestScore)
                    {
                        highestScore = currentScore;
                        bestMove = move;
                    }
                    alpha = Math.Max(alpha, highestScore);
                    if (beta <= alpha)
                    { // beta cut-off
                        break;
                    }

                    if (timer.ElapsedMilliseconds > timeLimit)
                    {
                        bestMove.Score = highestScore;
                        return new Tuple<Move, Boolean>(bestMove, false); // recursion not completed, return false
                    }
                }
                bestMove.Score = highestScore;
                return new Tuple<Move, Boolean>(bestMove, true);

            }
            else if (state.Player == GameEngine.COMPUTER) // computer's turn  (the random event node)
            {
                bestMove = new ComputerMove();
                double lowestScore = Double.MaxValue, currentScore = Double.MaxValue;

                List<Move> moves = state.GetMoves();

                foreach (Move move in moves)
                {
                    State resultingState = state.ApplyMove(move);
                    currentScore = IterativeDeepeningAlphaBeta(resultingState, depth - 1, alpha, beta, timeLimit, timer).Item1.Score;

                    if (currentScore < lowestScore)
                    {
                        lowestScore = currentScore;
                        bestMove = move;
                    }
                    beta = Math.Min(beta, lowestScore);
                    if (beta <= alpha)
                        break;

                    if (timer.ElapsedMilliseconds > timeLimit)
                    {
                        bestMove.Score = lowestScore;
                        return new Tuple<Move, Boolean>(bestMove, false); // recursion not completed, return false
                    }
                }
                bestMove.Score = lowestScore;
                return new Tuple<Move, Boolean>(bestMove, true);
            }
            else throw new Exception();
        }
Пример #9
0
 void Update()
 {
     if (state == State.waiting)
     {
         if (start)
         {
             Data.Paused = true;
             Data.PauseEnabled = false;
             state = State.displaying;
             pageChars=pageStrings[currentPage].ToCharArray();
             style.fontSize = (int)(Screen.width * fontsize[currentPage]);
             style2.fontSize = (int)(Screen.width * fontsize[currentPage]);
         }
     }
     else if (state == State.displaying)
     {
         if (CustomInput.PauseFreshPress)
         {
             for (int i = Mathf.RoundToInt(currentLetter); i < pageChars.Length;i++ )
                 currentText.Append(pageChars[i]);
             state = State.paused;
         }
         else
         {
             if (currentLetter == 0)
                 currentText.Append(pageChars[(int)currentLetter]);
             int a = (int)(currentLetter);
             float temp = textSpeed;
             if (CustomInput.AcceptHeld)
                 temp *= 2;
             currentLetter += temp * Time.deltaTime;
             if (a < (int)currentLetter && currentLetter < pageChars.Length)
             {
                 currentText.Append(pageChars[(int)currentLetter]);
                 audio.PlayOneShot(audio.clip);
             }
             if (currentLetter >= pageChars.Length)
             {
                 //currentText = new System.Text.StringBuilder(pageStrings[currentPage]);
                 state = State.paused;
             }
         }
     }
     else
     {
         if (CustomInput.AcceptFreshPress || CustomInput.PauseFreshPress)
         {
             currentLetter = 0;
             currentPage++;
             if (currentPage >= pages.Length)
             {
                 Data.Paused = false;
                 Data.PauseEnabled = true;
                 CustomInput.voidPause();
                 Destroy(this.gameObject);
             }
             else
             {
                 pageChars = pageStrings[currentPage].ToCharArray();
                 currentText = new System.Text.StringBuilder();
                 state = State.displaying;
                 style.fontSize = (int)(Screen.width * fontsize[currentPage]);
                 style2.fontSize = (int)(Screen.width * fontsize[currentPage]);
             }
         }
     }
 }
Пример #10
0
 // adds a child node to the list of children
 // after exploring a move - removes the move from untried
 public Node AddChild(Move move, State state)
 {
     Node child = new Node(move, this, state);
     this.untriedMoves.Remove(move);
     this.children.Add(child);
     return child;
 }
Пример #11
0
        // Applies the move to this state and returns the resulting state
        public State ApplyMove(Move move)
        {
            if (move is PlayerMove)
            {
                int[][] clonedBoard = BoardHelper.CloneBoard(this.board);
                if (((PlayerMove)move).Direction == DIRECTION.LEFT)
                {
                    State state = ApplyLeft(clonedBoard);
                    state.GeneratingMove = move;
                    return state;
                }
                else if (((PlayerMove)move).Direction == DIRECTION.RIGHT)
                {
                    State state = ApplyRight(clonedBoard);
                    state.GeneratingMove = move;
                    return state;
                }

                else if (((PlayerMove)move).Direction == DIRECTION.DOWN)
                {
                    State state = ApplyDown(clonedBoard);
                    state.GeneratingMove = move;
                    return state;
                }
                else if (((PlayerMove)move).Direction == DIRECTION.UP)
                {
                    State state = ApplyUp(clonedBoard);
                    state.GeneratingMove = move;
                    return state;
                }
                else throw new Exception();
            }
            else if (move is ComputerMove)
            {
                State result = new State(BoardHelper.CloneBoard(this.board), points, GameEngine.PLAYER);
                int xPosition = ((ComputerMove)move).Position.Item1;
                int yPosition = ((ComputerMove)move).Position.Item2;
                int tileValue = ((ComputerMove)move).Tile;
                result.Board[xPosition][yPosition] = tileValue;
                result.GeneratingMove = move;
                return result;
            }
            else
            {
                throw new Exception();
            }
        }
Пример #12
0
        private State ApplyUp(int[][] clonedBoard)
        {
            List<Cell> merged = new List<Cell>();

            for (int i = 0; i < clonedBoard.Length; i++)
            {
                for (int j = clonedBoard.Length - 1; j >= 0; j--)
                {
                    if (BoardHelper.CellIsOccupied(clonedBoard, i, j) && j < 3)
                    {
                        int k = j;
                        while (k < 3 && !BoardHelper.CellIsOccupied(clonedBoard, i, k + 1))
                        {
                            int value = clonedBoard[i][k];
                            clonedBoard[i][k] = 0;
                            clonedBoard[i][k + 1] = value;
                            k = k + 1;
                        }
                        if (k < 3 && BoardHelper.CellIsOccupied(clonedBoard, i, k + 1)
                            && !BoardHelper.TileAlreadyMerged(merged, i, k)
                            && !BoardHelper.TileAlreadyMerged(merged, i, k + 1))
                        {
                            // check if we can merge the two tiles
                            if (clonedBoard[i][k] == clonedBoard[i][k + 1])
                            {
                                int value = clonedBoard[i][k + 1] * 2;
                                clonedBoard[i][k + 1] = value;
                                merged.Add(new Cell(i, k + 1));
                                clonedBoard[i][k] = 0;
                                points += value;
                            }
                        }
                    }
                }
            }
            State result = new State(clonedBoard, this.points, GameEngine.COMPUTER);
            return result;
        }
Пример #13
0
        private State ApplyRight(int[][] clonedBoard)
        {
            List<Cell> merged = new List<Cell>();

            for (int j = 0; j < board.Length; j++)
            {
                for (int i = board.Length - 1; i >= 0; i--)
                {
                    if (BoardHelper.CellIsOccupied(clonedBoard, i, j) && i < 3)
                    {
                        int k = i;
                        while (k < 3 && !BoardHelper.CellIsOccupied(clonedBoard, k + 1, j))
                        {
                            int value = clonedBoard[k][j];
                            clonedBoard[k][j] = 0;
                            clonedBoard[k + 1][j] = value;
                            k = k + 1;
                        }
                        if (k < 3 && BoardHelper.CellIsOccupied(clonedBoard, k + 1, j)
                            && !BoardHelper.TileAlreadyMerged(merged, k, j)
                            && !BoardHelper.TileAlreadyMerged(merged, k + 1, j))
                        {
                            // check if we can merge the two tiles
                            if (clonedBoard[k][j] == clonedBoard[k + 1][j])
                            {
                                int value = clonedBoard[k + 1][j] * 2;
                                clonedBoard[k + 1][j] = value;
                                merged.Add(new Cell(k + 1, j));
                                clonedBoard[k][j] = 0;
                                points += value;
                            }
                        }
                    }
                }
            }

            State result = new State(clonedBoard, this.points, GameEngine.COMPUTER);
            return result;
        }
Пример #14
0
 // Checks if two states are equal
 public bool Equals(State s)
 {
     if ((object)s == null)
     {
         return false;
     }
     for (int i = 0; i < GameEngine.ROWS; i++)
     {
         for (int j = 0; j < GameEngine.COLUMNS; j++)
         {
             if (board[i][j] != s.board[i][j])
                 return false;
         }
     }
     return true;
 }
Пример #15
0
        // Recursive part of iterative deepening Expectimax
        private Tuple<Move, Boolean> IterativeDeepeningExpectimax(State state, int depth, double timeLimit, Stopwatch timer)
        {
            Move bestMove;

            if (depth == 0 || state.IsGameOver())
            {
                if (state.Player == GameEngine.PLAYER)
                {
                    bestMove = new PlayerMove(); // dummy action, as there will be no valid move
                    bestMove.Score = AI.Evaluate(state);
                    return new Tuple<Move, Boolean>(bestMove, true);
                }
                else if (state.Player == GameEngine.COMPUTER)
                {
                    bestMove = new ComputerMove(); // dummy action, as there will be no valid move
                    bestMove.Score = AI.Evaluate(state);
                    return new Tuple<Move, Boolean>(bestMove, true);
                }
                else throw new Exception();
            }
            if (state.Player == GameEngine.PLAYER) // AI's turn
            {
                bestMove = new PlayerMove();
                double highestScore = Double.MinValue, currentScore = Double.MinValue;

                List<Move> moves = state.GetMoves();
                foreach (Move move in moves)
                {
                    State resultingState = state.ApplyMove(move);
                    currentScore = IterativeDeepeningExpectimax(resultingState, depth - 1, timeLimit, timer).Item1.Score;

                    if (currentScore > highestScore)
                    {
                        highestScore = currentScore;
                        bestMove = move;
                    }
                    if (timer.ElapsedMilliseconds > timeLimit)
                    {
                        bestMove.Score = highestScore;
                        return new Tuple<Move, Boolean>(bestMove, false); // recursion not completed, return false
                    }
                }
                bestMove.Score = highestScore;
                return new Tuple<Move, Boolean>(bestMove, true);
            }
            else if (state.Player == GameEngine.COMPUTER) // computer's turn  (the random event node)
            {
                bestMove = new ComputerMove();

                // return the weighted average of all the child nodes's scores
                double average = 0;
                List<Cell> availableCells = state.GetAvailableCells();
                List<Move> moves = state.GetAllComputerMoves(availableCells);
                int moveCheckedSoFar = 0;
                foreach (Move move in moves)
                {
                    State resultingState = state.ApplyMove(move);

                    average += StateProbability(((ComputerMove)move).Tile) * IterativeDeepeningExpectimax(resultingState, depth - 1, timeLimit, timer).Item1.Score;
                    moveCheckedSoFar++;
                    if (timer.ElapsedMilliseconds > timeLimit)
                    {
                        bestMove.Score = average / moveCheckedSoFar;
                        return new Tuple<Move, Boolean>(bestMove, false); // recursion not completed, return false
                    }
                }
                bestMove.Score = average / moves.Count;
                return new Tuple<Move, Boolean>(bestMove, true);
            }
            else throw new Exception();
        }