[enemyHp, enemyDamage] = File.stream!("inputs/day22.txt") |> Stream.map(fn line -> [num] = Regex.run(~r/\d+/, line) {num, ""} = Integer.parse(num) num end) |> Enum.take(2) defmodule BattleSimulator do def magicMissile({playerMana, {playerHp, enemyHp, enemyDamage, hardMode}, effects}) do {53, {playerMana, {playerHp, enemyHp - 4, enemyDamage, hardMode}, effects}} end def drain({playerMana, {playerHp, enemyHp, enemyDamage, hardMode}, effects}) do {73, {playerMana, {playerHp + 2, enemyHp - 2, enemyDamage, hardMode}, effects}} end def shield({playerMana, health, {shield, poison, recharge}}) when shield == 0 do {113, {playerMana, health, {shield + 6, poison, recharge}}} end def shield(_) do nil end def poison({playerMana, health, {shield, poison, recharge}}) when poison == 0 do {173, {playerMana, health, {shield, poison + 6, recharge}}} end def poison(_) do nil end def recharge({playerMana, health, {shield, poison, recharge}}) when recharge == 0 do {229, {playerMana, health, {shield, poison, recharge + 5}}} end def recharge(_) do nil end def payManaCost({manaCost, {playerMana, health, effects}}) do {manaCost, {playerMana - manaCost, health, effects}} end def shieldEffect({manaCost, {playerMana, health, {shield, poison, recharge}}}) when shield > 0 do {manaCost, {playerMana, health, {shield - 1, poison, recharge}}} end def shieldEffect(state) do state end def difficultyCheck( {manaCost, {playerMana, {playerHp, enemyHp, enemyDamage, hardMode}, status}} ) when hardMode == true do {manaCost, {playerMana, {playerHp - 1, enemyHp, enemyDamage, hardMode}, status}} end def difficultyCheck(state) do state end def poisonEffect( {manaCost, {playerMana, {playerHp, enemyHp, enemyDamage, hardMode}, {shield, poison, recharge}}} ) when poison > 0 do {manaCost, {playerMana, {playerHp, enemyHp - 3, enemyDamage, hardMode}, {shield, poison - 1, recharge}}} end def poisonEffect(state) do state end def rechargeEffect({manaCost, {playerMana, health, {shield, poison, recharge}}}) when recharge > 0 do {manaCost, {playerMana + 101, health, {shield, poison, recharge - 1}}} end def rechargeEffect(state) do state end def outcomeCheck({manaCost, {_, {_, enemyHp, _, _}, _}}) when enemyHp <= 0 do {manaCost, :victory} end def outcomeCheck({manaCost, {_, {playerHp, _, _, _}, _}}) when playerHp <= 0 do {manaCost, :defeat} end def outcomeCheck(state) do state end def enemyAttack( {manaCost, {playerMana, {playerHp, enemyHp, enemyDamage, hardMode}, {shield, poison, recharge}}} ) do armor = if shield > 0 do 7 else 0 end {manaCost, {playerMana, {playerHp - max(1, enemyDamage - armor), enemyHp, enemyDamage, hardMode}, {shield, poison, recharge}}} end def enemyAttack(state) do state end def nextRound(state) do playerImmediateEffects = [ magicMissile(state), drain(state), shield(state), poison(state), recharge(state) ] Enum.filter(playerImmediateEffects, &(!is_nil(&1))) |> Enum.filter(fn {manaCost, {playerMana, _, _}} -> manaCost <= playerMana end) |> Enum.map(&payManaCost/1) |> Enum.map(&shieldEffect/1) |> Enum.map(&poisonEffect/1) |> Enum.map(&rechargeEffect/1) |> Enum.map(&outcomeCheck/1) |> Enum.map(&enemyAttack/1) |> Enum.map(&outcomeCheck/1) |> Enum.map(&difficultyCheck/1) |> Enum.map(&shieldEffect/1) |> Enum.map(&poisonEffect/1) |> Enum.map(&rechargeEffect/1) |> Enum.map(&outcomeCheck/1) end def findMinManaCostVictory(:victory) do 0 end def findMinManaCostVictory(state) do Enum.filter(nextRound(state), fn {_, :defeat} -> false _ -> true end) |> Enum.map(fn {manaCost, nextState} -> remainingManaCost = findMinManaCostVictory(nextState) if remainingManaCost == nil do nil else manaCost + remainingManaCost end end) |> Enum.filter(&(!is_nil(&1))) |> Enum.min(fn -> nil end) end end manaCost = BattleSimulator.findMinManaCostVictory({500, {50, enemyHp, enemyDamage, false}, {0, 0, 0}}) IO.puts("Minimum mana cost to Victory: #{manaCost}") manaCostHardMode = BattleSimulator.findMinManaCostVictory({500, {50, enemyHp, enemyDamage, true}, {0, 0, 0}}) IO.puts("Minimum mana cost to Victory (Hard Mode): #{manaCostHardMode}")