diff --git a/ruby/day_21.rb b/ruby/day_21.rb new file mode 100644 index 0000000..3e6945d --- /dev/null +++ b/ruby/day_21.rb @@ -0,0 +1,99 @@ +require "letters" + +class Dude < Struct.new(*%i[ name hp damage armor ]) + def attack!(dude) + damage = self.damage - dude.armor + damage = 1 unless damage > 0 + + dude.hp = dude.hp - damage + return unless dude.hp > 0 + + dude.attack!(self) + end + + def equipped(equipment) + self.class.new(name, hp, + self.damage + equipment.damage_bonus, + self.armor + equipment.armor_bonus) + end + + def win_against?(dude) + self_hp = self.hp + dude_hp = dude.hp + + self.attack!(dude) + result = (self.hp > 0) + + self.hp = self_hp + dude.hp = dude_hp + + result + end +end + +Item = Struct.new(*%i[ name cost damage armor ]) +NULL_ITEM = Item.new("Nothing", 0, 0, 0) + +class Equipment < Struct.new(*%i[ weapon armor rings ]) + attr_accessor *%i[ cost damage_bonus armor_bonus ] + + def initialize(*) + super + + equipment = [self.weapon, self.armor].concat(self.rings) + self.cost = equipment.map(&:cost).inject(:+) + self.damage_bonus = equipment.map(&:damage).inject(:+) + self.armor_bonus = equipment.map(&:armor).inject(:+) + end +end + +weapons, armors, rings = DATA.read.split("\n\n").map {|set| + set.split("\n").drop(1).map {|attrs| + attrs = attrs.split(/\s+/) + name = attrs[0..-4].join(" ") + attrs = attrs[-3..-1].map(&:to_i) + Item.new(name, *attrs) + } +} +armors << NULL_ITEM +rings << NULL_ITEM +rings = rings.combination(2).to_a +rings << [NULL_ITEM, NULL_ITEM] + +equipments = weapons.flat_map {|weapon| + armors.flat_map {|armor| + rings.flat_map {|rings| + Equipment.new(weapon, armor, rings) + } + } +}.sort_by {|e| e.cost } + +BOSS = Dude.new("boss", 100, 8, 2) +YOU = Dude.new("you", 100, 0, 0) + +puts equipments.find {|e| + YOU.equipped(e).win_against?(BOSS) +}.cost + +__END__ +Weapons: Cost Damage Armor +Dagger 8 4 0 +Shortsword 10 5 0 +Warhammer 25 6 0 +Longsword 40 7 0 +Greataxe 74 8 0 + +Armor: Cost Damage Armor +Leather 13 0 1 +Chainmail 31 0 2 +Splintmail 53 0 3 +Bandedmail 75 0 4 +Platemail 102 0 5 + +Rings: Cost Damage Armor +Damage +1 25 1 0 +Damage +2 50 2 0 +Damage +3 100 3 0 +Defense +1 20 0 1 +Defense +2 40 0 2 +Defense +3 80 0 3