Example #1
0
        /// <summary>
        /// Game theory algorithm to maximize the possible return value.
        /// If the maximum recursion depth is reached or the current player's pits are all empty, the difference of the
        /// Kalahs is regarded as the gains of the respective recursion path.
        /// Otherwise, in a loop each non-empty pit is moved, and the minimizer for this move is called.
        /// </summary>
        /// <param name="gameBoard">The game board that is currently valid in the recursion path</param>
        /// <param name="currentPlayer">The current player</param>
        /// <param name="currentRecursionDepth">The current recursion depth we are already in</param>
        /// <param name="maxRecursionDepth">The maximum recursion depth (a constant)</param>
        /// <returns></returns>
        private int Maximizer(Kalaha.Model.GameBoard gameBoard, Player currentPlayer,
                              int currentRecursionDepth, int maxRecursionDepth)
        {
            //            Logging.I.LogMessage("Maximizer: entering depth " + currentRecursionDepth + Environment.NewLine,
            //                                    Logging.LogLevel.DeepDebug);
            gameBoard.LogBoard(currentPlayer, Opponent(currentPlayer), Logging.LogLevel.DeepDebug);

            if (gameBoard.PlayerOnlyHasEmptyHouses(currentPlayer) ||
                gameBoard.PlayerOnlyHasEmptyHouses(Opponent(currentPlayer)))
            {
                // One player's pits are all empty.
                // --> Calculate the difference between the two Kalahs:
                int retValue = gameBoard.GetKalahOfPlayer(currentPlayer).GetNumberofSeeds() -
                               gameBoard.GetKalahOfPlayer(Opponent(currentPlayer)).GetNumberofSeeds();

                if (Rules.I.CaptureSeedsAtEndOfGame())
                {
                    // The rule to capture all seeds in the houses at the end is enabled
                    // --> Take these seeds into account, too:
                    retValue = retValue + gameBoard.SumOfAllSeedsOfPlayer(currentPlayer) -
                                          gameBoard.SumOfAllSeedsOfPlayer(Opponent(currentPlayer));
                }

                //                Logging.I.LogMessage("Maximizer on depth " + currentRecursionDepth + ": Pits empty. Returning " +
                //                                        retValue + "." + Environment.NewLine,
                //                                        Logging.LogLevel.DeepDebug);
                return retValue;
            }

            if (currentRecursionDepth == maxRecursionDepth)
            {
                // We have reached the "bottom" of the recursion.
                // --> Return the difference of the two Kalahs the maximum value:
                int retValue = gameBoard.GetKalahOfPlayer(currentPlayer).GetNumberofSeeds() -
                               gameBoard.GetKalahOfPlayer(Opponent(currentPlayer)).GetNumberofSeeds();
                //                Logging.I.LogMessage("Maximizer on depth " + currentRecursionDepth + ": maxRecursionDepth " + maxRecursionDepth +
                //                                        " reached. Returning " + retValue + "." + Environment.NewLine,
                //                                        Logging.LogLevel.DeepDebug);
                return retValue;
            }

            // We are still in the recursion path and drill deeper down.
            int max = -Basics.Infinity();

            for (int pitIndex = 0; pitIndex < gameBoard.GetNumOfHousesPerPlayer(); ++pitIndex)
            {
                // Create a copy of the current gameboard and take the copy to perform the move of one pit:
                Kalaha.Model.GameBoard localGameBoard = gameBoard.GetACopy();

                if (localGameBoard.GetPitOfPlayer(currentPlayer, pitIndex).ContainsASeed())
                {
                    // The pit of the current pitIndex has a seed in it, so perform the move now:
                    bool lastSeedFellIntoOwnKalah = false;
                    bool lastSeedFellIntoEmptyOwnPit = false;
                    int lastSeedsPit = -1;

                    //                    Logging.I.LogMessage("Maximizer on depth " + currentRecursionDepth + ": Moving pit " +
                    //                                            pitIndex + "." + Environment.NewLine,
                    //                                            Logging.LogLevel.DeepDebug);
                    localGameBoard.MoveHouse(currentPlayer, pitIndex,
                                             ref lastSeedFellIntoOwnKalah,
                                             ref lastSeedFellIntoEmptyOwnPit,
                                             ref lastSeedsPit,
                                             false);  // Do not generate the move's intermediate steps on a seed level

                    int opt = -Basics.Infinity();
                    if (Rules.I.PlayAgainWhenLastSeedInOwnKalah() && lastSeedFellIntoOwnKalah)
                    {
                        // It is the same player's turn again.
                        // --> Continue with the maximizer recursion:
                        opt = Maximizer(localGameBoard, currentPlayer,
                                        currentRecursionDepth + 1, maxRecursionDepth);
                        //                        Logging.I.LogMessage("Maximizer on depth " + currentRecursionDepth + ": Got back opt " +
                        //                                                opt + " from Maximizer one level deeper." + Environment.NewLine,
                        //                                                Logging.LogLevel.DeepDebug);
                    }
                    else
                    {
                        // Check if the "winning opposite pit" rule applies:
                        if (lastSeedFellIntoEmptyOwnPit)
                        {
                            // The last seed fell into an own empty house. This may mean that we can execute the capture move.
                            int numOfSeedsOfOpponentsHouse = gameBoard.GetOppositeHouse(currentPlayer, lastSeedsPit).GetNumberofSeeds();
                            bool captureMoveMayBeExecuted = false;
                            bool captureOpponentsHouse = false;

                            // Find out if under the current rule setting a capture move may be executed and whether the opponent's house may be captured:
                            Rules.I.GetCapturePermissions(numOfSeedsOfOpponentsHouse, ref captureMoveMayBeExecuted, ref captureOpponentsHouse);

                            if (captureMoveMayBeExecuted)
                            {
                                //                                Logging.I.LogMessage("Maximizer on depth " + currentRecursionDepth +
                                //                                                        ": The last seed fell into the empty house #" + (lastSeedsPit + 1) + ".\n",
                                //                                                        Logging.LogLevel.DeepDebug);
                                localGameBoard.PlayerWinsHouses(currentPlayer, lastSeedsPit, captureOpponentsHouse, false);
                            }
                            else
                            {
                                //                                Logging.I.LogMessage("Maximizer on depth " + currentRecursionDepth +
                                //                                                        ": The last seed fell into the empty house #" + (lastSeedsPit + 1) +
                                //                                                        " but rules prohibit the capture.\n",
                                //                                                        Logging.LogLevel.DeepDebug);
                            }
                        }
                        // It is the opponent's turn.
                        // --> Continue with the minimizer recursion:
                        opt = Minimizer(localGameBoard, Opponent(currentPlayer),
                                        currentRecursionDepth + 1, maxRecursionDepth);
                        //                        Logging.I.LogMessage("Maximizer on depth " + currentRecursionDepth + ": Got back opt " +
                        //                                                opt + " from Minimizer one level deeper." + Environment.NewLine,
                        //                                                Logging.LogLevel.DeepDebug);
                    }

                    if (currentRecursionDepth == 1)
                    {
                        // We are in the highest layer of the recursion, so we store the optimum we got
                        // in the respective array of maxima:
                        _maxValue[pitIndex] = opt;
                        //                        Logging.I.LogMessage("Maximizer on depth " + currentRecursionDepth + ": maxValue[" + pitIndex +
                        //                                                "] = " + opt + "." + Environment.NewLine,
                        //                                                Logging.LogLevel.DeepDebug);
                    }

                    // In any case we are interested in the best result we get from the deeper levels:
                    if (opt >= max)
                    {
                        // We have found a new maximum.
                        max = opt;
                        //                        Logging.I.LogMessage("Maximizer on depth " + currentRecursionDepth + ": New maximum found: " + max +
                        //                                                "." + Environment.NewLine,
                        //                                                Logging.LogLevel.DeepDebug);

                    }
                }
            }

            // Return the maximum we found across all possible moves:
            return max;
        }