Advent of Code 2024 - Day 2

Red-Nosed Reports

Written December 21st, 2024 | Attempted December 4th, 2024

Tags:Advent of CodeC++

With Day 2, we can see that the problems are starting to get a little more challenging. Not significantly so but I can tell that it's going to get harder.

# Part 1

The sample input we're given is as follows:

17 6 4 2 1
21 2 7 8 9
39 7 6 2 1
41 3 2 4 5
58 6 4 4 1
61 3 6 7 9

Each row is a “report” containing some X number of integers. For the first part, we have to confirm whether or not each report adheres to the following set of conditions:

  • From left to right, the integers are either strictly increasing or strictly decreasing.
  • The magnitude of the change between two adjacent numbers is at most three (strictly changing implies a minimum of one, since zeros would mean the set is now monotonic).

It is based on these requirements that the problem boils down to the following set of steps:

  1. Read in the file (arguably the biggest part).
  2. Parse each report and check if it meets the conditions.
  3. Count the number of reports that meet the conditions.

Based on these steps, I implemented the following C++ solution (this time opting to use ranges the first time around):

1#include <bits/stdc++.h>
2
3int main()
4{
5  std::ifstream input("day2_input.txt");
6
7  int safe_reports = 0;
8  std::vector<int> report;
9
10  std::string line;
11  std::string_view delim = " ";
12  while (std::getline(input, line))
13  {
14    auto split = line | std::views::split(delim)
15                      | std::views::transform([](auto rng) {
16                        auto str = std::string(&*rng.begin(), std::ranges::distance(rng));
17                        return std::stoi(str);
18                      });
19    for (auto num : split) {
20      report.push_back(num);
21    }
22
23    auto differences = report | std::views::adjacent_transform<2>([](int a, int b){
24      return b - a;
25    });
26    bool strictly_increasing_or_decreasing = std::ranges::all_of(differences, [](int i){ return i > 0; }) ||
27                                             std::ranges::all_of(differences, [](int i){ return i < 0; });
28    bool changes_within_range = std::ranges::all_of(differences, [](int i){ return std::abs(i) <= 3 && std::abs(i) >= 1; });
29    if (strictly_increasing_or_decreasing && changes_within_range) {
30      ++safe_reports;
31    }
32
33    report.clear();
34  }
35
36  std::cout << "safe reports: " << safe_reports << std::endl;
37  input.close();
38  return 0;
39}

# Part 2

The second part of the problem now asks us to include reports that could be turned “safe” by removing a single integer from the report. Since these input sizes are not monstrously big, we can brute force this by permuting the reports and checking if they meet the conditions. It's admittedly not the most efficient solution but it works.

1#include <bits/stdc++.h>
2
3std::vector<std::vector<int>> permute_vector(std::vector<int> &vec)
4{
5  std::vector<std::vector<int>> permutations;
6  for (int i = 0; i < vec.size(); ++i) {
7    std::vector<int> dest = vec;
8    dest.erase(dest.begin() + i);
9    permutations.push_back(dest);
10  }
11
12  return permutations;
13}
14
15int main()
16{
17  std::ifstream input("day2_input.txt");
18
19  int safe_reports = 0;
20  std::vector<int> report;
21  std::vector<std::vector<int>> failed_reports;
22
23  std::string line;
24  std::string_view delim = " ";
25  while (std::getline(input, line))
26  {
27    auto split = line | std::views::split(delim)
28                      | std::views::transform([](auto rng) {
29                        auto str = std::string(&*rng.begin(), std::ranges::distance(rng));
30                        return std::stoi(str);
31                      });
32    for (auto num : split) {
33      report.push_back(num);
34    }
35
36    auto differences = report | std::views::adjacent_transform<2>([](int a, int b){
37      return b - a;
38    });
39
40    bool strictly_increasing_or_decreasing = std::ranges::all_of(differences, [](int i){ return i > 0; }) ||
41                                             std::ranges::all_of(differences, [](int i){ return i < 0; });
42    bool changes_within_range = std::ranges::all_of(differences, [](int i){ return std::abs(i) <= 3 && std::abs(i) >= 1; });
43    if (strictly_increasing_or_decreasing && changes_within_range) {
44      ++safe_reports;
45    } else {
46      failed_reports.push_back(report);
47    }
48
49    report.clear();
50  }
51
52  for (auto report : failed_reports)
53  {
54    auto permutations = permute_vector(report);
55    for (auto permutation : permutations)
56    {
57      auto differences = permutation | std::views::adjacent_transform<2>([](int a, int b) {
58        return b - a;
59      });
60      bool strictly_increasing_or_decreasing = std::ranges::all_of(differences, [](int i){ return i > 0; }) ||
61                                               std::ranges::all_of(differences, [](int i){ return i < 0; });
62      bool changes_within_range = std::ranges::all_of(differences, [](int i){ return std::abs(i) <= 3 && std::abs(i) >= 1; });
63      if (strictly_increasing_or_decreasing && changes_within_range) {
64        ++safe_reports;
65        break;
66      }
67    }
68  }
69
70  std::cout << "safe reports: " << safe_reports << std::endl;
71
72  input.close();
73  return 0;
74}

Again, not terribly hard problems but they're definitely starting to ramp up a bit—whereas the first day was just a simple “read an array in and sort them” kind of problem, this one requires a little more thought into the algorithm used, but still not too bad.