diff --git a/2022/ruby/day_14.rb b/2022/ruby/day_14.rb
new file mode 100644
index 0000000..55d3c87
--- /dev/null
+++ b/2022/ruby/day_14.rb
@@ -0,0 +1,62 @@
+scan = ARGF.read.lines(chomp: true)
+
+cave = scan.each.with_object({}) {|line, cave|
+ line.split(" -> ")
+ .map { _1.split(?,).map(&:to_i) }
+ .each_cons(2) {|(ax,ay),(bx,by)|
+ Range.new(*[ax, bx].sort).each do |x|
+ Range.new(*[ay, by].sort).each do |y|
+ cave[[x, y]] = ?#
+ end
+ end
+ }
+}
+
+x_min, x_max = cave.keys.map(&:first).minmax
+y_min, y_max = cave.keys.map(&:last).minmax
+
+inspect_cave = -> {
+ puts (0..y_max+1).map {|y|
+ (x_min-1..x_max+1).map {|x|
+ cave[[x, y]] || ?.
+ }.join
+ }.join("\n")
+}
+
+# part one
+# sands = 0
+# loop do
+# inspect_cave.()
+# sands += 1
+
+# pos = [500, 0]
+# while next_pos = [0, -1, 1].map {|dx| pos.zip([dx, 1]).map { _1 + _2 }}.find { cave[_1].nil? }
+# pos = next_pos
+# break if pos[1] >= y_max
+# end
+# break if pos[1] >= y_max
+
+# cave[pos] = ?o
+# end
+
+# inspect_cave.()
+# p sands-1
+
+# part two
+cave.default_proc = ->(h,(x,y)) { h[[x, y]] = y == y_max + 2 ? ?# : nil }
+sands = 0
+loop do
+ # inspect_cave.()
+ sands += 1
+
+ pos = [500, 0]
+ while next_pos = [0, -1, 1].map {|dx| pos.zip([dx, 1]).map { _1 + _2 }}.find { cave[_1].nil? }
+ pos = next_pos
+ end
+ break if pos == [500, 0]
+
+ cave[pos] = ?o
+end
+
+inspect_cave.()
+p sands