Skip to content

Commit f55220e

Browse files
2024 day 5
1 parent e39b12c commit f55220e

File tree

4 files changed

+1695
-0
lines changed

4 files changed

+1695
-0
lines changed
+127
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
class DataReader
2+
def initialize(file)
3+
@lines = File.readlines(file).map(&:strip)
4+
end
5+
6+
def empty_line_index
7+
@lines.find_index { |line| line == "" }
8+
end
9+
10+
def ordering_rules
11+
@lines[0...empty_line_index] || []
12+
end
13+
14+
def pages_to_produce
15+
return [] unless empty_line_index
16+
17+
@lines[empty_line_index + 1..] || []
18+
end
19+
20+
def create_rules
21+
ordering_rules.map do |line|
22+
Rule.new(*line.split("|").map(&:to_i))
23+
end
24+
end
25+
26+
def create_page_updates
27+
pages_to_produce.map do |line|
28+
PageUpdate.new(line.split(",").map(&:to_i))
29+
end
30+
end
31+
end
32+
33+
class Rule
34+
attr_reader :page, :before_page
35+
36+
def initialize(page, before_page)
37+
@page = page
38+
@before_page = before_page
39+
end
40+
end
41+
42+
class PageUpdate
43+
attr_reader :numbers
44+
45+
def initialize(numbers)
46+
@numbers = numbers
47+
end
48+
49+
def middle_number
50+
numbers.empty? ? nil : numbers[numbers.length / 2]
51+
end
52+
end
53+
54+
class RuleScanner
55+
def initialize(rules, page_updates)
56+
@rules = rules
57+
@page_updates = page_updates
58+
end
59+
60+
def valid_updates
61+
@page_updates.select do |page_update|
62+
orderer = Orderer.new(@rules, page_update)
63+
orderer.in_order?
64+
end
65+
end
66+
67+
def invalid_updates
68+
@page_updates.reject do |page_update|
69+
orderer = Orderer.new(@rules, page_update)
70+
orderer.in_order?
71+
end
72+
end
73+
74+
def valid_total
75+
valid_updates.sum(&:middle_number)
76+
end
77+
78+
def invalid_total
79+
invalid_updates.map do |page_update|
80+
orderer = Orderer.new(@rules, page_update)
81+
orderer.reorder
82+
end.sum(&:middle_number)
83+
end
84+
end
85+
86+
class Orderer
87+
attr_reader :page_update
88+
89+
def initialize(rules, page_update)
90+
@rules = rules
91+
@page_update = page_update
92+
end
93+
94+
def rules_for(number)
95+
@rules.select { |r| r.page == number }
96+
end
97+
98+
def scoped_rules_for(number)
99+
rules_for(number).select { |r| page_update.numbers.include? r.before_page }
100+
end
101+
102+
def lower_priority_pages_for(number)
103+
scoped_rules_for(number).map(&:before_page).sort.uniq
104+
end
105+
106+
def in_order?
107+
numbers = page_update.numbers
108+
109+
numbers.each_with_index do |number, i|
110+
rest = numbers[i + 1..]
111+
return false unless lower_priority_pages_for(number).all? { |n| rest.include?(n) }
112+
end
113+
114+
true
115+
end
116+
117+
def reorder
118+
numbers = page_update.numbers
119+
return [] if numbers.empty?
120+
121+
numbers.sort! do |a, b|
122+
lower_priority_pages_for(a).include?(b) ? -1 : 1
123+
end
124+
125+
PageUpdate.new(numbers)
126+
end
127+
end
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,166 @@
1+
require "spec_helper"
2+
require "advent_05_print_queue"
3+
4+
describe "Puzzle" do
5+
context "with sample data" do
6+
let(:data) do
7+
DataReader.new("./spec/fixtures/advent-05-sample.txt")
8+
end
9+
10+
it "knows the ordering rules" do
11+
result = data.ordering_rules
12+
13+
expected = [
14+
"47|53", "97|13", "97|61", "97|47", "75|29", "61|13", "75|53",
15+
"29|13", "97|29", "53|29", "61|53", "97|53", "61|29", "47|13",
16+
"75|47", "97|75", "47|61", "75|61", "47|29", "75|13", "53|13"
17+
]
18+
19+
expect(result).to eq(expected)
20+
end
21+
22+
it "knows the pages to produce" do
23+
result = data.pages_to_produce
24+
25+
expected = %w[
26+
75,47,61,53,29
27+
97,61,53,29,13
28+
75,29,13
29+
75,97,47,61,53
30+
61,13,29
31+
97,13,75,29,47
32+
]
33+
34+
expect(result).to eq(expected)
35+
end
36+
37+
it "knows the rules for a number" do
38+
rules = data.create_rules
39+
page_update = PageUpdate.new([])
40+
41+
orderer = Orderer.new(rules, page_update)
42+
43+
rules = orderer.rules_for(75)
44+
45+
before_numbers = rules.map(&:before_page).sort
46+
47+
expect(before_numbers).to eq([13, 29, 47, 53, 61])
48+
end
49+
50+
it "knows which pages a number goes before" do
51+
rules = data.create_rules
52+
page_update = PageUpdate.new([75, 47, 61, 53, 29])
53+
54+
orderer = Orderer.new(rules, page_update)
55+
56+
expect(orderer.lower_priority_pages_for(75)).to eq([29, 47, 53, 61])
57+
end
58+
59+
it "knows if a page update is in the right order" do
60+
rules = data.create_rules
61+
page_update = PageUpdate.new([75, 47, 61, 53, 29])
62+
63+
orderer = Orderer.new(rules, page_update)
64+
65+
expect(orderer.in_order?).to be(true)
66+
end
67+
68+
it "knows if a page update is in the right order 2" do
69+
rules = data.create_rules
70+
page_update = PageUpdate.new([97, 61, 53, 29, 13])
71+
72+
orderer = Orderer.new(rules, page_update)
73+
74+
expect(orderer.in_order?).to be(true)
75+
end
76+
77+
it "knows if a page update is in the right order 3" do
78+
rules = data.create_rules
79+
page_update = PageUpdate.new([75, 29, 13])
80+
81+
orderer = Orderer.new(rules, page_update)
82+
83+
expect(orderer.in_order?).to be(true)
84+
end
85+
86+
it "knows when a page update is in the wrong order" do
87+
rules = data.create_rules
88+
page_update = PageUpdate.new([75, 97, 47, 61, 53])
89+
90+
orderer = Orderer.new(rules, page_update)
91+
92+
expect(orderer.in_order?).to be(false)
93+
end
94+
95+
it "knows when a page update is in the wrong order 2" do
96+
rules = data.create_rules
97+
page_update = PageUpdate.new([61, 13, 29])
98+
99+
orderer = Orderer.new(rules, page_update)
100+
101+
expect(orderer.in_order?).to be(false)
102+
end
103+
104+
it "knows when a page update is in the wrong order 3" do
105+
rules = data.create_rules
106+
page_update = PageUpdate.new([97, 13, 75, 29, 47])
107+
108+
orderer = Orderer.new(rules, page_update)
109+
110+
expect(orderer.in_order?).to be(false)
111+
end
112+
113+
it "knows the correctly-ordered updates" do
114+
rules = data.create_rules
115+
page_updates = data.create_page_updates
116+
117+
scanner = RuleScanner.new(rules, page_updates)
118+
119+
valid = scanner.valid_updates
120+
121+
expect(valid.length).to be(3)
122+
123+
expect(valid[0].numbers).to eq([75, 47, 61, 53, 29])
124+
expect(valid[0].middle_number).to eq(61)
125+
126+
expect(valid[1].numbers).to eq([97, 61, 53, 29, 13])
127+
expect(valid[1].middle_number).to eq(53)
128+
129+
expect(valid[2].numbers).to eq([75, 29, 13])
130+
expect(valid[2].middle_number).to eq(29)
131+
end
132+
133+
it "knows how to reorder" do
134+
rules = data.create_rules
135+
page_update = PageUpdate.new([75, 97, 47, 61, 53])
136+
137+
orderer = Orderer.new(rules, page_update)
138+
139+
expect(orderer.reorder.numbers).to eq([97, 75, 47, 61, 53])
140+
end
141+
end
142+
143+
context "with puzzle data" do
144+
let(:data) do
145+
DataReader.new("./spec/fixtures/advent-05.txt")
146+
end
147+
148+
it "solves part i" do
149+
rules = data.create_rules
150+
page_updates = data.create_page_updates
151+
152+
scanner = RuleScanner.new(rules, page_updates)
153+
154+
expect(scanner.valid_total).to eq(6260)
155+
end
156+
157+
it "solves part ii" do
158+
rules = data.create_rules
159+
page_updates = data.create_page_updates
160+
161+
scanner = RuleScanner.new(rules, page_updates)
162+
163+
expect(scanner.invalid_total).to eq(5346)
164+
end
165+
end
166+
end
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
47|53
2+
97|13
3+
97|61
4+
97|47
5+
75|29
6+
61|13
7+
75|53
8+
29|13
9+
97|29
10+
53|29
11+
61|53
12+
97|53
13+
61|29
14+
47|13
15+
75|47
16+
97|75
17+
47|61
18+
75|61
19+
47|29
20+
75|13
21+
53|13
22+
23+
75,47,61,53,29
24+
97,61,53,29,13
25+
75,29,13
26+
75,97,47,61,53
27+
61,13,29
28+
97,13,75,29,47

0 commit comments

Comments
 (0)