Dominion Strategy Forum

Please login or register.

Login with username, password and session length
Pages: 1 2 [All]

Author Topic: Dominulator: A flexible dominion engine simulator  (Read 18598 times)

0 Members and 1 Guest are viewing this topic.

Sparafucile

  • Thief
  • ****
  • Offline Offline
  • Posts: 98
  • Respect: +153
    • View Profile
Dominulator: A flexible dominion engine simulator
« on: October 04, 2013, 02:00:42 pm »
+18

Hey All, I would like to introduce you to a new simulator for Dominion that I have been working on.  Now called Dominulator

What makes this simulator different?

The other simulators I have seen have an emphasis on allowing non-programmers to easily create new strategies.  There are many obvious advantages this approach.  Sometimes, however, you may be interested in expressing a strategy that is tough to capture with a restricted language.   

This simulator focuses on creating a game engine that is completely separate from play decisions.  Every play decision should be able to be customized - but this also involves writing to a more complicated interface using c# code.   On top of the game engine, I have built enough infrastructure to provide high level strategies similar to what the other simulators have done.  You can create a buy order, trash order etc.  The strategies created tend to be relatively readable.

The goal is to eventually be complete, with all cards playable, and with all bugs squashed ;)   

Its not done yet - but done enough I thought I would share.  I have tested it with some cards from all sets - including Guilds, and Dark ages.   It can simulate games with Coin Tokens, and Ruins and Shelters, and Spoils etc etc...

Where can I find the code and learn more?

https://github.com/NathanTeeuwen/Dominulator

There is a readme in the GIT repository that I will be trying to keep up to date. 

Please make feature requests or report bugs by creating Issues on Github.   You can also reply to this post, and I will try to keep on top of it. 

If you would like to make a contribution, let me know.
« Last Edit: October 05, 2013, 12:47:19 am by Sparafucile »
Logged

SCSN

  • Mountebank
  • *****
  • Offline Offline
  • Posts: 2227
  • Respect: +7140
    • View Profile
Re: Dominulator: A flexible dominion engine simulator
« Reply #1 on: October 05, 2013, 06:36:05 pm »
0

One small bug in BaseSet.cs:

Quote
    public class Feast
       : Card
    {
        public Feast()
            : base("Feast", coinCost: 4, isAction: true)
        {
        }

        public override void DoSpecializedAction(PlayerState currentPlayer, GameState gameState)
        {
            currentPlayer.MoveCardFromPlayToTrash(gameState);
            currentPlayer.RequestPlayerGainCardFromSupply(
                gameState,
                card => card.CurrentCoinCost(currentPlayer) < 5
                , "cost of card < 5"
);
        }
    }

This should be <= 5.
Logged

Sparafucile

  • Thief
  • ****
  • Offline Offline
  • Posts: 98
  • Respect: +153
    • View Profile
Re: Dominulator: A flexible dominion engine simulator
« Reply #2 on: October 05, 2013, 10:00:05 pm »
0

Fixed feast
Logged

SCSN

  • Mountebank
  • *****
  • Offline Offline
  • Posts: 2227
  • Respect: +7140
    • View Profile
Re: Dominulator: A flexible dominion engine simulator
« Reply #3 on: October 06, 2013, 09:44:29 am »
0

I fixed the monument rebuild.  Added RebuildDuchy.   I also looked over Cultist.  It had an exception, but I removed it as I believe the card is fully implemented, though not tested.  Why don't you start with Cultist strategies :).  Let me know if there's a bug with it.

I was going to do hermit card next - as I am also interested in seeing what the hermit/market square strat would look like.

Let's continue here as this is actually the appropriate thread ;)

Two trivial points:

1. The RebuildDuke strategy file is called RebuildDuchy.cs, which is a bit confusing :)
2. The card type of Ruins is called "Ruin", but the proper term is actually "Ruins", see e.g. here.

About the Cultist implementation, there's one bug I found:

Quote
BigMoneyCultist begins turn
With hand: Copper,Copper,Cultist,Cultist,Silver,
  BigMoneyCultist Played Cultist.
    BigMoneyCultist Drew Silver into hand.
    BigMoneyCultist Drew Copper into hand.
    BigMoneyCultist Played Cultist.
      BigMoneyCultist Drew Estate into hand.
      BigMoneyCultist Drew Copper into hand.
      BigMoney gained Ruined Library.
    BigMoney gained Ruined Village.

It looks like the Ruins from the first Cultist is gained only after the second Cultist is played (and even after the Ruins from that Cultist is gained). This is wrong and it matters because when your opponent has a Watchtower or Trader in hand, he reveals this only at the moment when he gains/would gain the card. Knowing that your opponent has one of these cards in hand might deter you from playing the second Cultist, especially if it would trigger a reshuffle. As it is you'd learn of the reaction only after your final Cultist play.

I also saw that the Ruins are never actually played. I think a good heuristic, at least for BM-like strategies and given that you have no non-Ruins actions left in hand, would be to play Abandoned Mine if (ExpectedCoinValueAtEndOfTurn + 1) would get you a card of higher gain priority than ExpectedCoinValueAtEndOfTurn, else play Ruined Library if (ExpectedCoinValueAtEndOfTurn + 1) or (ExpectedCoinValueAtEndOfTurn + 2) would get you a card of higher gain priority, and play Survivors otherwise. You could also choose to not play Survivors if it would trigger a reshuffle and your current hand has above average money density.

Most of the BigMoney scripts contain this:

Code: [Select]
CardAcceptance.For<CardTypes.Province>(gameState => CountAllOwned<CardTypes.Gold>(gameState) > 2),
Preferring Province over Gold after just 2 Golds is better by quite a bit.

BigMoneyCultist.cs A simple BM-Cultist strategy. I didn't spend alot of time optimizing it, but it beats both BMDoubleWitch and DoubleJack, so it can't be that bad.

Code: [Select]
using Dominion;
using CardTypes = Dominion.CardTypes;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

//Author: SheCantSayNo

namespace Program
{
    public static partial class Strategies
    {
        public static class BigMoneyCultist
        {

            public static PlayerAction Player(int playerNumber)
            {
                return CustomPlayer(playerNumber);
            }
           
            public static PlayerAction CustomPlayer(int playerNumber, int secondSmithy = 15)
            {
                return new PlayerAction(
                            "BigMoneyCultist",
                            playerNumber,
                            purchaseOrder: PurchaseOrder(secondSmithy),
                            treasurePlayOrder: Default.TreasurePlayOrder(),
                            actionOrder: ActionOrder(),
                            trashOrder: Default.EmptyPickOrder(),
                            discardOrder: Default.EmptyPickOrder());
            }

            private static CardPickByPriority PurchaseOrder(int secondSmithy)
            {
                return new CardPickByPriority(
                           CardAcceptance.For<CardTypes.Cultist>(gameState => CountOfPile<CardTypes.Ruin>(gameState) > 2),
                           CardAcceptance.For<CardTypes.Province>(gameState => CountAllOwned<CardTypes.Gold>(gameState) > 1),
                           CardAcceptance.For<CardTypes.Duchy>(gameState => CountOfPile<CardTypes.Province>(gameState) <= 5),
                           CardAcceptance.For<CardTypes.Estate>(gameState => CountOfPile<CardTypes.Province>(gameState) < 3),
                           CardAcceptance.For<CardTypes.Gold>(),
                           CardAcceptance.For<CardTypes.Cultist>(),
                           CardAcceptance.For<CardTypes.Silver>());
            }

            private static CardPickByPriority ActionOrder()
            {
                return new CardPickByPriority(
                           CardAcceptance.For<CardTypes.Cultist>());
            }
        }
    }
}
« Last Edit: October 06, 2013, 09:47:19 am by SheCantSayNo »
Logged

Sparafucile

  • Thief
  • ****
  • Offline Offline
  • Posts: 98
  • Respect: +153
    • View Profile
Re: Dominulator: A flexible dominion engine simulator
« Reply #4 on: October 06, 2013, 01:58:16 pm »
0

  • Fixed RebuildDuke file to be named correctly.
  • Ruins are called ruins instead of Ruin. 
  • Fixed cultist to distribute ruins before drawing additional cultists. 
  • Strategies will now play ruins by default. 
The ruin it plays will be arbitrary.   The ordering you suggested seems reasonable.  I just haven't implemented it yet.  If you want to make that happen, implement CompareRuins method in Strategies.cs :).    Of course, you can customize the ruin play order for a specific strategy, but implementing CompareRuins will determine the default play order for ruins.
Logged

SCSN

  • Mountebank
  • *****
  • Offline Offline
  • Posts: 2227
  • Respect: +7140
    • View Profile
Re: Dominulator: A flexible dominion engine simulator
« Reply #5 on: October 07, 2013, 01:20:47 pm »
0


Ok, I first created a fixed default ordering to test whether it would work (it did). It obviously isn't terrific, but I expect it to be slightly better than random:

Code: [Select]
private static int CompareRuins(Card first, Card second, GameState gameState)
{
string[] DefaultRuinsOrder = {"Ruined Village", "Abandoned Mine", "Ruined Library", "Survivors",
"Ruined Market"};
return Array.IndexOf(DefaultRuinsOrder, first.name) <= Array.IndexOf(DefaultRuinsOrder, second.name) ? 0 : 1;
}

Onto the more advanced version: the first thing I want to, after always having it play Ruined Village, is to play Abandoned Mine if it allows you to buy a card of higher gain priority:

Code: [Select]
int coinsToSpend = gameState.players.CurrentPlayer.ExpectedCoinValueAtEndOfTurn;

if(first.name == "Ruined Village" || second.name == "Ruined Village")
{
return first.name == "Ruined Village" ? 0 : 1;
}

if((first.name == "Abandoned Mine" || second.name == "Abandoned Mine") &&
   GetGainPriority(coinsToSpend + 1) < GetGainPriority(coinsToSpend))
{
return first.name == "Abandoned Mine" ? 0 : 1;
}

However, I don't know how to convert an amount of Coins into the gain priority. I assume you need to communicate with the strategy's PurchaseOrder method in some way, but I'm not sure how this should be done.
Logged

Sparafucile

  • Thief
  • ****
  • Offline Offline
  • Posts: 98
  • Respect: +153
    • View Profile
Re: Dominulator: A flexible dominion engine simulator
« Reply #6 on: October 07, 2013, 03:48:07 pm »
0

Quote
However, I don't know how to convert an amount of Coins into the gain priority. I assume you need to communicate with the strategy's PurchaseOrder method in some way, but I'm not sure how this should be done.

Here's how I would do it.  First, the comparer must have access to the purchase order.  You can pass this in via a parameter.  That requires modifying the following methods:
Code: [Select]
Program.Strategies.Default.ActionPlayOrder(ICardPicker purchaseOrder)
Program.Strategies.Default.SortCardByDefaultActionOrder.Ctr(ICardPicker purchaseOrder)
Program.Strategies.Default.SortCardByDefaultActionOrder.Comparer.Ctr(GameState gameState, ICardPicker purchaseOrder)
Program.Strategies.Default.SortCardByDefaultActionOrder.CompareRuins(Card first, Card second, GameState gameState, ICardPicker purchaseOrder)

Next, how to use the purchase order now that you have it.

Similar choices have to be made in spending coin tokens.  So, I would examine how that gets done. 

Look at the Program.PlayerAction.GetCoinAmountToSpend method.  You can do something similar where you invoke

Code: [Select]
purchaseOrder.GetPreferredCard(gameState, shouldGainCard);

where the shouldGainCard delegate checks for exactly the coin cost you expect to have.  (Use ExpectedCoinValueAtEndOfTurn + 1).  If it returns a match of exactly equal, then you know that you must play the abandoned mine to get the bonus coin to make that purchase.
Logged

SCSN

  • Mountebank
  • *****
  • Offline Offline
  • Posts: 2227
  • Respect: +7140
    • View Profile
Re: Dominulator: A flexible dominion engine simulator
« Reply #7 on: October 07, 2013, 08:15:24 pm »
0

When I change

Code: [Select]
            public ICardPicker ActionPlayOrder()
            {
                return new CardPickFromWhatsInHand(new SortCardByDefaultActionOrder());
            }

to

Code: [Select]
public ICardPicker ActionPlayOrder(ICardPicker purchaseOrder)
            {
                return new CardPickFromWhatsInHand(new SortCardByDefaultActionOrder(purchaseOrder));
            }

(and apply the other modifications) I get the error message "Error   1   No overload for method 'ActionPlayOrder' takes 0 arguments   C:\Dominion\Dominulator-master\TestProgram\PlayerAction.cs   25":

Code: [Select]
static readonly ICardPicker defaultActionOrder = Strategies.Default.ActionPlayOrder();
When I pass purchaseOrder as an argument:

Code: [Select]
static readonly ICardPicker defaultActionOrder = Strategies.Default.ActionPlayOrder(purchaseOrder);
I get the errors "Error   1   An object reference is required for the non-static field, method, or property 'Program.PlayerAction.purchaseOrder'   C:\Dominion\Dominulator-master\TestProgram\PlayerAction.cs   25
" and "Error   2   An object reference is required for the non-static field, method, or property 'Program.Strategies.Default.ActionPlayOrder(Program.ICardPicker)'   C:\Dominion\Dominulator-master\TestProgram\PlayerAction.cs   25
". I googled the errors but became none the wiser from it, and certainly didn't learn how to fix it.

Here is my altered strategies.cs, fwiw:

Code: [Select]
using Dominion;
using CardTypes = Dominion.CardTypes;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Program
{
    public static partial class Strategies
    {
        private enum RelativeAmount
        {
            LessThan,
            LessThanEqual,
            GreaterThan,
            GreaterThanEqual,
            Equal
        }

        private static GameStatePredicate CountAllOwned<T>(RelativeAmount relativeAmount, int amount)
            where T : Card, new()
        {
            switch (relativeAmount)
            {
                case RelativeAmount.LessThan: return delegate(GameState gameState) { return CountAllOwned<T>(gameState) < amount; };
                case RelativeAmount.GreaterThan: return delegate(GameState gameState) { return CountAllOwned<T>(gameState) > amount; };
                case RelativeAmount.LessThanEqual: return delegate(GameState gameState) { return CountAllOwned<T>(gameState) <= amount; };
                case RelativeAmount.GreaterThanEqual: return delegate(GameState gameState) { return CountAllOwned<T>(gameState) >= amount; };
                case RelativeAmount.Equal: return delegate(GameState gameState) { return CountAllOwned<T>(gameState) == amount; };
                default: throw new System.Exception();
            }
        }

        private static int CountInDeck<T>(GameState gameState)
        {
            return gameState.players.CurrentPlayer.CardsInDeck.Where(card => card is T).Count();
        }

        private static int CountInDeckAndDiscard<T>(GameState gameState)
        {
            return gameState.players.CurrentPlayer.CardsInDeckAndDiscard.Where(card => card is T).Count();
        }

        private static int CountMightDraw<T>(GameState gameState, int maxCount)
        {
            if (gameState.players.CurrentPlayer.CardsInDeck.Count() >= maxCount)
                return CountInDeck<T>(gameState);
            else
                return CountInDeckAndDiscard<T>(gameState);
        }

        public static bool CardBeingPlayedIs<T>(GameState gameState)
                where T : Card, new()
        {
            var cardBeingPlayed = gameState.players.CurrentPlayer.CurrentCardBeingPlayed;
            return cardBeingPlayed != null && cardBeingPlayed.Is<T>();
        }

        public static int CostOfCard<T>(GameState gameState)
            where T : Card, new()
        {
            return Card.Type<T>().CurrentCoinCost(gameState.players.CurrentPlayer);
        }

        public static int CountAllOwned<T>(GameState gameState)
            where T : Card, new()
        {
            return CountAllOwned(Card.Type<T>(), gameState);
        }

        public static int CountAllOwned(Card cardType, GameState gameState)
        {
            return gameState.players.CurrentPlayer.AllOwnedCards.Where(card => card.Is(cardType)).Count();
        }

        public static int CountInHand(Card cardType, GameState gameState)
        {
            return gameState.players.CurrentPlayer.Hand.Where(card => card.Is(cardType)).Count();
        }

        public static int CountInHand<T>(GameState gameState)
            where T : Card, new()
        {
            return CountInHand(Card.Type<T>(), gameState);
        }

        public static int CountOfPile<T>(GameState gameState)
            where T : Card, new()
        {
            return CountOfPile(Card.Type<T>(), gameState);
        }

        public static int CountOfPile(Card cardType, GameState gameState)
        {
            return gameState.GetPile(cardType).Count();
        }

        private static int CountAllOwnedMatching(ICardPicker matchingCards, GameState gameState)
        {
            int result = 0;

            foreach (Card card in gameState.players.CurrentPlayer.AllOwnedCards)
            {
                if (matchingCards.GetPreferredCard(gameState, testCard => testCard.Is(card)) != null)
                {
                    result += 1;
                }
            }

            return result;
        }

        private static int PlayersPointLead(GameState gameState)
        {
            return gameState.players.CurrentPlayer.TotalScore() - gameState.players.OtherPlayers.First().TotalScore();
        }

        private static GameStatePredicate HasCardInHand<T>()
            where T : Card, new()
        {
            return delegate(GameState gameState)
            {
                return HasCardInHand<T>(gameState);
            };
        }

        private static bool HasCardInHand<T>(GameState gameState)
            where T : Card, new()
        {
            return gameState.players.CurrentPlayer.Hand.HasCard<T>();
        }

        internal static Card WhichCardFromInHand(ICardPicker matchingCards, GameState gameState)
        {
            return matchingCards.GetPreferredCard(gameState, card => gameState.players.CurrentPlayer.Hand.HasCard(card));
        }

        private static bool HasCardFromInHand(ICardPicker matchingCards, GameState gameState)
        {
            return WhichCardFromInHand(matchingCards, gameState) != null;
        }

        private static bool HandHasOnlyCardsFrom(ICardPicker matchingCards, GameState gameState)
        {
            foreach (Card card in gameState.players.CurrentPlayer.Hand)
            {
                if (matchingCards.GetPreferredCard(gameState, current => current.Is(card)) == null)
                {
                    return false;
                }
            }

            return true;
        }

        private static int CountInHandFrom(ICardPicker matchingCards, GameState gameState)
        {
            int result = 0;
            foreach (Card card in gameState.players.CurrentPlayer.Hand)
            {
                if (matchingCards.GetPreferredCard(gameState, current => current.Is(card)) != null)
                {
                    ++result;
                }
            }

            return result;
        }

        public static class Default
        {
            public static CardPickByPriority EmptyPickOrder()
            {
                return new CardPickByPriority();
            }

            public ICardPicker ActionPlayOrder(ICardPicker purchaseOrder)
            {
                return new CardPickFromWhatsInHand(new SortCardByDefaultActionOrder(purchaseOrder));
            }

            private class CardPickFromWhatsInHand
                : ICardPicker
            {
                private readonly IComparerFactory comparerFactory;

                public CardPickFromWhatsInHand(IComparerFactory comparerFactory)
                {
                    this.comparerFactory = comparerFactory;
                }

                public int AmountWillingtoOverPayFor(Card card, GameState gameState)
                {
                    throw new NotImplementedException();
                }

                public Card GetPreferredCard(GameState gameState, CardPredicate cardPredicate)
                {
                    IComparer<Card> comparer = this.comparerFactory.GetComparer(gameState);

                    PlayerState currentPlayer = gameState.players.CurrentPlayer;

                    Card cardToPlay = currentPlayer.Hand.Where(card => cardPredicate(card)).OrderBy(card => card, comparer).FirstOrDefault();
                    if (cardToPlay == null)
                        return null;

                    return cardToPlay;
                }

                public Card GetPreferredCardReverse(GameState gameState, CardPredicate cardPredicate)
                {
                    IComparer<Card> comparer = this.comparerFactory.GetComparer(gameState);

                    PlayerState currentPlayer = gameState.players.CurrentPlayer;

                    Card cardToPlay = currentPlayer.Hand.Where(card => cardPredicate(card)).OrderByDescending(card => card, comparer).FirstOrDefault();
                    if (cardToPlay == null)
                        return null;

                    return cardToPlay;
                }

                public IEnumerable<Card> GetNeededCards()
                {
                    yield break;
                }
            }

            private interface IComparerFactory
            {
                IComparer<Card> GetComparer(GameState gameState);
                IComparer<Card> GetComparer(GameState gameState, ICardPicker purchaseOrder);
            }

            private class SortCardByDefaultActionOrder
                : IComparerFactory
            {

                public SortCardByDefaultActionOrder()
                {
                }

   
                public SortCardByDefaultActionOrder(ICardPicker purchaseOrder)
                {
                }

                public IComparer<Card> GetComparer(GameState gameState)
                {
                    return new Comparer(gameState);
                }

                public IComparer<Card> GetComparer(GameState gameState, ICardPicker purchaseOrder)
                {
                    return new Comparer(gameState, purchaseOrder);
                }

                private class Comparer
                    : IComparer<Card>
                {
                    private readonly GameState gameState;
                    private readonly ICardPicker purchaseOrder;

                    public Comparer(GameState gameState)
                    {
                        this.gameState = gameState;
                    }

                    public Comparer(GameState gameState, ICardPicker purchaseOrder)
                    {
                        this.gameState = gameState;
                        this.purchaseOrder = purchaseOrder;
                    }

                    public int Compare(Card first, Card second)
                    {
                        if (first.plusAction != 0 ^ second.plusAction != 0)
                        {
                            return first.plusAction != 0 ? -1 : 1;
                        }

                        if (first.DefaultCoinCost != second.DefaultCoinCost)
                        {
                            return first.DefaultCoinCost - second.DefaultCoinCost;
                        }

                        if (first.isRuins && second.isRuins)
                        {
                            int result = CompareRuins(first, second, this.gameState, this.purchaseOrder);
                            if (result != 0)
                                return result;
                        }

                        return 0;
                    }
                }

                // TODO:  implement a better default choice of which Ruins to player.
                private static int CompareRuins(Card first, Card second, GameState gameState, ICardPicker purchaseOrder)
                {
                    PlayerState currentPlayer = gameState.players.CurrentPlayer;

                    int coinsToSpend = gameState.players.CurrentPlayer.ExpectedCoinValueAtEndOfTurn;

                    if (first.name == "Abandoned Mine" || second.name == "Abandoned Mine")
                    {
                        CardPredicate shouldGainCard = delegate(Card card)
                        {
                            int currentCardCost = card.CurrentCoinCost(currentPlayer);

                            return currentCardCost == coinsToSpend + 1;                                   
                        };

                        Card cardType = purchaseOrder.GetPreferredCard(gameState, shouldGainCard);
                        if (cardType != null)
                            return first.name == "Abandoned Mine" ? 0 : 1;

                        //Card Card1 = purchaseOrder.GetPreferredCard(
                        //        gameState,
                        //        card => coinsToSpend >= card.CurrentCoinCost(currentPlayer) &&
                        //        gameState.GetPile(card).Any());
                        //Card Card2 = purchaseOrder.GetPreferredCard(
                        //        gameState,
                        //        card => coinsToSpend + 1 >= card.CurrentCoinCost(currentPlayer) &&
                        //        gameState.GetPile(card).Any());

                        //if (Card1 != Card2)
                        //    return first.name == "Abandoned Mine" ? 0 : 1;
                    }

                    return 0;
                }
            }

            public static CardPickByPriority TreasurePlayOrder()
            {
                return new CardPickByPriority(
                    CardAcceptance.For<CardTypes.Contraband>(),       // play early to provide opponent as little information when banning
                    // base set first
                    CardAcceptance.For<CardTypes.Platinum>(),
                    CardAcceptance.For<CardTypes.Gold>(),
                    CardAcceptance.For<CardTypes.Silver>(),
                    CardAcceptance.For<CardTypes.Copper>(),
                    CardAcceptance.For<CardTypes.Spoils>(),
                    // alphabetical, all other treasures that dont really depend on order
                    CardAcceptance.For<CardTypes.Cache>(),
                    CardAcceptance.For<CardTypes.FoolsGold>(),
                    CardAcceptance.For<CardTypes.Loan>(),
                    CardAcceptance.For<CardTypes.Harem>(),
                    CardAcceptance.For<CardTypes.Hoard>(),
                    CardAcceptance.For<CardTypes.Masterpiece>(),
                    CardAcceptance.For<CardTypes.PhilosophersStone>(),
                    CardAcceptance.For<CardTypes.Quarry>(),
                    CardAcceptance.For<CardTypes.Stash>(),
                    CardAcceptance.For<CardTypes.Talisman>(),
                    // cards whose benefit is sensitive to ordering
                    CardAcceptance.For<CardTypes.Venture>(),          // playing this card might increase the number of treasures played
                    CardAcceptance.For<CardTypes.CounterFeit>(),      // after venture so that you have more variety to counterfeit
                    CardAcceptance.For<CardTypes.IllGottenGains>(),   // by playing after venture, you have more information about whether to gain the copper
                    CardAcceptance.For<CardTypes.HornOfPlenty>(),     // play relatively last so it has the most variety of cards to trigger with
                    CardAcceptance.For<CardTypes.Bank>());            // try to make bank as valuable as possibile.
            }

            public static CardPickByPriority DefaultDiscardOrder()
            {
                return new CardPickByPriority(
                    CardAcceptance.For<CardTypes.Province>(),
                    CardAcceptance.For<CardTypes.Duchy>(),
                    CardAcceptance.For<CardTypes.Estate>(),
                    CardAcceptance.For<CardTypes.OvergrownEstate>(),
                    CardAcceptance.For<CardTypes.Hovel>(),
                    CardAcceptance.For<CardTypes.Ruins>(),
                    CardAcceptance.For<CardTypes.Copper>(),
                    CardAcceptance.For<CardTypes.Curse>());
            }

            public static ICardPicker DefaultTrashOrder()
            {
                return new CardPickByPriority(
                    CardAcceptance.For<CardTypes.Curse>(),
                    CardAcceptance.For<CardTypes.Estate>(gameState => CountOfPile<CardTypes.Province>(gameState) == 8),
                    CardAcceptance.For<CardTypes.OvergrownEstate>(),
                    CardAcceptance.For<CardTypes.Hovel>(),
                    CardAcceptance.For<CardTypes.Copper>());
            }

            public static bool ShouldBuyProvinces(GameState gameState)
            {
                return CountAllOwned<CardTypes.Gold>(gameState) > 2;
            }

            public static bool ShouldGainIllGottenGains(GameState gameState)
            {
                return CountOfPile<CardTypes.Curse>(gameState) > 0;
            }

            public static bool ShouldPlaySalvager(ICardPicker trashOrder, GameState gameState)
            {
                return HasCardFromInHand(trashOrder, gameState);
            }

            public static GameStatePredicate ShouldPlaySalvager(ICardPicker trashOrder)
            {
                return delegate(GameState gameState)
                {
                    return HasCardFromInHand(trashOrder, gameState);
                };
            }

            public static GameStatePredicate ShouldPlayLookout(GameStatePredicate shouldBuyProvinces = null)
            {
                if (shouldBuyProvinces == null)
                {
                    shouldBuyProvinces = ShouldBuyProvinces;
                }
                return delegate(GameState gameState)
                {
                    return ShouldPlayLookout(gameState, shouldBuyProvinces);
                };
            }

            public static bool ShouldPlayLookout(GameState gameState, GameStatePredicate shouldBuyProvinces)
            {
                int cardCountToTrash = CountInDeck<CardTypes.Copper>(gameState);

                if (!shouldBuyProvinces(gameState))
                {
                    cardCountToTrash += CountInDeck<CardTypes.Estate>(gameState);
                }

                cardCountToTrash += CountInDeck<CardTypes.Hovel>(gameState);
                cardCountToTrash += CountInDeck<CardTypes.Necropolis>(gameState);
                cardCountToTrash += CountInDeck<CardTypes.OvergrownEstate>(gameState);

                cardCountToTrash += CountInDeck<CardTypes.Lookout>(gameState);

                int totalCardsOwned = gameState.players.CurrentPlayer.CardsInDeck.Count();

                return ((double)cardCountToTrash) / totalCardsOwned > 0.4;
            }
        }

        public static class DoubleWarehouse
        {
            // big money smithy player
            public static PlayerAction Player(int playerNumber)
            {
                return new PlayerAction(
                            "DoubleWarehouse",
                            playerNumber,
                            purchaseOrder: PurchaseOrder(),
                            actionOrder: ActionOrder(),
                            discardOrder: DiscardOrder());
            }

            static CardPickByPriority PurchaseOrder()
            {
                return new CardPickByPriority(
                           CardAcceptance.For<CardTypes.Province>(gameState => gameState.players.CurrentPlayer.AllOwnedCards.Where(card => card is CardTypes.Gold).Count() > 2),
                           CardAcceptance.For<CardTypes.Duchy>(gameState => CountOfPile<CardTypes.Province>(gameState) < 5),
                           CardAcceptance.For<CardTypes.Estate>(gameState => CountOfPile<CardTypes.Province>(gameState) < 2),
                           CardAcceptance.For<CardTypes.Gold>(),
                           CardAcceptance.For<CardTypes.Warehouse>(gameState => gameState.players.CurrentPlayer.AllOwnedCards.Where(card => card is CardTypes.Warehouse).Count() < 1),
                           CardAcceptance.For<CardTypes.Warehouse>(gameState => gameState.players.CurrentPlayer.AllOwnedCards.Where(card => card is CardTypes.Silver).Count() > 2 &&
                                                                                gameState.players.CurrentPlayer.AllOwnedCards.Where(card => card is CardTypes.Warehouse).Count() < 2),
                           CardAcceptance.For<CardTypes.Silver>());

            }

            static CardPickByPriority ActionOrder()
            {
                return new CardPickByPriority(
                    CardAcceptance.For<CardTypes.Warehouse>());
            }

            static CardPickByPriority DiscardOrder()
            {
                return new CardPickByPriority(
                    CardAcceptance.For<CardTypes.Province>(),
                    CardAcceptance.For<CardTypes.Duchy>(),
                    CardAcceptance.For<CardTypes.Estate>(),
                    CardAcceptance.For<CardTypes.Copper>(),
                    CardAcceptance.For<CardTypes.Silver>(),
                    CardAcceptance.For<CardTypes.Warehouse>(),
                    CardAcceptance.For<CardTypes.Gold>());
            }
        }

        public static class BigMoneyDelayed
        {
            public static PlayerAction Player(int playerNumber)
            {
                return new PlayerAction(
                            "BigMoneyDelayed",
                            playerNumber,
                            purchaseOrder: PurchaseOrder());
            }

            private static CardPickByPriority PurchaseOrder()
            {
                return new CardPickByPriority(
                           CardAcceptance.For<CardTypes.Province>(gameState => CountAllOwned<CardTypes.Gold>(gameState) > 3),
                           CardAcceptance.For<CardTypes.Duchy>(gameState => CountOfPile<CardTypes.Province>(gameState) < 5),
                           CardAcceptance.For<CardTypes.Estate>(gameState => CountOfPile<CardTypes.Province>(gameState) <= 2),
                           CardAcceptance.For<CardTypes.Gold>(),
                           CardAcceptance.For<CardTypes.Estate>(gameState => CountOfPile<CardTypes.Province>(gameState) <= 2),
                           CardAcceptance.For<CardTypes.Silver>());
            }
        }
    }
}
Logged

Sparafucile

  • Thief
  • ****
  • Offline Offline
  • Posts: 98
  • Respect: +153
    • View Profile
Re: Dominulator: A flexible dominion engine simulator
« Reply #8 on: October 07, 2013, 08:45:17 pm »
0

Thanks for giving it a go.  It's not a super trivial thing to do so no worries.   I made the changes you were looking for and pushed them up to the depot.  Take a look at the changelist, and let  me know if it's not sufficient.
Logged

SCSN

  • Mountebank
  • *****
  • Offline Offline
  • Posts: 2227
  • Respect: +7140
    • View Profile
Re: Dominulator: A flexible dominion engine simulator
« Reply #9 on: October 08, 2013, 01:26:34 pm »
0

Thanks, I'll have a look at it later.

I spent quite some time today improving the new HermitMarketSquare strategy. Let me know what you think of it!

Code: [Select]
using Dominion;
using CardTypes = Dominion.CardTypes;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Program
{
    public static partial class Strategies
    {
        public static class HermitMarketSquareImproved
        {
            public static PlayerAction Player(int playerNumber)
            {
                return new MyPlayerAction(playerNumber);
            }

            class MyPlayerAction
                : PlayerAction
            {
                public MyPlayerAction(int playerNumber)
                    : base("HermitMarketSquareImproved",
                        playerNumber,
                        purchaseOrder: PurchaseOrder(),
                        actionOrder: ActionOrder(TrashOrder()),
                        trashOrder: TrashOrder())
                {
                }

                private static CardPickByPriority PurchaseOrder()
                {
                    return new CardPickByPriority(
                               CardAcceptance.For<CardTypes.Province>(),
                               CardAcceptance.For<CardTypes.Duchy>(gameState => CountAllOwned<CardTypes.Province>(gameState) > 0),
                               CardAcceptance.For<CardTypes.Estate>(gameState => CountAllOwned<CardTypes.Province>(gameState) > 0),
                               CardAcceptance.For<CardTypes.Hermit>(ShouldGainHermit),
                               CardAcceptance.For<CardTypes.MarketSquare>(ShouldGainMarketSquare));
                }

                private static CardPickByPriority ActionOrder(ICardPicker trashOrder)
                {
                    return new CardPickByPriority(
                               CardAcceptance.For<CardTypes.Madman>(ShouldPlayMadman(trashOrder)),
                               CardAcceptance.For<CardTypes.Hermit>(IsDoingMegaTurn),
                               CardAcceptance.For<CardTypes.MarketSquare>(ShouldPlayMarketSquare(trashOrder)),
                               CardAcceptance.For<CardTypes.Hermit>(gameState => CountAllOwned<CardTypes.Madman>(gameState) -
                                   CountAllOwned<CardTypes.Hermit>(gameState) < 3),
                               CardAcceptance.For<CardTypes.Hermit>(gameState => CountAllOwned<CardTypes.Province>(gameState) > 0
                               ));                                       
                }

                private static CardPickByPriority TrashOrder()
                {
                    return new CardPickByPriority(
                               CardAcceptance.For<CardTypes.Estate>(gameState => IsDoingMegaTurn(gameState) && CountAllOwned<CardTypes.Province>(gameState) == 0),                               
                               CardAcceptance.For<CardTypes.Hermit>(IsDoingMegaTurn));
                }                     
            }

            private static bool ShouldGainHermit(GameState gameState)
            {
                if (PlayBigHermit(gameState))
                    return CountHermitsEverGained(gameState) < 9 && CountAllOwned<CardTypes.MarketSquare>(gameState) == 0;

                return CountHermitsEverGained(gameState) < 7 && CountAllOwned<CardTypes.MarketSquare>(gameState) == 0;
            }

            private static bool ShouldGainMarketSquare(GameState gameState)
            {
              PlayerState currentPlayer = gameState.players.CurrentPlayer;

              //Prioritize gaining Madmen over buying Market Squares once you have three squares
              if (CountAllOwned<CardTypes.Hermit>(gameState) > CountInDeckAndDiscard<CardTypes.Hermit>(gameState) +
                  CountInHand<CardTypes.Hermit>(gameState) &&
                  currentPlayer.Hand.Count < 4 &&
                  CountAllOwned<CardTypes.MarketSquare>(gameState) > 2)
                  return false;

              return true;
            }

            private static bool PlayBigHermit(GameState gameState)
            {
                //Play the 9-Hermit version if possible
                return CountOfPile<CardTypes.Hermit>(gameState) + CountHermitsEverGained(gameState) >= 9;
            }

            private static GameStatePredicate ShouldPlayMadman(ICardPicker trashOrder)
            {
                return delegate(GameState gameState)
                {
                    PlayerState currentPlayer = gameState.players.CurrentPlayer;

                    if (!currentPlayer.Hand.HasCard<CardTypes.Madman>())
                        return false;

                    if (CountAllOwned<CardTypes.Province>(gameState) > 0)
                        return true;

                    if (ShouldStartMegaTurn(gameState))
                        return true;

                    if (IsDoingMegaTurn(gameState))
                    {
                        if (currentPlayer.CardsInDeckAndDiscard.Count() > 5)
                            return true;
                    }

                    return false;
                };
            }

            private static GameStatePredicate ShouldPlayMarketSquare(ICardPicker trashOrder)
            {
                return delegate(GameState gameState)
                {
                    var currentPlayer = gameState.players.CurrentPlayer;

                    if (!IsDoingMegaTurn(gameState))
                    {
                        return !CanTrashForGold(gameState, trashOrder);                       
                    }
                    else
                    {

                        if (ShouldPlayMadman(trashOrder)(gameState))
                            return false;

                        int numberOfProvincesCanAfford = currentPlayer.ExpectedCoinValueAtEndOfTurn / Card.Type<CardTypes.Province>().CurrentCoinCost(currentPlayer);
                        if (currentPlayer.AvailableBuys < numberOfProvincesCanAfford)
                            return true;

                        return !CanTrashForGold(gameState, trashOrder);
                    }
                };
            }

            private static bool CanTrashForGold(GameState gameState, ICardPicker trashOrder)
            {
                PlayerState currentPlayer = gameState.players.CurrentPlayer;

                return trashOrder.GetPreferredCard(gameState, c => (currentPlayer.Hand.HasCard(c) || currentPlayer.Discard.HasCard(c)) && CardTypes.Hermit.CanTrashCard(c)) != null &&
                       currentPlayer.Hand.HasCard<CardTypes.Hermit>() &&
                       currentPlayer.Hand.HasCard<CardTypes.MarketSquare>();
            }

            private static bool ShouldStartMegaTurn(GameState gameState)
            {
                PlayerState currentPlayer = gameState.players.CurrentPlayer;

                int CountMSNotInPlay = CountInDeckAndDiscard<CardTypes.MarketSquare>(gameState) +
                  CountInHand<CardTypes.MarketSquare>(gameState);

                if(PlayBigHermit(gameState))
                    return currentPlayer.Hand.Count >= 5 &&
                           currentPlayer.Hand.Where(c => c.Is<CardTypes.Madman>()).Count() >= 2 &&
                           currentPlayer.AllOwnedCards.Where(c => c.Is<CardTypes.Madman>()).Count() >= 6 &&
                           CountMSNotInPlay >= 3 &&
                           CountHermitsEverGained(gameState) >= 9;

                return currentPlayer.Hand.Count >= 5 &&
                       currentPlayer.Hand.Where(c => c.Is<CardTypes.Madman>()).Count() >= 2 &&
                       currentPlayer.AllOwnedCards.Where(c => c.Is<CardTypes.Madman>()).Count() >= 4 &&
                       CountMSNotInPlay >= 3 &&
                       CountHermitsEverGained(gameState) >= 7;
            }

            private static bool IsDoingMegaTurn(GameState gameState)
            {
                PlayerState currentPlayer = gameState.players.CurrentPlayer;

                return currentPlayer.Hand.Count > 6;
            }

            private static int CountHermitsEverGained(GameState gameState)
            {
                PlayerState currentPlayer = gameState.players.CurrentPlayer;
                return CountAllOwned<CardTypes.Hermit>(gameState) + CountAllOwned<CardTypes.Madman>(gameState);
            }
        }
    }
}
Logged

Sparafucile

  • Thief
  • ****
  • Offline Offline
  • Posts: 98
  • Respect: +153
    • View Profile
Re: Dominulator: A flexible dominion engine simulator
« Reply #10 on: October 08, 2013, 08:21:13 pm »
0

Quote
I spent quite some time today improving the new HermitMarketSquare strategy. Let me know what you think of it!

Wow.  This strategy beats all the other ones right now.  Nicely done.   I just replaced the first version of the strategy, as we don't really need two of them around.
Logged

sudgy

  • Cartographer
  • *****
  • Offline Offline
  • Posts: 3431
  • Shuffle iT Username: sudgy
  • It's pronounced "SOO-jee"
  • Respect: +2707
    • View Profile
Re: Dominulator: A flexible dominion engine simulator
« Reply #11 on: October 08, 2013, 10:04:10 pm »
0

Just looking through the code, couldn't you make it better against other strategies by telling it to trash curses and ruins whenever possible?  It won't change it much, but it could be a bit better.

Edit: For Hermit/Market Square, that is.
Logged
If you're wondering what my avatar is, watch this.

Check out my logic puzzle blog!

   Quote from: sudgy on June 31, 2011, 11:47:46 pm

SCSN

  • Mountebank
  • *****
  • Offline Offline
  • Posts: 2227
  • Respect: +7140
    • View Profile
Re: Dominulator: A flexible dominion engine simulator
« Reply #12 on: October 09, 2013, 08:11:10 am »
0

Quote
I spent quite some time today improving the new HermitMarketSquare strategy. Let me know what you think of it!

Wow.  This strategy beats all the other ones right now.  Nicely done.   I just replaced the first version of the strategy, as we don't really need two of them around.

Thanks! I'm learning quite a bit from this, particularly how much execution details matters. The first script won only 18% against RebuildMonument iirc; some tweaking increased the win % to over 50. I already thought that people tend to focus too much on strategy and too little on execution details, but damn!

While incorporating sudgy's suggestion to always trash Curses and Ruins when it can, I tested it against BigMoneyCultist but got some very weird error. I then re-downloaded the program from GitHub and extracted it to a new location, but running

Code: [Select]
        static void Main()
        {
            var stopwatch = new System.Diagnostics.Stopwatch();
            stopwatch.Start();

            ComparePlayers(Strategies.HermitMarketSquare.Player(1), Strategies.BigMoneyCultist.Player(2));
            //CompareStrategyVsAllKnownStrategies(Strategies.HermitMarketSquare.Player(1));
           
            stopwatch.Stop();

            System.Console.WriteLine("");
            System.Console.WriteLine("Elapsed Time per game: {0}us", stopwatch.ElapsedMilliseconds * 1000 / totalGameCount);
            System.Console.WriteLine("Elapsed Time per Players Turn: {0}ns", (int)((double) stopwatch.ElapsedTicks / System.Diagnostics.Stopwatch.Frequency * 1000 * 1000 * 1000 / GameState.turnTotalCount));
            Console.ReadLine();
        }

right out of the box--i.e. with HermitMarketSquare unaltered from the GitHub version--still produces this dialog box:



It outputs all the requested gamelogs correctly, but doesn't write any results to the console.
Logged

Sparafucile

  • Thief
  • ****
  • Offline Offline
  • Posts: 98
  • Respect: +153
    • View Profile
Re: Dominulator: A flexible dominion engine simulator
« Reply #13 on: October 09, 2013, 10:42:15 am »
0

Fixed the assert.  Sorry about that.
Logged

SCSN

  • Mountebank
  • *****
  • Offline Offline
  • Posts: 2227
  • Respect: +7140
    • View Profile
Re: Dominulator: A flexible dominion engine simulator
« Reply #14 on: October 09, 2013, 12:05:20 pm »
0

Thanks.

Changing the TrashOrder method to the one below makes it trash Curses and Ruins whenever it can and makes the strategy also work with Shelters.

Code: [Select]
private static CardPickByPriority TrashOrder()
{
return new CardPickByPriority(
   CardAcceptance.For<CardTypes.Curse>(),
   CardAcceptance.For<CardTypes.RuinedVillage>(),
   CardAcceptance.For<CardTypes.RuinedMarket>(),
   CardAcceptance.For<CardTypes.Survivors>(),
   CardAcceptance.For<CardTypes.RuinedLibrary>(),
   CardAcceptance.For<CardTypes.AbandonedMine>(),                               
   CardAcceptance.For<CardTypes.Necropolis>(gameState => IsDoingMegaTurn(gameState)),
   CardAcceptance.For<CardTypes.OvergrownEstate>(gameState => IsDoingMegaTurn(gameState)),
   CardAcceptance.For<CardTypes.Hovel>(gameState => IsDoingMegaTurn(gameState)),
   CardAcceptance.For<CardTypes.Estate>(gameState => IsDoingMegaTurn(gameState)),
   CardAcceptance.For<CardTypes.Hermit>(IsDoingMegaTurn));
}

It improves the win % from 87% to 95% against BigMoneyDoubleWitch, and from 70% to 90% against BigMoneyCultist. Incidentally, when I just told it to trash "Ruins" instead of spelling out each Ruins individually, it refused to trash any of them at all.
Logged

sudgy

  • Cartographer
  • *****
  • Offline Offline
  • Posts: 3431
  • Shuffle iT Username: sudgy
  • It's pronounced "SOO-jee"
  • Respect: +2707
    • View Profile
Re: Dominulator: A flexible dominion engine simulator
« Reply #15 on: October 09, 2013, 12:08:15 pm »
0

It improves the win % from 87% to 95% against BigMoneyDoubleWitch, and from 70% to 90% against BigMoneyCultist. Incidentally, when I just told it to trash "Ruins" instead of spelling out each Ruins individually, it refused to trash any of them at all.

Maybe you should have told it to trash "Ruin" ;)
Logged
If you're wondering what my avatar is, watch this.

Check out my logic puzzle blog!

   Quote from: sudgy on June 31, 2011, 11:47:46 pm

SCSN

  • Mountebank
  • *****
  • Offline Offline
  • Posts: 2227
  • Respect: +7140
    • View Profile
Re: Dominulator: A flexible dominion engine simulator
« Reply #16 on: October 09, 2013, 12:11:10 pm »
0

  • Ruins are called ruins instead of Ruin. 

:P
Logged

SCSN

  • Mountebank
  • *****
  • Offline Offline
  • Posts: 2227
  • Respect: +7140
    • View Profile
Re: Dominulator: A flexible dominion engine simulator
« Reply #17 on: October 12, 2013, 03:59:24 am »
0

I downloaded the latest version from GitHub and wanted to experiment a bit with your Remake/HT strategies, but it refuses to compile, giving:

Quote
Error   1   'Dominion.GameState' does not contain a definition for 'CurrentCardBeingPlayed' and no extension method 'CurrentCardBeingPlayed' accepting a first argument of type 'Dominion.GameState' could be found (are you missing a using directive or an assembly reference?)   C:\Google Drive\Dominion\Dominulator-master\TestProgram\Kingdoms\ShouldRemakeOrHorseTradersIntoSoothayer.cs   71

Error   2   'Dominion.GameState' does not contain a definition for 'CurrentCardBeingPlayed' and no extension method 'CurrentCardBeingPlayed' accepting a first argument of type 'Dominion.GameState' could be found (are you missing a using directive or an assembly reference?)   C:\Google Drive\Dominion\Dominulator-master\TestProgram\Kingdoms\ShouldRemakeOrHorseTradersIntoSoothayer.cs   90
Logged

Sparafucile

  • Thief
  • ****
  • Offline Offline
  • Posts: 98
  • Respect: +153
    • View Profile
Re: Dominulator: A flexible dominion engine simulator
« Reply #18 on: October 12, 2013, 12:51:03 pm »
0

Are you still getting the error around ShouldRemakeOrHorseTradersIntoSoothayer.cs?  I just compiled this morning and dont see the issue with what's latest on the head.
Logged

SCSN

  • Mountebank
  • *****
  • Offline Offline
  • Posts: 2227
  • Respect: +7140
    • View Profile
Re: Dominulator: A flexible dominion engine simulator
« Reply #19 on: October 12, 2013, 01:00:24 pm »
0

It's working with the latest download.
Logged

SCSN

  • Mountebank
  • *****
  • Offline Offline
  • Posts: 2227
  • Respect: +7140
    • View Profile
Re: Dominulator: A flexible dominion engine simulator
« Reply #20 on: October 15, 2013, 12:17:29 pm »
+1

Advisor:

Code: [Select]
    public class Advisor
        : Card
    {
        public static Advisor card = new Advisor();

        private Advisor()
            : base("Advisor", coinCost: 4, isAction: true,  plusActions:1)
        {
        }

        public override void DoSpecializedAction(PlayerState currentPlayer, GameState gameState)
        {
            currentPlayer.RevealCardsFromDeck(3);
            Card cardType = gameState.players.PlayerLeft.actions.BanCardForCurrentPlayerRevealedCards(gameState);
            if (!currentPlayer.cardsBeingRevealed.HasCard(cardType))
            {
                throw new Exception("Must ban a card currently being revealed");
            }
            currentPlayer.MoveRevealedCardToDiscard(cardType, gameState);
            currentPlayer.MoveAllRevealedCardsToHand();
        }
    }
Logged

Sparafucile

  • Thief
  • ****
  • Offline Offline
  • Posts: 98
  • Respect: +153
    • View Profile
Re: Dominulator: A flexible dominion engine simulator
« Reply #21 on: October 15, 2013, 12:40:21 pm »
0

Ty.  Advisor and Herald are now finished.
Logged

SCSN

  • Mountebank
  • *****
  • Offline Offline
  • Posts: 2227
  • Respect: +7140
    • View Profile
Re: Dominulator: A flexible dominion engine simulator
« Reply #22 on: October 15, 2013, 12:46:24 pm »
0

Ty.  Advisor and Herald are now finished.

Oh nice, I just started working on Herald and got the basic mechanics to function correctly but got the exception "With more than one card in revealed cards it's ambiguous which order to move cards on top of deck" to show up sometimes, which shouldn't occur because it always reveals one card only. I'm interested in how you did it :)

Edit: ah, I didn't include "currentPlayer.cardsBeingRevealed.RemoveCard(revealedCard);", I figured cards would be removed automatically upon being played.
« Last Edit: October 15, 2013, 12:49:33 pm by SheCantSayNo »
Logged

SCSN

  • Mountebank
  • *****
  • Offline Offline
  • Posts: 2227
  • Respect: +7140
    • View Profile
Re: Dominulator: A flexible dominion engine simulator
« Reply #23 on: October 15, 2013, 01:04:00 pm »
+1

Fortress:

Code: [Select]
    public class Fortress
      : Card
    {
        public static Fortress card = new Fortress();

        private Fortress()
            : base("Fortress", coinCost: 4, isAction: true, plusActions: 2, plusCards:1)
        {
        }

        public override void DoSpecializedTrash(PlayerState currentPlayer, GameState gameState)
        {           
            gameState.trash.RemoveCard(this);
            gameState.players.CurrentPlayer.hand.AddCard(this);
        }
    }

Edit: hmm, if your opponent trashes the Fortress with Saboteur/Knights/Rogue, he gets the card in his hand... I can't find an easy way to circumvent this, as there doesn't seem to be a way for a card to figure out its latest owner.

Btw, Saboteur is misspelled as "Sabateur" in the code.
« Last Edit: October 15, 2013, 01:28:53 pm by SheCantSayNo »
Logged

SCSN

  • Mountebank
  • *****
  • Offline Offline
  • Posts: 2227
  • Respect: +7140
    • View Profile
Re: Dominulator: A flexible dominion engine simulator
« Reply #24 on: October 15, 2013, 02:24:55 pm »
+1

Fixed Fortress:

Code: [Select]
    public class Fortress
      : Card
    {
        public static Fortress card = new Fortress();

        private Fortress()
            : base("Fortress", coinCost: 4, isAction: true, plusActions: 2, plusCards:1)
        {
        }

        public override void DoSpecializedTrash(PlayerState currentPlayer, GameState gameState)
        {           
            gameState.trash.RemoveCard(this);
            for (int i = 0; i < gameState.players.PlayerCount; i++)
            {
                if (gameState.players[i].CardsBeingRevealed.Contains(this))
                {
                    gameState.players[i].hand.AddCard(this);
                    return;
                }
            }
            currentPlayer.hand.AddCard(this);
        }
    }

It does require modifying the MoveRevealedCardToTrash method in PlayerState.cs:

Code: [Select]
internal void MoveRevealedCardToTrash(Card typeOfCard, GameState gameState)
        {
            this.MoveCardToTrash(typeOfCard, gameState);
            Card card = this.cardsBeingRevealed.RemoveCard(typeOfCard);
            if (card == null)
            {
                throw new Exception("Revealed cards did not have the specified card");
            }           
        }

It now trashes the card before removing it from the revealed cards stack, so that Fortress gets the chance to figure out which player is revealing it.
Logged

SCSN

  • Mountebank
  • *****
  • Offline Offline
  • Posts: 2227
  • Respect: +7140
    • View Profile
Re: Dominulator: A flexible dominion engine simulator
« Reply #25 on: October 15, 2013, 03:09:52 pm »
+1

Philosopher's Stone:

Code: [Select]
    public class PhilosophersStone
       : Card
    {
        public static PhilosophersStone card = new PhilosophersStone();

        private PhilosophersStone()
            : base("PhilosophersStone", coinCost: 3, potionCost:1, isTreasure: true)
        {
        }

        public override void DoSpecializedAction(PlayerState currentPlayer, GameState gameState)
        {
            int stoneValue = currentPlayer.CardsInDeckAndDiscard.Count() / 5;           
            currentPlayer.AddCoins(stoneValue);   
        }
    }
Logged

Sparafucile

  • Thief
  • ****
  • Offline Offline
  • Posts: 98
  • Respect: +153
    • View Profile
Re: Dominulator: A flexible dominion engine simulator
« Reply #26 on: October 15, 2013, 03:30:23 pm »
0

Thx for mentioning the issue you found while implementing fortress.  The solution you came up with is ok for fortress, but the problem you mentioned was actually system with all cards that had an effect on trashing.   Check out the change I pushed up.  You can see how I fixed the problem so that it would fix all cards.   I haven't actually tried the fix, so let me know if there are further issues with it.

Also, pushed up the philosophers stone implementation.
« Last Edit: October 15, 2013, 03:34:53 pm by Sparafucile »
Logged

SCSN

  • Mountebank
  • *****
  • Offline Offline
  • Posts: 2227
  • Respect: +7140
    • View Profile
Re: Dominulator: A flexible dominion engine simulator
« Reply #27 on: October 17, 2013, 05:38:33 am »
0

It seems to work fine!

I'm trying to implement the on buy effect of Mint (which the code didn't mention as missing):

Code: [Select]
public override void DoSpecializedWhenBuy(PlayerState currentPlayer, GameState gameState)
        {
            foreach (Card card in currentPlayer.cardsInPlay)
            {
                if (card.isTreasure)
                {
                    currentPlayer.cardsInPlay.RemoveCard(card);
                    currentPlayer.MoveCardToTrash(card, gameState);
                }
            }
        } 

However, this trashes only part of the treasures, and I can't figure out what's going wrong:

Quote
BigMoney ends turn with deck: Copper(7), Estate(3), Silver(2)

BigMoney begins turn
With hand: Copper,Copper,Copper,Silver,Silver,
  BigMoney Played Silver.
    +2 Coin = 2 all together.
  BigMoney Played Silver.
    +2 Coin = 4 all together.
  BigMoney Played Copper.
    +1 Coin = 5 all together.
  BigMoney Played Copper.
    +1 Coin = 6 all together.
  BigMoney Played Copper.
    +1 Coin = 7 all together.
  BigMoney bought Mint.
    BigMoney trashed Copper.
    BigMoney trashed Copper.
    BigMoney trashed Silver.
  BigMoney Discarded Copper.
  BigMoney Discarded Copper.
  BigMoney Discarded Copper.
  BigMoney Discarded Silver.
  BigMoney Discarded Silver.
  BigMoney Drew Estate into hand.
  BigMoney Drew Estate into hand.
  BigMoney Drew Estate into hand.
  BigMoney Drew Copper into hand.
  BigMoney Drew Copper into hand.
  BigMoney ends turn with deck: Copper(5), Estate(3), Mint(1), Silver(1),

(I had to comment out the content of Mint's DoSpecializedAction method to get it to work at all, as the RequestPlayerRevealCardFromHand doesn't seem to function yet.)
Logged

SCSN

  • Mountebank
  • *****
  • Offline Offline
  • Posts: 2227
  • Respect: +7140
    • View Profile
Re: Dominulator: A flexible dominion engine simulator
« Reply #28 on: October 17, 2013, 07:36:11 am »
0

I implemented Tactician but I'm not sure whether it's the right way, as I had to modify the PlayerState to keep track of the number of "activated" Tacticians (it is possible to activate more than one in some rare circumstances), as doing it via a bool within the Tactician class itself resulted in some very weird behavior when you played a Tactician a few turns in a row. I'm guessing that because it's a static class, the Tacticians don't really exist as individual copies where each could possibly contain a different value of a bool?

Code: [Select]
public class Tactician
       : Card
    {
        public static Tactician card = new Tactician();

        private Tactician()
            : base("Tactician", coinCost: 5, isAction: true, isDuration: true)
        {
        }

        public override void DoSpecializedDurationActionAtBeginningOfTurn(PlayerState currentPlayer, GameState gameState)
        {
            if (currentPlayer.ActivatedTacticians > 0)
            {
                currentPlayer.DrawAdditionalCardsIntoHand(5);
                currentPlayer.AddBuys(1);
                currentPlayer.AddActions(1);
                currentPlayer.RemoveTactician(1);
            }
        }

        public override void DoSpecializedAction(PlayerState currentPlayer, GameState gameState)
        {           
            if (currentPlayer.hand.Count() > 0)
            {
                currentPlayer.DiscardHand(gameState);
                currentPlayer.AddTactician(1);
            }
        }
    }

Additions to PlayerState.cs:

Code: [Select]
public int ActivatedTacticians { get { return this.turnCounters.ActivatedTacticians; } }

internal void AddTactician(int coinAmount)
        {
            this.turnCounters.AddTactician(this, coinAmount);
        }

        internal void RemoveTactician(int coinAmount)
        {
            this.turnCounters.RemoveTactician(this, coinAmount);
        }

Additions to PlayerTurnCounters.cs:

Code: [Select]
private int activatedTacticians = 0;

internal int ActivatedTacticians
        {
            get
            {
                return this.activatedTacticians;
            }

        }

        public void AddTactician(PlayerState playerState, int count)
        {
            if (count > 0)
            {
                this.activatedTacticians += count;
            }
        }

        internal void RemoveTactician(PlayerState playerState, int count)
        {
            this.activatedTacticians -= count;
            if (this.activatedTacticians < 0)
            {
                this.activatedTacticians = 0;
            }
        }
« Last Edit: October 17, 2013, 07:48:12 am by SheCantSayNo »
Logged

SCSN

  • Mountebank
  • *****
  • Offline Offline
  • Posts: 2227
  • Respect: +7140
    • View Profile
Re: Dominulator: A flexible dominion engine simulator
« Reply #29 on: October 17, 2013, 08:00:58 am »
0

I noticed a mistake in the Vineyard definition:

Quote
private Vineyard()
            : base("Vineyard", coinCost: 0, potionCost:1, isAction: true, victoryPoints: playerState => playerState.AllOwnedCards.Where(card => card.isAction).Count()/3)

Vineyard is not an action!
       
Logged

rrwoods

  • Navigator
  • ****
  • Offline Offline
  • Posts: 72
  • Respect: +32
    • View Profile
Re: Dominulator: A flexible dominion engine simulator
« Reply #30 on: October 17, 2013, 08:03:52 am »
0

It seems to work fine!

I'm trying to implement the on buy effect of Mint (which the code didn't mention as missing):

Code: [Select]
public override void DoSpecializedWhenBuy(PlayerState currentPlayer, GameState gameState)
        {
            foreach (Card card in currentPlayer.cardsInPlay)
            {
                if (card.isTreasure)
                {
                    currentPlayer.cardsInPlay.RemoveCard(card);
                    currentPlayer.MoveCardToTrash(card, gameState);
                }
            }
        } 

However, this trashes only part of the treasures, and I can't figure out what's going wrong:

Quote
BigMoney ends turn with deck: Copper(7), Estate(3), Silver(2)

BigMoney begins turn
With hand: Copper,Copper,Copper,Silver,Silver,
  BigMoney Played Silver.
    +2 Coin = 2 all together.
  BigMoney Played Silver.
    +2 Coin = 4 all together.
  BigMoney Played Copper.
    +1 Coin = 5 all together.
  BigMoney Played Copper.
    +1 Coin = 6 all together.
  BigMoney Played Copper.
    +1 Coin = 7 all together.
  BigMoney bought Mint.
    BigMoney trashed Copper.
    BigMoney trashed Copper.
    BigMoney trashed Silver.
  BigMoney Discarded Copper.
  BigMoney Discarded Copper.
  BigMoney Discarded Copper.
  BigMoney Discarded Silver.
  BigMoney Discarded Silver.
  BigMoney Drew Estate into hand.
  BigMoney Drew Estate into hand.
  BigMoney Drew Estate into hand.
  BigMoney Drew Copper into hand.
  BigMoney Drew Copper into hand.
  BigMoney ends turn with deck: Copper(5), Estate(3), Mint(1), Silver(1),

(I had to comment out the content of Mint's DoSpecializedAction method to get it to work at all, as the RequestPlayerRevealCardFromHand doesn't seem to function yet.)
You're removing elements from the same collection you're iterating over. Unless I'm mistaken this is basically guaranteed to result in undefined behavior.
Logged

SCSN

  • Mountebank
  • *****
  • Offline Offline
  • Posts: 2227
  • Respect: +7140
    • View Profile
Re: Dominulator: A flexible dominion engine simulator
« Reply #31 on: October 17, 2013, 08:37:14 am »
0

Copying the initial play area to a new variable and iterating over that one while removing the cards from the actual play area results in the same behavior :(
Logged

SCSN

  • Mountebank
  • *****
  • Offline Offline
  • Posts: 2227
  • Respect: +7140
    • View Profile
Re: Dominulator: A flexible dominion engine simulator
« Reply #32 on: October 17, 2013, 09:18:13 am »
0

Golem: it works but instead of asking the player in what order to play the actions, it just plays them in the order they are revealed. I'm still trying to figure out how that should be done.

Code: [Select]
public class Golem
       : Card
    {
        public static Golem card = new Golem();

        private Golem()
            : base("Golem", coinCost: 4, potionCost: 1, isAction: true)
        {
        }

        public override void DoSpecializedAction(PlayerState currentPlayer, GameState gameState)
        {
            Card actionOne, actionTwo = null;
            gameState.gameLog.PushScope();
            while (true)
            {
                actionOne = currentPlayer.DrawAndRevealOneCardFromDeck();
                if (actionOne == null)
                    break;

                if (actionOne.isAction && actionOne.name != "Golem")
                {
                    while (true)
                    {
                        actionTwo = currentPlayer.DrawAndRevealOneCardFromDeck();
                        if (actionTwo == null)
                            break;

                        if (actionTwo.isAction && actionTwo.name != "Golem")
                            break;
                    }
                    break;
                }
            }

            currentPlayer.MoveRevealedCardsToDiscard(cardToMove => !cardToMove.Equals(actionOne)
                && !cardToMove.Equals(actionTwo), gameState);
            gameState.gameLog.PopScope();

            if (actionOne != null && actionTwo != null)
            {
                //TODO: Ask player for play order
                currentPlayer.cardsBeingRevealed.RemoveCard(actionOne);
                currentPlayer.DoPlayAction(actionOne, gameState);

                currentPlayer.cardsBeingRevealed.RemoveCard(actionTwo);
                currentPlayer.DoPlayAction(actionTwo, gameState);
            }

            if (actionOne != null ^ actionTwo != null)
            {
                Card actionCard = actionOne != null ? actionOne : actionTwo;
                currentPlayer.cardsBeingRevealed.RemoveCard(actionCard);
                currentPlayer.DoPlayAction(actionCard, gameState);
            }
        }
    }
Logged

SCSN

  • Mountebank
  • *****
  • Offline Offline
  • Posts: 2227
  • Respect: +7140
    • View Profile
Re: Dominulator: A flexible dominion engine simulator
« Reply #33 on: October 17, 2013, 12:51:21 pm »
0

I don't think the Wishing Well implementation is correct:

Quote
        public override void DoSpecializedAction(PlayerState currentPlayer, GameState gameState)
        {
            Card cardType = currentPlayer.GuessCardTopOfDeck(gameState);

            Card revealedCard = currentPlayer.DrawAndRevealOneCardFromDeck();
            if (revealedCard != cardType)
            {
                currentPlayer.MoveAllRevealedCardsToHand();
            }

The "!=" should be "==".

Quote
  else
            {
                currentPlayer.MoveRevealedCardsToDiscard(gameState);
            }
        }
    }

The card shouldn't be discarded, but returned to the top of the deck.
Logged

Sparafucile

  • Thief
  • ****
  • Offline Offline
  • Posts: 98
  • Respect: +153
    • View Profile
Re: Dominulator: A flexible dominion engine simulator
« Reply #34 on: October 17, 2013, 03:53:51 pm »
+1

Thanks for all the great contributions.  I have made the following changes:

  • Fixed Vineyard to not be an action
  • Fixed Wishing Well to put card back on top of deck, and fixed the comparison from != to ==
  • Added golem implementation
  • Added tactician implmentation
  • Fixed mint to trash treasures in play when bought

The reason your mint implemenation wasn't working as expected was because you were modifying a list which is computed automatically.  CardsPlayed contains Duration cards plus cards in the play area.   You wanted to remove a card from the play area.  Sorry for the naming confusion.

I modified your tactician and golem implementations a little.  I haven't tested any of them ;)  So let me know if there are issues.
Logged

SCSN

  • Mountebank
  • *****
  • Offline Offline
  • Posts: 2227
  • Respect: +7140
    • View Profile
Re: Dominulator: A flexible dominion engine simulator
« Reply #35 on: October 18, 2013, 04:50:00 am »
0

When compiling I get the error:

Quote
Error   1   'Program.PlayerAction.ChooseCardToPlayFirst(Dominion.GameState, Dominion.Card, Dominion.Card)': no suitable method found to override   C:\Google Drive\Dominion\Dominulator-master\TestProgram\PlayerAction.cs   69

Commenting out the mentioned override makes it run, and all the cards seem to work :)
« Last Edit: October 18, 2013, 05:07:21 am by SheCantSayNo »
Logged

SCSN

  • Mountebank
  • *****
  • Offline Offline
  • Posts: 2227
  • Respect: +7140
    • View Profile
Re: Dominulator: A flexible dominion engine simulator
« Reply #36 on: October 18, 2013, 06:07:14 am »
0

Pillage: it now discards a good card from the other player's hand.

Code: [Select]
    public class Pillage
      : Card
    {
        public static Pillage card = new Pillage();

        private Pillage()
            : base("Pillage", coinCost: 5, isAction: true, attackDependsOnPlayerChoice: true, requiresSpoils:true)
        {
        }

        public override void DoSpecializedAction(PlayerState currentPlayer, GameState gameState)
        {           
            currentPlayer.MoveCardFromPlayToTrash(gameState);

            PlayerState.AttackAction attackAction = delegate(PlayerState currentPlayer2, PlayerState otherPlayer, GameState gameState2)
            {
                if (otherPlayer.Hand.Count >= 5)
                {
                    otherPlayer.RevealHand();
                    Card cardType = currentPlayer2.actions.GetCardFromOtherPlayersHandToDiscard(gameState2, otherPlayer);                   
                    if (!otherPlayer.Hand.HasCard(cardType))
                    {
                        throw new Exception("Must discard a card from the revealed hand");
                    }
                    otherPlayer.DiscardCardFromHand(gameState2, cardType);
                }
            };
            currentPlayer.AttackOtherPlayers(gameState, attackAction);

            currentPlayer.GainCardsFromSupply(gameState, Cards.Spoils, 2);
        }       
    }
Logged

SCSN

  • Mountebank
  • *****
  • Offline Offline
  • Posts: 2227
  • Respect: +7140
    • View Profile
Re: Dominulator: A flexible dominion engine simulator
« Reply #37 on: October 18, 2013, 11:17:48 am »
0

Treasury: you're now able to top-deck it if no victory card was bought.

Code: [Select]
public class Treasury
       : Card
    {
        public static Treasury card = new Treasury();

        private Treasury()
            : base("Treasury", coinCost: 5, isAction: true, plusActions: 1, plusCards: 1, plusCoins: 1)
        {
            this.doSpecializedCleanupAtStartOfCleanup = DoSpecializedCleanupAtStartOfCleanup;
        }

        private new void DoSpecializedCleanupAtStartOfCleanup(PlayerState currentPlayer, GameState gameState)
        {
            if (!currentPlayer.CardsBoughtThisTurn.AnyWhere(card => card.isVictory))
            {
                currentPlayer.RequestPlayerTopDeckCardsFromPlay(gameState,
                acceptableCard => acceptableCard == Treasury.card,
                isOptional: true);
            }           
        }
    }

Treasury and Smugglers require the following additions to PlayerTurnCounters.cs:

Quote
internal SetOfCards cardsBannedFromPurchase;
   internal SetOfCards cardsBoughtThisTurn;
        internal SetOfCards cardsGainedThisTurn;

        internal int copperAdditionalValue = 0;

        internal PlayerTurnCounters(CardGameSubset gameSubset)
        {
            cardsBannedFromPurchase = new SetOfCards(gameSubset);
            cardsBoughtThisTurn = new SetOfCards(gameSubset);
            cardsGainedThisTurn = new SetOfCards(gameSubset);

        }

        internal void InitializeTurn()
        {
            this.availableActionCount = 1;
            this.availableBuys = 1;
            this.buysUsed = 0;
            this.availableCoins = 0;
            this.availablePotions = 0;
            this.copperAdditionalValue = 0;
            this.cardsBannedFromPurchase.Clear();
            this.cardsBoughtThisTurn.Clear();
            this.cardsGainedThisTurn.Clear();

        }

PlayerState.cs:

Code: [Select]
        public SetOfCards CardsBoughtThisTurn { get { return this.turnCounters.cardsBoughtThisTurn; } }
        public SetOfCards CardsGainedThisTurn { get { return this.turnCounters.cardsGainedThisTurn; } }

To the method "internal void GainCard" in PlayerState.cs:

           
Quote
if (gainReason == GainReason.Buy)
            {
                card.DoSpecializedWhenBuy(this, gameState);
                this.turnCounters.cardsBoughtThisTurn.Add(card);
            }

            if (this == gameState.players.CurrentPlayer)
            {
                this.turnCounters.cardsGainedThisTurn.Add(card);
            }
Logged

SCSN

  • Mountebank
  • *****
  • Offline Offline
  • Posts: 2227
  • Respect: +7140
    • View Profile
Re: Dominulator: A flexible dominion engine simulator
« Reply #38 on: October 18, 2013, 11:23:29 am »
0

Smugglers:

Code: [Select]
    public class Smugglers
       : Card
    {
        public static Smugglers card = new Smugglers();

        private Smugglers()
            : base("Smugglers", coinCost: 3, isAction: true)
        {
        }

        public override void DoSpecializedAction(PlayerState currentPlayer, GameState gameState)
        {           
            Card smuggledCard = null;

            var smuggleTargets = gameState.players.PlayerRight.CardsGainedThisTurn.Where(card =>
                card.CurrentCoinCost(currentPlayer) <= 6 && card.potionCost == 0 && gameState.CardGameSubset.HasCard(card));

            if (smuggleTargets.Count() == 1)
            {
                smuggledCard = currentPlayer.GainCardFromSupply(gameState, smuggleTargets.First());               
            }

            if (smuggleTargets.Count() > 1)
            {
                smuggledCard = currentPlayer.RequestPlayerGainCardFromSupply(gameState, card => smuggleTargets.Contains(card),
                    "Choose a card to smuggle", isOptional: false);
            }
        }
    }

I discovered a very weird bug with Smugglers, though. When I circumvent the NotImplemented exception by letting it always smuggle the first card of smuggleTargets and I run a BigMoney variant that buys Smugglers against RebuildAdvanced, I get a NotImplemented exception originating from the RebuildAdvanced strategy(!?) when it plays a Rebuild the turn after the Smugglers player could potentially choose between multiple cards to Smuggle, even though the smuggling itself went completely fine.
Logged

Sparafucile

  • Thief
  • ****
  • Offline Offline
  • Posts: 98
  • Respect: +153
    • View Profile
Re: Dominulator: A flexible dominion engine simulator
« Reply #39 on: October 18, 2013, 01:26:32 pm »
0

Thx for the implementations.  We are getting close to first pass completion of the cards!

  • Modified pillage to remove the todo  (the change was a lot simpler actually ;) )
  • Submitted implementation for smugglers and treasury

Quote
I run a BigMoney variant that buys Smugglers against RebuildAdvanced, I get a NotImplemented exception

The bigmoney variant that plays with a single card has very default action order.  It plays whatever actions is has.   When playing smugglers vs rebuild, the smugglers player is picking up a rebuild card.    However, we haven't implemented a default "NameACard" method in PlayerAction.  Every strategy that knew it was going to obtain a rebuild card has a customized "NameACard" method avoiding the exception.     

I think our eventual goal should be that for every possible decision a player might make, the default strategy does something not too bad.  (similar to Geronimos approach).  Players can customize specifics if they want to achieve better.   Right now, we don't have defaults for everything (like Rebuild NameACard), which can cause these exceptions if a specific method isnt implemented.
Logged

SCSN

  • Mountebank
  • *****
  • Offline Offline
  • Posts: 2227
  • Respect: +7140
    • View Profile
Re: Dominulator: A flexible dominion engine simulator
« Reply #40 on: October 20, 2013, 12:19:12 pm »
0

Thx for the implementations. We are getting close to first pass completion of the cards!

Yay!

I've tried to make a start with Band of Misfits. My idea was to request a supply card "misfitTarget" that is both an action and costs less than this.CurrentCoinCost, and then to copy all it's properties (e.g. this.isDuration = misfitTarget.isDuration, this.isAttack = misfitTarget.isAttack; there should be a way to do all these at once? Maybe just "this = misfitTarget;"?) and then to reset them upon the card leaving play, but the two main problems with that are:

1) All those properties are currently set to be read-only, and I don't want to make any non-minimal changes to the game-engine until I feel I have completely mastered how it works, and certainly not without first discussing them with you.

2) I'm still not sure whether the cards are actually instantiated, and if they are not any change applied to one copy of Band of Misfits will be applied to all of them, which is certainly not what you want.

Quote
The bigmoney variant that plays with a single card has very default action order.  It plays whatever actions is has.   When playing smugglers vs rebuild, the smugglers player is picking up a rebuild card.    However, we haven't implemented a default "NameACard" method in PlayerAction.  Every strategy that knew it was going to obtain a rebuild card has a customized "NameACard" method avoiding the exception.     

I think our eventual goal should be that for every possible decision a player might make, the default strategy does something not too bad.  (similar to Geronimos approach).  Players can customize specifics if they want to achieve better.   Right now, we don't have defaults for everything (like Rebuild NameACard), which can cause these exceptions if a specific method isnt implemented.

Ah yes, good. Right now I think different cards (like Rebuild and Doctor) use the same NameACard method, so that if you want to use both cards in a strategy you have to write one override and make its choices conditional on which card is currently being played, which is very cumbersome and it shouldn't be too hard  to just make card-specific methods with card-specific defaults. If you can design and implement a proper structure for this (preferably with at least one example), I'm more than happy to implement a bunch of reasonable defaults (like the one from RebuildAdvanced, which I think is about as close to optimal as possible on this level of specification).
Logged

flies

  • Minion
  • *****
  • Offline Offline
  • Posts: 629
  • Shuffle iT Username: flies
  • Statistical mechanics of hard rods on a 1D lattice
  • Respect: +348
    • View Profile
    • ask the atheists
Re: Dominulator: A flexible dominion engine simulator
« Reply #41 on: October 20, 2013, 02:10:02 pm »
0

I've tried to make a start with Band of Misfits. My idea was to request a supply card "misfitTarget" that is both an action and costs less than this.CurrentCoinCost, and then to copy all it's properties (e.g. this.isDuration = misfitTarget.isDuration, this.isAttack = misfitTarget.isAttack; there should be a way to do all these at once? Maybe just "this = misfitTarget;"?) and then to reset them upon the card leaving play, but the two main problems with that are:

1) All those properties are currently set to be read-only, and I don't want to make any non-minimal changes to the game-engine until I feel I have completely mastered how it works, and certainly not without first discussing them with you.

2) I'm still not sure whether the cards are actually instantiated, and if they are not any change applied to one copy of Band of Misfits will be applied to all of them, which is certainly not what you want.
can you create a new card (the copied card) and play that, then have BoM trashdelete it when it leaves play?  This effectively puts two cards in play, which might create weird behavior in some cases?  You'd have to do things like check if the spawned card has been trashed and then trash BoM in that case....
« Last Edit: October 20, 2013, 02:44:15 pm by flies »
Logged
Gotta be efficient when most of your hand coordination is spent trying to apply mascara to your beard.
flies Dominionates on youtube

Sparafucile

  • Thief
  • ****
  • Offline Offline
  • Posts: 98
  • Respect: +153
    • View Profile
Re: Dominulator: A flexible dominion engine simulator
« Reply #42 on: October 20, 2013, 06:29:51 pm »
0

Cards are immutable.  You can't change them by design.   This does cause some inconveniences for Band of Misfits.   I put in a beginning skeleton of how we can handle this card.   The idea is that while the card is in play, I am passing the band of misfits around, but also the card to play it as.  This covers common usage of the card and I believe covers the case like Feast where the card trashes itself.   It doesn't cover a lot of other edge cases though - ssee the comments on Band Of Misfits.    Similar techniques should be able to be worked in to cover the remaining edge cases.  We should be able to pass around the effect to be used during cleanup etc ... just haven't gotten around to it yet.

We can consider having different NameACard methods.  (So it's easier to tell the reason you are naming a card).   For now, I would just check in the NameACard method which card is in play, and act appropriately.  We can factor this method out later ...
Logged

SCSN

  • Mountebank
  • *****
  • Offline Offline
  • Posts: 2227
  • Respect: +7140
    • View Profile
Re: Dominulator: A flexible dominion engine simulator
« Reply #43 on: October 20, 2013, 09:48:09 pm »
0

Quote
HermitMarketSquare begins turn
With hand: Copper,Estate,Hermit,Hermit,Hermit,
  HermitMarketSquare Played Hermit.
    HermitMarketSquare gained Hermit.
  HermitMarketSquare Played Copper.
    +1 Coin = 1 all together.
  HermitMarketSquare Discarded Copper.
  HermitMarketSquare Discarded Hermit.
    HermitMarketSquare trashed Hermit.
    HermitMarketSquare gained Duchy.
      ... and placed card on top of deck

  HermitMarketSquare Discarded Estate.
  HermitMarketSquare Discarded Hermit.
  HermitMarketSquare Discarded Hermit.
  HermitMarketSquare Drew Duchy into hand.
  HermitMarketSquare Drew Estate into hand.
  HermitMarketSquare Drew Hermit into hand.
  HermitMarketSquare Drew Copper into hand.
  HermitMarketSquare Drew Hermit into hand.
  HermitMarketSquare ends turn with deck: Copper(7), Duchy(1), Estate(3), Hermit(6)

Code: [Select]
public override void DoSpecializedDiscardFromPlay(PlayerState currentPlayer, GameState gameState)
        {           
            if (currentPlayer.turnCounters.BuysUsed == 0)
            {               
                currentPlayer.MoveCardBeingDiscardedToTrash(gameState);
                currentPlayer.RequestPlayerGainCardFromSupply(gameState,
                    card => card == Cards.Duchy || card.IsType(Cards.Prize),
                    "Must either gain a duchy or a prize",
                    isOptional:false,
                    defaultLocation:DeckPlacement.TopOfDeck);           
            }           
        }

Oops ;)
Logged

Sparafucile

  • Thief
  • ****
  • Offline Offline
  • Posts: 98
  • Respect: +153
    • View Profile
Re: Dominulator: A flexible dominion engine simulator
« Reply #44 on: October 21, 2013, 02:48:14 am »
0

Oops indeed :)   I think I snuck A regression on hermit when I was doing tournament.  No idea how I did that.  Kinda wrecked the hermit/market square strategy didn't it ...
Logged

Sparafucile

  • Thief
  • ****
  • Offline Offline
  • Posts: 98
  • Respect: +153
    • View Profile
Re: Dominulator: A flexible dominion engine simulator
« Reply #45 on: October 23, 2013, 07:40:44 pm »
+1

Now graphing some data!.   The compare players method has the option of creating an HTML report which will show a graphical report of the performance of the 2 strategies compared.

Check out the attachment.

More visualizations to come later!
Logged

Sparafucile

  • Thief
  • ****
  • Offline Offline
  • Posts: 98
  • Respect: +153
    • View Profile
Re: Dominulator: A flexible dominion engine simulator
« Reply #46 on: September 12, 2014, 09:55:13 am »
+1

I have recently gone back to work full time, so I am not spending nearly as much time on the simulator.   I still have time to implement any strategies/functionality and/or integrate any changes people may have.

I haven't pushed any changes in a while because I've been busy refactoring the code for clarity.    I just pushed those changes up to github now.  I have also updated the readme.md.  Please check it out.   There's a lot more functionality now than when I first released the simulator :)  You may find the webapp interesting.

Happy simulating.
Logged
Pages: 1 2 [All]
 

Page created in 0.141 seconds with 20 queries.