Things are starting to get tricky—or at least they seem that way at first glance.
The problem
In part 1, whenever validation failed, the report was automatically marked as unsafe, and I could move on.
However, in part 2, the problem dampener changes things. Now, I have to check if a report could be considered safe if one of the levels is excluded.
The plan
Initially, I assumed (mistakenly) that a report with only one issue could be saved and that reports with more than one issue would remain unsafe, no matter what. To test this theory, I modified the validate method to count the number of issues in the report:
issues_found = 0
def validate(ar, action)
ar.each_cons(2) do |a,b|
return false if issues_found > 1
issues_found += 1 if a.send(action.to_sym, b) || !(a - b).abs.between?(1,3)
end
true
end
When I ran this on the example dataset, it marked all reports as safe—because every report had only one problematic level. Clearly, my assumption was wrong.
A closer look
After carefully re-reading the description, it became clear that I needed to check whether removing a single level from an unsafe report would make it safe. The problem wasn’t that the reports themselves were overly problematic—it was me rushing through the requirements late at night. :D
The only solution that came to mind was to validate all “permutations” of the array, where each permutation is the original array with one level removed.
A new method: validate_permutations
To implement this, I created a validate_permutations method. This method takes a report (array) as input, removes one level at a time, and runs the original validate method on the resulting array.
Here’s the method:
def validate_permutations(ar)
(0...ar.size).each do |i|
permutation = ar.reject.with_index { |_, j| j == i }
action = permutation[0] > permutation[1] ? "<" : ">"
return true if validate(permutation, action)
end
false
end
Key details:
- For each index in the array, a new “permutation” is created by removing the element at that index.
- The validate method is called on the modified array.
- If any permutation passes validation, the method returns true immediately. Otherwise, it completes the loop and returns false.
The final task is to count how many reports have at least one valid permutation.
Final implementation
input = File.read("puzzle_input.txt")
# input = File.read "./example.txt"
reports = input.split("\n").map { |i| i.split.map(&:to_i) }
safe_count = 0
loose_safe_count = 0
def validate(ar, action)
ar.each_cons(2) do |a,b|
return false if a.send(action.to_sym, b) || !(a - b).abs.between?(1,3)
end
true
end
def validate_permutations(ar)
(0...ar.size).each do |i|
permutation = ar.reject.with_index { |_, j| j == i }
action = permutation[0] > permutation[1] ? "<" : ">"
return true if validate(permutation, action)
end
false
end
reports.each do |r|
action = r[0] > r[1] ? "<" : ">"
safe_count += 1 if validate(r, action)
loose_safe_count += 1 if validate_permutations(r)
end
puts safe_count
puts loose_safe_count
Possible improvements
For larger datasets, the validate_permutations method could become inefficient due to the creation and validation of multiple permutations. Here are some potential optimizations:
-
Stop Early: The method already stops early when a valid permutation is found, but ensuring minimal intermediate array creation could reduce overhead.
-
Custom Validation Logic: Instead of generating permutations, tweak the validate method to simulate the removal of one level during the iteration, bypassing the need for a separate method.
These optimizations could further improve performance, but for the current problem size, the implementation works well as-is.