Skip to content

Commit

Permalink
2024 day 5
Browse files Browse the repository at this point in the history
  • Loading branch information
invalidusrname committed Dec 8, 2024
1 parent 133cfaf commit ebed17e
Show file tree
Hide file tree
Showing 4 changed files with 1,695 additions and 0 deletions.
127 changes: 127 additions & 0 deletions 2024/ruby/lib/advent_05_print_queue.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
class DataReader
def initialize(file)
@lines = File.readlines(file).map(&:strip)
end

def empty_line_index
@lines.find_index { |line| line == "" }
end

def ordering_rules
@lines[0...empty_line_index] || []
end

def pages_to_produce
return [] unless empty_line_index

@lines[empty_line_index + 1..] || []
end

def create_rules
ordering_rules.map do |line|
Rule.new(*line.split("|").map(&:to_i))
end
end

def create_page_updates
pages_to_produce.map do |line|
PageUpdate.new(line.split(",").map(&:to_i))
end
end
end

class Rule
attr_reader :page, :before_page

def initialize(page, before_page)
@page = page
@before_page = before_page
end
end

class PageUpdate
attr_reader :numbers

def initialize(numbers)
@numbers = numbers
end

def middle_number
numbers.empty? ? nil : numbers[numbers.length / 2]
end
end

class RuleScanner
def initialize(rules, page_updates)
@rules = rules
@page_updates = page_updates
end

def valid_updates
@page_updates.select do |page_update|
orderer = Orderer.new(@rules, page_update)
orderer.in_order?
end
end

def invalid_updates
@page_updates.reject do |page_update|
orderer = Orderer.new(@rules, page_update)
orderer.in_order?
end
end

def valid_total
valid_updates.sum(&:middle_number)
end

def invalid_total
invalid_updates.map do |page_update|
orderer = Orderer.new(@rules, page_update)
orderer.reorder
end.sum(&:middle_number)
end
end

class Orderer
attr_reader :page_update

def initialize(rules, page_update)
@rules = rules
@page_update = page_update
end

def rules_for(number)
@rules.select { |r| r.page == number }
end

def scoped_rules_for(number)
rules_for(number).select { |r| page_update.numbers.include? r.before_page }
end

def lower_priority_pages_for(number)
scoped_rules_for(number).map(&:before_page).sort.uniq
end

def in_order?
numbers = page_update.numbers

numbers.each_with_index do |number, i|
rest = numbers[i + 1..]
return false unless lower_priority_pages_for(number).all? { |n| rest.include?(n) }
end

true
end

def reorder
numbers = page_update.numbers
return [] if numbers.empty?

numbers.sort! do |a, b|
lower_priority_pages_for(a).include?(b) ? -1 : 1
end

PageUpdate.new(numbers)
end
end
166 changes: 166 additions & 0 deletions 2024/ruby/spec/advent_05_print_queue_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
require "spec_helper"
require "advent_05_print_queue"

describe "Puzzle" do
context "with sample data" do
let(:data) do
DataReader.new("./spec/fixtures/advent-05-sample.txt")
end

it "knows the ordering rules" do
result = data.ordering_rules

expected = [
"47|53", "97|13", "97|61", "97|47", "75|29", "61|13", "75|53",
"29|13", "97|29", "53|29", "61|53", "97|53", "61|29", "47|13",
"75|47", "97|75", "47|61", "75|61", "47|29", "75|13", "53|13"
]

expect(result).to eq(expected)
end

it "knows the pages to produce" do
result = data.pages_to_produce

expected = %w[
75,47,61,53,29
97,61,53,29,13
75,29,13
75,97,47,61,53
61,13,29
97,13,75,29,47
]

expect(result).to eq(expected)
end

it "knows the rules for a number" do
rules = data.create_rules
page_update = PageUpdate.new([])

orderer = Orderer.new(rules, page_update)

rules = orderer.rules_for(75)

before_numbers = rules.map(&:before_page).sort

expect(before_numbers).to eq([13, 29, 47, 53, 61])
end

it "knows which pages a number goes before" do
rules = data.create_rules
page_update = PageUpdate.new([75, 47, 61, 53, 29])

orderer = Orderer.new(rules, page_update)

expect(orderer.lower_priority_pages_for(75)).to eq([29, 47, 53, 61])
end

it "knows if a page update is in the right order" do
rules = data.create_rules
page_update = PageUpdate.new([75, 47, 61, 53, 29])

orderer = Orderer.new(rules, page_update)

expect(orderer.in_order?).to be(true)
end

it "knows if a page update is in the right order 2" do
rules = data.create_rules
page_update = PageUpdate.new([97, 61, 53, 29, 13])

orderer = Orderer.new(rules, page_update)

expect(orderer.in_order?).to be(true)
end

it "knows if a page update is in the right order 3" do
rules = data.create_rules
page_update = PageUpdate.new([75, 29, 13])

orderer = Orderer.new(rules, page_update)

expect(orderer.in_order?).to be(true)
end

it "knows when a page update is in the wrong order" do
rules = data.create_rules
page_update = PageUpdate.new([75, 97, 47, 61, 53])

orderer = Orderer.new(rules, page_update)

expect(orderer.in_order?).to be(false)
end

it "knows when a page update is in the wrong order 2" do
rules = data.create_rules
page_update = PageUpdate.new([61, 13, 29])

orderer = Orderer.new(rules, page_update)

expect(orderer.in_order?).to be(false)
end

it "knows when a page update is in the wrong order 3" do
rules = data.create_rules
page_update = PageUpdate.new([97, 13, 75, 29, 47])

orderer = Orderer.new(rules, page_update)

expect(orderer.in_order?).to be(false)
end

it "knows the correctly-ordered updates" do
rules = data.create_rules
page_updates = data.create_page_updates

scanner = RuleScanner.new(rules, page_updates)

valid = scanner.valid_updates

expect(valid.length).to be(3)

expect(valid[0].numbers).to eq([75, 47, 61, 53, 29])
expect(valid[0].middle_number).to eq(61)

expect(valid[1].numbers).to eq([97, 61, 53, 29, 13])
expect(valid[1].middle_number).to eq(53)

expect(valid[2].numbers).to eq([75, 29, 13])
expect(valid[2].middle_number).to eq(29)
end

it "knows how to reorder" do
rules = data.create_rules
page_update = PageUpdate.new([75, 97, 47, 61, 53])

orderer = Orderer.new(rules, page_update)

expect(orderer.reorder.numbers).to eq([97, 75, 47, 61, 53])
end
end

context "with puzzle data" do
let(:data) do
DataReader.new("./spec/fixtures/advent-05.txt")
end

it "solves part i" do
rules = data.create_rules
page_updates = data.create_page_updates

scanner = RuleScanner.new(rules, page_updates)

expect(scanner.valid_total).to eq(6260)
end

it "solves part ii" do
rules = data.create_rules
page_updates = data.create_page_updates

scanner = RuleScanner.new(rules, page_updates)

expect(scanner.invalid_total).to eq(5346)
end
end
end
28 changes: 28 additions & 0 deletions 2024/ruby/spec/fixtures/advent-05-sample.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
47|53
97|13
97|61
97|47
75|29
61|13
75|53
29|13
97|29
53|29
61|53
97|53
61|29
47|13
75|47
97|75
47|61
75|61
47|29
75|13
53|13

75,47,61,53,29
97,61,53,29,13
75,29,13
75,97,47,61,53
61,13,29
97,13,75,29,47
Loading

0 comments on commit ebed17e

Please sign in to comment.