Day 5: Binary Boarding
Puzzle Description
Today is a Ruby day, but my original solution was so embarassingly incompetent that I decided to completely rewrite it for 2024.
Solution Summary
- Parse input into a
List[...]
-
Solve
- For
part1
, get the highest seat id. - For
part2
, find a gap in seat ids and get that seat.
- For
Part 1
The wording in the problem description is intentionally poor. If you look at both of the solutions, neither actually require knowing anything about the row id or column id outside of using them to assemble the seat id. And convieniently, the input is already assembled such that when translated to binary it's already the seat id.
To show this, let's consider Seat Id 7:
0000000111
Let's split it into it's row and column ids:
0000000
111
The seat id is the row id * 8 + the column id. We know that * 8 is just another way of saying << 3, so let's align our bits:
0000000
111
We're back where we started, proving that our initial input is the seat id when converted to binary. (This isn't a rigorous proof but it's good enough for us!)
This is why I omitted the type parameter of the List
earlier - it's just a List[Int]
.
Let's parse our input into a List[Int]
:
def parse(str: String): List[Int] =
str.linesIterator.map: line =>
line.foldLeft(0):
case (acc, c) =>
c match
case 'F' | 'L' => acc << 1
case 'B' | 'R' => (acc << 1) + 1
.toList
def parse(input)
lines = input.strip().split("\n")
values = []
for line in lines do
line = line.strip()
line.gsub!(/F/, "0")
line.gsub!(/B/, "1")
line.gsub!(/L/, "0")
line.gsub!(/R/, "1")
values.push(line.to_i 2)
end
return values
end
Then part1
is taking this input and getting the max:
Part 2
Part 2 also seems hard, but look closer at the wording - it specifies specifically a seat that is missing from our input but with seats on either side of it.
This sounds like a perfect use for sliding
:
def part2(input: List[Int]): Int =
input.sorted.sliding(2).collectFirst:
Function.unlift:
case List(l, r) =>
Option.when(r - l > 1)(r - 1)
.get
Ruby uses each_cons
for its sliding.
def part2(input)
resWindow = input.sort.each_cons(2).detect { |chunk| chunk[1] - chunk[0] > 1 }
return resWindow[1] - 1
end
Final Code
def parse(str: String): List[Int] =
str.linesIterator.map: line =>
line.foldLeft(0):
case (acc, c) =>
c match
case 'F' | 'L' => acc << 1
case 'B' | 'R' => (acc << 1) + 1
.toList
def part1(input: List[Int]): Int = input.max
def part2(input: List[Int]): Int =
input.sorted.sliding(2).collectFirst:
Function.unlift:
case List(l, r) =>
Option.when(r - l > 1)(r - 1)
.get
def parse(input)
lines = input.strip().split("\n")
values = []
for line in lines do
line = line.strip()
line.gsub!(/F/, "0")
line.gsub!(/B/, "1")
line.gsub!(/L/, "0")
line.gsub!(/R/, "1")
values.push(line.to_i 2)
end
return values
end
def part1(input)
return input.max
end
def part2(input)
resWindow = input.sort.each_cons(2).detect { |chunk| chunk[1] - chunk[0] > 1 }
return resWindow[1] - 1
end
Benchmark
Part 1
Mean |
Error |
|
---|---|---|
JVM |
0.593 ms |
+/- 0.094 ms |
JS |
1.171 ms |
+/- 0.355 ms |
Native |
0.247 ms |
+/- 0.001 ms |
Part 2
Mean |
Error |
|
---|---|---|
JVM |
1.726 ms |
+/- 0.140 ms |
JS |
3.456 ms |
+/- 0.084 ms |
Native |
0.456 ms |
+/- 0.002 ms |