diff --git a/.github/workflows/cmake-single-platform.yml b/.github/workflows/cmake-single-platform.yml index ecb34bf..0831f39 100644 --- a/.github/workflows/cmake-single-platform.yml +++ b/.github/workflows/cmake-single-platform.yml @@ -22,6 +22,7 @@ jobs: steps: - uses: actions/checkout@v4 with: + token: ${{ secrets.DATA_PAT }} submodules: recursive - name: Configure CMake diff --git a/CMakeLists.txt b/CMakeLists.txt index a2e84c3..063eebe 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,6 +1,8 @@ cmake_minimum_required(VERSION 3.20) project(advent2024 LANGUAGES CXX) +option(DAY7_CONSTEXPR "Enables computing solution at compile-time" OFF) + set(CMAKE_EXPORT_COMPILE_COMMANDS ON) add_compile_options(-Wall -Wextra -Wpedantic -O2) @@ -20,6 +22,9 @@ add_challenge(day5) ### Day 7 add_challenge(day7) target_compile_options(day7 PRIVATE -fconstexpr-ops-limit=3355443200) +if(DAY7_CONSTEXPR) + target_compile_options(day7 PRIVATE -DDAY7_CONSTEXPR) +endif() # Read the content of the input file file(READ ${CMAKE_CURRENT_SOURCE_DIR}/data/2024/day7.txt FILE_CONTENT) @@ -32,16 +37,16 @@ file(WRITE ${CMAKE_CURRENT_SOURCE_DIR}/data/2024/day7_constexpr.txt ${FILE_CONTE add_challenge(day8) -add_challenge(day9) +# add_challenge(day9) add_challenge(day10) add_challenge(day11) add_challenge(day12) add_challenge(day13) add_challenge(day14) -add_challenge(day15) -add_challenge(day16) -add_challenge(day17) -add_challenge(day18) +# add_challenge(day15) +# add_challenge(day16) +# add_challenge(day17) +# add_challenge(day18) add_challenge(day19) add_challenge(day24) add_challenge(day25) diff --git a/include/common_headers.hpp b/include/common_headers.hpp index c15740b..9120dbc 100644 --- a/include/common_headers.hpp +++ b/include/common_headers.hpp @@ -2,10 +2,13 @@ #include "utils/input_parser.hpp" #include +#include +#include #include #include #include #include +#include #include #include #include \ No newline at end of file diff --git a/include/utils/numeric_algorithm.hpp b/include/utils/numeric_algorithm.hpp index 679b3d6..2bffc75 100644 --- a/include/utils/numeric_algorithm.hpp +++ b/include/utils/numeric_algorithm.hpp @@ -1,6 +1,6 @@ #pragma once -#include +#include #include [[nodiscard]] constexpr size_t numOfDigits(uint64_t num) noexcept { diff --git a/src/day1.cpp b/src/day1.cpp index 2966dbb..a9c9041 100644 --- a/src/day1.cpp +++ b/src/day1.cpp @@ -1,4 +1,5 @@ #include +#include #include #include #include diff --git a/src/day11.cpp b/src/day11.cpp index 6114a68..2fa535c 100644 --- a/src/day11.cpp +++ b/src/day11.cpp @@ -2,6 +2,7 @@ #include "utils/numeric_algorithm.hpp" #include +#include #include diff --git a/src/day13.cpp b/src/day13.cpp new file mode 100644 index 0000000..284e5b5 --- /dev/null +++ b/src/day13.cpp @@ -0,0 +1,192 @@ +#include "common_headers.hpp" +#include +#include + +void printHelp() +{ + std::cerr << "\nUsage:\n" + << "The program requires 2 args: (part1, part2) and the path to the file." + << "\nFor example, ./day7 part1 data/day7.txt"; +} + +using Matrix = std::vector>; +using Vector = std::vector; + +struct EquationParams final +{ + Matrix coords; + Vector result; + + void appendXY(std::pair xy) { + coords.resize(2); + coords[0].push_back(xy.first); + coords[1].push_back(xy.second); + } + + void setResult(std::pair resultXY) { + result.push_back(resultXY.first); + result.push_back(resultXY.second); + } +}; + + +void gaussianElimination(Matrix& A, Vector& b) { + int n = A.size(); + + for (int i = 0; i < n; ++i) { + // Partial pivoting + int maxRow = i; + for (int k = i + 1; k < n; ++k) { + if (std::abs(A[k][i]) > std::abs(A[maxRow][i])) { + maxRow = k; + } + } + + // Swap rows in A and b + std::swap(A[i], A[maxRow]); + std::swap(b[i], b[maxRow]); + + // Check for singularity + if (std::abs(A[i][i]) < 1e-10) { + throw std::runtime_error("Matrix is singular or nearly singular"); + } + + // Eliminate column i below the pivot + for (int k = i + 1; k < n; ++k) { + double factor = A[k][i] / A[i][i]; + for (int j = i; j < n; ++j) { + A[k][j] -= factor * A[i][j]; + } + b[k] -= factor * b[i]; + } + } +} + +Vector backSubstitution(const Matrix& A, const Vector& b) { + int n = A.size(); + Vector x(n); + + for (int i = n - 1; i >= 0; --i) { + x[i] = b[i]; + for (int j = i + 1; j < n; ++j) { + x[i] -= A[i][j] * x[j]; + } + x[i] /= A[i][i]; + } + + return x; +} + +Vector solveLinearSystem(Matrix A, Vector b) { + gaussianElimination(A, b); + return backSubstitution(A, b); +} + +bool checkResultCorrectness(const Vector& result, double upperBoundValue) { + for(auto r : result) { + if(!std::isfinite(r) || std::abs(std::trunc(std::round(r)) - r) > 0.0003 || r > upperBoundValue) { + if(r > 100) { + } + std::cerr << "\nsolution incorrect: " << r ; + return false; + } + } + return true; +} + +size_t solveFirstPart(const std::vector& equationParams) { + constexpr size_t costButtonA{3}; + constexpr size_t costButtonB{1}; + size_t tokens{}; + + for(const auto& equation : equationParams) { + Vector result = solveLinearSystem(equation.coords, equation.result); + if(checkResultCorrectness(result, 100)) { + tokens += static_cast(std::round(result.at(0))) * costButtonA + static_cast(std::round(result.at(1)) * costButtonB); + } + } + return tokens; +} + +size_t solveSecondPart(const std::vector& equationParams) { + constexpr size_t costButtonA{3}; + constexpr size_t costButtonB{1}; + size_t tokens{}; + + for(const auto& equation : equationParams) { + Vector result = solveLinearSystem(equation.coords, equation.result); + if(checkResultCorrectness(result, 10000000000000)) { + tokens += static_cast(std::round(result.at(0))) * costButtonA + static_cast(std::round(result.at(1)) * costButtonB); + } + } + return tokens; +} + + +[[nodiscard]] std::pair parseXY(const std::string& input) { + std::pair values{}; + + // Regular expression to match the input pattern + std::regex pattern(R"((\w+ \w+|\w+): X[+=](\-?\d+), Y[+=](\-?\d+))"); + std::smatch matches; + + std::string::const_iterator searchStart(input.cbegin()); + std::regex_search(searchStart, input.cend(), matches, pattern); + // std::cerr << "\nx " << matches[2] << " y " << matches[3]; + return std::pair{std::stoll(matches[2]), std::stoll(matches[3])}; +} + +[[nodiscard]] EquationParams parse(const std::string& buttonA, const std::string& buttonB, const std::string& result) { + EquationParams params; + params.appendXY(parseXY(buttonA)); + params.appendXY(parseXY(buttonB)); + params.setResult(parseXY(result)); + return params; +} + +[[nodiscard]] std::vector parseEquationParams(const std::vector& inputLines) { + std::vector equationParams; + for(size_t i = 0; i + 3 <= inputLines.size();) { + if(inputLines[i].empty()) { + ++i; + } + else { + equationParams.emplace_back(parse(inputLines[i], inputLines[i + 1], inputLines[i + 2])); + i += 3; + } + } + return equationParams; +} + +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::vector inputVec; + readInput(argv[2], std::back_inserter(inputVec)); + std::vector equationParams = parseEquationParams(inputVec); + + + if(task == "part1") { + std::cout << solveFirstPart(equationParams); + } + else { + for(auto& param : equationParams) { + param.result[0] += 10000000000000; + param.result[1] += 10000000000000; + } + std::cout << solveSecondPart(equationParams); + } + + return 0; +} \ No newline at end of file diff --git a/src/day1_part2.cpp b/src/day1_part2.cpp index 8543138..3ac72dd 100644 --- a/src/day1_part2.cpp +++ b/src/day1_part2.cpp @@ -1,4 +1,5 @@ #include +#include #include #include #include diff --git a/src/day2_part1.cpp b/src/day2_part1.cpp new file mode 100644 index 0000000..1c2f7c3 --- /dev/null +++ b/src/day2_part1.cpp @@ -0,0 +1,83 @@ +#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 diff --git a/src/day4.cpp b/src/day4.cpp index 600dd31..93cb273 100644 --- a/src/day4.cpp +++ b/src/day4.cpp @@ -1,5 +1,8 @@ #include "common_headers.hpp" +#include + + /* Part 1. The task is to count the word `XMAS` horizontally, vertically, diagonally. diff --git a/src/day6.cpp b/src/day6.cpp index 9c5292a..b3f90ac 100644 --- a/src/day6.cpp +++ b/src/day6.cpp @@ -1,7 +1,7 @@ #include "common_headers.hpp" #include "Cursor.hpp" -#include +#include #include #include @@ -150,80 +150,6 @@ template std::cout << "\nmoves " << moves; return unique.size(); } -#if 0 -template -[[nodiscard]] size_t solveSecondPart2(TGraph& graph) -{ - std::vector> colObst(graph.size()); - std::vector> rowObst(graph[0].size()); - - for(int r = 0; r < graph.size(); ++r) { - for(int c = 0; c < graph[0].size(); ++c) { - if(graph[r][c] == '#') { - colObst[c].emplace(r); - rowObst[r].emplace(c); - } - } - } - - constexpr std::array figures{'|', '-', '|', '-'}; - auto [direction, row, col] = findInitialPosition(graph); - graph[row][col] = '.'; - Cursor cursor{direction, row, col}; - size_t moves{}; - while(cursor.x() < graph.size() && cursor.y() < graph[0].size() && cursor.x() >= 0 && cursor.y() >= 0) { - const auto curChar{graph[cursor.x()][cursor.y()]}; - - if(curChar == '#') { - cursor.stepBack(); - cursor.turnRight(); - } - else { - - if(curChar != '.') { - switch (cursor.direction()) - { - case Cursor::Direction::Right: - if(curChar & Dirs::Down) { - ++moves; - unique.emplace(cursor.x(), cursor.y(), cursor.direction()); - std::cerr << "\nhit right: " << cursor.x() << " " << cursor.y(); - } - break; - case Cursor::Direction::Up: - if(curChar & Dirs::Right) { - ++moves; - unique.emplace(cursor.x(), cursor.y(), cursor.direction()); - - std::cerr << "\nhit up: " << cursor.x() << " " << cursor.y(); - } - break; - case Cursor::Direction::Down: - if(curChar & Dirs::Left) { - ++moves; - unique.emplace(cursor.x(), cursor.y(), cursor.direction()); - - std::cerr << "\nhit down: " << cursor.x() << " " << cursor.y(); - } - break; - case Cursor::Direction::Left: - if(curChar & Dirs::Up) { - ++moves; - unique.emplace(cursor.x(), cursor.y(), cursor.direction()); - - std::cerr << "\nhit left: " << cursor.x() << " " << cursor.y(); - } - break; - } - } - - cursor.move(); - } - } - std::cout << "\nmoves " << moves; - return unique.size(); -} -#endif void printHelp() { diff --git a/src/day7.cpp b/src/day7.cpp index 19691fd..6871a7b 100644 --- a/src/day7.cpp +++ b/src/day7.cpp @@ -259,6 +259,8 @@ int main(int argc, char* argv[]) << " elapsed " << std::chrono::duration_cast(end-start).count(); } else if(task == "part1_constexpr") { + + #if defined(DAY7_CONSTEXPR) const auto constexpr_context = [] { constexpr size_t N{numOfEquastions(gInput)}; std::array equations{}; @@ -266,7 +268,6 @@ int main(int argc, char* argv[]) return equations; }; - constexpr auto equations{constexpr_context()}; constexpr auto firstPartAns{solveFirstPart(equations)}; std::cout << firstPartAns; @@ -274,6 +275,9 @@ int main(int argc, char* argv[]) // TODO: solving second part takes a lot of time and consumes lots of memory. // constexpr auto secondPartAns{solveSecondPart(equations)}; // std::cout << "\n" << secondPartAns; + #else + std::cout << "\ndisabled. DAY7_CONSTEXPR compile variable should be defined"; + #endif } else { std::cerr << "\nfirst arg can be either `part1`, `part2`, or `part1_constexpr`\n";