-
Notifications
You must be signed in to change notification settings - Fork 44
Writing a strategy
You may find it easiest to write a strategy by following the example of an existing one. Here's an example of the SingleBaron strategy. It plays mostly Big Money, but it buys a single Baron. If attacked, it chooses its discards to try to keep an Estate and a Baron in hand when possible.
{
name: 'SingleBaron'
requires: ['Baron']
gainPriority: (state, my) -> [
"Colony" if my.countInDeck("Platinum") > 0
"Province" if state.countInSupply("Colony") <= 6
"Duchy" if 0 < state.gainsToEndGame() <= 5
"Estate" if 0 < state.gainsToEndGame() <= 2
"Platinum"
"Gold"
"Baron" if my.countInDeck("Baron") == 0
"Silver"
"Copper" if state.gainsToEndGame() <= 2
]
discardPriority: (state, my) -> [
"Colony"
"Province"
"Duchy"
"Curse"
"Estate" if my.countInHand("Baron") == 0 \
or my.countInHand("Estate") > 1
"Copper"
"Baron" if my.countInHand("Estate") == 0
null
"Silver"
"Estate"
"Baron"
]
}
There are two ways to make a decision: with a priority function or a value function.
A priority function takes in a game state, and returns an ordered list of choices it would like to make. It will check each item in the list in order, and pick the first one that's allowed.
Choices can be followed by conditions such as if my.countInHand("Copper") > 0
. If the condition is false, that entry gets skipped.
For an example, here's the default trashPriority
.
trashPriority: (state, my) -> [
"Curse"
"Estate" if state.gainsToEndGame() > 4
"Copper" if my.getTotalMoney() > 4
"Potion" if my.turnsTaken >= 10
"Estate" if state.gainsToEndGame() > 2
]
A value function takes a game state and a specific choice it could make, and assigns a number to that choice. The higher the number, the more it prefers that choice. If the AI can't decide based on its priority list for that decision, or if the priority list is empty, it will ask the value function instead.
Positive values are better than doing nothing, and negative values are worse. If the AI is given the option of doing nothing, it will be represented by null
in the list of possible choices, and it will automatically get a value of 0.
Here's the corresponding trashValue
function:
trashValue: (state, card, my) ->
0 - card.vp - card.cost
The last parameter to each of these is called my
, and it refers to this AI's PlayerState. Some other code might use state.current
to mean almost the same thing... but that's the current player's state, which may not be you if there's an attack or reaction going on.
See Decisions for a list of all decisions the AI can make, or the basicAI literate documentation for detailed documentation alongside the code of the default decisions.
TODO
Strategies should include a list of cards they require
, as the example above does. The simulator will only choose kingdoms with all the required cards in them, ensuring that your strategy works as intended.
Strategies can also have an author
, which I hope to use when building a database of these strategies.