Example #1
0
        int _zIndexOfTouchFields = Basics.Infinity(); // The touch fields always have to stay on top

        #endregion Fields

        #region Constructors

        // --- Methods of the class ---
        /// <summary>The constructor</summary>
        /// <param name="gameBoard">The GameBoard to consider as the basis</param>
        public GameBoard(Kalaha.Model.GameBoard gameBoard,
                            Canvas gameBoardCanvas,
                            PointerEventHandler PointerEnteredHouse,
                            PointerEventHandler PointerExitedHouse,
                            RoutedEventHandler TouchFieldSelected)
        {
            _gameBoard = gameBoard;
            _canvasToPaintOn = gameBoardCanvas;

            KalahPosition kalahPosition;
            kalahPosition = GetKalahPosition();
            CreatePitsAndSetCoordinatesAndSizes(kalahPosition);
            CreateImagesAndTouchFieldsAndNumFieldsOfHouses(PointerEnteredHouse, PointerExitedHouse, TouchFieldSelected);
            CreateImagesAndNumFieldsOfKalahs();
        }
Example #2
0
        /// <summary>Visually executes the move.</summary>
        /// <param name="move">The move to perform</param>
        /// <param name="pit">The array of visual pits</param>
        /// <param name="nextMethodToCall">The method to be called once all moves have been performed</param>
        /// <param name="moveFast">Flag whether or not to move the seeds very fast</param>
        public async static void ExecuteMove(Move move, Kalaha.View.Model.Pit[] pit, NextMethodToCall nextMethodToCall, bool moveFast)
        {
            // Take the seed movement that comes next in the move:
            SeedMovement seedMovement = move.GetNextSeedMovement();

            // Remove this seed movement from the move:
            move.RemoveSeedmovement(seedMovement);

            // Get data out of the seed movement:
            Kalaha.View.Model.Pit fromPit = pit[seedMovement.FromPit];
            Kalaha.View.Model.Pit toPit = pit[seedMovement.ToPit];
            int numSeeds = seedMovement.NumberOfSeeds;

            // Prepare the move executor by telling it the total number of seeds:
            MoveExecutor.PrepareMove(numSeeds);
            
//DEBUG     Logging.I.LogMessage("Performing move with " + fromPit + ", " + toPit + ", and " + numSeeds + ".\n");

            for (int seedIndex = 0; seedIndex < numSeeds; ++seedIndex)
            {
                Seed seedToMove = fromPit.GetSomeSeedAndRemoveIt();
                Point newPlace = toPit.FindPlaceForNewSeed(seedToMove.GetWidth(), seedToMove.GetHeight());

                Storyboard story = CreateLinearMoveStory(seedToMove.GetImage(), seedToMove.GetTopLeftCorner(), newPlace, moveFast);
                
                // Set the zIndex of the seed to a high number it order to put it visually "on top" of the stack:
                seedToMove.SetZIndex(_seedMoveCounter);
                _seedMoveCounter++;

//DEBUG         Logging.I.LogMessage("ExcuteMove (fromPit = " + fromPit + ", toPit = " + toPit + ", numSeeds = " + numSeeds +
//DEBUG                              "), seedIndex = " + seedIndex + " -> Before asyncLock.\n");

                // The following line is pretty tricky: We jump out of this method if some other seed is still being visualized.
                // This causes the complete call stack to return to the original call of the GameBoardPage.ButtonClickedOnPit() method.
                // Only if one seed is fully animated, the lock is released and the next seed will be animated:
          //      using (var releaser = await _asyncLock.LockAsync())
                {
//DEBUG             Logging.I.LogMessage("ExcuteMove (fromPit = " + fromPit + ", toPit = " + toPit + ", numSeeds = " + numSeeds +
//DEBUG                                  "), seedIndex = " + seedIndex + " -> Inside asyncLock.\n");
                    await story.BeginAsync();
                    story.Stop();

                }

                // Play the sound when moving the seed:
                seedToMove.PlayMovingSound();

                // Rotate the image to the angle that it had at the time of creation of the seed:
                seedToMove.RotateImage();


//DEBUG         Logging.I.LogMessage("ExcuteMove (fromPit = " + fromPit + ", toPit = " + toPit + ", numSeeds = " + numSeeds +
//DEBUG                              "), seedIndex = " + seedIndex + " -> After asyncLock.\n");

                toPit.MoveSeedHere(seedToMove, newPlace);

                // Check if in all the asynchronous storyboards we are now done with the last storyboard for this move:
                if (MoveIsCompletelyVisualized())
                {
//DEBUG             Logging.I.LogMessage("This was the last seed to be completed.\n");

                    // This is the last seed that has been visualized. All aynchronous calls have been executed.
                    // If there is some seed movement left in the original move, we perform this now by calling the visualization method
                    // recursively. If there is nothing left, and some original caller of this method told us what to do once we are done, we do it now:
                    if (move.GetNumberOfSeedMovements() > 0)
                    {
//DEBUG                 Logging.I.LogMessage("Recursively calling the visualizer.\n");
                        ExecuteMove(move, pit, nextMethodToCall, moveFast);
                    }
                    else
                    {
                        // We are completely done with the move.
                        if (nextMethodToCall != null)
                        {
//DEBUG                     Logging.I.LogMessage("Calling the next method to call.\n");
                            nextMethodToCall();
                        }
                    }
                }
            }
        }
Example #3
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;
        }