Advent of Code '24 — Day 2

Day two! And things are already tricksy!

Today's puzzle was about deciphering reports, to determine if each level in the report is deemed safe.

A safe "level" is on that each number in the row increases or decreases only, and only by a maximum of 3.

Link to puzzle

Part 1

In the first part, we needed to work out how many levels were safe.

E.g. given:

7 6 4 2 1
1 2 7 8 9
9 7 6 2 1
1 3 2 4 5
8 6 4 4 1
1 3 6 7 9

Only the first and last reports are safe as they're the only ones that follow the 2 rules.

A simple enough solution for this one:

  def part1(input)
    input.map do |line|
      dir = nil
      vals = line.split(" ").each_cons(2).map do |first, second|
        next_dir = if Integer(first) > Integer(second)
          :increase
        elsif Integer(first) < Integer(second)
          :decrease
        else
          :no_change
        end
        dir = next_dir if dir.nil?
        (Integer(first) - Integer(second)).abs > 3 || dir != next_dir
      end

      vals.all?(false) ? 1 : 0
    end.sum
  end

First, mapping over each line, checking items in pairs of 2 and checking if we're constantly increasing or decreasing, or if we're stepping in increments greater than 3.

⭐️ Success! Albeit a little verbose!

Part 2

The second part was tricky!

My solution to the first part wasn't really flexible enough to accommodate the second part, so a rewrite was required.

In part 2, we learned that some levels can be made safe, by removing an entry in the level. I totally misread this, and thought that only otherwise unsafe entries could be removed. So made a number of wrong entries before realising that any entry could be removed to make the level safe.

So here's my brute-force attempt after banging my head against it for a bit:

def part2(input)
  input.count { |levels|
    levels = levels.split(" ")
    safe?(levels) || safe_by_removing_level?(levels)
  }
end

def safe?(report_result)
  res = report_result.each_cons(2).map do |first, second|
    if Integer(first) < Integer(second) && Integer(second) - Integer(first) <= 3
      :increase
    elsif Integer(first) > Integer(second) && Integer(first) - Integer(second) <= 3
      :decrease
    else
      :bad_step
    end
  end

  res.all? { |x| x == :increase } || res.all? { |x| x == :decrease }
end

def safe_by_removing_level?(report)
  0.upto(report.size).find do |i|
    sub = report.dup
    sub.delete_at(i)
    safe?(sub)
  end
end

This time, we still check that every level is safe, but if it's not, we then remove one entry from the level at a time to see if it would be safe.

⭐️ Success!

Performance

Even though this was brute-forced, it still performed OK. Though each level only had ~6 entries to loop through, so not a huge set of data to work with.

day 02
├─ part 1 — 0.002105s
╰─ part 2 — 0.008360s

Day two: https://github.com/dNitza/advent-of-code/tree/main/2024/02

Published

by Daniel Nitsikopoulos

in posts

Tagged

© 2010 - 2024 Daniel Nitsikopoulos. All rights reserved.

🕸💍  →