Example #1
0
        private void ClaimNoble(Noble noble)
        {
            if (noble == null)
            {
                // If no noble is specified, try selecting one who's requirements have or will be met.
                // Most of the time there won't be any. Sometimes there will be one. Rarely there will be multiple.
                noble = Board.Nobles.Where(n => Utilities.RequirementsMet(n.Requirements, CurrentPlayer.Bonuses)).FirstOrDefault();
            }
            else
            {
                // Verify noble is available
                if (!Board.Nobles.Contains(noble))
                {
                    throw new InvalidOperationException($"Noble {noble.Id} not found.");
                }

                // Verify noble requirements have been met
                if (!Utilities.RequirementsMet(noble.Requirements, CurrentPlayer.Bonuses))
                {
                    throw new InvalidOperationException($"The requirements for noble {noble.Id} have not been met.");
                }
            }

            // Claim it
            if (noble != null)
            {
                Board.TakeNoble(noble);
                CurrentPlayer.AddNoble(noble);
            }
        }
Example #2
0
        // Buy 1 Card from the middle or from reserve.
        // Gold will be consumed automatically if required
        // Claim a noble if possible. If none is specified and you can afford one it will
        //  be claimed automatically.
        public void Purchase(string id, Noble noble = null)
        {
            ThrowIfGameOver();

            // Verify card is available or in reserve
            var fromReserve   = CurrentPlayer.Reserve.Where(c => c.Id == id).SingleOrDefault();
            var fromAvailable = Board.AvailableCards.Where(c => c.Id == id).SingleOrDefault();
            var card          = fromReserve ?? fromAvailable ?? throw new InvalidOperationException($"Card {id} could not be found.");

            // Verify card is affordable (inlcuding gold as needed)
            if (!Utilities.CanAfford(card.Cost, CurrentPlayer.TotalGems))
            {
                throw new InvalidOperationException($"Cannot afford card id {id}.");
            }

            // Pay price (e.g. return tokens to bank as needed)
            foreach (var cost in card.Cost)
            {
                var diff = cost.Value - CurrentPlayer.Bonuses[cost.Key];
                if (diff > 0)
                {
                    var goldNeeded = diff - CurrentPlayer.Disks[cost.Key];
                    if (goldNeeded > 0)
                    {
                        CurrentPlayer.RemoveDisks(GemType.Gold, goldNeeded);
                        Board.Bank.Return(GemType.Gold, goldNeeded);
                        diff -= goldNeeded;
                    }
                    if (diff > 0)
                    {
                        CurrentPlayer.RemoveDisks(cost.Key, diff);
                        Board.Bank.Return(cost.Key, diff);
                    }
                }
            }

            // Move card from reserve or available to player's stack
            if (fromReserve != null)
            {
                CurrentPlayer.TransferFromReserve(card);
            }
            else
            {
                Board.TakeCard(card);
                CurrentPlayer.AddCard(fromAvailable);
            }

            ClaimNoble(noble);
            //  Design bug: Claiming nobles is not part of the purchase action. Since it's possible for you
            //  to be able to afford more than one when you're only allowed to take one, on your subsequent turn
            //  you may claim another without an additional purchase. On that turn you may still have a choice,
            //  so you need to be able specify a noble on any type of action.
            //  Proposal: Many different actions require similar parameters. Make a general state object that
            //  includes disk aquisitions, discards, noble selection, and a card selection id. The secret reservation
            //  level is the only unique parameter.

            _passCount = 0;

            AdvanceGame();
        }
Example #3
0
        // Reserve 1 Card to your hand and take 1 gold.
        // Hand limit
        // Disk limit
        // You may discard the aquired gold (but why would you?)
        public void ReserveCard(string id, GemType?discard, Noble noble = null)
        {
            ThrowIfGameOver();

            // Verify reserve limit (3)
            if (CurrentPlayer.Reserve.Count == 3)
            {
                throw new InvalidOperationException($"Too many cards already reserved.");
            }

            // Verify card is available
            if (Board.AvailableCards.Where(c => c.Id == id).SingleOrDefault() == null)
            {
                throw new InvalidOperationException($"Card id {id} not found.");
            }

            // Verify disk limit 10 (if gold available then it will be taken and a discard must be specified).
            //  discard must not be specified if current disks < 10.
            if (Board.Bank.Available[GemType.Gold] > 0 && CurrentPlayer.TotalDisks == 10)
            {
                if (!discard.HasValue)
                {
                    throw new InvalidOperationException("Discard required.");
                }
                // Verify player owns discard or is discarding the gold aquired.
                if (CurrentPlayer.Disks[discard.Value] == 0)
                {
                    throw new InvalidOperationException($"{discard.Value} is not available to discard.");
                }
            }

            ClaimNoble(noble);

            // Move card from available to reserve
            var card = Board.AvailableCards.Where(c => c.Id == id).Single();

            Board.TakeCard(card);
            CurrentPlayer.AddReserve(card);

            // Discard specified
            if (discard.HasValue)
            {
                CurrentPlayer.RemoveDisks(discard.Value, 1);
                Board.Bank.Return(discard.Value, 1);
            }

            // Gain gold
            if (Board.Bank.Available[GemType.Gold] > 0)
            {
                Board.Bank.Take(GemType.Gold, 1);
                CurrentPlayer.AddDisks(GemType.Gold, 1);
            }

            _passCount = 0;

            AdvanceGame();
        }
Example #4
0
        internal void TakeNoble(Noble noble)
        {
            // Remove from Nobles, no replacement
            var removed = _nobles.Remove(noble);

            // Verify available
            if (!removed)
            {
                throw new InvalidOperationException($"Missing noble {noble.Id}");
            }
        }
Example #5
0
        // The current player can take one of these actions. Doing so automaticlly advances play to the next player.

        // Take 3 Gems of different colors.  As many as possible if 3 colors not available.
        // Can't take Gold
        // Disk limit
        public void TakeDistinctGems(IList <GemType> types, IEnumerable <KeyValuePair <GemType, int> > discards, Noble noble = null)
        {
            ThrowIfGameOver();

            // Verify not Gold
            if (types.Where(type => type == GemType.Gold).Any())
            {
                throw new InvalidOperationException("Gold cannot be selected.");
            }

            // Verify The types are distinct
            if (types.Distinct().Count() < types.Count)
            {
                throw new InvalidOperationException("Types must be unique.");
            }

            // Can you request 0? E.g. pass. It seems like it's possible to pass because the bank may run out of disks
            //  and your reserve could be full, yet you may still not be able to afford anything.
            // 0-3 count
            if (types.Count > 3)
            {
                throw new InvalidOperationException("Too many types selected.");
            }

            // TODO: You can only take less than three if there aren't three distinct disks in the bank.

            // At least one exists in the bank
            foreach (var disk in types)
            {
                if (Board.Bank.Available[disk] == 0)
                {
                    throw new InvalidOperationException($"No {disk} in the bank.");
                }
            }

            // If discarding, verify new total is exactly 10. Can't discard and get below 10.
            var totalDiscards = discards?.Select(pair => pair.Value).Sum() ?? 0;
            var newTotal      = CurrentPlayer.TotalDisks + types.Count - totalDiscards;

            if (newTotal > 10)
            {
                throw new InvalidOperationException("Insufficient discards.");
            }

            // Verify the player owns specified discards.
            if (discards != null)
            {
                foreach (var set in discards)
                {
                    if (CurrentPlayer.Disks[set.Key] < set.Value)
                    {
                        throw new InvalidOperationException("Discard count mismatch.");
                    }
                }
            }

            ClaimNoble(noble);

            // Discard gems
            if (discards != null)
            {
                foreach (var type in discards)
                {
                    CurrentPlayer.RemoveDisks(type.Key, type.Value);
                    Board.Bank.Return(type.Key, type.Value);
                }
            }

            // Aquire gems
            foreach (var type in types)
            {
                Board.Bank.Take(type, 1);
                CurrentPlayer.AddDisks(type, 1);
            }

            if (types.Count == 0)
            {
                _passCount++;
            }
            else
            {
                _passCount = 0;
            }

            AdvanceGame();
        }
Example #6
0
        // Draw secretly from any deck
        // Reserve 1 Card to your hand and take 1 gold.
        // Hand limit
        // Disk limit
        // Empty deck
        // You may discard the aquired gold (byt why would you?)
        public void ReserveSecret(int level, GemType?discard, Noble noble = null)
        {
            ThrowIfGameOver();

            // Verify reserve limit (3)
            if (CurrentPlayer.Reserve.Count == 3)
            {
                throw new InvalidOperationException($"Too many cards already reserved.");
            }


            // Verify level 1-3
            if (level < 1 || level > 3)
            {
                throw new InvalidOperationException($"Invlaid level {level} selected.");
            }

            // Verify level has cards
            if (Board.CheckLevelDeckIsEmpty(level))
            {
                throw new InvalidOperationException($"Level {level} deck is empty.");
            }

            // Verify disk limit 10 (if gold available then it will be taken and a discard must be specified).
            //  discard must not be specified if current disks < 10.
            if (Board.Bank.Available[GemType.Gold] > 0 && CurrentPlayer.TotalDisks == 10)
            {
                if (!discard.HasValue)
                {
                    throw new InvalidOperationException("Discard required.");
                }
                // Verify player owns discard or is discarding the gold aquired.
                if (CurrentPlayer.Disks[discard.Value] == 0)
                {
                    throw new InvalidOperationException($"{discard.Value} is not available to discard.");
                }
            }

            ClaimNoble(noble);

            // Move card from deck to reserve
            var card = Board.TakeSecret(level);

            CurrentPlayer.AddReserve(card);

            // Discard specified
            if (discard.HasValue)
            {
                CurrentPlayer.RemoveDisks(discard.Value, 1);
                Board.Bank.Return(discard.Value, 1);
            }

            // Gain gold
            if (Board.Bank.Available[GemType.Gold] > 0)
            {
                Board.Bank.Take(GemType.Gold, 1);
                CurrentPlayer.AddDisks(GemType.Gold, 1);
            }

            _passCount = 0;

            AdvanceGame();
        }
Example #7
0
        // Take 2 Gems of the same color.  Color must have at least 4 gems available.
        // Can't take Gold
        // Disk limit
        public void TakeTwoGems(GemType type, IEnumerable <KeyValuePair <GemType, int> > discards, Noble noble = null)
        {
            ThrowIfGameOver();

            // Verify not Gold
            if (type == GemType.Gold)
            {
                throw new InvalidOperationException("Gold cannot be selected.");
            }

            // Verify The bank has sufficient quantity. Color must have at least 4 gems available.
            if (Board.Bank.Available[type] < 4)
            {
                throw new InvalidOperationException($"Insufficient {type} in the bank, there must be at least 4.");
            }

            // If discarding, verify new total is exactly 10. Can't discard and get below 10.
            var totalDiscards = discards?.Select(pair => pair.Value).Sum() ?? 0;
            var newTotal      = CurrentPlayer.TotalDisks + 2 - totalDiscards;

            if (newTotal > 10)
            {
                throw new InvalidOperationException("Insufficient discards.");
            }

            // Verify the player owns specified discards.
            if (discards != null)
            {
                foreach (var set in discards)
                {
                    if (CurrentPlayer.Disks[set.Key] < set.Value)
                    {
                        throw new InvalidOperationException("Discard count mismatch.");
                    }
                }
            }

            ClaimNoble(noble);

            // Discard gems
            if (discards != null)
            {
                foreach (var group in discards)
                {
                    CurrentPlayer.RemoveDisks(group.Key, group.Value);
                    Board.Bank.Return(group.Key, group.Value);
                }
            }

            // Aquire gems
            Board.Bank.Take(type, 2);
            CurrentPlayer.AddDisks(type, 2);

            _passCount = 0;

            AdvanceGame();
        }
Example #8
0
 internal void AddNoble(Noble noble)
 {
     Points += noble.PointValue;
     _nobles.Add(noble);
 }