// Check for equality
        public override bool Equals(object other)
        {
            CheckerState otherCheckerState = other as CheckerState;

            if (other == null)
            {
                return(false);
            }

            if (other == this)
            {
                return(true);
            }

            // If there are any points of difference, return false
            for (int rowIndex = 0; rowIndex < board.Length; ++rowIndex)
            {
                for (int colIndex = 0; colIndex < board[rowIndex].Length; ++colIndex)
                {
                    if (board[rowIndex][colIndex] != otherCheckerState.board[rowIndex][colIndex])
                    {
                        return(false);
                    }
                }
            }

            // Otherwise, return true
            return(true);
        }
 // Connect playerOne and playerTwo the the event signals from CheckerState
 public static void WireEvents(CheckerState checkerState, Agent playerOne, Agent playerTwo)
 {
     // Wire up player behaviors to state events
     CheckerState.WhiteWins += playerOne.Victory;
     CheckerState.WhiteWins += playerTwo.Defeat;
     CheckerState.BlackWins += playerOne.Defeat;
     CheckerState.BlackWins += playerTwo.Victory;
 }
        // Copy constructor
        public CheckerState(CheckerState src)
        {
            board = new Piece[8][];

            for (int rowIndex = 0; rowIndex < 8; ++rowIndex)
            {
                // Create a new row
                board[rowIndex] = new Piece[8];

                // Copy src members into new row
                for (int colIndex = 0; colIndex < 8; ++colIndex)
                {
                    board[rowIndex][colIndex] = src.board[rowIndex][colIndex];
                }
            }
        }
        // Modify, clone, and restore the current state to produce a successor state
        private CheckerState makeMove(int rowSrc, int colSrc, int rowDest, int colDest, bool isJump)
        {
            // The piece to move
            Piece curPiece = board[rowSrc][colSrc];
            Piece midPiece = board[(rowSrc + rowDest) / 2][(colSrc + colDest) / 2];

            // Modify
            board[rowSrc][colSrc]   = Piece.Blank;
            board[rowDest][colDest] = curPiece;

            if (isJump)
            {
                board[(rowSrc + rowDest) / 2][(colSrc + colDest) / 2] = Piece.Blank;
            }

            // Convert any men to kings
            if (board[rowDest][colDest] == Piece.White && rowDest == 0)
            {
                board[rowDest][colDest] = Piece.WhiteKing;
            }

            if (board[rowDest][colDest] == Piece.Black && rowDest == 7)
            {
                board[rowDest][colDest] = Piece.BlackKing;
            }

            // Clone
            CheckerState modState = new CheckerState(this);

            // Restore
            if (isJump)
            {
                board[(rowSrc + rowDest) / 2][(colSrc + colDest) / 2] = midPiece;
            }

            board[rowDest][colDest] = Piece.Blank;
            board[rowSrc][colSrc]   = curPiece;

            // Return new state
            return(modState);
        }
        // Recursively generate all states that could result from a jump
        private List <IState> getJumps(int row, int col, Move move, CheckerState state)
        {
            // Ending states
            List <IState> successors = new List <IState>();

            // Current player's piece
            Piece curPiece = board[row][col];
            bool  isKing   = (curPiece == Piece.WhiteKing || curPiece == Piece.BlackKing) ? true : false;

            // Enemy piece types
            Piece otherPiece;
            Piece otherPieceKing;

            // What direction in which to move
            int advance;

            if (move == Move.White)
            {
                otherPiece     = Piece.Black;
                otherPieceKing = Piece.BlackKing;
                advance        = -1;
            }
            else
            {
                otherPiece     = Piece.White;
                otherPieceKing = Piece.WhiteKing;
                advance        = 1;
            }

            // If there is a forward-left enemy piece
            if (inBounds(row + advance, col - 1) &&
                (board[row + advance][col - 1] == otherPiece ||
                 board[row + advance][col - 1] == otherPieceKing))
            {
                // If there is a blank cell to bridge to
                if (inBounds(row + (advance * 2), col - 2) &&
                    (board[row + (advance * 2)][col - 2] == Piece.Blank))
                {
                    // Add any possible successors
                    CheckerState modState = state.makeMove(row, col, row + (advance * 2), col - 2, true);

                    // If current piece becomes a king
                    if (isKing == false &&
                        (modState.board[row + (advance * 2)][col - 2] == Piece.WhiteKing ||
                         modState.board[row + (advance * 2)][col - 2] == Piece.BlackKing))
                    {
                        // End all successive jumps
                        successors.Add(modState);
                    }
                    else
                    {
                        // Otherwise, find all possible branching possibilities
                        successors.AddRange(getJumps(row + (advance * 2), col - 2, move, modState));
                    }
                }
            }

            // If there is a forward-right enemy piece
            if (inBounds(row + advance, col + 1) &&
                (board[row + advance][col + 1] == otherPiece ||
                 board[row + advance][col + 1] == otherPieceKing))
            {
                // If there is a blank cell to bridge to
                if (inBounds(row + (advance * 2), col + 2) &&
                    (board[row + (advance * 2)][col + 2] == Piece.Blank))
                {
                    // Add any possible successors
                    CheckerState modState = state.makeMove(row, col, row + (advance * 2), col + 2, true);

                    // If current piece becomes a king
                    if (isKing == false &&
                        (modState.board[row + (advance * 2)][col + 2] == Piece.WhiteKing ||
                         modState.board[row + (advance * 2)][col + 2] == Piece.BlackKing))
                    {
                        // End all successive jumps
                        successors.Add(modState);
                    }
                    else
                    {
                        // Otherwise, find all possible branching possibilities
                        successors.AddRange(getJumps(row + (advance * 2), col + 2, move, modState));
                    }
                }
            }

            // If the current piece is already a king
            if (isKing)
            {
                // If there is a back-left enemy piece
                if (inBounds(row - advance, col - 1) &&
                    (board[row - advance][col - 1] == otherPiece ||
                     board[row - advance][col - 1] == otherPieceKing))
                {
                    // If there is a blank cell to bridge to
                    if (inBounds(row - (advance * 2), col - 2) &&
                        (board[row - (advance * 2)][col - 2] == Piece.Blank))
                    {
                        // Add any possible successors
                        CheckerState modState = state.makeMove(row, col, row - (advance * 2), col - 2, true);
                        successors.AddRange(getJumps(row - (advance * 2), col - 2, move, modState));
                    }
                }

                // If there is a back-right enemy piece
                if (inBounds(row - advance, col + 1) &&
                    (board[row - advance][col + 1] == otherPiece ||
                     board[row - advance][col + 1] == otherPieceKing))
                {
                    // If there is a blank cell to bridge to
                    if (inBounds(row - (advance * 2), col + 2) &&
                        (board[row - (advance * 2)][col + 2] == Piece.Blank))
                    {
                        // Add any possible successors
                        CheckerState modState = state.makeMove(row, col, row - (advance * 2), col + 2, true);
                        successors.AddRange(getJumps(row - (advance * 2), col + 2, move, modState));
                    }
                }
            }

            // If no further jumps were found, this is a terminal state (base case)
            if (successors.Count == 0)
            {
                successors.Add(state);
            }

            // Return all viable successor jump states found
            return(successors);
        }
        // Train two agents to compete in an adversarial game
        public static void TrainZeroSum(long practiceGames, bool showOutput, Agent agentOne, Agent agentTwo)
        {
            // Report the number of practice games
            if (showOutput)
            {
                Console.WriteLine($"Number of Practice Games: {practiceGames}");
            }

            // Create starting state
            IState       state        = new CheckerState();
            CheckerState checkerState = state as CheckerState;

            // Put agents in training mode
            agentOne.TrainingMode(1.0);
            agentTwo.TrainingMode(1.0);

            // Use repeated wins as a benchmark
            bool enoughTraining = false;
            long games          = 0;
            int  ticks          = 0;
            bool dotPrinted     = false;

            // Reset dotPrinted after it has moved on
            Action <object, EventArgs> resetDotPrinted = (object sender, EventArgs e) => dotPrinted = false;

            CheckerState.WhiteWins += resetDotPrinted;
            CheckerState.BlackWins += resetDotPrinted;

            // Begin training progress bar
            if (showOutput)
            {
                Console.Write("[ ");
            }

            // Watch each agent evolve
            while (!enoughTraining)
            {
                // Train the first agent
                checkerState = agentOne.Act(checkerState.SuccessorsBlack) as CheckerState;
                checkerState.GoalTest();

                games = agentOne.Victories + agentTwo.Victories + agentOne.Draws;

                // Account for every 1% of progress
                if (games % (practiceGames / 100) == 0 && !dotPrinted)
                {
                    // Update competitiveness at quarters
                    if (ticks % 25 == 0)
                    {
                        // Display quarter in progress bar
                        if (showOutput)
                        {
                            Console.Write($"[{ticks}]");
                        }

                        // Make each agent more competitive
                        agentOne.TrainingMode((100.0 - ticks) / 100.0);
                        agentTwo.TrainingMode((100.0 - ticks) / 100.0);
                    }

                    // Update ticks
                    ticks += 1;

                    // Don't include hundredth tick
                    if (ticks < 100 && showOutput)
                    {
                        Console.Write(".");
                    }

                    dotPrinted = true;
                }

                // Check if total number of practices have been met
                if (games >= practiceGames)
                {
                    enoughTraining = true;
                }

                // Train the second agent, assuming they are not sufficiently trained
                if (!enoughTraining)
                {
                    checkerState = agentTwo.Act(checkerState.SuccessorsWhite) as CheckerState;
                    checkerState.GoalTest();

                    games = agentOne.Victories + agentTwo.Victories + agentOne.Draws;

                    // Account for every 1% of progress
                    if (games % (practiceGames / 100) == 0 && !dotPrinted)
                    {
                        // Update competitiveness at quarters
                        if (ticks % 25 == 0)
                        {
                            // Display quarter in progress bar
                            if (showOutput)
                            {
                                Console.Write($"[{ticks}]");
                            }

                            // Make each agent more competitive
                            agentOne.TrainingMode((100.0 - ticks) / 100.0);
                            agentTwo.TrainingMode((100.0 - ticks) / 100.0);
                        }

                        // Update ticks
                        ticks += 1;

                        // Don't include hundredth tick
                        if (ticks < 100 && showOutput)
                        {
                            Console.Write(".");
                        }

                        dotPrinted = true;
                    }

                    // Check if total number of practices have been met
                    if (games >= practiceGames)
                    {
                        enoughTraining = true;
                    }
                }
            }

            // End progress bar
            if (showOutput)
            {
                Console.WriteLine(" ]");
                Console.WriteLine("\n");
            }

            // Put agents in competitive mode
            agentOne.CompeteMode();
            agentTwo.CompeteMode();
        }
Exemple #7
0
        public void Run()
        {
            if (!quiet)
            {
                Console.WriteLine("RL Checkers");
            }

            IState       state        = new CheckerState();
            CheckerState checkerState = state as CheckerState;

            Agent playerOne = new Agent();
            Agent playerTwo = new Agent();

            // Wire player behaviors to state events
            CheckerState.WireEvents(checkerState, playerOne, playerTwo);

            // Train agents
            if (!useInFile)
            {
                CheckerState.TrainZeroSum(training, !quiet, playerOne, playerTwo);
            }

            // Handle Serialization
            if (useInFile)
            {
                Utilities.ReadInFile(inFile, playerOne, playerTwo);
            }
            if (useOutFile)
            {
                Utilities.WriteOutFile(outFile, playerOne);
            }

            // Determine victory, defeat, and cat's game events
            bool playerTwoVictory = false;
            bool playerTwoDefeat  = false;

            Action <object, EventArgs> declareVictory = (object sender, EventArgs e) => playerTwoVictory = true;
            Action <object, EventArgs> declareDefeat  = (object sender, EventArgs e) => playerTwoDefeat = true;

            CheckerState.WhiteWins += declareVictory;
            CheckerState.BlackWins += declareDefeat;

            // Player competes with computer
            while (!playerTwoVictory && !playerTwoDefeat)
            {
                checkerState = playerOne.Act(checkerState.SuccessorsBlack) as CheckerState;
                checkerState.GoalTest();

                if (!playerTwoVictory && !playerTwoDefeat)
                {
                    // Display move options
                    List <IState> options   = checkerState.SuccessorsWhite;
                    int           moveIndex = 0;
                    bool          validMove = false;

                    // Prompt the user
                    while (!validMove)
                    {
                        try
                        {
                            // Receive user input
                            CheckerState.PrintOptions(options);
                            Console.Write("Please select a move: ");
                            moveIndex = Int32.Parse(Console.ReadLine()) - 1;

                            // If move is not possible, throw exception
                            if (moveIndex < 0 || moveIndex >= options.Count)
                            {
                                throw new InvalidMoveException(moveIndex + 1);
                            }

                            // If no error was raised, mark the move as valid
                            validMove = true;
                        }
                        catch (InvalidMoveException e)
                        {
                            Console.Write(e.Message);
                            Console.WriteLine("; please select from the options listed.");
                        }
                    }

                    // Assign the state
                    checkerState = options[moveIndex] as CheckerState;
                    checkerState.GoalTest();
                }

                // Select outcome
                if (playerTwoVictory)
                {
                    Console.WriteLine("Human player wins!");
                }

                if (playerTwoDefeat)
                {
                    Console.WriteLine("Computer wins!");
                }
            }
        }