Example #1
0
        /// <summary>Moves all seeds into the respective Kalahs.</summary>
        public Move CollectFinalSeeds(Player firstPlayer, Player secondPlayer)
        {
            Move move = new Move();
            Player currentPlayer = firstPlayer;
            for (int playerIndex = 0; playerIndex < 2; ++playerIndex)
            {
                Pit currentPlayersKalah = GetKalahOfPlayer(currentPlayer);
                int internalIndexOfKalah = GetInternalIndexOfKalah(currentPlayer);

                for (int pitIndex = 0; pitIndex < _numHousesPerPlayer; ++pitIndex)
                {
                    Pit currentPit = GetPitOfPlayer(currentPlayer, pitIndex);

                    if (currentPit.GetNumberofSeeds() > 0)
                    {
                        int currentPitsInternalIndex = GetInternalPitIndex(currentPlayer, pitIndex);
                        move.AddSeedMovement(new SeedMovement(currentPitsInternalIndex, internalIndexOfKalah, currentPit.GetNumberofSeeds()));

                        currentPlayersKalah.AddSeeds(currentPit.GetNumberofSeeds());
                        currentPit.RemoveAllSeeds();
                    }
                }
                currentPlayer = secondPlayer;
            }

            return move;
        }
Example #2
0
        /// <summary>
        /// Returns the next undo move to be taken together with the player whose turn it is after that move.
        /// The undo move is removed from the internal list of the UndoManager.
        /// </summary>
        /// <param name="undoMove">The move to undo the last move that one of the two players made.</param>
        /// <param name="nextPlayer">The player whose turn it is after the undoMove</param>
        public void GetNextUndoMove(ref Move undoMove, ref Player nextPlayer)
        {
            if (_moveList.Count == 0)
            {
                throw new PSTException("UndoManager.GetNextUndoMove: No move left.");
            }

            // Store the respective first elements of the internal lists in the reference parameters:
            undoMove = _moveList[0];
            nextPlayer = _playerList[0];

            // Remove both entries from the lists:
            _moveList.RemoveAt(0);
            _playerList.RemoveAt(0);
        }
Example #3
0
        /// <summary>
        /// Returns the backward move of the given move.
        /// A "backward move" of a move has all the seed movements in the converse order, and each seed movement has swapped "FromPit" and "ToPit".
        /// </summary>
        public Move GetBackwardMove()
        {
            Move backMove = new Move();

            // Walk backwards through the list of seed movements:
            for (int index = _seedMovementList.Count - 1; index >= 0;  --index)
            {
                // Take the seed movement at position "index":
                SeedMovement origSeedMovement = _seedMovementList[index];

                // Create a new seed movement that has swapped FromPit and ToPit:
                SeedMovement backSeedMovement = new SeedMovement(origSeedMovement.ToPit, origSeedMovement.FromPit, origSeedMovement.NumberOfSeeds);

                // Add the new seed movement to the backward move:
                backMove.AddSeedMovement(backSeedMovement);
            }

            return backMove;
        }
Example #4
0
        /// <summary>
        /// Remembers the given move as a move that has to be added to the last "normal" move of the same player.
        /// This is supposed to be used for capture moves or "final" moves.
        /// </summary>
        /// <param name="move">The move to remember</param>
        /// <param name="currentPlayer">The player who made the move</param>
        public void RememberFollowUpMove(Move move, Player currentPlayer)
        {
            // We need an existing player list:
            if (_playerList.Count == 0)
            {
                throw new PSTException("UndoManager.RememberFollowUpMove: player list is empty.");
            }

            // We need an existing move list:
            if (_moveList.Count == 0)
            {
                throw new PSTException("UndoManager.RememberFollowUpMove: move list is empty.");
            }

            // We can only add a follow-up move for the same player as exists as the first entry in the list of players:
            if (_playerList[0] != currentPlayer)
            {
                throw new PSTException("UndoManager.RememberFollowUpMove: Unexpected player: " + currentPlayer + ". Expected " + _playerList[0] + ".");
            }

            // Get the move's backward move:
            Move backMove = move.GetBackwardMove();

            // Integrate the backward move into the move that is the first in the already existing list of "undo moves":
            _moveList[0].IntegrateMove(backMove);
        }
Example #5
0
        /// <summary>
        /// Remembers the given move as a "normal" by the given player.
        /// </summary>
        /// <param name="move">The move to remember</param>
        /// <param name="currentPlayer">The player who made the move</param>
        public void RememberNormalNove(Move move, Player currentPlayer)
        {
            // A "normal" move is quite easy to remember: We store a new list entry with the move's "backward move".

            // Get the move's backward move:
            Move backMove = move.GetBackwardMove();

            // Add the backward move and the current player at the beginnings of the respecive lists:
            _moveList.Insert(0, backMove);
            _playerList.Insert(0, currentPlayer);
        }
Example #6
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 #7
0
 /// <summary>
 /// Adds the SeedMovements of "moveToIntegrate" in the parameter
 /// </summary>
 /// <param name="move"></param>
 /// <returns></returns>
 public void IntegrateMove(Move moveToIntegrate)
 {
     for (int index = 0; index < moveToIntegrate.GetNumberOfSeedMovements(); ++index)
     {
         AddSeedMovementToTheFront(moveToIntegrate.GetSeedMovement(index));
     }
 }
Example #8
0
        /// <summary>
        /// The given player wins his own pit (given by "ownPitIndex") and the opposite pit. Both pit's contents move into
        /// the player's Kalah.</summary>
        /// <param name="player">The player who wins the pit's contents</param>
        /// <param name="ownPitIndex">The index of the player's pit</param>
        /// <param name="captureOpponentsHouse">A flag whether the seeds in the opponent's house may be captured, too</param>
        /// <param name="returnMove">A flag whether or not to return the move in detail</param>
        /// <returns>The details of this move</returns>
        public Move PlayerWinsHouses(Player player, int ownPitIndex, bool captureOpponentsHouse, bool returnMove)
        {
            Pit ownPit = GetPitOfPlayer(player, ownPitIndex);
            Pit kalahToBeFilled = GetKalahOfPlayer(player);
            int internalKalahIndex = -1;  // Only used when generating a Move
            Move move = null;

            if (returnMove)
            {
                move = new Move();
                internalKalahIndex = GetInternalIndexOfKalah(player);
            }

            if (captureOpponentsHouse)
            {
                // Also the opponent's house shall be captured.
                Pit opponentsPit = GetOppositeHouse(player, ownPitIndex);
                if (returnMove)
                {
                    // Move the opponent's seeds to the player's Kalah:
                    move.AddSeedMovement(new SeedMovement(GetInternalIndexOfOppositeHouse(player, ownPitIndex), internalKalahIndex,
                                         opponentsPit.GetNumberofSeeds()));
                }

                kalahToBeFilled.AddSeeds(opponentsPit.GetNumberofSeeds());
                opponentsPit.RemoveAllSeeds();
            }

            if (returnMove)
            {
                // Move the one seed from the own house into the player's Kalah:
                move.AddSeedMovement(new SeedMovement(GetInternalPitIndex(player, ownPitIndex), internalKalahIndex, 1));
            }

            // Fill the own Kalah with the own seed and remove the own seed from the house:
            kalahToBeFilled.AddSeeds(1);
            ownPit.RemoveAllSeeds();

            return move;
        }
Example #9
0
        /// <summary>
        /// Performs the move given in the parameter.
        /// </summary>
        /// <param name="move">The move to perform</param>
        public void PerformMove(Move move)
        {
            //DEBUG     Logging.I.LogMessage("Entering GameBoard.PerformMove().\n");
            // Go through all seed movements of the move and add and remove seeds of the respective pits:
            for (int index = 0; index < move.GetNumberOfSeedMovements(); ++index)
            {
                SeedMovement seedMovement = move.GetSeedMovement(index);
            //DEBUG         Logging.I.LogMessage("             Removing " + seedMovement.NumberOfSeeds + " seeds from pit " + seedMovement.FromPit +
            //DEBUG                              " and adding them to pit " + seedMovement.ToPit + ".\n");

                _pit[seedMovement.FromPit].RemoveSeeds(seedMovement.NumberOfSeeds);
                _pit[seedMovement.ToPit].AddSeeds(seedMovement.NumberOfSeeds);
            }
        }
Example #10
0
        /// <summary>Selects the given house and distributes the seeds of that house among
        /// the following pits, one seed per pit. Takes into account the direction of sowing, depending on the rules.</summary>
        /// <param name="player">The player whose pit is to be moved</param>
        /// <param name="houseIndexToBeMoved">The index of the house to be moved</param>
        /// <param name="lastSeedFellInOwnKalah">A reference parameter which gets the information whether the
        /// <param name="lastSeedFellIntoEmptyOwnHouse">A reference parameter which gets the information whether the
        /// last seed fell into empty pit that is owned by the player.</param>
        /// <param name="lastSeedsHouse">If "lastSeedFellIntoEmptyOwnHouse" is true, then this reference parameter
        /// gets the index of the house that the last seed fell into. Otherwise "lastSeedsHouse" gets the value -1 (for "undefined").
        /// <exception cref="KalahaException">Index out of range</exception>
        public Move MoveHouse(Player player, int houseIndexToBeMoved,
                                ref bool lastSeedFellIntoOwnKalah,
                                ref bool lastSeedFellIntoEmptyOwnHouse,
                                ref int lastSeedsHouse,
                                bool returnMove)
        {
            if ((houseIndexToBeMoved < 0) || (houseIndexToBeMoved >= _numHousesPerPlayer))
            {
                throw new PSTException("MovePit: houseIndex out of range: " + houseIndexToBeMoved);
            }

            lastSeedFellIntoOwnKalah = false;
            lastSeedFellIntoEmptyOwnHouse = false;
            lastSeedsHouse = -1;

            // Create a move object if this is requested by the caller of the method:
            Move move = null;
            if (returnMove)
            {
                move = new Move();
            }

            // Calculate the array index of the house from which we take the seeds out:
            int internalHouseIndex = houseIndexToBeMoved + (player.GetId() * (_numHousesPerPlayer + 1));

            // Keep the number of seeds to be distributed in mind:
            int numOfSeeds = _pit[internalHouseIndex].GetNumberofSeeds();

            // The distribution of seeds is done in a fast way, using the internal array structure.
            // Find out about the sowing direction: It is clockwise if the rule is simply defined that way or if the rule
            // is set to "Cross-Kalah" and the number of seeds is odd:
            bool sowingDirectionIsClockwise = ((Rules.I.GetDirectionOfSowing() == Rules.DirectionOfSowing.Clockwise) ||
                                               ((Rules.I.GetDirectionOfSowing() == Rules.DirectionOfSowing.CrossKalah) && ((numOfSeeds % 2) == 1)));

            // Empty the pit we start from:
            _pit[internalHouseIndex].RemoveAllSeeds();

            // Distribute the seeds:
            int maybeOneMore = 0;
            Pit kalahOfOpponent = GetKalahOfOpponent(player);
            int loopIndex = 0;
            for (; loopIndex < numOfSeeds+maybeOneMore; ++loopIndex)
            {
                // Depending on the sowing direction, we set the index of the pit that gets a seed accordingly:
                int pitIndex = -1;
                if (sowingDirectionIsClockwise)
                {
                    pitIndex = (internalHouseIndex + 10*_numPitsInTotal - loopIndex - 1) % _numPitsInTotal;
                }
                else
                {
                    pitIndex = (internalHouseIndex + 1 + loopIndex) % _numPitsInTotal;
                }

                if (_pit[pitIndex] != kalahOfOpponent)
                {
                    // This is not the Kalah of the opponent --> Add a seed:
                    _pit[pitIndex].AddASeed();
                    if (returnMove)
                    {
                        // Store this step in the move:
                        move.AddSeedMovement(new SeedMovement(internalHouseIndex, pitIndex, 1));
                    }
                }
                else
                {
                    // This is the opponent's Kalah --> Do not add a seed but instead increase the loop by 1:
                    ++maybeOneMore;
                }
            }

            // Calculate which pit the last seed fell into, depending on the sowing direction:
            int pitOfLastSeed = -1;
            if (sowingDirectionIsClockwise)
            {
                pitOfLastSeed = (internalHouseIndex + 10*_numPitsInTotal - loopIndex) % _numPitsInTotal;
            }
            else
            {
                pitOfLastSeed = (internalHouseIndex + loopIndex) % _numPitsInTotal;
            }

            lastSeedFellIntoOwnKalah = (_pit[pitOfLastSeed] == GetKalahOfPlayer(player));

            if (!lastSeedFellIntoOwnKalah)
            {
                // Check whether the last seed fell into an empty own pit:
                if ((_pit[pitOfLastSeed].GetNumberofSeeds() == 1) && IsPlayersOwnPit(player, pitOfLastSeed))
                {
                    // Yes, it did.
                    lastSeedFellIntoEmptyOwnHouse = true;

                    // We have to calculate the player's pit from the internal pit:
                    lastSeedsHouse = pitOfLastSeed % (_numHousesPerPlayer + 1);
                }
            }

            return move;
        }
Example #11
0
 /// <summary>Shows the given move in detail.</summary>
 /// <param name="move">The move to be shown</param>
 /// <param name="nextMethodToCall">The method that shall be called after the visualization has finished</param>
 /// <param name="moveFast">Flag whether or not to move the seeds very fast</param>
 public void VisualizeMove(Move move, NextMethodToCall nextMethodToCall, bool moveFast = false)
 {
     MoveExecutor.ExecuteMove(move, _pit, nextMethodToCall, moveFast);
 }