Skip to content

Commit ba88800

Browse files
committed
Day 12
1 parent 7bb4d07 commit ba88800

File tree

1 file changed

+230
-0
lines changed

1 file changed

+230
-0
lines changed

2021/day12.rb

+230
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,230 @@
1+
#!/usr/bin/env ruby
2+
3+
require "minitest/autorun"
4+
require "forwardable"
5+
require "set"
6+
7+
class Cave
8+
def inspect
9+
"#<#{@label}@#{@visits}>"
10+
end
11+
12+
def to_s
13+
"#{@label}@#{visits}"
14+
end
15+
16+
def self.factory(letter)
17+
case letter
18+
when "start", "end" then BigCave.new(letter)
19+
when 'a'..'z' then LittleCave.new(letter)
20+
when 'A'..'Z' then BigCave.new(letter)
21+
else
22+
fail ArgumentError, letter
23+
end
24+
end
25+
26+
attr_accessor :visits, :label
27+
def initialize(letter)
28+
@label = letter
29+
@visits = 0
30+
end
31+
32+
def visit
33+
self.visits += 1
34+
end
35+
36+
def end?
37+
label == "end"
38+
end
39+
end
40+
41+
class BigCave < Cave
42+
def visited?
43+
false
44+
end
45+
end
46+
47+
class LittleCave < Cave
48+
def visited?
49+
@visits > 0
50+
end
51+
end
52+
53+
class Path
54+
attr_accessor :path, :visited
55+
def initialize(path = [])
56+
@path = path
57+
@visited = Set.new(path)
58+
end
59+
60+
def inspect
61+
"#<Path##{object_id} #{path.inspect} @visited=#{visited.inspect}"
62+
end
63+
64+
def <<(label)
65+
visited << label
66+
path << label
67+
end
68+
69+
def visited?(label)
70+
visited.include?(label)
71+
end
72+
end
73+
74+
class Graph
75+
extend Forwardable
76+
attr_accessor :hash, :nodes
77+
def initialize(hash = Hash.new { |h,k| h[k] = Array.new })
78+
@hash = hash
79+
end
80+
81+
def_delegators :@hash, :[], :[]=, :<<, :inspect
82+
83+
def paths
84+
self["start"]
85+
.flat_map { |node| paths2(node, [Path.new(["start"])])}
86+
.compact
87+
.map(&:path)
88+
.to_set
89+
end
90+
91+
# Takes a node and a list of paths
92+
# Computes all paths from node to :end:
93+
def paths2(node, paths)
94+
$stderr.puts "paths2(#{node}, #{paths})"
95+
96+
paths.each do |path|
97+
path << node
98+
end
99+
100+
return paths if node == "end"
101+
102+
self[node].flat_map { |visit|
103+
paths.flat_map { |path|
104+
next if path.visited?(visit)
105+
paths2(visit, [path])
106+
}
107+
}
108+
end
109+
end
110+
111+
Class.new(Minitest::Test) do
112+
def self.name
113+
File.basename(__FILE__, '.rb').capitalize
114+
end
115+
116+
LINES = DATA.readlines.map(&:chomp).map(&:freeze).freeze
117+
118+
def parser(lines)
119+
graph = Graph.new.tap { |graph|
120+
lines.map { |line|
121+
v0, v1 = line.strip.split("-")
122+
graph[v0] << v1 unless v1 == "start" || v0 == "end"
123+
graph[v1] << v0 unless v0 == "start" || v1 == "end"
124+
}
125+
}
126+
end
127+
128+
def setup
129+
@sample_input = parser(LINES)
130+
@input = begin
131+
parser(File.read("inputs/#{File.basename(__FILE__, '.rb')}.txt").lines)
132+
rescue Errno::ENOENT
133+
[]
134+
end
135+
end
136+
137+
def test_cave_factory
138+
assert_instance_of BigCave, Cave.factory("start")
139+
assert_instance_of BigCave, Cave.factory("end")
140+
assert_instance_of BigCave, Cave.factory("B")
141+
assert_instance_of BigCave, Cave.factory("Y")
142+
assert_instance_of LittleCave, Cave.factory("n")
143+
assert_instance_of LittleCave, Cave.factory("y")
144+
end
145+
146+
def test_little_cave_visiting
147+
cave = LittleCave.new("test")
148+
assert !cave.visited?
149+
150+
cave.visit
151+
152+
assert cave.visited?
153+
end
154+
155+
def test_big_cave_visiting
156+
cave = BigCave.new("test")
157+
assert !cave.visited?
158+
159+
cave.visit
160+
161+
assert !cave.visited?
162+
end
163+
164+
def test_parser
165+
expected = { "start"=>%w[A b], "A"=>%w[c b end], "c"=>%w[A], "b"=>%w[A d end], "d"=>%w[b] }
166+
167+
assert_equal expected.inspect, @sample_input.inspect
168+
end
169+
170+
def test_path_search_start_end
171+
graph = parser(["start-end"])
172+
173+
# A graph of start-end, is just that
174+
paths = graph.paths
175+
176+
assert_equal [%w[start end]].to_set, graph.paths
177+
end
178+
179+
def test_path_search_start_A_end
180+
graph = parser(<<~EOT.each_line)
181+
start-A
182+
A-end
183+
EOT
184+
185+
paths = graph.paths
186+
187+
assert_equal [%w[start A end]].to_set, graph.paths
188+
end
189+
190+
def test_path_search_start_A_B_end
191+
graph = parser(<<~EOT.each_line)
192+
start-A
193+
start-B
194+
A-B
195+
A-end
196+
B-end
197+
EOT
198+
199+
paths = graph.paths
200+
201+
expected = [
202+
%w[start A end],
203+
%w[start B end],
204+
%w[start A B end],
205+
%w[start B A end],
206+
].to_set
207+
208+
assert_equal expected, graph.paths
209+
end
210+
211+
def test_part1a
212+
end
213+
214+
def test_part1b
215+
end
216+
217+
def test_part2a
218+
end
219+
220+
def test_part_2b
221+
end
222+
end
223+
__END__
224+
start-A
225+
start-b
226+
A-c
227+
A-b
228+
b-d
229+
A-end
230+
b-end

0 commit comments

Comments
 (0)