Ejemplo n.º 1
0
    private MoveMgr.Move BestMove(int playerNum, List <MoveMgr.Move> availableMoves)
    {
        Grid2D grid = _Board.CloneGrid();

        MoveMgr.Move bestMove = null;

        bestMove = GetWinMove(availableMoves, grid);
        if (null != bestMove)
        {
            return(bestMove);
        }

        bestMove = GetBlockOpponentWinMove(availableMoves, GetOpponents(playerNum), grid);
        if (null != bestMove)
        {
            return(bestMove);
        }

        bestMove = Get3CornerMove(availableMoves, grid);
        if (null != bestMove)
        {
            return(bestMove);
        }

        return(RandomMove(playerNum, availableMoves));
    }
Ejemplo n.º 2
0
    /// <summary>
    /// Checks for diagonal win Falling from left to right.
    /// </summary>
    /// <param name="move">Last move made</param>
    /// <returns>true if move wins</returns>
    /// <remarks>Works with any size grid</remarks>
    public bool HasDiagWinFromCell_Fall(MoveMgr.Move move)
    {
        int counter = 1;

        // Up
        for (int r = move.Row - 1, c = move.Col - 1; r >= 0 && c >= 0; r--, c--)
        {
            if (move.PlayerNum != _grid[r, c])
            {
                break;
            }
            ++counter;
        }
        // Down
        for (int r = move.Row + 1, c = move.Col + 1; r < NumRows && c < NumCols; r++, c++)
        {
            if (move.PlayerNum != _grid[r, c])
            {
                break;
            }
            ++counter;
        }

        return(counter >= NumConnectionsToWin);
    }
Ejemplo n.º 3
0
    private MoveMgr.Move BestMove_MCTS(int playerNum, List <MoveMgr.Move> availableMoves)
    {
        Grid2D grid = _Board.CloneGrid();

        MoveMgr.Move bestMove = _MCTS.GetMove(grid, playerNum);
        return(bestMove);
    }
Ejemplo n.º 4
0
    private float GetProbability(int playerNum, MoveMgr.Move move, List <MoveMgr.Move> availableMoves, Grid2D grid)
    {
        // TODO design a "Grid2D" class that allows board to be manipulated without side effects (notifing others and yada)
        if (grid.DoesMoveWin(move))
        {
            return(Win);
        }
        if (availableMoves.Count == 0)
        {
            return(Draw);
        }

        // Apply move to local grid copy.  New grid class should have grid.Add(move) and grid.Remove(move)
        grid.AddMove(move);

        // TODO: apply opponent moves from available moves
        // Recurse GetProbabablity() for each opponent move

        float probablity = 0f;

        for (int i = 0; i < availableMoves.Count - 1; i++)
        {
            List <MoveMgr.Move> nextMoves = availableMoves.GetRange(0, availableMoves.Count);
            nextMoves.Remove(move);
            // probablity += GetOpponetProbability(opponentNum, availableMoves[i], nextMoves, grid);
        }
        return(probablity / (float)(availableMoves.Count - 1));
    }
Ejemplo n.º 5
0
        public Node AddChild(MoveMgr.Move move, IGameState state)
        {
            Node n = new Node(state, move, this);

            _untriedMoves.Remove(move);
            _children.Add(n);
            return(n);
        }
Ejemplo n.º 6
0
    /// <summary>
    /// Undo a move and rollback state
    /// </summary>
    /// <param name="move">Only check row,col and sets to empty</param>
    public void ClearCell(MoveMgr.Move move)
    {
        _grid[move.Row, move.Col] = 0;

        --NumMovesMade;
        GameState      = PlayState.InProgress;
        LastPlayerTurn = LastPlayerTurn <= 1 ? NumPlayers : LastPlayerTurn - 1;
    }
Ejemplo n.º 7
0
 /// <summary>
 /// Check if move is in a corner.
 /// </summary>
 /// <param name="move">A move to check</param>
 /// <returns>true if move is in any corner</returns>
 public bool IsCorner(MoveMgr.Move move)
 {
     return
         ((move.Row == 0 && move.Col == 0) ||
          (move.Row == 0 && move.Col == (NumCols - 1)) ||
          (move.Row == (NumRows - 1) && move.Col == 0) ||
          (move.Row == (NumRows - 1) && move.Col == (NumCols - 1)));
 }
Ejemplo n.º 8
0
    public float GetResult(MoveMgr.Move move, int playerNumPerspective)
    {
        Debug.Assert(IsGameOver);

        if (IsDraw)
        {
            return(0.75f);
        }
        return(WinningPlayer == playerNumPerspective ? 1.0f : 0.0f);
    }
Ejemplo n.º 9
0
    /// <summary>
    /// Try to eliminate BestMoves from availableMoves.  If only good moves exist, return the lesser good.
    /// </summary>
    /// <param name="playerNum"></param>
    /// <param name="availableMoves"></param>
    /// <returns></returns>
    private MoveMgr.Move WorstMove(int playerNum, List <MoveMgr.Move> availableMoves)
    {
        Grid2D grid = _Board.CloneGrid();

        MoveMgr.Move bestMove = null;

        // Skip all winning moves
        do
        {
            if (availableMoves.Count == 1)
            {
                return(availableMoves[0]);
            }
            else if (null != bestMove)
            {
                availableMoves.Remove(bestMove);
                bestMove.Dispose();
            }
            bestMove = GetWinMove(availableMoves, grid);
        }while (null != bestMove);

        // Skip all blocking moves
        List <int> opponents = GetOpponents(playerNum);

        do
        {
            if (availableMoves.Count == 1)
            {
                return(availableMoves[0]);
            }
            else if (null != bestMove)
            {
                availableMoves.Remove(bestMove);
                bestMove.Dispose();
            }
            bestMove = GetBlockOpponentWinMove(availableMoves, opponents, grid);
        }while (null != bestMove);

        // Skip all 3 corner
        do
        {
            if (availableMoves.Count == 1)
            {
                return(availableMoves[0]);
            }
            else if (null != bestMove)
            {
                availableMoves.Remove(bestMove);
                bestMove.Dispose();
            }
            bestMove = Get3CornerMove(availableMoves, grid);
        }while (null != bestMove);

        return(RandomMove(playerNum, availableMoves));
    }
Ejemplo n.º 10
0
    /// <summary>
    /// Records a move if valid.
    /// </summary>
    /// <param name="move">1-NumPlayers</param>
    /// <remarks>Use CanMove to determine if MakeMove will record the move</remarks>
    public override void MakeMove(MoveMgr.Move move)
    {
        if (!CanMove(move))
        {
            Debug.LogWarning("Invalid move, not recording move.");
            return;
        }
        _LiveGrid.AddMove(move);

        base.MakeMove(move);
    }
Ejemplo n.º 11
0
    private void ExecuteLastAIMove()
    {
        if (null == NextAIMove)
        {
            return;
        }

        if (Time.fixedTime > (AiStartMoveTime + AiTimeDelay))
        {
            MoveMgr.Move tmpMove = NextAIMove;
            NextAIMove = null;
            _BoardState.MakeMove(tmpMove);
        }
    }
Ejemplo n.º 12
0
 /// <summary>
 /// Checks if move is valid in current state.
 /// </summary>
 /// <param name="playerNum">1-NumPlayers</param>
 /// <param name="row">0-(NumRows-1)</param>
 /// <param name="col">0-(NumCols-1)</param>
 /// <returns>true if move is valid</returns>
 public bool CanMove(MoveMgr.Move move)
 {
     if (PlayerTurn != move.PlayerNum)
     {
         Debug.LogError("player: " + move.PlayerNum + " tried to move but it's #" + PlayerTurn + "'s turn.");
         return(false);
     }
     if (!IsCellOpen(move.Row, move.Col))
     {
         Debug.LogError("Tried to play in an occupied cell (" + move.Row + "," + move.Col + ").");
         return(false);
     }
     return(true);
 }
Ejemplo n.º 13
0
 private void AIMoved(MoveMgr.Move move, List <MoveMgr.Move> availableMoves, AIMoveCallback callback)
 {
     try
     {
         Debug.Assert(null != move);
         availableMoves.Remove(move);
         callback(move);
     }
     finally
     {
         // Release to pool
         availableMoves.ForEach(delegate(MoveMgr.Move m)
         {
             m.Dispose();
         });
     }
 }
Ejemplo n.º 14
0
 /// <summary>
 /// Check if opponent about to win, and block their win
 /// </summary>
 /// <param name="availableMoves">List of available moves</param>
 /// <param name="opponentNums">List of oppenent numbers</param>
 /// <param name="grid">The grid to test against</param>
 /// <returns>A blocking move or null if no wins possible in next play</returns>
 private MoveMgr.Move GetBlockOpponentWinMove(List <MoveMgr.Move> availableMoves, List <int> opponentNums, Grid2D grid)
 {
     foreach (int oppNum in opponentNums)
     {
         foreach (MoveMgr.Move move in availableMoves)
         {
             using (MoveMgr.Move oppMove = MoveMgr.Move.Fetch(oppNum, move))
             {
                 if (grid.DoesMoveWin(oppMove))
                 {
                     return(move);
                 }
             }
         }
     }
     return(null);
 }
Ejemplo n.º 15
0
    public IEnumerator GetMove(int playerNum, GameState.PlayerType playerType, AIMoveCallback callback)
    {
        MoveMgr.Move        bestMove       = null;
        List <MoveMgr.Move> availableMoves = GetPlayableCells(playerNum);

        yield return(null);

        switch (playerType)
        {
        case GameState.PlayerType.AI_Easy:
        {
            bestMove = WorstMove(playerNum, availableMoves);
            break;
        }

        case GameState.PlayerType.AI_Medium:
        {
            bestMove = RandomMove(playerNum, availableMoves);
            break;
        }

        case GameState.PlayerType.AI_Hard:
        {
            bestMove = BestMove(playerNum, availableMoves);
            break;
        }

        case GameState.PlayerType.AI_Hard_MCTS:
        {
            bestMove = BestMove_MCTS(playerNum, availableMoves);
            break;
        }

        default:
        {
            Debug.LogAssertion("Unhandled AI type: " + playerType.ToString());
            break;
        }
        }

        AIMoved(bestMove, availableMoves, callback);
    }
Ejemplo n.º 16
0
        float _probablity; // TODO: This number highly coupled to a given player.  Consider storing as an array for each player.

        public Node(IGameState state, MoveMgr.Move move, Node parent)
        {
            ++NodeCount;
            Parent    = parent;
            Move      = move;
            PlayerNum = move.PlayerNum;
            if (state.IsDraw)
            {
                Winner = DRAW;
            }
            else
            {
                Winner = state.WinningPlayer;  // will return 0 if no current winner.
            }
            _untriedMoves = state.GetAvailableMoves(state.PlayerTurn);
            _children     = new List <Node>(_untriedMoves.Count);

            _visits     = 0;
            _probablity = 0f;
        }
Ejemplo n.º 17
0
    public bool DoesMoveWin(MoveMgr.Move move)
    {
        bool wasTmpValuePushed = false;

        if (IsCellOpen(move))
        {
            AddMove(move);
            wasTmpValuePushed = true;
        }

        bool isWin =
            HasHorzWin(move) ||
            HasVertWin(move) ||
            HasDiagWinFromCell(move);

        if (wasTmpValuePushed)
        {
            ClearCell(move);
        }
        return(isWin);
    }
Ejemplo n.º 18
0
    /// <summary>
    /// Callback from mouse click on a cell in the Tic-Tac-Toe Grid2D
    /// </summary>
    /// <param name="rowcol">Comma delimited string with row,col of cell clicked</param>
    public void OnClickSpot(string rowcol)
    {
        if (!IsHumanTurn)
        {
            Debug.LogWarning("Not a Human's turn");
            return;
        }

        if (null != _Hint)
        {
            BoardView.ClearHint(_Hint.Row, _Hint.Col);
            _Hint = null;
        }

        // Parse row,col clicked
        string[] rc  = rowcol.Split(',');
        int      row = System.Convert.ToInt32(rc[0]);
        int      col = System.Convert.ToInt32(rc[1]);

        _BoardState.MakeMove(MoveMgr.Move.Fetch(_BoardState.PlayerTurn, row, col));
    }
Ejemplo n.º 19
0
    void IPlayerMoveObserver.Update(MoveMgr.Move move)
    {
        // Visual Update
        BoardView.SetCell(move.Row, move.Col, GetPlayerSymbol(move.PlayerNum));

        // Check for win
        if (_BoardState.IsWinningMove(move))
        {
            GameOver(move.PlayerNum);
            return;
        }

        // Check for Draw
        if (_BoardState.IsBoardFull)
        {
            GameOver(0);
            return;
        }

        UpdatePlayerTurn();
    }
Ejemplo n.º 20
0
    public bool HasVertWin(MoveMgr.Move move)
    {
        int maxConnected = 0;
        int counter      = 0;

        for (int row = 0; row < NumRows; row++)
        {
            if (move.PlayerNum == _grid[row, move.Col])
            {
                ++counter;
                if (counter > maxConnected)
                {
                    maxConnected = counter;
                }
            }
            else
            {
                counter = 0;
            }
        }
        return(maxConnected >= NumConnectionsToWin);
    }
Ejemplo n.º 21
0
    public bool HasHorzWin(MoveMgr.Move move)
    {
        int maxConnected = 0;
        int counter      = 0;

        for (int col = 0; col < NumCols; col++)
        {
            if (move.PlayerNum == _grid[move.Row, col])
            {
                ++counter;
                if (counter > maxConnected)
                {
                    maxConnected = counter;
                }
            }
            else
            {
                counter = 0;
            }
        }
        return(maxConnected >= NumConnectionsToWin);
    }
Ejemplo n.º 22
0
    /// <summary>
    /// Only available on Human turn in a Human vs AI game.
    /// Removes last A.I. Move, then last Human move.
    /// </summary>
    public void UndoMove()
    {
        if (!IsHumanVsAIGame())
        {
            Debug.LogError("Only Human vs AI games allow undo.");
            return;
        }
        if (!IsHumanTurn)
        {
            Debug.LogError("Can only undo on a Human turn.");
            return;
        }

        for (int p = 0; p < NumPlayers; p++)
        {
            MoveMgr.Move move = _BoardState.Undo();
            if (null == move)
            {
                return;
            }
            BoardView.SetCell(move.Row, move.Col, string.Empty);
        }
    }
Ejemplo n.º 23
0
    public void AddMove(MoveMgr.Move move)
    {
        if (move.PlayerNum == 0)
        {
            ClearCell(move);
            return;
        }

        _grid[move.Row, move.Col] = move.PlayerNum;
        LastPlayerTurn            = move.PlayerNum;
        ++NumMovesMade;

        if (DoesMoveWin(move))
        {
            GameState = (PlayState)move.PlayerNum;
        }
        else if (!HasMoves)
        {
            GameState = PlayState.Draw;
        }

        return;
    }
Ejemplo n.º 24
0
 public bool HasDiagWinFromCell(MoveMgr.Move move)
 {
     return(HasDiagWinFromCell_Rise(move) || HasDiagWinFromCell_Fall(move));
 }
Ejemplo n.º 25
0
 public bool IsCellOpen(MoveMgr.Move move)
 {
     return(_grid[move.Row, move.Col] == 0);
 }
Ejemplo n.º 26
0
    public MoveMgr.Move GetMove(IGameState rootState, int playerNum, int maxIterations = 5000)
    {
        // Tree reuse

        /*
         * if (null == _root)
         * {
         *  _root = new Node(rootState, MoveMgr.Move.Fetch(playerNum, 0, 0), null);
         * } else
         * {
         *  _root = _root.AdvanceToCurrentState(rootState);
         * }
         */

        // Fresh tree each time (clears visits and probablities)
        ClearState();
        _root = new Node(rootState, MoveMgr.Move.Fetch(playerNum, 0, 0), null);



        for (int i = 0; i < maxIterations; ++i)
        {
            Node       node  = _root; //TODO: may need to recreate tree each time until probablites array and visits array implemented.  Need to save state for each player.
            IGameState state = rootState.CloneState();

            // Select
            while (node.HasUntriedMoves == false && node.HasChildren) // Fully expanded and non-terminal
            {
                node = node.UCTSelectChild();
                state.AddMove(node.Move);
            }

            // Expand
            if (node.HasUntriedMoves)
            {
                MoveMgr.Move move = node.GetRandomUntriedMove();
                state.AddMove(move);
                node = node.AddChild(move, state);  // Add child and descend
            }

            // Simulate (Rollout - not adding nodes to Terminal state )
            while (!state.IsGameOver)
            {
                MoveMgr.Move move = state.GetRandomMove(state.PlayerTurn);
                state.AddMove(move);
            }

            // Prioritize if a win or lose, blacklist/whitelist
            if (node.IsTerminal && !node.IsDraw)
            {
                if (node.Winner != playerNum)
                {
                    // must blacklist parent which is 'this' players move.
                    //This player cannot lose on their turn, but can play a move that will make them lose on opponents turn.
                    if (null != node.Parent)
                    {
                        node.BlackListParent();
                    }
                }
                else
                {
                    node.WhiteList();
                }
            }

            // Backpropagate
            while (null != node)
            {
                node.Update(state.GetResult(node.Move, playerNum));
                node = node.Parent;
            }
        }
        _root = _root.GetMostVisitedChild();

        _root.TrimTreeTop();

        return(_root.Move);
    }
Ejemplo n.º 27
0
 private void HintCallback(MoveMgr.Move move)
 {
     _Hint = move;
     BoardView.SetHint(_Hint.Row, _Hint.Col);
 }
Ejemplo n.º 28
0
 private void AIMovedCallback(MoveMgr.Move move)
 {
     NextAIMove = move;
 }
Ejemplo n.º 29
0
    /// <summary>
    /// Searches for 3 corner win move.  If that doesn't exist, random any corner.
    /// </summary>
    /// <param name="availableMoves">Available moves for the current player</param>
    /// <param name="grid">The grid to test against</param>
    /// <returns>A valid corner move or null if no corner available</returns>
    private MoveMgr.Move Get3CornerMove(List <MoveMgr.Move> availableMoves, Grid2D grid)
    {
        List <MoveMgr.Move> cornerMoves = new List <MoveMgr.Move>(4);

        foreach (MoveMgr.Move m in availableMoves)
        {
            if (grid.IsCorner(m))
            {
                cornerMoves.Add(m);
            }
        }
        if (cornerMoves.Count == 0)
        {
            return(null);
        }
        else if (cornerMoves.Count == 1)
        {
            return(cornerMoves[0]);
        }
        else if (cornerMoves.Count < 4)
        {
            // 2nd move, counter via center move
            if (availableMoves.Count == (grid.NumCells - 1))
            {
                MoveMgr.Move bestMove = GetCenterMove(availableMoves);
                if (null != bestMove)
                {
                    return(bestMove);
                }
            }
            // 4th move, counter
            if (availableMoves.Count == (grid.NumCells - 1))
            {
                List <int> opponents = GetOpponents(availableMoves[1].PlayerNum);
                if (opponents.Count == 1)
                {
                    if (DoesPlayerHaveOpposingCorners(opponents[0], grid))
                    {
                        MoveMgr.Move bestMove = GetAnySideCenterMove(availableMoves);
                        if (null != bestMove)
                        {
                            return(bestMove);
                        }
                    }
                }
            }

            // Find opposing corner move which:
            // 1. Prevents 3 corner win by opponent.
            // 2. Setup for 3 corner win.
            foreach (MoveMgr.Move m in cornerMoves)
            {
                int row = m.Row == 0 ? grid.MaxRowIndex : 0;
                int col = m.Col == 0 ? grid.MaxColIndex : 0;
                if (grid[row, col] != 0)
                {
                    return(m);
                }
            }
        }

        // Any random corner.  Random to prevent same play each game
        int index = UnityEngine.Random.Range(0, cornerMoves.Count);

        return(cornerMoves[index]);
    }
Ejemplo n.º 30
0
 public bool WasMoveMade(MoveMgr.Move move)
 {
     return(_grid[move.Row, move.Col] == move.PlayerNum);
 }