Dominion Strategy Forum

Please login or register.

Login with username, password and session length

Show Posts

This section allows you to view all posts made by this member. Note that you can only see posts made in areas you currently have access to.

Messages - Sparafucile

Filter to certain boards:

Pages: 1 2 3 [4]
76
Simulation / Dominulator: A flexible dominion engine simulator
« on: October 04, 2013, 02:00:42 pm »
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.

77
Wow.  Your advanced rebuild strategy is quite detailed.     Thanks for the additional strategies and comparisons.  I'm glad you are having fun playing with the simulator.  You seem to have picked it up quite quickly.  I will see  - I may start a dedicated post to my simulator.

I checked in the strategies you authored.   I took the liberty of formatting the code, and refactoring for reuse.  Please pull from the head of the GIT repository.  Let me know if I missed anything when reorganzing.

Quote
I'd very much like to specify both the starting decks for each player and the supply piles. As a concrete example: I want to be able to let both players start with 2 Rebuilds and 4 Duchies in their deck (and 0 Duchies left in the supply), but one of the players has an Estate whereas the other has none.

I also made a submission to demonstrate how you can tweak the starting decks of the players.  I took your example, and gave each player 5 silvers and 2 golds.  That seems to be roughly what RebuildAdvanced gets in practice.

Looks like starting with an estate is a liability once all the duchies are gone:

42.9% win RebuildAdvanced with a starting estate
54.4% win RebuildAdvanced with only duchies

Here's how I setup assymettric starting decks:
Code: [Select]
static void Main()
        {
            ComparePlayers(Strategies.RebuildAdvanced.Player(1), Strategies.RebuildAdvanced.Player(2), startingDeckPerPlayer: StartingDecksForRebuildWithEstateAdvantage.StartingDecks);           
        }

        class StartingDecksForRebuildWithEstateAdvantage
            : StartingDeckBuilder
        {
            static new public IEnumerable<CardCountPair>[] StartingDecks
            {
                get
                {
                    return StartingDecks(
                       StartingDeck(
                            CardWithCount<CardTypes.Copper>(7),
                            CardWithCount<CardTypes.Estate>(1),
                            CardWithCount<CardTypes.Silver>(5),
                            CardWithCount<CardTypes.Gold>(2),
                            CardWithCount<CardTypes.Rebuild>(2),
                            CardWithCount<CardTypes.Duchy>(4)),
                       StartingDeck(
                            CardWithCount<CardTypes.Copper>(7),
                            CardWithCount<CardTypes.Silver>(5),
                            CardWithCount<CardTypes.Gold>(2),
                            CardWithCount<CardTypes.Rebuild>(2),
                            CardWithCount<CardTypes.Duchy>(4))
                            );
                }
            }
        }

78
Simulation / Re: How much difference the shelters can make?
« on: October 03, 2013, 06:54:54 pm »
Quote
This bit doesn't seem right, forager being non-terminal and all

Good point.  Guess I wasn't thinking when I wrote that.  I will update and remove that part.

79
Quote
fucks things up even more. How can I access the score from within the PurchaseOrder() and ActionOrder() methods?

Kudos to you for braving this on your own with documentation.   Let me help a little.  There is no user level way of interacting with the simulator.  Just programmatic right now. 

The design of the simulator I'm writing is modular.   The game engine itself makes no AI decisions.  It just enforces the rules of the game.  You plug into the game engine players that implement IPlayerAction interface.  Check it out.  There's a lot of methods in there, and the list is changing and growing as I am refining the game engine.   I have implemented one generic AI, that implements IPlayerAction.  It's the PlayerAction class.    This class mirrors the behavior of other simulators you may be familar with.  You plug in a purchase order, an action order, a discard order, etc, and the PlayerAction instance translates this answer the questions the game engine needs.   The question you are looking at, is what do these Purchase order and discard order things look like.   Well, those implement the ICardPicker interface.  It looks like this:

public interface ICardPicker
    {
        int AmountWillingtoOverPayFor(Card card, GameState gameState);
        Type GetPreferredCard(GameState gameState, CardPredicate cardPredicate);
        IEnumerable<Card> GetNeededCards();
    }

There's 2 implemenations right now - CardPickByBuildOrder and CardPickByPriority, and combinations thereof.   All the strategies I have coded up so far use this approach.   If you want to create your own way of picking cards that doesnt fit in this category, you must implement ICardPicker.  The most important method is GetPreferredCard.   The gamestate (mentioned above) is passed in.   All this is background to help you understand what's going on.

Sounds like what you want to do is something like this:

(look for this text below:  gameState.players.CurrentPlayer.TotalScore() < 50)

Code: [Select]
private static CardPickByPriority PurchaseOrder()
            {
                return new CardPickByPriority(
                           CardAcceptance.For<CardTypes.Province>(),
                           CardAcceptance.For<CardTypes.Duchy>(gameState => CountAllOwned<CardTypes.Estate>(gameState) < CountAllOwned<CardTypes.Rebuild>(gameState)),
                           CardAcceptance.For<CardTypes.Rebuild>(gameState => CountAllOwned<CardTypes.Rebuild>(gameState) < 3 && gameState.players.CurrentPlayer.TotalScore() < 50),
                           CardAcceptance.For<CardTypes.Gold>(),                           
                           CardAcceptance.For<CardTypes.Silver>());
            }


80
Quote
how do I get the score, or rather the difference between my #VP and that of my opponent? Quite a number of decisions depend on that

If you need your score mid game, use gameState.players.CurrentPlayer.TotalScore().   If you are vs 1 opponent, the other opponents score is: gameState.players.OtherPlayers.First().TotalScore()

If you are looking for the end game score, look at the Program.ComparePlayers method.   It extracts the score for the two players and shows you how you can do it.   If you pass in showDistribution:true to the method, it will print out a display like this:

Player 1 Score Delta distribution
=================================
24 points:   0.1% = 1
22 points:   0.2% = 2
20 points:   0.2% = 2
18 points:   0.5% = 5
17 points:   0.2% = 2
16 points:   0.5% = 5
15 points:   0.1% = 1
14 points:   0.7% = 7
...
5 points:   2.4% = 24
4 points:   4.1% = 41
3 points:   5.5% = 55
2 points:   6.5% = 65
1 points:   4.5% = 45
0 points:   12.3% = 123
-1 points:   3.6% = 36
-2 points:   5.2% = 52
-3 points:   4.4% = 44
-4 points:   4.7% = 47
-5 points:   3.1% = 31
....
-10 points:   2.2% = 22
-18 points:   0.3% = 3
-20 points:   0.1% = 1
-22 points:   0.1% = 1

81
Simulation / Re: Simulating challenge: beat Smithy
« on: October 02, 2013, 11:02:16 pm »
Old topic, but a simple solution.

Using the same bot, but modifying it to buy double council room instead of smithy results in a 49.7% win for the double council room, 43.6% win for smithy and 6.7% tie.   

tl;dr  Double Council room beats this single smithy bot slightly

82
Simulation / Re: How much difference the shelters can make?
« on: October 02, 2013, 10:13:48 pm »
Quote
Have you found similar results for other matchups?

In my experience playing dominion, not yet.  That's what inspired this post :)   

Though I havent been looking out for it either.   I think what makes this strategy unique is that:

1) It's not very strong in the first place.  By that, I mean it may beat big money, but not by a land slide, probably just a few points.  Maybe difference of winning the province split.
2) It can benefit from the hovel more than most due to the great hall synergy.
3) It has trashing that is integral, but not trash for benefit.

I did just run a simulation where I compared BigMoney with a mirror match modified in one slight way.  The opponent buys a great hall as soon as possible if a hovel is in hand.   The single-great-hall buying strategy wins 64% of the time.   (Buying a single estate instead of a great-hall actually makes it lose with a 40% win rate).

My rule of thumb now is, if great-hall is on the board, by hovel as soon as possible.

83
Simulation / Re: How much difference the shelters can make?
« on: October 02, 2013, 07:13:35 pm »
Quote
Hovel to be trashed long before you're buying your first Province, so I don't expect Hovel's reaction to end up mattering at all.

The reaction is triggered on a very early great hall buy.   That's a key part of the strategy!  Buy end of turn 4 you have probably trashed 2 cards.  One to forager, and one by purchasing great hall.

Just tried your suggestions.

Buying a second forager  (opening Armory, forager, forager) and holding out for the first double province buy causes the win rate to go to 39%.

Holding out for a double province buy, but using a single forager is a 75% win rate.

Double forager, but picking up provinces whenever possible is a 63% win rate.

Single forager, and pick up province whenever possible is the highest win rate.  My intuition was similar to yours in real life.  When i played this game for real, I went single forager, but held out for a double province buy - because that felt cooler.  Turns out that picking up the province ASAP is a little better vs big money.

84
Apologies its not more friendly to setup. 

For whatever reason, the test program is not setup as the startup project.  In solution explorer, find the test program, right click on it, and set as startup project.

The output of the program is to the console - so set a break point on the last line of the main function to see the output before the window auto closes.

Also, there should be 100 result files in the TestProgram\Results directory.  (it runs 1000 times per simulation by default, but u dont need 100 logs do u?)

Is it possible to submit future technical troubles through the GIT website, so as not to bombard this forum with technical help?

There is a rebuild strategy I already coded that should get u started.

The main program has a method called ComparePlayers that takes 2 strategies as parameters. You can specify how  many iterations to run there.


85
Simulation / How much difference the shelters can make?
« on: October 02, 2013, 06:52:04 pm »
I played an interesting game the other day, and got me thinking, how much difference do the shelters really make?  Well, with this combination of cards, it really isnt better than big money with a 53% win rate.  With shelters, it has a 89% win rate.  Quite the difference.  Lets take a closer look what's going on:

The cards for the combo are Armory, Conspirator, Forager and Great hall.

The strategy is simple.  Open Armory to gain cards, and forager to trash cards.  Gain great halls and conspirators in a 1:1 ratio until you have enough money to buy provinces.

The interesting thing is how this strategy performs with shelters and without shelters:

89.1% win rate again Big money with shelters
53.9% win rate again Big money with estates

Lets take a closer look at each shelter and what effect is having. 

Hovel
If we pretend that the hovel card does not have a reaction - i.e. it can no longer trash itself on gaining a victory.  That brings the win rate down to 74%.   That's 15% win rate just for the reaction!

Necropolis
The default strategy actually attempts to keep the necroplis.  The +2 actions are good for starting off the conspirator chain, which we like.  It also allows us to play the armory for conspirator, top decking it, and then still have actiosn left to draw it for play this turn.  If we trash the necropolis after the overgrown estate, then the win rate goes down to 83%.   

Overgrown estate
The +1 card from trashing this card is very minimal.   1% difference in win rate.

Opportunity cost of points vs Shelters
One of the benefits of estates over shelters is they have victory points.   If our combo is trashing the starting estates, that gives big money a 3 point lead that must be overcome.  A shelter game typically favors the trashing engine.   If we pretend the shelters each worth 1 point, the win rate for the combo is down to 83%.   

In summary:
  • 15% comes from hovel
  • 1% comes from overgrown estate
  • 5% comes from necropolis
  • 5% comes from opportunity cost of victory points
So there you have it.  The hovel reaction actually had the largest impact on win rate.  They are not addiditive effects, in combination this produces 36% win rate difference for this strategy.

The above analysis is for only one strategy, your mileage will vary.   This set of cards is particularly sensitive to the bonus trashing from hovel.   Dont ignore the impact of shelters!

Here's the code for the strategy:

Code: [Select]
private static CardPickByPriority PurchaseOrder()
{
    return new CardPickByPriority(
                CardAcceptance.For<CardTypes.Armory>(gameState => CountAllOwned<CardTypes.Armory>(gameState) < 1),
                CardAcceptance.For<CardTypes.Forager>(gameState => CountAllOwned<CardTypes.Forager>(gameState) < 1),
                CardAcceptance.For<CardTypes.Province>(),
                CardAcceptance.For<CardTypes.Duchy>(gameState => CountOfPile<CardTypes.Province>(gameState) <= 1),                           
                CardAcceptance.For<CardTypes.Estate>(gameState => CountOfPile<CardTypes.Province>(gameState) == 0),
                CardAcceptance.For<CardTypes.GreatHall>(gameState => gameState.players.CurrentPlayer.AvailableBuys > 1 && gameState.players.CurrentPlayer.AvailableCoins == 6),
                CardAcceptance.For<CardTypes.GreatHall>(gameState => gameState.players.CurrentPlayer.Hand.HasCard<CardTypes.Hovel>()),
                CardAcceptance.For<CardTypes.GreatHall>(gameState => CountAllOwned<CardTypes.GreatHall>(gameState) < CountAllOwned<CardTypes.Conspirator>(gameState)),
                CardAcceptance.For<CardTypes.Conspirator>(),
                CardAcceptance.For<CardTypes.GreatHall>());
}

private static CardPickByPriority ActionOrder()
{
    return new CardPickByPriority(
                CardAcceptance.For<CardTypes.Necropolis>(),
                CardAcceptance.For<CardTypes.Armory>(gameState => gameState.players.CurrentPlayer.AvailableActions > 0),                           
                CardAcceptance.For<CardTypes.GreatHall>(),
                CardAcceptance.For<CardTypes.Conspirator>(gameState => gameState.players.CurrentPlayer.CountCardsPlayedThisTurn >= 2),
                CardAcceptance.For<CardTypes.Forager>(gameState => HasCardFromInHand(TrashOrder(), gameState)),                           
                CardAcceptance.For<CardTypes.Conspirator>(),
                CardAcceptance.For<CardTypes.Armory>());
}

private static CardPickByPriority TrashOrder()
{
    return new CardPickByPriority(                           
                CardAcceptance.For<CardTypes.OvergrownEstate>(),
                CardAcceptance.For<CardTypes.Estate>(),                           
                CardAcceptance.For<CardTypes.Necropolis>(),
                CardAcceptance.For<CardTypes.Copper>(),
                CardAcceptance.For<CardTypes.Hovel>());
}

86
[What simulator is that?]

One I've been working on.  Haven't publicized the code much yet.

If u  install visual studio express, u can find the code here:  https://github.com/NathanTeeuwen/Dominion

87
Dominion General Discussion / Re: Is this a big money board?
« on: October 02, 2013, 01:46:45 pm »
Quote
The engine completely obliterates any big money approach here, to the point that I'd be stunned if it wouldn't win 100% of the time.

Confirmed via simulation that you can win quite easily over 95% of the time vs big money using the approach outlined above.   If you are interested in the details, please visit us in the simulation forum:

http://forum.dominionstrategy.com/index.php?topic=9554.0

88
This topic was inspired by the following forum post, but this forum seems more appropriate to the discussion about simulation.  Please see this topic for discussion:

http://forum.dominionstrategy.com/index.php?topic=9456.0

the tl;dr is that you can beat big money over 95% of the time quite easily with the cards in that post.

It took a bit to get the simulation working, but a tuned engine handidly can beat big money over 96% of the time on a board with Rats, Watchtower, Armory, Wandering Minstrell amd Bridge.   It also beats many many other strategies I have tried it against.   When comparing against BigMoney, here's some interesting things I learned.

For an opening build order, my initial guess was ok, but not optimal.
  • Initial guess: Armory, Silver, WanderingMinstrel, Watchtower, Rats, Watchtower:   89% win rate
  • Rats, Watchtower, Watchtower, Armory: 93.5% win rate
  • Rats, Watchtower, Armory, Watchtower: 95.3% win rate
  • Armory, Watchtower, Rats, Watchtower: 96.7% win rate

The quickest build seems to favor getting Watchtower/Rats fairly early for maximal trashing.

I confirmed the suspicion mentioned in the other post that adding a jester actually hurts vs big money.  Adding a jester brings the win rate down to 82%.
What effect does the armory have on the strategy?  If you ignore the armory, your winrate goes down to 82%.
Dont want to get the rats for the trashing?  Your win rate goes down to 85%
Wandering minstrell allows you to order the actions placed on top of the deck.  Just placing the cards randomly results in a 83.9%.  Using a relatively naive ordering, achieved the 96% win rate.
What effect does shelters have?  If you play with estates instead of shelters, win rate goes down to 90%.
3 watchtowers seems to be the right number.  After all, u need a wandering minstrell for every watchtower you play to keep chaining...

Furthermore, a tuned engine with these cards can beat out otherwise simple but strong contenders:
  • 77.6% win vs DoubleWitch
  • 72.6% win vs DoubleJack
It can go toe to toe with interesting engine combinations, for example:
  • 60.4% win vs EmbassyTunnelSpiceMerchantPlaza
  • 53.7% win vs FishingVillageChapelPoorHouse
  • 52.3% win vs Rebuild
It can even take some games off of a deck tuned to beat big money 99.89% of the time.  (though the strategies aren't tuned against each other)
  • 33.8% win vs GardensBeggarIronworks

Here's the complete strategy, using the simulator I've been playing around with.

Code: [Select]
private static ICardPicker PurchaseOrder()
{
    var highPriority = new CardPickByPriority(
            CardAcceptance.For<CardTypes.Province>(gameState => gameState.players.CurrentPlayer.AvailableBuys >= 4 || CountAllOwned<CardTypes.Province>(gameState) > 0),
            CardAcceptance.For<CardTypes.Duchy>(gameState => CountAllOwned<CardTypes.Province>(gameState) >= 3),
            CardAcceptance.For<CardTypes.Estate>(gameState => CountOfPile<CardTypes.Province>(gameState) <= 1));
                               
    var buildOrder = new CardPickByBuildOrder(
        new CardTypes.Armory(),
        new CardTypes.Watchtower(),                   
        new CardTypes.Rats(),                   
        new CardTypes.Watchtower());
               

    var lowPriority = new CardPickByPriority(
            CardAcceptance.For<CardTypes.Bridge>(ShouldBuyBridge),                       
            CardAcceptance.For<CardTypes.WanderingMinstrell>(),                       
            CardAcceptance.For<CardTypes.Watchtower>(gameState => CountAllOwned<CardTypes.Watchtower>(gameState) < 3));

    return new CardPickConcatenator(highPriority, buildOrder, lowPriority);
}

private static bool ShouldBuyBridge(GameState gameState)
{
    return CountAllOwned<CardTypes.WanderingMinstrell>(gameState) > CountAllOwned<CardTypes.Bridge>(gameState) + CountAllOwned<CardTypes.Watchtower>(gameState) + 1;
}

private static bool ShouldPlayArmory(GameState gameState)
{
    return CountAllOwned<CardTypes.Bridge>(gameState) < 5;
}

private static bool CanPlayTerminalWhileChaining(GameState gameState)
{
    return gameState.players.CurrentPlayer.AvailableActions >= 1;
}

private static bool CanPlay2TerminalsWhileChaining(GameState gameState)
{
    return gameState.players.CurrentPlayer.AvailableActions >= 2;
}

private static bool WillRatsComboWork(GameState gameState)
{
    return HasCardInHand<CardTypes.Watchtower>(gameState) &&
            HasCardInHand<CardTypes.Rats>(gameState) &&
            HasCardFromInHand(TrashOrderWithoutRats(), gameState);
}

private static CardPickByPriority ActionOrder()
{
    return new CardPickByPriority(
                CardAcceptance.For<CardTypes.Armory>(gameState => CanPlay2TerminalsWhileChaining(gameState) && ShouldPlayArmory(gameState)),
                CardAcceptance.For<CardTypes.Jester>(gameState => CanPlay2TerminalsWhileChaining(gameState)),
                CardAcceptance.For<CardTypes.Bridge>(gameState => CanPlay2TerminalsWhileChaining(gameState)),
                CardAcceptance.For<CardTypes.Watchtower>(gameState => CanPlayTerminalWhileChaining(gameState) && !WillRatsComboWork(gameState) && gameState.players.CurrentPlayer.Hand.Count() <= 5),
                CardAcceptance.For<CardTypes.Rats>(gameState => WillRatsComboWork(gameState)),
                CardAcceptance.For<CardTypes.WanderingMinstrell>(),
                CardAcceptance.For<CardTypes.Necropolis>(),
                CardAcceptance.For<CardTypes.Jester>(),
                CardAcceptance.For<CardTypes.Armory>(ShouldPlayArmory),
                CardAcceptance.For<CardTypes.Bridge>(),                           
                CardAcceptance.For<CardTypes.Watchtower>());
}

// dicard order used to tune which actions to put back with wandering minstrell
private static CardPickByPriority DiscardOrder()
{
    return new CardPickByPriority(
        CardAcceptance.For<CardTypes.Armory>(),
        CardAcceptance.For<CardTypes.Bridge>(),
        CardAcceptance.For<CardTypes.Necropolis>(),
        CardAcceptance.For<CardTypes.Watchtower>(),
        CardAcceptance.For<CardTypes.WanderingMinstrell>(),
        CardAcceptance.For<CardTypes.Rats>());
}

private static CardPickByPriority TrashOrder()
{
    return new CardPickByPriority(
                CardAcceptance.For<CardTypes.Rats>(gameState => CountAllOwned<CardTypes.Rats>(gameState) > 0),
                CardAcceptance.For<CardTypes.Curse>(),
                CardAcceptance.For<CardTypes.Estate>(),
                CardAcceptance.For<CardTypes.OvergrownEstate>(),                         
                CardAcceptance.For<CardTypes.Hovel>(),
                CardAcceptance.For<CardTypes.Necropolis>(),
                CardAcceptance.For<CardTypes.Copper>());
}

private static CardPickByPriority TrashOrderWithoutRats()
{
    return new CardPickByPriority(
                CardAcceptance.For<CardTypes.Curse>(),
                CardAcceptance.For<CardTypes.Estate>(),
                CardAcceptance.For<CardTypes.OvergrownEstate>(),
                CardAcceptance.For<CardTypes.Hovel>(),
                CardAcceptance.For<CardTypes.Necropolis>(),                           
                CardAcceptance.For<CardTypes.Copper>()
                );
}

89
Simulation / Re: Guilds simulator results
« on: October 01, 2013, 08:04:21 pm »
Quote
Not that surprising.  BM is almost certain to have a Copper in hand to discard, and having a coin token instead of a Copper in hand can only ever help

Well every plaza you give up the opportunity of having a silver.  With one plaza, the benefit of smoothing outweighs the cost, but as more plazas are added, it gets worse and worse:

2 plazas:

47.3% win for BigMoneyWithCard<Plaza>
46.4% win for BigMoney

3 plazas:

34.5% win for BigMoneyWithCard<Plaza>
58.9% win for BigMoney

90
Simulation / Re: Feodum vs. Duke?
« on: September 28, 2013, 03:11:02 pm »
Quote
On your last shuffle, everything but points are trashable, and you get kind of a spy effect..
Quote

I agree in theory.  In practice, I suspect this is not likely to make a huge difference in performance.  It is only beneficial when you happen to have 2/3 of your next 3 cards as treasures.

Also, I have not tried yet to have the bots figure out when the last reshuffle is ...   On big money like bots, this should be relatively easy (though not trivial).   On more chaining style decks, or in decks with cards like rebuild, this is much more difficult.

91
Simulation / Re: Feodum vs. Duke?
« on: September 27, 2013, 08:21:41 pm »
Quote
What are your buy and play rules for Lookout?

Always buy exactly one lookout soon as you can.

Only play lookout when the count of trashable cards in your remaining deck (not your hand or discard - just your deck) is at least in a 40% ratio.  If you have too many good cards coming up, dont play lookout, else you might have to trash a good one.

At game beginning, trashable cards are copper, estate and shelters.  In the end game, after provinces are being bought, only coppers and shelters are trashable.
   

92
Simulation / Re: Guilds simulator results
« on: September 27, 2013, 07:55:30 pm »
Interestingly enough, plaza also beats big money:

54.9% win for BigMoneyWithCard<Plaza>
40.3% win for BigMoney
4.8% there is a tie.

93
Simulation / Re: Feodum vs. Duke?
« on: September 27, 2013, 06:05:21 pm »
Quote
Well, it's kinda hard to find a strategy that performs worse than Big Money

You'ld be surprised.  Lots of things I have simulated lose to big money out of the gate.   

Did you know that buying lookout only is worse than buying big money?   Me and my friends almost always buy lookout when it's on the table, because we heavily bias towards any trashing option - but maybe we shouldn't

56.6% win for BigMoney
38.6% win for Lookout
4.8% there is a tie.




94
Simulation / Re: Request: Rebuild
« on: June 22, 2013, 05:06:36 pm »
Where's this simulator that has Dark Ages cards?

I've been working on my own in my spare time.   I haven't posted the code anywhere yet, as it's far from complete and buggy still.

95
Simulation / Re: Request: Rebuild
« on: June 21, 2013, 10:19:35 pm »
Thank you for the suggestions.

Quote
Does this say to buy Duchies when you own more Rebuilds than Estates?

Yes.  That is correct.  I will play with this a little later as you suggested. 

My thinking in the heuristic you pointed out reflects my experience with the card.   You want to buy duchies, only after you have 1 or 2 rebuilds.   You can't reduce your estate count until you have rebuilds - making this an interesting heuristic.   From my playing with the card, I actually greatly prefer converting all of my estates to duchies, before converting any duchies to provinces.   Once the other player starts obtaining duchies, the duchies quickly run out, you lose the ability to convert estates.  At this point rebuild becomes no more than a province pile emptier.   I think the way i've coded it up accomplishes this.  I will try some other things you mention above and see if they improve the Rebuild AI.

96
Simulation / Re: Request: Rebuild
« on: June 21, 2013, 08:45:45 pm »
I'm just beginning this one.   Here's some preliminary numbers:


88.46% win for Rebuild
10.28% win for BigMoney
1.26% there is a tie.

63.76% win for Rebuild
33.05% win for BigMoneyWithCard<Wharf>
3.19% there is a tie.

64.66% win for Rebuild
33.01% win for BigMoneyWithCard<Mountebank>
2.33% there is a tie.

48.92% win for Rebuild
48.75% win for BigMoneyWithCard<Witch>
2.33% there is a tie.

40.98% win for Rebuild
57.15% win for BigMoneyWithCard<YoungWitch>
1.87% there is a tie.

Rebuild uses a strategy of buying up to 3 rebuilds.  Rebuild is higher priority buy than gold.  You can consider the "Name a card" to always be province.   I tried variations on the name a card, and they don't impact rankings materially.

The competing strategy is like a big money.  It does buy up to 2 copies of the card being tested. (e.g. up to 2 copies of wharf).  It prioritizes getting these 2 copies over getting gold.  It will get 2 gold before greening.

Here's c# code for the rebuild strategy.
Code: [Select]
            private static CardPickByPriority PurchaseOrder()
            {
                return new CardPickByPriority(
                           CardAcceptance.For<CardTypes.Province>(),
                           CardAcceptance.For<CardTypes.Duchy>(gameState => CountAllOwned<CardTypes.Estate>(gameState) < CountAllOwned<CardTypes.Rebuild>(gameState)),
                           CardAcceptance.For<CardTypes.Estate>(gameState => gameState.GetPile<CardTypes.Province>().Count() <= 2 || gameState.GetPile<CardTypes.Duchy>().Count() == 0),
                           CardAcceptance.For<CardTypes.Rebuild>(gameState => CountAllOwned<CardTypes.Rebuild>(gameState) < 3),
                           CardAcceptance.For<CardTypes.Gold>(),                           
                           CardAcceptance.For<CardTypes.Silver>());
            }

                public override Type NameACard(GameState gameState)
                {
                   
                    PlayerState currentPlayer = gameState.players.CurrentPlayer;
                   
                    if (gameState.GetPile<CardTypes.Duchy>().Count() == 0)
                    {
                        return typeof(CardTypes.Estate);
                    }

                    if (CountMightDraw<CardTypes.Province>(gameState) > 0)
                    {
                        return typeof(CardTypes.Province);
                    }

                    if (CountMightDraw<CardTypes.Estate>(gameState) > 0)
                    {
                        return typeof(CardTypes.Duchy);
                    }                   
                   
                    return typeof(CardTypes.Province);
                }

This is what the competing strategy looks like:
Code: [Select]
private static IGetMatchingCard PurchaseOrder(int cardCount)
            {
                return new CardPickByPriority(
                           CardAcceptance.For<CardTypes.Province>(gameState => CountAllOwned<CardTypes.Gold>(gameState) > 2),
                           CardAcceptance.For<CardTypes.Duchy>(gameState => gameState.GetPile<CardTypes.Province>().Count() < 4),
                           CardAcceptance.For<CardTypes.Estate>(gameState => gameState.GetPile<CardTypes.Province>().Count() < 2),
                           CardAcceptance.For<T>(gameState => CountAllOwned<T>(gameState) < cardCount),
                           CardAcceptance.For<CardTypes.Gold>(),                           
                           CardAcceptance.For<CardTypes.Estate>(gameState => gameState.GetPile<CardTypes.Province>().Count() < 4),
                           CardAcceptance.For<CardTypes.Silver>());

            }

<edit> Fixed mountebank bug</edit>

97
Simulation / Re: Feodum vs. Duke?
« on: June 21, 2013, 04:35:29 pm »
Short answer:  My simulation shows DuchyDukeEmbassyWarehouse strategy winning in a 3:2 ratio over DevelopFeodum. It's a little better, but not a huge amount.  Not the 9:1 ratio you were hoping for.

DuchyDukeWarehouseEmbassy vs FeodumDevelop

DuchyDukeWarehouseEmbassy won: 58.58 percent of the time.
FeodumDevelop won: 40.14 percent of the time.
Ties: 1.28 percent of the time.

Both strategies seem better than big money.  That's always a good sign that they are decently coded.

DuchyDukeWarehouseEmbassy vs BigMoney

DuchyDukeWarehouseEmbassy : 62.46 percent of the time.
BigMoney: 35.65 percent of the time.
Ties: 1.89 percent of the time.

FeodumDevelop vs BigMoney

FeodumDevelop won: 72.77 percent of the time.
BigMoney won: 25.36 percent of the time.
Ties: 1.87 percent of the time.


There's a lot of details missing on how to code up the strategies.   I coded up feodem/develop so it tries to obtain 12 silvers, and after that always maximizes it's points when choosing whether to trash a feodum.   The feodum AI does trash coppers, but at low priority.

The simulator probably isn't replicating how you actually played though.   I find that seldom does the feodom player actually make it to 8 provinces.  Usually, the game piles out instead.

Example game where DuchyDuke won:

Game ended in 33 turns.
Player1 Won!
Player1 total score: 91
  Copper(7), Duchy(8 ), Duke(8 ), Embassy(1), Estate(3), Silver(8 ), Warehouse(2),
Player2 total score: 72
  Copper(3), Develop(2), Embassy(2), Feodum(6), Province(3), Silver(29),
Trash contains: Copper(4), Estate(3), Feodum(2),

Example game where Feodum won:

Game ended in 32 turns.
Player2 Won!
Player1 total score: 80
  Copper(7), Duchy(7), Duke(8 ), Embassy(1), Estate(3), Silver(12), Warehouse(3),
Player2 total score: 87
  Copper(4), Develop(2), Duchy(1), Embassy(1), Feodum(6), Province(5), Silver(27),
Trash contains: Copper(3), Estate(3), Feodum(2),

Here's what I came up with based off of your description:  (hopefully you can infer from the c# code what the strategy was, cause it would be too time consuming to write up the details ;) )

Quote
I opened Silver/Warehouse, got a few Silvers, a single Embassy, then went hard for Duchies, then Dukes. On $4 or less I would get Silvers but I got up to three Warehouses.
Code: [Select]
private static IGetMatchingCard PurchaseOrder()
            {
                var highPriority = new CardPickByPriority(
                     CardAcceptance.For<CardTypes.Embassy>(gameState => CountAllOwned<CardTypes.Embassy>(gameState) < 1),
                     CardAcceptance.For<CardTypes.Duchy>(),
                     CardAcceptance.For<CardTypes.Duke>());

                var buildOrder = new CardPickByBuildOrder(
                    new CardTypes.Silver(),
                    new CardTypes.Warehouse(),
                    new CardTypes.Silver(),
                    new CardTypes.Silver(),
                    new CardTypes.Silver(),
                    new CardTypes.Silver(),
                    new CardTypes.Warehouse(),
                    new CardTypes.Silver(),
                    new CardTypes.Silver(),
                    new CardTypes.Silver(),
                    new CardTypes.Silver(),
                    new CardTypes.Warehouse());

                var lowPriority = new CardPickByPriority(
                           CardAcceptance.For<CardTypes.Silver>());

                return new CardPickConcatenator(highPriority, buildOrder, lowPriority);
            }

Quote
My opponent opened Develop/Feodum, didn't collide them on the first shuffle, but looked to collide them by buying/gaining more copies of each and did a couple of times. He scooped up all the Feoda quickly, and ended up trashing three of them. I think he had 20-something Silvers total (Feoda were worth 5 each) and the Silver pile was in danger of running out by the end of the game.
Code: [Select]
private static IGetMatchingCard PurchaseOrder()
            {
                return new CardPickByPriority(
                           CardAcceptance.For<CardTypes.Province>(),
                           CardAcceptance.For<CardTypes.Develop>(ShouldGainDevelop),
                           CardAcceptance.For<CardTypes.Feodum>(ShouldGainFeodum),
                           CardAcceptance.For<CardTypes.Silver>());
            }

            private static IGetMatchingCard GainOrder()
            {
                return new CardPickByPriority(
                           CardAcceptance.For<CardTypes.Develop>(ShouldGainDevelop),
                           CardAcceptance.For<CardTypes.Feodum>(ShouldGainFeodum),
                           CardAcceptance.For<CardTypes.Silver>(),
                           CardAcceptance.For<CardTypes.Duchy>(),
                           CardAcceptance.For<CardTypes.Feodum>(),
                           CardAcceptance.For<CardTypes.Develop>());
            }

            private static CardPickByPriority TrashOrder()
            {
                return new CardPickByPriority(
                           CardAcceptance.For<CardTypes.Duchy>(),
                           CardAcceptance.For<CardTypes.Feodum>(ShouldTrashFeodum),
                           CardAcceptance.For<CardTypes.Estate>(),
                           CardAcceptance.For<CardTypes.Copper>());
            }

            private static bool ShouldGainDevelop(GameState gameState)
            {
                return CountAllOwned<CardTypes.Develop>(gameState) < 2 &&
                       CountAllOwned<CardTypes.Feodum>(gameState) >= CountAllOwned<CardTypes.Develop>(gameState);
            }

            private static bool ShouldPlayDevelop(GameState gameState)
            {
                var currentPlayer = gameState.players.CurrentPlayer;

                Type result;
                if (currentPlayer.Hand.Where(card => card.Is<CardTypes.Develop>()).Count() > 1)
                {
                    result = TrashOrder().GetMatchingCard(gameState, card => currentPlayer.Hand.HasCard(card));
                }
                else
                {
                    result = TrashOrder().GetMatchingCard(gameState, card => currentPlayer.Hand.HasCard(card) && !card.Is<CardTypes.Develop>());
                }

                return result != null;
            }

            private static bool ShouldTrashFeodum(GameState gameState)
            {
                int countFeodumRemaining = gameState.GetPile<CardTypes.Feodum>().Count();

                int countSilvers = CountAllOwned<CardTypes.Silver>(gameState);
                int countFeodum = CountAllOwned<CardTypes.Feodum>(gameState);

                if (countSilvers < 12)
                {
                    return true;
                }

                int scoreTrashNothing = CardTypes.Feodum.VictoryCountForSilver(countSilvers) * countFeodum;
                int scoreTrashFeodum = CardTypes.Feodum.VictoryCountForSilver((countSilvers + 4)) * (countFeodum - 1);

                return scoreTrashFeodum > scoreTrashNothing;
            }

            private static bool ShouldGainFeodum(GameState gameState)
            {
                int countFeodumRemaining = gameState.GetPile<CardTypes.Feodum>().Count();

                int countSilvers = CountAllOwned<CardTypes.Silver>(gameState);
                int countFeodum = CountAllOwned<CardTypes.Feodum>(gameState);

                if (countSilvers < 1)
                {
                    return false;
                }

                int scoreGainFeodum = CardTypes.Feodum.VictoryCountForSilver(countSilvers) * (countFeodum + 1);
                int scoreGainSilver = CardTypes.Feodum.VictoryCountForSilver((countSilvers + 1)) * (countFeodum);

                return scoreGainFeodum > scoreGainSilver;
            }

98
Simulation / Re: prizes
« on: June 21, 2013, 03:23:44 pm »
My answer is n=10.  You may find the following data useful:

Followers Cost, Followers Win %, Big Money Win%, Tie%
0, 95.15, 4.45, 0.4
1, 95.15, 4.45, 0.4
2, 95.15, 4.45, 0.4
3, 94.78, 4.75, 0.47
4, 94.7, 4.83, 0.47
5, 90.65, 8.63, 0.72
6, 80.4, 18.19, 1.41
7, 76.41, 22.01, 1.58
8, 68.96, 29.49, 1.55
9, 53.39, 45.12, 1.49
10, 30.9, 67.95, 1.15
11, 13.18, 86.22, 0.6
12, 4.12, 95.73, 0.15
13, 1.02, 98.94, 0.04
14, 0.12, 99.86, 0.02
15, 0, 100, 0

Eventually, the followers buyer never wins because he can't afford the followers, and thus never allowed to buy green according to the rules ... ;)

Details on player behavior: (modulo the followers constraint, they play identically)

Buy Province only after owning 3 gold
Otherwise Buy Duchies if available Province count <= 4
Otherwise Buy Estate if available Province count <= 2
Otherwise Buy gold
Otherwise Buy Silver

Pages: 1 2 3 [4]

Page created in 1.931 seconds with 18 queries.