示例#1
0
    /**
     * determines if the given GridCell is a valid move for the given player
     */
    private bool isValidMove(ReversiGraph graph, ReversiGraph.GridCell given, Player player)
    {
        if (given.State != ReversiGraph.CellState.EMPTY)
        {
            return(false);
        }

        ReversiGraph.CellState seeking = GetSeekingState(player);

        //loop through each possible direction to determine if a move is possible
        foreach (Direction direction in (Direction[])Enum.GetValues(typeof(Direction)))
        {
            if (given.Edges.ContainsKey(direction))
            {
                //traverse the current direction, a move is valid only if we find another same state cell AT LEAST two cells away.
                int depth = graph.Traverse(given, seeking, direction, null, 0);
                if (depth > 1)
                {
                    return(true);
                }
            }
        }

        return(false);
    }
示例#2
0
    /**
     * uses the AI to decide a move for the given player
     */
    private void AsyncronousAIMove(ReversiGraph graph, Player player)
    {
        //verify it is this player's turn
        if (currentTurn != player || IsGameFinished())
        {
            return;
        }

        //evaluate the moves and determine the best one
        //Func<ReversiGraph, Player, int, int, int, Move> searcher = ( ReversiGraph g, Player p, int a, int b, int diff ) => negamax( graph, player, Int32.MinValue, Int32.MaxValue, aiDifficulty )

        //make sure to toggle the states properly so that we don't accidentally start another async task
        aiMoveReady = false;
        aiThinking  = true;

        //set up our delegate instance
        currentSearchingTask = ((ReversiGraph g, Player p, int a, int b, int ai) => negamax(g, p, a, b, ai));

        //set our AI start time
        aiStartTime = Time.time;

        //invoke the delegate asyncronously and store the reference to the AsyncResult used to retrieve the result
        bestMove = currentSearchingTask.BeginInvoke(graph, player, Int32.MinValue, Int32.MaxValue, aiDifficulty, null, null);


        //Move bestMove = negamax( graph, player, Int32.MinValue, Int32.MaxValue, aiDifficulty );
        //performMove( graph, graph.Cells[bestMove.Cell], player );
    }
示例#3
0
 /**
  * initialize the graph
  */
 public void Start()
 {
     gameGraph    = new ReversiGraph();
     gamePiece    = (GameObject)Resources.Load("piece_model");
     currentTurn  = Player.WHITE;
     aiDifficulty = 3;
     aiMoveReady  = false;
     aiVSai       = false;
     gameover     = false;
     aiThinking   = false;
 }
示例#4
0
        private ReversiGraph(ReversiGraph other)
        {
            //for a copy constructor, we create a fresh instance of the Cells map
            this.Cells = new Dictionary <string, GridCell>();
            this.dirty = true;

            //add a clone of each cell to our map
            foreach (GridCell cell in other.Cells.Values)
            {
                Cells.Add(cell.Name, cell.Clone());
            }
        }
示例#5
0
    /**
     * retrieves a set of all possible moves for the given player
     */
    private HashSet <ReversiGraph.GridCell> GetAllPossibleMoves(ReversiGraph graph, Player player)
    {
        HashSet <ReversiGraph.GridCell> allMoves = new HashSet <ReversiGraph.GridCell>();

        foreach (ReversiGraph.GridCell cell in graph.Cells.Values)
        {
            if (isValidMove(graph, cell, player))
            {
                allMoves.Add(cell);
            }
        }
        return(allMoves);
    }
示例#6
0
    /**
     * performs a move for the given player
     */
    private void performMove(ReversiGraph graph, ReversiGraph.GridCell given, Player player)
    {
        if (given.State != ReversiGraph.CellState.EMPTY)
        {
            return;
        }
        //if ( currentTurn != player ) return;

        //instantiate a new hashset which will be used to keep track of any grid cells which need to be flipped as a result of this move
        HashSet <ReversiGraph.GridCell> flipsNeeded = new HashSet <ReversiGraph.GridCell>();

        //determine which color we are seeking
        ReversiGraph.CellState seeking = GetSeekingState(player);

        //loop through each possible direction and flip any necessary pieces
        foreach (Direction direction in (Direction[])Enum.GetValues(typeof(Direction)))
        {
            //traverse the given direction if needed
            if (given.Edges.ContainsKey(direction))
            {
                //traverse the direction
                graph.Traverse(given, seeking, direction, flipsNeeded, 0);
            }
        }

        //we don't need to flip the current cell (although the code is structured such that this would not cause an issue)
        flipsNeeded.Remove(given);

        //go through and flip each of the cells which need to be flipped
        foreach (ReversiGraph.GridCell cell in flipsNeeded)
        {
            cell.Flip(graph == gameGraph);
        }

        //place a piece at the current position, only if we are working with the actual game board
        if (gameGraph == graph)
        {
            Quaternion rotation = (player == Player.WHITE ? Quaternion.identity : new Quaternion(0f, 0f, 3.1415f, 0f));
            GameObject newPiece = (GameObject)Instantiate(gamePiece, given.SpawnPoint, rotation);
            given.State = seeking;
            given.Model = newPiece;
        }
    }
示例#7
0
    /**
     * performs the negamax algorithm using alpha/beta pruning to determine the best possible move
     */
    private Move negamax(ReversiGraph graph, Player player, int alpha, int beta, int depth)
    {
        if (graph == null)
        {
            return(null);
        }

        if (depth <= 0 || graph.IsFinished())
        {
            return(new Move(null, graph.DetermineAdvantageFor(player)));
        }

        //if we haven't reached the bottom of the traversal tree and the graph has possible moves, we need to check them further.
        HashSet <ReversiGraph.GridCell> moves = GetAllPossibleMoves(graph, player);
        Move bestMove = new Move(null, alpha);

        //we have moves available! that other guy is going down
        if (moves.Count > 0)
        {
            //to add an element of randomness, I'm going to alter this algorithm to give the AI a way to randomly choose between equal moves
            ArrayList bestMoves = new ArrayList();

            foreach (ReversiGraph.GridCell cell in moves)
            {
                //we need to duplicate our graph for the recursive call, so that our current board isn't maniplulated. luckily this is efficient
                //because we don't copy all the edges in every graph clone, we use a lookup table for that.
                ReversiGraph cloned = graph.Clone();

                //perform the move on the cloned graph using the current player
                performMove(cloned, cloned.Cells[cell.Name], player);

                //use recursion to determine the opponents next best move
                Player oppPlayer = GetOppositePlayer(player);

                //extra comment for debugging issue (monodevelop sucks)
                Move opponentsBestMove = negamax(cloned, oppPlayer, -beta, -alpha, depth - 1);

                //we flip our opponents score, to determine how much we like it. if the move is better for us, we save it.
                int score = -opponentsBestMove.Score;
                if (alpha < score)
                {
                    alpha    = score;
                    bestMove = new Move(cell.Name, score);

                    //storing a list of equal moves will give us an element of randomness. we still store bestMove though in the event of pruning
                    bestMoves.Clear();
                    bestMoves.Add(bestMove);
                }
                else if (alpha == score)
                {
                    bestMoves.Add(new Move(cell.Name, score));
                }

                //this is the alpha beta pruning. If this statement evaluates to true, it means we cannot possibly find a more optimum move down the recursive tree in this direction
                if (alpha >= beta)
                {
                    return(bestMove);
                }
            }

            if (bestMoves.Count > 1)
            {
                //bestMove = (Move) bestMoves[UnityEngine.Random.Range( 0, bestMoves.Count - 1 )];
                bestMove = (Move)bestMoves[new System.Random().Next(bestMoves.Count)];
            }
        }

        //if we have no moves available and given that the depth is above 0, it is the other player's turn
        else
        {
            //check for the opposite player
            moves = GetAllPossibleMoves(graph, GetOppositePlayer(player));

            //our opponent is able to move
            if (moves.Count > 0)
            {
                //we don't need to clone the graph, since we made no changes to it, so recursively call negamax as the opposite player
                Move opponentsBestMove = negamax(graph, GetOppositePlayer(player), -beta, -alpha, depth - 1);

                //we need to invert the returned move, since we want the opposite outcome as our opponent
                bestMove = new Move(opponentsBestMove.Cell, -opponentsBestMove.Score);
            }
            //if no moves are available for him either, the game is over and we determine the winner
            else
            {
                int difference = graph.DetermineAdvantageFor(player);

                //if the advantage is positive, we win!
                if (difference > 0)
                {
                    bestMove = new Move(null, Int32.MaxValue);
                }
                //tie game?
                else if (difference == 0)
                {
                    bestMove = new Move(null, 0);
                }
                //if we get a negative number, we will lose this game.
                else
                {
                    bestMove = new Move(null, Int32.MinValue);
                }
            }
        }

        return(bestMove);
    }