Last active
November 26, 2018 04:41
-
-
Save barbazul/a6461eceeecae00679eb77444c50bcc9 to your computer and use it in GitHub Desktop.
Adapted Androminion Chuck AI for Dominiate simulator
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# This is a port of the Chuck AI from Androminion for the Dominiate simulator | |
# Original https://github.com/mehtank/androminion/blob/master/vdom/src/com/vdom/players/VDomPlayerChuck.java | |
# Simulator http://rspeer.github.io/dominiate/play.html | |
# | |
# Chuck really likes to buy potion cards | |
# If potion cards are unavailable, he mostly buys random treasures and action | |
# favoring expensive cards over cheap ones. | |
# Starts greening when he can afford a Province or Colony or after turn 12 | |
# If there are no potion-cost actions it will at most buy 5 random actions but | |
# it will never buy Vineyard with less than 5 actions in deck, so sometimes it | |
# will buy Potion and never use it. ¯\_(ツ)_/¯ | |
# Play priority is Treasure Map above all (?) then cantrips and villages and | |
# then terminals (throning them if available) | |
# | |
# Commented out some pseudocode with ignored logic due to cards not implemented | |
# in Dominiate | |
{ | |
name: 'Chuck', | |
author: 'dominionator, mehtank, tkdennis, schoeggu and androminionfan (taken from Github contributos)' | |
requires: [], | |
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 | |
gainPriority: (state, my) -> | |
midGame = 12 | |
actionCardsMax = 5 | |
priority = [ | |
null if my.coins == 0 | |
"Colony" | |
"Platinum" if my.turnsTaken < midGame | |
# Prince is not implemented in Dominiate | |
#"Prince" if my.turnsTaken < midGame && | |
# state.countInSupply("Colony") > 0 && | |
# my.countInDeck("Prince") < 2 && | |
# princeCandidatesCount > 2 * my.countInDeck("Prince") | |
"Province" | |
"Vineyard" if my.turnsTaken > midGame && | |
my.numActionCardsInDeck() >= 9 | |
# && will not gain curse due to embargo | |
"Duchy" if my.turnsTaken > midGame | |
# && will not gain curse due to embargo | |
# try cards with potion before silver | |
"Vineyard" if my.turnsTaken > midGame && | |
my.numActionCardsInDeck() >= 6 | |
# && will not gain curse due to embargo | |
"Posession" if my.countInDeck("Posession") < 2 && | |
my.coins < state.cardInfo["Vineyard"].costInCoins(state) + 3 | |
"Golem" if my.countInDeck("Golem") < 2 && | |
my.coins < state.cardInfo["Golem"].costInCoins(state) + 3 | |
"Familiar" if my.countInDeck("Familiar") < 2 && | |
my.coins < state.cardInfo["Familiar"].costInCoins(state) + 3 && | |
my.turnsTaken <= midGame && state.countInSupply("Curse") > 3 | |
"Alchemist" if my.coins < state.cardInfo["Alchemist"].costInCoins(state) + 3 | |
"Philosopher's Stone" if my.coins < state.cardInfo["Philosopher's Stone"].costInCoins(state) + 3 | |
"Scrying Pool" if my.coins < state.cardInfo["Scrying Pool"].costInCoins(state) + 3 | |
"Apothecary" if my.countInDeck("Apothecary") < 2 && | |
my.coins < state.cardInfo["Apothecary"].costInCoins(state) + 3 | |
"University" if my.countInDeck("University") < 2 && | |
my.coins < state.cardInfo["University"].costInCoins(state) + 3 | |
] | |
if Math.random() < 0.25 | |
priority.push("Gold") # if will not gain curse due to embargo | |
priority.push("Silver") # if will not gain curse due to embargo | |
# Random cards amognst the most expensive available | |
randList = [] | |
cost = my.coins | |
highestCost = 0 | |
while cost >= 0 | |
for c, count of state.supply | |
card = state.cardInfo[c] | |
multiplierCount = my.countInDeck("Throne Room") + my.countInDeck("King's Court") | |
# Should also skip ruins and consider disciple and crown in mutipliers calculation | |
# Those cards are not implemented in Dominiate | |
continue if card.costInCoins(state) != cost || | |
c == "Curse" || | |
c == "Copper" || | |
c == "Rats" || | |
c == "Potion" && !this.shouldBuyPotion(my) || | |
c == "Throne Room" && multiplierCount > 2 || | |
c == "King's Court" && multiplierCount > 2 || | |
!card.isAction && !card.isTreasure | |
continue if card.isAction && my.numActionCardsInDeck() >= actionCardsMax | |
# continue if will gain curse due to Embargo | |
highestCost = card.costInCoins(state) if highestCost == 0 | |
randList.push(c) | |
break if --cost < highestCost - 2 | |
# prefer silver instead of masterpiece if you can't overpay by 2 | |
masterpiecePos = randList.indexOf("Masterpiece") | |
if randList.indexOf("Silver") > -1 && masterpiecePos > -1 && my.coins < 5 | |
randList[masterpiecePos] = undefined | |
randList = this.shuffle(randList) | |
for r in randList | |
if r? | |
priority.push(r) | |
priority.push("Gold") | |
priority.push("Silver") # if will not gain curse due to Embargo | |
if my.turnsTaken > midGame # && will not gain curse due to Embargo | |
priority.push("Estate") | |
priority.push(null) | |
priority | |
trashPriority: (state, my) -> | |
[ | |
"Curse" | |
"Overgrown Estate" | |
"Ruined Village" | |
"Ruined Market" | |
"Survivors" | |
"Ruined Library" | |
"Abandoned Mine" | |
# Any other Ruins. Ruins are not implemented in Dominiate | |
"Hovel" | |
"Estate" | |
"Copper" | |
"Masterpiece" | |
] | |
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
Easy optimization: Instead of iterating hand each choice for generating randList, precalculate from kingdom only once and simply shuffle each time.
Dominiate will ignore unavailable choices