Day 21: RPG Simulator 20XX
Section
defmodule RPGShop do
@weapons [
%{name: "Dagger", cost: 8, damage: 4, armor: 0},
%{name: "Shortsword", cost: 10, damage: 5, armor: 0},
%{name: "Warhammer", cost: 25, damage: 6, armor: 0},
%{name: "Longsword", cost: 40, damage: 7, armor: 0},
%{name: "Greataxe", cost: 74, damage: 8, armor: 0}
]
@armor [
%{name: "None", cost: 0, damage: 0, armor: 0},
%{name: "Leather", cost: 13, damage: 0, armor: 1},
%{name: "Chainmail", cost: 31, damage: 0, armor: 2},
%{name: "Splintmail", cost: 53, damage: 0, armor: 3},
%{name: "Bandedmail", cost: 75, damage: 0, armor: 4},
%{name: "Platemail", cost: 102, damage: 0, armor: 5}
]
@rings [
%{name: "None", cost: 0, damage: 0, armor: 0},
%{name: "Damage +1", cost: 25, damage: 1, armor: 0},
%{name: "Damage +2", cost: 50, damage: 2, armor: 0},
%{name: "Damage +3", cost: 100, damage: 3, armor: 0},
%{name: "Defense +1", cost: 20, damage: 0, armor: 1},
%{name: "Defense +2", cost: 40, damage: 0, armor: 2},
%{name: "Defense +3", cost: 80, damage: 0, armor: 3}
]
def simulate_combat(player, boss) do
player_damage = max(player.damage - boss.armor, 1)
boss_damage = max(boss.damage - player.armor, 1)
player_turns = div(boss.hp + player_damage - 1, player_damage)
boss_turns = div(player.hp + boss_damage - 1, boss_damage)
player_turns <= boss_turns
end
def item_combinations do
for weapon <- @weapons,
armor <- @armor,
ring1 <- @rings,
ring2 <- @rings,
ring1 != ring2 do
[weapon, armor, ring1, ring2]
end
end
def calculate_cost_and_stats(items) do
Enum.reduce(items, %{cost: 0, damage: 0, armor: 0}, fn item, acc ->
%{
cost: acc.cost + item.cost,
damage: acc.damage + item.damage,
armor: acc.armor + item.armor
}
end)
end
def find_least_cost_winning_combination(player_hp, boss_hp, boss_damage, boss_armor) do
boss = %{hp: boss_hp, damage: boss_damage, armor: boss_armor}
item_combinations()
|> Enum.map(&calculate_cost_and_stats/1)
|> Enum.filter(fn stats ->
player = %{hp: player_hp, damage: stats.damage, armor: stats.armor}
simulate_combat(player, boss)
end)
|> Enum.min_by(& &1.cost)
end
def find_most_expensive_losing_combination(player_hp, boss_hp, boss_damage, boss_armor) do
boss = %{hp: boss_hp, damage: boss_damage, armor: boss_armor}
item_combinations()
|> Enum.map(&calculate_cost_and_stats/1)
|> Enum.filter(fn stats ->
player = %{hp: player_hp, damage: stats.damage, armor: stats.armor}
not simulate_combat(player, boss)
end)
|> Enum.max_by(& &1.cost, fn -> %{cost: 0, damage: 0, armor: 0} end)
end
end
player_hp = 100
boss_hp = 109
boss_damage = 8
boss_armor = 2
RPGShop.find_least_cost_winning_combination(player_hp, boss_hp, boss_damage, boss_armor)
%{cost: 111, damage: 7, armor: 4}
RPGShop.find_most_expensive_losing_combination(player_hp, boss_hp, boss_damage, boss_armor)
%{cost: 188, damage: 7, armor: 3}