1
0
Fork 0

solve 2015 day 21

This commit is contained in:
sloane 2024-11-27 14:18:00 -05:00
parent 9b99d9446a
commit 132bd5dcf3
Signed by: sloanelybutsurely
SSH key fingerprint: SHA256:8SBnwhl+RY3oEyQxy1a9wByPzxWM0x+/Ejc+sIlY5qQ
3 changed files with 195 additions and 2 deletions

View file

@ -17,7 +17,7 @@
| | | [1] | [2] | [3] | [4] | [5] | | | | [1] | [2] | [3] | [4] | [5] |
| [6] | [7] | [8] | [9] | [10] | [11] | [12] | | [6] | [7] | [8] | [9] | [10] | [11] | [12] |
| [13] | [14] | [15] | [16]| [17] | [18] | [19] | | [13] | [14] | [15] | [16]| [17] | [18] | [19] |
| [20] | 21 | 22 | 23 | 24 | 25 | | | [20] | [21] | 22 | 23 | 24 | 25 | |
[1]: ./lib/2015/1.ex [1]: ./lib/2015/1.ex
[2]: ./lib/2015/2.ex [2]: ./lib/2015/2.ex
@ -39,3 +39,4 @@
[18]: ./lib/2015/18.ex [18]: ./lib/2015/18.ex
[19]: ./lib/2015/19.ex [19]: ./lib/2015/19.ex
[20]: ./lib/2015/20.ex [20]: ./lib/2015/20.ex
[21]: ./lib/2015/21.ex

192
2015/lib/2015/21.ex Normal file
View file

@ -0,0 +1,192 @@
import AOC
aoc 2015, 21 do
defmodule Weapon do
defstruct [:damage, :cost]
def damage(%Weapon{damage: damage}), do: damage
def damage(nil), do: 0
def cost(%Weapon{cost: cost}), do: cost
def cost(nil), do: 0
end
defmodule Armor do
defstruct [:defence, :cost]
def defence(%Armor{defence: defence}), do: defence
def defence(nil), do: 0
def cost(%Armor{cost: cost}), do: cost
def cost(nil), do: 0
end
defmodule Ring do
defstruct damage: 0, defence: 0, cost: 0
def defence(%Ring{defence: defence}), do: defence || 0
def defence(nil), do: 0
def damage(%Ring{damage: damage}), do: damage || 0
def damage(nil), do: 0
def cost(%Ring{cost: cost}), do: cost
def cost(nil), do: 0
end
defmodule Character do
defstruct [:hp, :weapon, :armor, rings: []]
def cost(%Character{} = char) do
Weapon.cost(char.weapon) + Armor.cost(char.armor) +
(Enum.map(char.rings, &Ring.cost/1) |> Enum.sum())
end
def damage(%Character{rings: rings} = char) do
Weapon.damage(char.weapon) + (Enum.map(rings, &Ring.damage/1) |> Enum.sum())
end
def defence(%Character{rings: rings} = char) do
Armor.defence(char.armor) + (Enum.map(rings, &Ring.defence/1) |> Enum.sum())
end
def equip(%Character{weapon: nil} = char, %Weapon{} = weapon) do
%Character{char | weapon: weapon}
end
def equip(%Character{armor: nil} = char, %Armor{} = armor) do
%Character{char | armor: armor}
end
def equip(%Character{rings: []} = char, %Ring{} = ring) do
%Character{char | rings: [ring]}
end
def equip(%Character{rings: [ring_1]} = char, %Ring{} = ring_2) do
%Character{char | rings: [ring_1, ring_2]}
end
def equip(%Character{} = char, nil), do: char
def attack(%Character{} = attacker, %Character{} = defender) do
damage = damage(attacker)
defence = defence(defender)
%Character{defender | hp: defender.hp - max(1, damage - defence)}
end
def defeated?(%Character{hp: hp}), do: hp <= 0
end
def p1 do
boss = boss()
for weapon <- weapons(),
armor <- [nil | armor()],
ring_1 <- [nil | rings()],
ring_2 <- [nil | rings() -- [ring_1]],
reduce: :infinity do
min_cost ->
player =
%Character{hp: 100}
|> Character.equip(weapon)
|> Character.equip(armor)
|> Character.equip(ring_1)
|> Character.equip(ring_2)
curr_cost = Character.cost(player)
if curr_cost < min_cost do
case battle(player, boss) do
{:win, _} -> curr_cost
{:lose, _} -> min_cost
end
else
min_cost
end
end
end
def p2 do
boss = boss()
for weapon <- weapons(),
armor <- [nil | armor()],
ring_1 <- [nil | rings()],
ring_2 <- [nil | rings() -- [ring_1]],
reduce: 0 do
max_cost ->
player =
%Character{hp: 100}
|> Character.equip(weapon)
|> Character.equip(armor)
|> Character.equip(ring_1)
|> Character.equip(ring_2)
curr_cost = Character.cost(player)
if max_cost < curr_cost do
case battle(player, boss) do
{:win, _} -> max_cost
{:lose, _} -> curr_cost
end
else
max_cost
end
end
end
defp battle(player, boss) do
player_attack = fn {player, boss} ->
boss = Character.attack(player, boss)
if Character.defeated?(boss) do
{:halt, {:win, player}}
else
{:cont, {player, boss}}
end
end
boss_attack = fn {player, boss} ->
player = Character.attack(boss, player)
if Character.defeated?(player) do
{:halt, {:lose, player}}
else
{:cont, {player, boss}}
end
end
Stream.cycle([player_attack, boss_attack])
|> Enum.reduce_while({player, boss}, fn turn, characters ->
turn.(characters)
end)
end
defp boss do
%Character{hp: 103, weapon: %Weapon{damage: 9}, armor: %Armor{defence: 2}}
end
defp weapons do
[
%Weapon{cost: 8, damage: 4},
%Weapon{cost: 10, damage: 5},
%Weapon{cost: 25, damage: 6},
%Weapon{cost: 40, damage: 7},
%Weapon{cost: 74, damage: 8}
]
end
defp armor do
[
%Armor{cost: 13, defence: 1},
%Armor{cost: 31, defence: 2},
%Armor{cost: 53, defence: 3},
%Armor{cost: 75, defence: 4},
%Armor{cost: 102, defence: 5}
]
end
defp rings do
[
%Ring{cost: 25, damage: 1},
%Ring{cost: 50, damage: 2},
%Ring{cost: 100, damage: 3},
%Ring{cost: 20, defence: 1},
%Ring{cost: 40, defence: 2},
%Ring{cost: 80, defence: 3}
]
end
end

View file

@ -6,7 +6,7 @@
| Year | Stars | Languages | | Year | Stars | Languages |
| - | - | - | | - | - | - |
| [2015] | **40/50** 🌟 | Elixir | | [2015] | **42/50** 🌟 | Elixir |
| 2016 | | | | 2016 | | |
| [2017] | **18/50** 🌟 | Haskell | | [2017] | **18/50** 🌟 | Haskell |
| 2018 | | | | 2018 | | |