Skip to content

Instantly share code, notes, and snippets.

@tyrcho
Last active August 29, 2015 14:21
Show Gist options
  • Save tyrcho/6da6d147dd65c8af5504 to your computer and use it in GitHub Desktop.
Save tyrcho/6da6d147dd65c8af5504 to your computer and use it in GitHub Desktop.
Simple card game simulation
import scala.util.Random
import scala.annotation.tailrec
object SimpleHSApp extends App {
import Minions._
val rush = Deck("rush", Map(minions(1) -> 10, minions(2) -> 10, minions(3) -> 10))
val slow = Deck("slow", Map(minions(3) -> 10, minions(4) -> 10, minions(2) -> 10))
println(List.fill(1000)(play(rush, slow, DefaultStrategy)).count(identity))
def play(deck1: Deck, deck2: Deck, strat: Strategy): Boolean = { //true if d1 wins (d1 starts)
val init1 = deck1.makeInitial(true)
val init2 = deck2.makeInitial(false)
val state = GameState(init1, init2)
@tailrec
def playStep(state: GameState, p1Turn: Boolean = true): Boolean = {
val next = strat.playTurn(state)
println(next)
next.winner1 match {
case Some(res) =>
res != p1Turn
case None => playStep(next, !p1Turn)
}
}
playStep(state)
}
}
trait Strategy {
def playTurn(state: GameState): GameState
}
object DefaultStrategy extends Strategy {
def playTurn(state: GameState): GameState = {
val mine = state.state1.startTurn
val opp = state.state2
val availMana = mine.mana + (if (mine.hasCoin) 1 else 0)
val availCards = mine.playableSets().sortBy(_.map(-_.cost).sum).headOption
val played = availCards match {
case Some(minions) => mine.play(minions)
case None => mine
}
val dmg = mine.inPlay.map(_.attack).sum
val damaged = opp.damage(dmg)
GameState(damaged, played)
}
}
case class Minion(attack: Int, health: Int, cost: Int)
case class Deck(name: String, cards: Map[Minion, Int]) {
def makeInitial(starts: Boolean): PlayerBoardState = {
val drawn = if (starts) 3 else 4
val cardsList = for ((card, count) <- cards) yield List.fill(count)(card)
PlayerBoardState(name, Random.shuffle(cardsList.flatten.toList), hasCoin = !starts).draw(drawn)
}
}
case class GameState(state1: PlayerBoardState, state2: PlayerBoardState) {
def reverse = GameState(state2, state1)
def winner1: Option[Boolean] =
if (state1.health <= 0) Some(false)
else if (state2.health <= 0) Some(true)
else None
}
case class PlayerBoardState(
name: String,
deck: List[Minion],
hand: List[Minion] = Nil,
inPlay: List[Minion] = Nil,
health: Int = 30,
maxMana: Int = 0,
mana: Int = 0,
hasCoin: Boolean = false) {
def startTurn = {
val max = math.min(10, maxMana + 1)
val current = max
val drawn :: rest = deck
copy(maxMana = max,
mana = current,
deck = rest,
hand = drawn :: hand)
}
def draw(count: Int = 1): PlayerBoardState =
if (count >= 1) draw(count - 1).draw
else this
def draw: PlayerBoardState = {
val drawn :: rest = deck
copy(deck = rest,
hand = drawn :: hand)
}
def damage(amount: Int) =
copy(health = health - amount)
def availableMana = mana + (if (hasCoin) 1 else 0)
def playableSets(mana: Int = availableMana, selected: List[Minion] = Nil, possible: List[Minion] = hand): List[List[Minion]] =
possible match {
case h :: t =>
val withIt = if (h.cost <= mana) playableSets(mana - h.cost, h :: selected, t) else Nil
withIt ::: playableSets(mana, selected, t)
case Nil => List(selected)
}
def play(m: List[Minion]) = {
val totalCost = m.map(_.cost).sum
copy(
hand = hand.diff(m),
mana = mana - totalCost,
inPlay = m ::: inPlay,
hasCoin = hasCoin && totalCost <= mana)
}
override def toString = s"$name : $health HP with ${hand.size} in hand and ${inPlay.size} in play (coin:$hasCoin).\n"
}
object Minions {
val minions = (for {
i <- 1 to 6
att = i
health = i + 1
cost = i
} yield i -> Minion(att, health, cost)).toMap
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment