Day 21.0
change
commit b083499a1fca54e2215b332ef834c8b679b3e2c4
author Alpha Chen <alpha.chen@gmail.com>
date
parent 76af1b6d
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