Code for the Advent of Code event
aoc
advent-of-code
1# frozen_string_literal: true
2
3require 'matrix'
4
5class Grid
6 attr_reader :width, :height
7
8 def initialize(width, height, walls = nil)
9 @width = width
10 @height = height
11 @walls = Set.new
12 self.walls = walls if walls
13 end
14
15 def in_bounds?(pos)
16 x, y = pos.to_a
17 x >= 0 && x < width && y >= 0 && y < height
18 end
19
20 def passable?(pos)
21 !@walls.include? pos
22 end
23
24 def add_wall(pos)
25 @walls.add pos
26 end
27
28 def walls=(positions)
29 positions.each { add_wall _1 }
30 end
31
32 def neighbors(pos)
33 x, y = pos.to_a
34 adjacent = [[x + 1, y], [x - 1, y], [x, y - 1], [x, y + 1]].map { Vector[*_1] }
35 adjacent.reverse! if (x + y).even?
36 adjacent.select { in_bounds?(_1) && passable?(_1) }
37 end
38
39 def draw(**opts)
40 puts '___' * width
41 (0...height).each do |y|
42 (0...width).each do |x|
43 print draw_tile(Vector[x, y], opts)
44 end
45 puts
46 end
47 puts '~~~' * width
48 end
49
50 protected
51
52 def draw_tile(pos, opts)
53 return '###' unless passable?(pos)
54 return ' Z ' if opts.key?(:goal) && opts[:goal] == pos
55 return ' A ' if opts.key?(:start) && opts[:start] == pos
56 return ' @ ' if opts.key?(:path) && opts[:path].include?(pos)
57 if opts.key?(:point_to) && !opts[:point_to][pos].nil?
58 x1, y1 = pos.to_a
59 x2, y2 = opts[:point_to][pos].to_a
60 return ' ↑ ' if y2 == y1 - 1
61 return ' ↓ ' if y2 == y1 + 1
62 return ' ← ' if x2 == x1 - 1
63 return ' → ' if x2 == x1 + 1
64 end
65 return sprintf(' %-2d', opts[:number][pos]) if opts.key?(:number) && opts[:number].key?(pos)
66 ' . '
67 end
68end
69
70class GridWithWeights < Grid
71 def initialize(width, height, walls = nil)
72 super
73 @weights = Hash.new(1)
74 end
75
76 def add_weight(pos, weight)
77 @weights[pos] = weight
78 end
79
80 def cost(_source, destination)
81 @weights[destination]
82 end
83
84 protected
85
86 def draw_tile(pos, opts)
87 return sprintf(' %-2d', @weights[pos]) if opts[:weights]
88 super
89 end
90end