Skip to content

Instantly share code, notes, and snippets.

@tyrcho
Forked from tyrcho/README.md
Last active August 29, 2015 14:06
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save tyrcho/72c73ccb73f4f9b4f233 to your computer and use it in GitHub Desktop.
Save tyrcho/72c73ccb73f4f9b4f233 to your computer and use it in GitHub Desktop.
Implementation of Hearthstone mulligans with scala-js-fiddle.

Open with scala-js-fiddle for a demo

Implementation of Hearthstone mulligans with scala-js-fiddle.

Examples

  • There are 2 important cards in a deck, but you don't keep them in the mulligan phase (let's say 2 Doomguards in zoo). What is the probability to have at least one in hand at a given turn, if the player did not start the game ?
  • You think an opponent plays one Sylvanas. You started the game. What is the probability he has drawn it, assuming he does not keep it in the mulligans ?
  • You play 4 acceleration (Innervate, Wild Growth) cards. By mulliganing everything except those, what is the probability to have exactly one in starting hand ?

Note : mulligan rules clarification. You cannot get the same card you just throw back.

import scala.util.Random
object ScalaJSExample extends js.JSApp{
def main()={
val warriorStarts = Simulation("Warrior, starting, looks for a Fiery War Axe", targetCards = 2, otherMulligans = 15, keep = true)
val warrior = Seq(warriorStarts, warriorStarts.copy(name = "Warrior, coin, looks for a Fiery War Axe", starts = false))
val warrior2Starts = Simulation("Warrior, starting, looks desperately for a Fiery War Axe", targetCards = 2, otherMulligans = 28, keep = true)
val warrior2 = Seq(warrior2Starts, warrior2Starts.copy(name = "Warrior, coin, looks desperately for a Fiery War Axe", starts = false))
val warlockStarts = Simulation("Warlock, starting, chance of Doomguard", targetCards = 2, otherMulligans = 15, keep = false)
val warlock = Seq(warlockStarts, warlockStarts.copy(name = "Warlock, coin, chance of Doomguard", starts = false))
val druidStarts = Simulation("Druid, starting, chance of acceleration", targetCards = 4, otherMulligans = 26, keep = true)
val druid = Seq(druidStarts, druidStarts.copy(name = "Druid, coin, chance of acceleration", starts = false))
for {
s <- druid
} {
println(s.name)
for {
i <- 0 to 10
p = s.copy(turn = i).simulate
} println(f"turn $i : $p%.1f")
}
}
case class Simulation(
name: String,
starts: Boolean = true,
targetCards: Int = 2,
turn: Int = 2,
otherMulligans: Int = 20,
keep: Boolean = true,
size: Int = 5000) {
def initialSize = if (starts) 3 else 4
val keyCards = List.fill(targetCards)(KeyCard)
val mulliganedCards = List.fill(otherMulligans)(EndCard)
val keepCards = List.fill(30 - keyCards.size - mulliganedCards.size)(StartCard)
val deck = keyCards ::: mulliganedCards ::: keepCards
def details = {
val key = deck.count(_ == KeyCard)
val mull = deck.count(_ == EndCard)
val keep = deck.count(_ == StartCard)
s"$key key cards, $mull cards to mull, $keep cards to keep"
}
def simulate: Float = {
val results = List.fill(size)(simulateOnce)
results.count(identity) * 100F / size
}
def simulateOnce: Boolean = {
val initial = Situation(Random.shuffle(deck))
val initialHand = initial.draw(initialSize)
//println(s"hand : ${initialHand.hand}")
val mulligans = initialHand.mulligan
//println(s"after mull : ${mulligans.hand}")
val end = mulligans.draw(turn)
end.hand.find(_ == KeyCard).nonEmpty
}
trait Card {
def shouldMulligan: Boolean
}
object KeyCard extends Card {
val shouldMulligan = !keep
override val toString="key"
}
object StartCard extends Card {
val shouldMulligan = false
override val toString="start"
}
object EndCard extends Card {
val shouldMulligan = true
override val toString="end"
}
case class Situation(deck: List[Card], hand: List[Card] = Nil) {
def draw: Situation = {
val (h, t) = (deck.head, deck.tail)
Situation(deck = t, hand = h :: hand)
}
def mulligan: Situation= {
val discard = hand.filter(_.shouldMulligan)
//println(s"discard $discard")
val Situation(deck, h)=draw(discard.size)
Situation(deck, h.diff(discard))
}
def replace: Situation = {
val (h, t) = (hand.head, hand.tail)
Situation(hand = t, deck = Random.shuffle(h :: deck))
}
final def replace(n: Int): Situation =
if (n == 0) this
else replace.replace(n - 1)
final def draw(n: Int): Situation =
if (n == 0) this
else draw.draw(n - 1)
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment