diff --git a/2023/ruby/day_18.rb b/2023/ruby/day_18.rb
new file mode 100644
index 0000000..6351c65
--- /dev/null
+++ b/2023/ruby/day_18.rb
@@ -0,0 +1,93 @@
+require "matrix"
+
+# part one
+plan = ARGF.read.scan(/([UDLR])\s+(\d+)\s+\(#(\w+)\)/)
+ .map { [_1.to_sym, _2.to_i, _3] }
+
+# part two
+plan = plan.map { _3.chars }.map {|hex|
+ dist = hex[0..4].join.to_i(16)
+ dir = "RDLU".chars.fetch(hex.last.to_i).to_sym
+ [dir, dist]
+}
+
+deltas = { U: [-1,0], D: [1,0], L: [0,-1], R: [0,1] }
+
+points = plan.inject([[0,0]]) {|points, (dir, dist, _)|
+ delta = deltas.fetch(dir).map { _1*dist }
+ next_point = points.last.zip(delta).map { _1 + _2 }
+ points << next_point
+}
+
+p [
+ (0.5 * points.each_cons(2).sum { Matrix.columns(_1).determinant }.abs).to_i,
+ plan.select {|dir,_,_| %i[ D L ].include?(dir) }.sum { _2 },
+ 1,
+].sum
+
+# trench = plan.inject([[0,0]]) {|trench, (dir, dist, _)|
+# delta = deltas.fetch(dir)
+# dist.times do
+# trench << trench.last.zip(delta).map { _1 + _2 }
+# end
+# trench
+# }.uniq
+
+# y_range = Range.new(*trench.map(&:first).minmax)
+# x_range = Range.new(*trench.map(&:last).minmax)
+
+# y_range.each do |y|
+# x_range.each do |x|
+# print trench.include?([y,x]) ? ?# : ?.
+# end
+# puts
+# end
+
+# part one
+# lagoon = trench.to_set
+
+# seed = [1, x_range.find {|x| lagoon.include?([0,x]) } + 1]
+# frontier = [seed]
+# until frontier.empty?
+# current = frontier.shift
+# lagoon << current
+
+# frontier.concat(
+# [[-1,0],[1,0],[0,-1],[0,1]].map {|delta|
+# current.zip(delta).map { _1 + _2 }
+# }.reject { lagoon.include?(_1) || frontier.include?(_1) }
+# )
+# end
+
+# p lagoon.size
+
+# row_counts = {}
+
+# count = trench.size
+# y_range.each do |y|
+# inside = nil
+
+# segments = trench.select {|y_,_| y_ == y }.map(&:last).sort.slice_when { _1+1 != _2 }
+# if row_count = row_counts[segments]
+# count += row_count
+# next
+# end
+
+# row_count = 0
+# segments.each do |segment|
+# if [-1,1].all? {|dy| [[y+dy,segment[0]],[y+dy,segment[-1]]].any? { trench.include?(_1) }}
+# if inside
+# row_count += segment[0] - inside
+# inside = nil
+# else
+# inside = segment[-1] + 1
+# end
+# elsif inside
+# row_count += segment[0] - inside
+# inside = segment[-1] + 1
+# end
+# end
+# row_counts[segments] = row_count
+# count += row_count
+# end
+# p count