[2018][ruby][15.1] parse grid
diff --git a/2018/ruby/day_15.rb b/2018/ruby/day_15.rb
new file mode 100644
index 0000000..154d324
--- /dev/null
+++ b/2018/ruby/day_15.rb
@@ -0,0 +1,94 @@
+require "minitest"
+
+class Combat
+end
+
+class Grid
+ def self.parse(s)
+ occupied = {}
+ s.chomp.lines.each.with_index do |row, y|
+ row.chomp.chars.each.with_index do |pos, x|
+ case pos
+ when ?#
+ occupied[[y, x]] = :wall
+ when ?E
+ occupied[[y, x]] = Elf.new
+ when ?G
+ occupied[[y, x]] = Goblin.new
+ when ?.
+ else
+ fail "Invalid character: #{pos.inspect}"
+ end
+ end
+ end
+ self.new(occupied)
+ end
+
+ def initialize(occupied)
+ @occupied = occupied
+ end
+
+ def [](pos)
+ y, x = pos
+ @occupied[[y, x]]
+ end
+
+ def to_s
+ max_y = @occupied.keys.map(&:first).max
+ max_x = @occupied.keys.map(&:last).max
+ (0..max_y).map {|y|
+ (0..max_x).map {|x|
+ case @occupied[[y, x]]
+ when :wall
+ ?#
+ when Elf
+ ?E
+ when Goblin
+ ?G
+ when nil
+ ?.
+ else
+ fail "Unexpected object: #{@occupied[[y, x]]}"
+ end
+ }.join
+ }.join(?\n)
+ end
+end
+
+class TestGrid < Minitest::Test
+ def test_parse
+ grid = Grid.parse(<<~GRID)
+ #######
+ #E..G.#
+ #...#.#
+ #.G.#G#
+ #######
+ GRID
+
+ assert_equal :wall, grid[[0,0]]
+ assert_equal :wall, grid[[2,4]]
+ assert_equal :wall, grid[[4,6]]
+
+ assert_instance_of Elf, grid[[1,1]]
+ assert_instance_of Goblin, grid[[1,4]]
+ assert_instance_of Goblin, grid[[3,5]]
+
+ assert_nil grid[[1,2]]
+ assert_nil grid[[2,5]]
+ end
+end
+
+class Unit
+end
+
+class Elf < Unit
+end
+
+class Goblin < Unit
+end
+
+if __FILE__ == $0
+ require "minitest/autorun" and exit if ENV["TEST"]
+
+
+end