From 76190642c31da1f2ea6fd015a4e2b8237e2cb44b Mon Sep 17 00:00:00 2001 From: bt Date: Sun, 9 Mar 2025 14:16:31 +0800 Subject: [PATCH] Implemented the second part of day2 --- CMakeLists.txt | 3 +- src/day2.cpp | 128 +++++++++++++++++++++++++++++++++++++++++++++ src/day2_part1.cpp | 83 ----------------------------- 3 files changed, 130 insertions(+), 84 deletions(-) create mode 100644 src/day2.cpp delete mode 100644 src/day2_part1.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 063eebe..c39d6ae 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -14,10 +14,11 @@ endfunction(add_challenge) add_challenge(day1) add_challenge(day1_part2) -add_challenge(day2_part1) +add_challenge(day2) add_challenge(day3) add_challenge(day4) add_challenge(day5) +add_challenge(day6) ### Day 7 add_challenge(day7) diff --git a/src/day2.cpp b/src/day2.cpp new file mode 100644 index 0000000..3750e52 --- /dev/null +++ b/src/day2.cpp @@ -0,0 +1,128 @@ +#include +#include +#include +#include +#include +#include +#include + +namespace { +void parseLevels(const std::string& report, std::vector& out) +{ + if(report.empty()) return; + + const char *begin = &report[0]; + const char * const end = &report[report.size()]; + std::from_chars_result result{}; + int val{}; + while(begin != end) { + result = std::from_chars(begin, end, val); + if(result.ec != std::errc()) { + std::cerr << "\nreport: " << report; + throw std::logic_error{"Invalid report input string"}; + } + begin = std::min(result.ptr + 1, end); + out.push_back(val); + } +} + +template +[[nodiscard]] bool safe(std::span levels, const unsigned tolerate, const S& safer) noexcept +{ + unsigned violations = 0; + for (size_t i = 1; i < levels.size() && violations <= tolerate; ++i) { + if (!safer(levels[i-1], levels[i])) { + ++violations; + + if(i + 1 == levels.size()) break; + + + // We have to figure out which value(i - 1 or i) have to be *removed. + // This results in the next index position. + + // Edge cases: + // 59 62 65 64 65 <- 65(2nd) element should be removed + // 36 34 36 33 30 <- 34(2nd) element should be removed + + if(safer(levels[i - 1], levels[i + 1])) { + ++i; + } + // Prev should be deleted because current and next elements are safe. + else if(safer(levels[i], levels[i + 1])) { + // However, we must check that current element and prevous elements build safe consequence. + if(i > 1) { + // 1 2 3 10 12. 2 and 10 are not safe. This results in increase in violations. + violations += !safer(levels[i - 2], levels[i]); + } + } + } + } + return violations <= tolerate; +} + +[[nodiscard]] constexpr bool isLevelsSafe(int largelLevel, int smallerLevel) noexcept +{ + const auto diff{largelLevel - smallerLevel}; + return 1 <= diff && diff <= 3; +} + +[[nodiscard]] bool isAscendingSafe(std::span levels, unsigned tolerate) noexcept +{ + return safe(levels, tolerate, [](int smaller, int larger) { + return isLevelsSafe(larger, smaller); + }); +} + +[[nodiscard]] bool isDescendingSafe(std::span levels, unsigned tolerate) noexcept +{ + return safe(levels, tolerate, [](int larger, int smaller) { + return isLevelsSafe(larger, smaller); + }); +} + +[[nodiscard]] bool isSafe(std::span levels, unsigned tolerateLevel) noexcept +{ + return isAscendingSafe(levels, tolerateLevel) || isDescendingSafe(levels, tolerateLevel); +} + +void printHelp() +{ + std::cerr << "\nUsage:\n" + << "The program requires 2 args: (part1, part2) and the path to the file." + << "\nFor example, ./day2 part1 data/day2.txt"; +} + +} //< anonymous namespace + +int main(int argc, char *argv[]) { + if(argc != 3) { + printHelp(); + return 1; + } + std::string_view task{argv[1]}; + if(task != "part1" && task != "part2") { + std::cerr << "\nfirst arg can be either `part1` or `part2`\n"; + printHelp(); + return 1; + } + + std::ifstream ifile(argv[2]); + if(!ifile) { + std::cerr << "\nFile cannot be open"; + return 1; + } + + const unsigned tolerateLevel = task == "part1" ? 0 : 1; + + size_t count{}; + std::string report; + std::vector levels; + while(std::getline(ifile, report)) { + levels.clear(); + parseLevels(report, levels); + count += isSafe(levels, tolerateLevel); + } + std::cout << "\nanswer: " << count; + + return 0; +} \ No newline at end of file diff --git a/src/day2_part1.cpp b/src/day2_part1.cpp deleted file mode 100644 index 1c2f7c3..0000000 --- a/src/day2_part1.cpp +++ /dev/null @@ -1,83 +0,0 @@ -#include -#include -#include -#include -#include -#include - -namespace { -void parseLevels(const std::string& report, std::vector& out) -{ - if(report.empty()) return; - - const char *begin = &report[0]; - const char * const end = &report[report.size()]; - std::from_chars_result result{}; - int val{}; - while(begin != end) { - result = std::from_chars(begin, end, val); - if(result.ec != std::errc()) { - std::cerr << "\nreport: " << report; - throw std::logic_error{"Invalid report input string"}; - } - begin = std::min(result.ptr + 1, end); - out.push_back(val); - } -} - -[[nodiscard]] constexpr bool isLevelsSafe(int largelLevel, int smallerLevel) noexcept -{ - const auto diff{largelLevel - smallerLevel}; - return 1 <= diff && diff <= 3; -} - - -[[nodiscard]] bool isAscendingSafe(const std::vector& levels) noexcept -{ - return std::adjacent_find(levels.begin(), levels.end(), [](int smallerLevel, int largerLevel) { - return !isLevelsSafe(largerLevel, smallerLevel); - }) == levels.end(); -} - -[[nodiscard]] bool isDescendingSafe(const std::vector& levels) noexcept -{ - return std::adjacent_find(levels.rbegin(), levels.rend(), [](int smallerLevel, int largerLevel) { - return !isLevelsSafe(largerLevel, smallerLevel); - }) == levels.rend(); -} - -[[nodiscard]] bool isSafe(const std::vector& levels) noexcept -{ - if(levels.front() < levels.back()) { - return isAscendingSafe(levels); - } - return isDescendingSafe(levels); -} -} //< anonymous namespace - -int main(int argc, char *argv[]) { - if(argc != 2) { - std::cerr << "\nUsage:\n" - << "The program requires the path to the file."; - return 1; - } - - std::ifstream ifile(argv[1]); - if(!ifile) { - std::cerr << "\nFile cannot be open"; - return 1; - } - - size_t count{}; - std::string report; - std::vector levels; - while(std::getline(ifile, report)) { - // std::cerr << "\nline " << report; - levels.clear(); - parseLevels(report, levels); - count += isSafe(levels); - } - std::cout << count; - - return 0; -} \ No newline at end of file