I woke up early (not by choice) and read today’s part 1 puzzle description in bed and at breakfast coffee. The part 1 was relatively quick and easy to implement, among replying to emails. Code spoilers below.
Then I had other things to do, went for a run (7 km, really windy, cloudy and partly rainy), then sauna and lunch. I tend to take Monday mornings to myself since I teach from 12 until 18.30.
In between supervising students in Zoom (and handling emails), I worked on the part 2 of the puzzle. I tried to find a fancy mathematical way to handle the part 2 change to part 1. Couldn’t get any of those attempts to work (I am not a math person) so I ended up in recursively trying to remove a single element from the data to see if it then satisfies the puzzle problem.
I’m sure there is a fancy solution using cleverly all the Swift algorithms and data structures etc. My solutions tend to be plain old imperative programming style kind of solutions. Well at least I got it working, and it’s not slow:
$ swift run -c release AdventOfCode 2 --benchmark
Building for production...
[5/5] Linking AdventOfCode
Build of product 'AdventOfCode' complete! (1.84s)
Executing Advent of Code challenge 2...
Part 1: 663
Part 2: 692
Part 1 took 0.003531042 seconds, part 2 took 0.005103042 seconds.
This year’s puzzles (so far) do not require any especially fast ways to do things, though, data sets are small… Maybe they’ll come later.
Here’s the part 1 solution for today’s puzzle:
func part1() -> Any {
var safeLines = 0
let matrix: Matrix<Int> = parse(data: data)
for lineNumber in 0..<matrix.height {
let line = matrix.elements[lineNumber]
let direction = line[1]! - line[0]!
if abs(direction) > 3 || direction == 0 {
continue
}
var lineIsSafe = true
for columnNumber in 2..<line.count {
let nextDirection = line[columnNumber]! - line[columnNumber - 1]!
if direction.signum() != nextDirection.signum() || abs(nextDirection) > 3 || nextDirection == 0 {
lineIsSafe = false
break
}
}
if lineIsSafe {
safeLines += 1
}
}
return safeLines
}
For Part 2 I implemented a recursive function to solve the partially safe lines to see if changing one level would satisfy the puzzle, limiting the level of recursion so that only one attempt is made to fix an issue in one of the items in the levels by removing it:
private func isSafe(_ line: [Int?], level: Int) -> Bool {
var diffs: [Int] = []
for index in 1..<line.count {
diffs.append(line[index]! - line[index - 1]!)
}
let tooLargeDiffsCount = diffs.filter( { abs($0) > 3 } ).count
let noDiffCount = diffs.filter( { $0 == 0 } ).count
var dirChangeCount = 0
for index in 1..<diffs.count {
if diffs[index] != diffs[index - 1] {
if diffs[index].signum() != diffs[index - 1].signum() {
dirChangeCount += 1
}
}
}
if tooLargeDiffsCount == 0 && noDiffCount == 0 && dirChangeCount == 0 {
return true
} else if level < 1 {
for index in 0..<line.count {
var attempt = line
attempt.remove(at: index)
if isSafe(attempt, level: level + 1) {
return true
}
}
}
return false
}
I might check later how others have solved the puzzle to see if I’d get some inspiration for other styles of solving the puzzle in Swift.