|
| 1 | +package AdventOfCode2015 |
| 2 | + |
| 3 | +object Day22: |
| 4 | + val MagicMissile = Spell(53, 1) |
| 5 | + val Drain = Spell(73, 1) |
| 6 | + val Shield = Spell(113, 6) |
| 7 | + val Poison = Spell(173, 6) |
| 8 | + val Recharge = Spell(229, 5) |
| 9 | + val spells = Set(MagicMissile, Drain, Shield, Poison, Recharge) |
| 10 | + |
| 11 | + case class Spell(cost: Int, duration: Int) |
| 12 | + |
| 13 | + case class State(hero: Int, mana: Int, spent: Int, armor: Int, boss: Int, damage: Int, active: Map[Spell, Int]): |
| 14 | + def applyEffects: State = active.foldLeft(copy(armor = 0)) { case (state, (spell, duration)) => |
| 15 | + val next = state.effect(spell) |
| 16 | + if duration == 1 then next.copy(active = state.active.removed(spell)) |
| 17 | + else next.copy(active = state.active.updated(spell, duration - 1)) |
| 18 | + } |
| 19 | + def effect(spell: Spell): State = spell match |
| 20 | + case MagicMissile => copy(boss = boss - 4) |
| 21 | + case Drain => copy(hero = hero + 2, boss = boss - 2) |
| 22 | + case Shield => copy(armor = 7) |
| 23 | + case Poison => copy(boss = boss - 3) |
| 24 | + case Recharge => copy(mana = mana + 101) |
| 25 | + def heroTurn(spell: Spell): State = copy(mana = mana - spell.cost, spent = spent + spell.cost, active = active + (spell -> spell.duration)) |
| 26 | + def bossTurn: State = copy(hero = hero - (damage - armor).max(1)) |
| 27 | + def hardMode: State = copy(hero = hero - 1) |
| 28 | + |
| 29 | + def parse(input: Seq[String]): State = |
| 30 | + val Seq(health, damage) = input.map(_.filter(_.isDigit).toInt) |
| 31 | + State(50, 500, 0, 0, health, damage, Map()) |
| 32 | + |
| 33 | + def fight(start: State, hard: Boolean): Int = |
| 34 | + var result = Int.MaxValue |
| 35 | + |
| 36 | + def helper(state: State, playerTurn: Boolean): Unit = |
| 37 | + val first = if hard && playerTurn then state.hardMode else state |
| 38 | + val second = first.applyEffects |
| 39 | + |
| 40 | + if first.spent >= result || first.hero <= 0 then () |
| 41 | + else if second.boss <= 0 then result = result.min(second.spent) |
| 42 | + else if !playerTurn then helper(second.bossTurn, !playerTurn) |
| 43 | + else spells |
| 44 | + .diff(second.active.keySet) |
| 45 | + .filter(_.cost <= second.mana) |
| 46 | + .foreach(spell => helper(second.heroTurn(spell), !playerTurn)) |
| 47 | + end helper |
| 48 | + |
| 49 | + helper(start, true) |
| 50 | + result |
| 51 | + end fight |
| 52 | + |
| 53 | + def part1(input: Seq[String]): Int = fight(parse(input), false) |
| 54 | + |
| 55 | + def part2(input: Seq[String]): Int = fight(parse(input), true) |
| 56 | + |
| 57 | + def main(args: Array[String]): Unit = |
| 58 | + val data = io.Source.fromResource("AdventOfCode2015/Day22.txt").getLines().toSeq |
| 59 | + println(part1(data)) |
| 60 | + println(part2(data)) |
0 commit comments