How it works

The simulator is written in Java which I chose because I use it in my daily job as a software engineer at AXA Bank Belgium.

Buy Rules

The simulator uses a set of customizable Buy Rules for the automated players which are exported/imported via XML. Here’s the simplest of the bunch that still wins games:

<player name=“BM – Basic Money”>

<buy name=“Province”/>

<buy name=“Gold” />

<buy name=“Silver” />

</player>

These buy rules will be evaluated from top to bottom whenever a player wants to buy a card. So if a player has $5 the simulator will first try to buy Province, fail, then Gold, fail and finally succeed in buying Silver. Most strategies will have more complex buy rules involving conditions that must be met. Here’s the famous Gardens/Workshop strategy:

<player name=“COMBO – Workshop/Gardens”>

<buy name=“Gardens” >

<condition>

<left type=“countCardsInDeck” attribute=“Workshop” />

<operator type=“greaterThan” />

<right type=“constant” attribute=“8” />

</condition>

</buy>

<buy name=“Workshop” />

<buy name=“Estate”/>

<buy name=“Silver” />

<buy name=“Copper” />

</player>

If the player has $4 to spend the simulator will try to buy Gardens, but fail if the player has less than 9 Workshops in his current deck, so will instead buy another Workshop. If it has $3 it will buy Estate over Silver unless Estates are no longer in the supply. The simulator will do some things for you so you don’t have to put them in the buy rules:

  • check if it has the necessary $ to buy the card (taking into account Bridge)
  • check if the card is still in the supply
  • check if buying the card will not mean Suicide (like buying the last Province or empty a third pile when behind)

You can create your own strategies using these buy rules by clicking on the Edit/Create button. There are a bunch of options to get the buying conditions just right for your strategy (you can count cards in your deck or in the supply, check if you’ve played a Goons, see if you have more VP than your opponent…). If you want, you can exchange your strategies with friends by clicking the VXML-button and copying the XML-code which your friends can then paste after pushing the VXML-button on an empty Player. There’s also the possibility to load/save your strategies to disk (go to File, then Save)

Play Rules

Card Paramaters

Each card has these parameters that are used frequently by the simulator engine:

  • A play priority number to determine the order in which cards will be played. Village-type cards will have the highest play priority and terminals the lowest. Stronger terminals will have a higher play priority (eg Mountebank> Moneylender). This number is also used for Treasure cards (eg Bank and Horn of Plenty have the lowest). For some cards these numbers are dynamically determined (eg Conspirator will increase its play priority once 2 actions have been played)
  • A discard priority number to determine the order in which cards will be discarded. This can happen due to Militia-effects, Warehouse, etc. Victory and Curse cards will have the highest discard priority while very strong cards like Possession the lowest. Sometimes this number is determined dynamically (eg Treasure Map will have a very low discard priority if there’s a second Treasure Map in hand, otherwise it will be the highest)
  • A trash priority number to determine the order in which cards will be trashed due to effects like Chapel, Bishop, Thief, etc. This number is the same as the discard priority number except for the Victory cards and Curses. Sometimes this number is determined dynamically (eg Estate and Duchie will look at the buy rules of its owner player to determine if it wants to get trashed or not)
  • a set of card types that can be used to tweak the buy rules. eg Village has card types “Kingdom, Action, Cycler and Village”. Princess has card types “Prize, Action and Terminal”.
  • a Cost that contains a $cost and a Potion cost (this will be changed dynamically if BridgeQuarry or Princess are played)
  • A $-value that shows how much $ this produces when played
  • A VP-value that indicates how many victory points this is worth (this will be changed dynamically for Gardens, Fairgrounds…)

Individual Card Code

On top of these, each individual card has its own play rules that are hard coded into the software and can not be viewed or modified by the user (sorry 😦 ).

Here’s the individual code of a very simple card (Village):

public class VillageCard extends DomCard {
  public void play() {
    owner.addActions(2);
    owner.drawCards(1);
  }
}

And here’s a slightly more complex card (Chapel):

public class ChapelCard extends DomCard {
  public void play() {
    Collections.sort(owner.getCardsInHand(),SORT_FOR_TRASHING);
    for ( int i=0; i<4 && !owner.getCardsInHand().isEmpty();i++) {
      DomCard theCardToTrash=owner.getCardsInHand().get( 0 );
      if (theCardToTrash.getTrashPriority()>=20){
        return;
      }
      if (owner.trashingReducesBuyingPowerThisTurn( theCardToTrash )){
        return;
      }
      if (getTotalMoneyInDeckAfterTrashing(theCardToTrash) < 6){
        return;
      }
      owner.trash(owner.removeCardFromHand( theCardToTrash));
    }
  }
}

The experienced player (and programmer) will realize this implementation of Chapel will cover most common situations, but fails in a lot of circumstances. For instance this will never trash a Silver (its trashPriority is 20) while that may be desirable in very heavy action-engine decks that don’t want Treasure.

Each card knows its owner and can ask questions to its owner to determine how it should play (eg the Chapel card will ask its owner if trashing the next card will not hinder him from buying the card he wants).

Cards that gain cards

I devised a clever system for cards like Workshop and Remodel to determine what it wants to gain: just look in the buy rules!
public class WorkshopCard extends DomCard {

public void play() {

theDesiredCard = owner.getDesiredCard(new DomCost( 4, 0));

owner.gain(theDesiredCard);

}

}

The “getDesiredCard-function” will go through the buy rules of the Workshop’s owner and return the card it could buy for $4 (this will also gain Mountebank if Bridge was played before Workshop!)

Native Village’s mysterious playstrategy-option

Finally I added a “playStrategy” option for some cards as an experiment to give the user a way to define in the buy rules how a card should be played in his strategy. For now this has only been implemented for Native Village which has three “play Strategies”:

  1. standard: play native village for storage unless the cards on the mat will improve this turn’s buying power
  2. bigTurnBridge: play native village for storage until the opponent has 24 VP points more than you
  3. bigTurnGoons: play native village for storage unless you have 3 or more Goons in your entire deck (this has not been tested much yet…)

I don’t know if this is a good way to satisfy the user’s desire to determine how to play his strategy….???

The big problem of non-trivial decisions

When you start creating your own strategies you’ll find the simulator will make many mistakes in situations that seem trivial to you. The problem is that the simulator doesn’t really know how to play the game or what its purpose is so it will not know it can trash a Province with his Salvager if the player is ahead in VP-points unles this situation is coded in the SalvagerCard. For each such card their are countless situations where it should be played differently than normal which might seem obvious for a human player who understands the game and its purpose, but will be ignored by the simulator. So take this into account when using the simulator before you draw any conclusions about any card that requires non-trivial decisions by the player.

How are the games played

Here’s a step-by-step of how the program simulates a game of Dominion:

  • First the board and players are reset from the previous game
  • Let each player take their turn until the game is over (3 piles, Province or Colony gone):
    • The player resolves Duration effects
    • The player plays his actions as long as he has actions left according to the “play priority number” of each card. When a card is chosen to be played, its own implementation is executed.
    • The player plays his treasures and buys cards according to his buy rules until he has no more buys left
    • The player discards all cards and draws his next hand
  • During the game all kinds of statistics are saved to generate the graphs at the end of the simulation (money generated each turn, VP gained and lost each turn,…)

Questions? Leave a comment!

  1. Ted Lorance
    July 15, 2011 at 10:24 pm

    I was curious if it would be possible to (or if there is already a place where you can) see the “hardwired” play decisions to aid in using this amazing program. I am particularly interested in the play decisions for Pirate Ship.

    • Geronimoo
      July 16, 2011 at 9:24 am

      I’m planning to do this, but haven’t come around to it. Here are Pirate Ship’s play rules (they’re very simple):

      “get the $ from the ship if it would allow to buy a better card, otherwise attack”

      This means if you have $5 available and you have 3 tokens on your Pirate Ship mat, it will get the $3 to buy a Province. It also means If you have $5 available and you have 1 token it will take the $1 to get Gold (if it’s in your buy rules) instead of attacking.

      It’s far from ideal, but it works for a lot of situations.

  2. DG
    January 16, 2012 at 4:01 pm

    When simulating 3 and 4 player games the total wins for each player plus ties seems to add up to between 95-98%. I’m not sure how rounding errors or any weighting of the figures would produce 2-5% ‘missing games’. There are also times when the graph pane shows a win rate of 31.056 for a script, say, but the edit pane shows a win rate of 30%.

    The simulation results still seem a reasonable comparison of the scripts but it would be good to know how these figures are arrived at.

  3. OT Piccolo
    June 21, 2013 at 11:32 pm

    Hi,

    I saw that you made that Simulator open source on github. I wanted to ask how I could help you with further developing, as it sounded that you had already some Cards implemented for Guilds. I experimented with a bit on some Dark Age Cards, but now I’m unsure, not that you already implemented them and just didn’t commit it to github. Or if you already disconnected the development or whatever.

    • June 22, 2013 at 11:46 am

      I pretty much stopped the development.

      • OT Piccolo
        June 22, 2013 at 2:58 pm

        So, if I would update it, would you even publish the new contents on your site? Or is the simulator no longer really used, and pretty much dead?

      • June 24, 2013 at 11:38 am

        If you send me the signed jars and it seems to be working well I’ll publish it.

  4. July 30, 2013 at 10:02 pm

    I am very interested in seeing your code, but have never used GITHub. I am wondering if you would tell me what the steps are to DL the code to my development machine?

  1. No trackbacks yet.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

%d bloggers like this: