Skip to content

Instantly share code, notes, and snippets.

@barbazul
Created November 26, 2018 04:33
Show Gist options
  • Save barbazul/3a643ac85795715afe3c1bbd231e3ba8 to your computer and use it in GitHub Desktop.
Save barbazul/3a643ac85795715afe3c1bbd231e3ba8 to your computer and use it in GitHub Desktop.
Adapted Androminion Sarah AI for Dominiate simulator
# This is a port of the Sarah AI from Androminion for the Dominiate simulator
# Original https://github.com/mehtank/androminion/blob/master/vdom/src/com/vdom/players/VDomPlayerSarah.java
# Simulator http://rspeer.github.io/dominiate/play.html
#
# Sarah has several scripted behaviors for preconstructed kingdoms which are
# ignored in this implementation.
# So what is left is basically the same as Chuck but likes to buy Grand Market
# There is a bias that I left out by which it tends to buy less basic treasures
# if there are 5 or more cards from Cornucopia expansion
#
# Commented out some pseudocode with ignored logic due to cards not implemented
# in Dominiate
{
name: 'Sarah',
author: 'dominionator, schoeggu, mehtank, tkdennis and androminionfan (taken from Github contributos)'
requires: [],
trashPriority: (state, my) ->
# trash in this order!
[
"Curse"
"Rats"
"Overgrown Estate"
"Ruined Village"
"Ruined Market"
"Survivors"
"Ruined Library"
"Abandoned Mine"
# Any other Ruins. Ruins are not implemented in Dominiate
"Hovel"
"Estate"
"Copper"
"Masterpiece"
]
gainPriority: (state, my) ->
midGame = 12
randomGreen = Math.random()
MAX_OF_ONE_ACTION_CARD = 4
silverMax = 4
silverLine = 0.5
actionCardMax = 5
multiplierCount = my.countInDeck("Throne Room") + my.countInDeck("King's Court")
maxMultipliers = 2
priority = [
"Colony"
"Platinum" if my.coins >= 9 && my.turnsTaken < midGame
# Prince is not implemented in Dominiate
#"Prince" if my.turnsTaken < midGame && state.countInSupply("Colony") > 0
# && my.countInDeck("Prince") < 2
# && princeCandidates.length >= 2 + 2 * my.countInDeck("Prince")
"Province"
"Vineyard" if my.turnsTaken > midGame &&
my.coins <= 7 &&
randomGreen < 0.25 &&
my.numActionCardsInDeck() >= 9
# && will not gain curse due to embargo
"Duchy" if my.turnsTaken > midGame &&
my.coins <= 7 &&
randomGreen < 0.25
# && will not gain curse due to embargo
"Vineyard" if my.turnsTaken > midGame &&
my.coins <= 7 &&
randomGreen < 0.25 &&
my.numActionCardsInDeck() >= 6
# && will not gain curse due to embargo
"Possession" if my.countInDeck("Possession") < 2
"Grand Market" if Math.random() * MAX_OF_ONE_ACTION_CARD * 2 < my.countInDeck("Grand Market")
# try cards with potion before silver
# Yes, Possession has already been skipped at this point ¯\_(ツ)_/¯
# And Possession is not implemented anyway
#"Possession" if my.countInDeck("Possession") < 2 &&
# my.coins < state.cardInfo["Possession"].costInCoins(state) + 3 &&
# my.turnsTaken <= midGame
# # && will not gain curse due to Embargo
"Golem" if my.countInDeck("Golem") < 2 &&
my.coins < state.cardInfo["Golem"].costInCoins(state) + 3 &&
my.turnsTaken <= midGame
# && will not gain curse due to Embargo
"Familiar" if my.countInDeck("Familiar") < 2 &&
my.coins < state.cardInfo["Familiar"].costInCoins(state) + 3 &&
my.turnsTaken <= midGame &&
state.countInSupply("Curse") > 3
# && will not gain curse due to Embargo
"Alchemist" if my.coins < state.cardInfo["Alchemist"].costInCoins(state) + 3 &&
my.turnsTaken <= midGame
# && will not gain curse due to Embargo
"Philosopher's Stone" if my.coins < state.cardInfo["Philosopher's Stone"].costInCoins(state) + 3 &&
my.turnsTaken <= midGame
# && will not gain curse due to Embargo
"Scrying Pool" if my.coins < state.cardInfo["Scrying Pool"].costInCoins(state) + 3 &&
my.turnsTaken <= midGame
# && will not gain curse due to Embargo
"Apothecary" if my.countInDeck("Apothecary") < 2 &&
my.coins < state.cardInfo["Apothecary"].costInCoins(state) + 3 &&
my.turnsTaken <= midGame
# && will not gain curse due to Embargo
"University" if my.countInDeck("University") < 2 &&
my.coins < state.cardInfo["University"].costInCoins(state) + 3 &&
my.turnsTaken <= midGame
# && will not gain curse due to Embargo
"Silver" if my.coins <= silverMax &&
Math.random() > silverLine
# && will not gain curse due to Embargo
"Gold" if my.coins == 6 &&
Math.random() > silverLine
# && will not gain curse due to Embargo
]
cost = my.coins
while cost >= 0
randList = []
for card, quantity of state.supply
c = state.cardInfo[card]
continue if c.costInCoins(state) != cost
continue if c.isAction && my.numActionCardsInDeck() >= actionCardMax ||
card == "Masterpiece" ||
card == "Gold" ||
card == "Platinum" ||
card == "Curse" ||
# Also skip ruins (which are not implemented)
card == "Copper" ||
card == "Rats" ||
card == "Potion" && !this.shouldBuyPotion(my) ||
card == "Throne Room" && multiplierCount >= maxMultipliers ||
card == "Disciple" && multiplierCount >= maxMultipliers ||
card == "King's Court" && multiplierCount >= maxMultipliers ||
# Will get Curse due to Embargo
!c.isAction && !c.isTreasure # && is not an Event
currentCount = my.countInDeck(card)
if c.isTreasure ||
c.isVictory ||
currentCount == 0 ||
Math.random() * MAX_OF_ONE_ACTION_CARD < currentCount
randList.push(card)
randList = this.shuffle(randList)
# prefer silver instead of masterpiece if you can't overpay by 2
masterpieceIndex = randList.indexOf("Masterpiece")
if masterpieceIndex > -1 &&
randList.indexOf("Silver") > -1 &&
my.coins < 5
randList[masterpieceIndex] = undefined
for card in randList
priority.push(card)
cost--
priority.push("Platinum")
priority.push("Gold") # if will not gain curse due to Embargo
priority.push("Silver") # if will not gain curse due to Embargo
priority.push(null)
priority
playPriority: (state, my) ->
COST_MAX = 14
priority = [
"Treasure Map" if my.countInHand("Treasure Map") >= 2
]
# play prince if action card candidate available. (skipped Prince is not implemented)
# princeCards = this.princeCardCandidates(state, my)
# But if a non candidate action cantrip or village is available prefer it instead
for card in my.hand
if card.actions > 0 # && princeCards.indexOf(card) < 0
priority.push(card.name)
# Prince is not implemented in Dominate
# priority.push("Prince") if princeCards.length > 0
priority.push("Throne Room")
# Disciple is not implemented in Dominiate
# priority.push("Disciple")
priority.push("King's Court")
cost = COST_MAX
while cost >= 0
randList = []
for card in my.hand
continue if card.name == "Treasure Map" ||
card.name == "Tactician" && my.countInPlay("Tactician") > 0
if card.costInCoins(state) == cost
randList.push(card)
randList = this.shuffle(randList)
for r in randList
if r?
priority.push(r.name);
cost--
priority.push(null)
priority
playValue: (state, card, my) ->
# don't play rats
return -Infinity if card.name == "Rats"
null
shuffle: (v) ->
i = v.length
while i
j = parseInt(Math.random() * i)
i -= 1
temp = v[i]
v[i] = v[j]
v[j] = temp
v
shouldBuyPotion: (my) ->
return false if my.countInDeck("Potion") > 2
return false if my.countInDeck("Potion") > 1 && Math.random() > 0.16
return false if my.countInDeck("Potion") > 0 && Math.random() > 0.25
true
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment