Compare commits
1 Commits
Author | SHA1 | Date | |
---|---|---|---|
d0dae5159b |
@ -3,9 +3,13 @@ Language: Cpp
|
|||||||
BasedOnStyle: LLVM
|
BasedOnStyle: LLVM
|
||||||
AccessModifierOffset: -4
|
AccessModifierOffset: -4
|
||||||
AlignAfterOpenBracket: DontAlign
|
AlignAfterOpenBracket: DontAlign
|
||||||
|
AlignOperands: DontAlign
|
||||||
AllowAllArgumentsOnNextLine: false
|
AllowAllArgumentsOnNextLine: false
|
||||||
|
#AllowAllConstructorInitializersOnNextLine: true
|
||||||
AllowAllParametersOfDeclarationOnNextLine: false
|
AllowAllParametersOfDeclarationOnNextLine: false
|
||||||
AllowShortFunctionsOnASingleLine: false
|
AllowShortFunctionsOnASingleLine: false
|
||||||
|
#BinPackArguments: true
|
||||||
|
#BinPackParameters: true
|
||||||
BreakBeforeBraces: Allman
|
BreakBeforeBraces: Allman
|
||||||
BreakBeforeConceptDeclarations: Always
|
BreakBeforeConceptDeclarations: Always
|
||||||
BreakBeforeTernaryOperators: true
|
BreakBeforeTernaryOperators: true
|
||||||
@ -39,4 +43,5 @@ SpaceBeforeCaseColon: true
|
|||||||
SpaceBeforeParens: ControlStatements
|
SpaceBeforeParens: ControlStatements
|
||||||
|
|
||||||
# Deprecated
|
# Deprecated
|
||||||
|
#AllowAllConstructorInitializersOnNextLine: true
|
||||||
AlwaysBreakTemplateDeclarations: true
|
AlwaysBreakTemplateDeclarations: true
|
||||||
|
72
README.md
72
README.md
@ -16,96 +16,40 @@ The solution contains a unit test project to help troubleshoot issues and preven
|
|||||||
|
|
||||||
### Day 1: Historian Hysteria
|
### Day 1: Historian Hysteria
|
||||||
|
|
||||||
:mag_right: Puzzle: <https://adventofcode.com/2024/day/1>, :white_check_mark: Solver: [`HistorianHysteria.cpp`](src/HistorianHysteria.cpp)
|
:mag_right: Puzzle: <https://adventofcode.com/2024/day/1>, :white_check_mark: Solver: [`HistorianHysteria.cpp`](AdventOfCode2024/HistorianHysteria.cpp)
|
||||||
|
|
||||||
I'm using a `std::multiset` to collect and sort the values for both lists. This allows to use a single iteration of the left list and two iterations of the right list simultaneously to solve both parts. Nice application of iterators.
|
I'm using a `std::multiset` to collect and sort the values for both lists. This allows to use a single iteration of the left list and two iterations of the right list simultaneously to solve both parts. Nice application of iterators.
|
||||||
|
|
||||||
### Day 2: Red-Nosed Reports
|
### Day 2: Red-Nosed Reports
|
||||||
|
|
||||||
:mag_right: Puzzle: <https://adventofcode.com/2024/day/2>, :white_check_mark: Solver: [`RedNosedReports.cpp`](src/RedNosedReports.cpp)
|
:mag_right: Puzzle: <https://adventofcode.com/2024/day/2>, :white_check_mark: Solver: [`RedNosedReports.cpp`](AdventOfCode2024/RedNosedReports.cpp)
|
||||||
|
|
||||||
Here, we have a few conditionals to determine on the fly which of the numbers would make the report safe if dropped. The amount of cases is actually quite manageable.
|
Here, we have a few conditionals to determine on the fly which of the numbers would make the report safe if dropped. The amount of cases is actually quite manageable.
|
||||||
|
|
||||||
### Day 3: Mull It Over
|
### Day 3: Mull It Over
|
||||||
|
|
||||||
:mag_right: Puzzle: <https://adventofcode.com/2024/day/3>, :white_check_mark: Solver: [`MullItOver.cpp`](src/MullItOver.cpp)
|
:mag_right: Puzzle: <https://adventofcode.com/2024/day/3>, :white_check_mark: Solver: [`MullItOver.cpp`](AdventOfCode2024/MullItOver.cpp)
|
||||||
|
|
||||||
A simple [finite state machine](include/aoc/StringStateMachine.hpp) crawling along the input character by character solves both parts nicely. The algorithm tracks whether `mul` instructions are enabled or not, but ignores this setting for part 1.
|
A simple [finite state machine](AdventOfCode2024/StringStateMachine.h) crawling along the input character by character solves both parts nicely. The algorithm tracks whether `mul` instructions are enabled or not, but ignores this setting for part 1.
|
||||||
|
|
||||||
### Day 4: Ceres Search
|
### Day 4: Ceres Search
|
||||||
|
|
||||||
:mag_right: Puzzle: <https://adventofcode.com/2024/day/4>, :white_check_mark: Solver: [`CeresSearch.cpp`](src/CeresSearch.cpp)
|
:mag_right: Puzzle: <https://adventofcode.com/2024/day/4>, :white_check_mark: Solver: [`CeresSearch.cpp`](AdventOfCode2024/CeresSearch.cpp)
|
||||||
|
|
||||||
For this puzzle I added a class for [points in two-dimensional space](include/aoc/Point2.hpp), so I can use these for simplifying directional computations. With that, the algorithm looks for all `X` and `A` for part 1 and 2, respectively, and tries to find the remaining characters, starting from that `X` or `A`.
|
For this puzzle I added a class for [points in two-dimensional space](AdventOfCode2024/Point2.h), so I can use these for simplifying directional computations. With that, the algorithm looks for all `X` and `A` for part 1 and 2, respectively, and tries to find the remaining characters, starting from that `X` or `A`.
|
||||||
|
|
||||||
### Day 5: Print Queue
|
### Day 5: Print Queue
|
||||||
|
|
||||||
:mag_right: Puzzle: <https://adventofcode.com/2024/day/5>, :white_check_mark: Solver: [`PrintQueue.cpp`](src/PrintQueue.cpp)
|
:mag_right: Puzzle: <https://adventofcode.com/2024/day/5>, :white_check_mark: Solver: [`PrintQueue.cpp`](AdventOfCode2024/PrintQueue.cpp)
|
||||||
|
|
||||||
My implementation uses an ordering matrix (a two-dimensional boolean array) to track which page combinations are ordered, and then queries that matrix for each ordered combination of pages in a single line. The same matrix can then also be used for a custom sort function for part 2.
|
My implementation uses an ordering matrix (a two-dimensional boolean array) to track which page combinations are ordered, and then queries that matrix for each ordered combination of pages in a single line. The same matrix can then also be used for a custom sort function for part 2.
|
||||||
|
|
||||||
### Day 7: Bridge Repair
|
|
||||||
|
|
||||||
:mag_right: Puzzle: <https://adventofcode.com/2024/day/7>, :white_check_mark: Solver: [`BridgeRepair.cpp`](src/BridgeRepair.cpp)
|
|
||||||
|
|
||||||
The algorithm recursively tries the different operators from left to right until all the calibration numbers have been used. Since the result from any of the operator cannot be less than its operands, any branch of the recursion that surpasses the test value can be aborted.
|
|
||||||
|
|
||||||
### Day 8: Resonant Collinearity
|
|
||||||
|
|
||||||
:mag_right: Puzzle: <https://adventofcode.com/2024/day/8>, :white_check_mark: Solver: [`ResonantCollinearity.cpp`](src/ResonantCollinearity.cpp)
|
|
||||||
|
|
||||||
For any pair of antennas of the same frequency, the algorithm adds the difference of their positions to one of these positions and subtracts it from the other to find the antinodes. For part 2, this process is repeated for each of the two directions until the found antinodes leave the defined area. Counting duplicate antinodes is prevented by keeping track of their locations in a two-dimensional array covering the area.
|
|
||||||
|
|
||||||
### Day 9: Disk Fragmenter
|
|
||||||
|
|
||||||
:mag_right: Puzzle: <https://adventofcode.com/2024/day/9>, :white_check_mark: Solver: [`DiskFragmenter.cpp`](src/DiskFragmenter.cpp)
|
|
||||||
|
|
||||||
The input string is processed simultaneously from both ends to calculate the checksums of files or file blocks without actually moving them. The algorithm does not have to keep track of positions for specific files or file blocks. For part two, a list of empty spaces is maintained, but only up to the point where the front and back processing meets. An array of indices per possible file size, where each index tracks the first empty space that could hold a file of that size and advances as needed, is also kept to speed up the search in the list of empty spaces.
|
|
||||||
|
|
||||||
### Day 10: Hoof It
|
### Day 10: Hoof It
|
||||||
|
|
||||||
:mag_right: Puzzle: <https://adventofcode.com/2024/day/10>, :white_check_mark: Solver: [`HoofIt.cpp`](src/HoofIt.cpp)
|
:mag_right: Puzzle: <https://adventofcode.com/2024/day/10>, :white_check_mark: Solver: [`HoofIt.cpp`](AdventOfCode2024/HoofIt.cpp)
|
||||||
|
|
||||||
The algorithm starts at the trail ends (the `9`'s) and searches all options from there downwards until trailheads are reached. For each visited map location, it tracks unique reachable end points (for part 1) and number of possible paths to end points (for part 2). These values are accumulated, if during the search the same map location is encountered again.
|
The algorithm starts at the trail ends (the `9`'s) and searches all options from there downwards until trailheads are reached. For each visited map location, it tracks unique reachable end points (for part 1) and number of possible paths to end points (for part 2). These values are accumulated, if during the search the same map location is encountered again.
|
||||||
|
|
||||||
### Day 11: Plutonian Pebbles
|
|
||||||
|
|
||||||
:mag_right: Puzzle: <https://adventofcode.com/2024/day/11>, :white_check_mark: Solver: [`PlutonianPebbles.cpp`](src/PlutonianPebbles.cpp)
|
|
||||||
|
|
||||||
Since the order of the pebbles does not actually matter, we can simply blink from one line of pebbles to the next and count only cardinalities of each number in the current line.
|
|
||||||
|
|
||||||
### Day 12: Garden Groups
|
|
||||||
|
|
||||||
:mag_right: Puzzle: <https://adventofcode.com/2024/day/12>, :white_check_mark: Solver: [`GardenGroups.cpp`](src/GardenGroups.cpp)
|
|
||||||
|
|
||||||
The algorithm uses two stacks to flood-fill each garden region; one stack for all plots inside the region, and one stack to collect all other plots encountered during the flood-fill. Once a region has been traversed, it just pops the next plot from the latter stack. This gives us naturally the area of a region.
|
|
||||||
|
|
||||||
The perimeter of a region is the sum of neighbors for all plots encountered during the traversal, that are not in the same region, also counting neighbors outside the defined area. The number of sides of a region is equal to the number of corners of a region, which can also be counted per plot while doing the region traversal. A corner occurs for each pair of adjacent cardinal neighbors if they are either both outside of the current region, or if they are both inside the region and the diagonal neighbor in between them is not.
|
|
||||||
|
|
||||||
Adding up the products of area and perimeter, or area and corner count, per region gives us the solution.
|
|
||||||
|
|
||||||
### Day 13: Claw Contraption
|
|
||||||
|
|
||||||
:mag_right: Puzzle: <https://adventofcode.com/2024/day/13>, :white_check_mark: Solver: [`ClawContraption.cpp`](src/ClawContraption.cpp)
|
|
||||||
|
|
||||||
For each claw machine, we have to solve a simple linear equation system to get the number of required button pushes. Conveniently, for the given data, all of these systems have a unique solution. However, some solutions have to be excluded because the number of button pushes is not integer, or because the solutions are too high for part 1. The required integer tests are done by performing integer division, and comparing the dividend with the product of the integer quotient and the divisor.
|
|
||||||
|
|
||||||
### Day 14: Restroom Redoubt
|
|
||||||
|
|
||||||
:mag_right: Puzzle: <https://adventofcode.com/2024/day/14>, :white_check_mark: Solver: [`RestroomRedoubt.cpp`](src/RestroomRedoubt.cpp)
|
|
||||||
|
|
||||||
For part 1, the resulting quadrant for each robot can be calculated directly, by multiplying the velocity vector by 100 and adding it to the position vector, modulo width or height to stay within the target range.
|
|
||||||
|
|
||||||
When observing a few resulting robot configurations after each passed second, it becomes apparent that some of them show vertical or horizontal bands of clustering, and that these repeat every `width` or `height` seconds, respectively. With a bit of modulo calculus, the time after which the vertical and horizontal bands overlap can be determined, which is the solution for part 2.
|
|
||||||
|
|
||||||
### Day 15: Warehouse Woes
|
|
||||||
|
|
||||||
:mag_right: Puzzle: <https://adventofcode.com/2024/day/15>, :white_check_mark: Solver: [`WarehouseWoes.cpp`](src/WarehouseWoes.cpp)
|
|
||||||
|
|
||||||
The idea for this solver was pretty straight forward. The algorithm moves the robot around the warehouse following the instructions, and checks whether boxes in its way can be pushed, that is whether there is an empty spot behind the box, or stack of boxes.
|
|
||||||
|
|
||||||
It is more complicated for part 2 of course, since here horizontal and vertical directions behave differently. For vertical pushes there are two phases, the first one where individual boxes to be pushed are found, tracked, and checked for walls behind them, and a second phase where all those boxes are moved in reverse order.
|
|
||||||
|
|
||||||
## Thanks
|
## Thanks
|
||||||
|
|
||||||
* [Alexander Brouwer](https://github.com/Bromvlieg) for getting the project set up with CMake.
|
* [Alexander Brouwer](https://github.com/Bromvlieg) for getting the project set up with CMake.
|
||||||
|
@ -1,33 +0,0 @@
|
|||||||
// Solutions to the Advent Of Code 2024.
|
|
||||||
// Copyright (C) 2025 Stefan Müller
|
|
||||||
//
|
|
||||||
// This program is free software: you can redistribute it and/or modify it under
|
|
||||||
// the terms of the GNU General Public License as published by the Free Software
|
|
||||||
// Foundation, either version 3 of the License, or (at your option) any later
|
|
||||||
// version.
|
|
||||||
//
|
|
||||||
// This program is distributed in the hope that it will be useful, but WITHOUT
|
|
||||||
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
|
||||||
// FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU General Public License along with
|
|
||||||
// this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
#include <aoc/Solver.hpp>
|
|
||||||
|
|
||||||
class BridgeRepair
|
|
||||||
: public Solver
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
virtual const std::string getPuzzleName() const override;
|
|
||||||
virtual const int getPuzzleDay() const override;
|
|
||||||
virtual void processDataLine(const std::string& line) override;
|
|
||||||
virtual void finish() override;
|
|
||||||
private:
|
|
||||||
bool testCalibration(const long long int testValue, const std::vector<std::pair<int, int>>& calibrationNumbers,
|
|
||||||
const size_t currentIndex, const bool allowConcatenation, const long long int accumulatedTestValue);
|
|
||||||
};
|
|
@ -1,5 +1,5 @@
|
|||||||
// Solutions to the Advent Of Code 2024.
|
// Solutions to the Advent Of Code 2024.
|
||||||
// Copyright (C) 2024 Stefan Müller
|
// Copyright (C) 2024 Stefan Müller
|
||||||
//
|
//
|
||||||
// This program is free software: you can redistribute it and/or modify it under
|
// This program is free software: you can redistribute it and/or modify it under
|
||||||
// the terms of the GNU General Public License as published by the Free Software
|
// the terms of the GNU General Public License as published by the Free Software
|
||||||
@ -24,7 +24,7 @@ class CeresSearch
|
|||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
virtual const std::string getPuzzleName() const override;
|
virtual const std::string getPuzzleName() const override;
|
||||||
virtual const int getPuzzleDay() const override;
|
virtual const std::string getInputFileName() const override;
|
||||||
virtual void finish() override;
|
virtual void finish() override;
|
||||||
private:
|
private:
|
||||||
const char xmas_[4] = { 'X', 'M', 'A', 'S' };
|
const char xmas_[4] = { 'X', 'M', 'A', 'S' };
|
||||||
|
@ -1,38 +0,0 @@
|
|||||||
// Solutions to the Advent Of Code 2024.
|
|
||||||
// Copyright (C) 2025 Stefan Müller
|
|
||||||
//
|
|
||||||
// This program is free software: you can redistribute it and/or modify it under
|
|
||||||
// the terms of the GNU General Public License as published by the Free Software
|
|
||||||
// Foundation, either version 3 of the License, or (at your option) any later
|
|
||||||
// version.
|
|
||||||
//
|
|
||||||
// This program is distributed in the hope that it will be useful, but WITHOUT
|
|
||||||
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
|
||||||
// FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU General Public License along with
|
|
||||||
// this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <aoc/Solver.hpp>
|
|
||||||
#include <aoc/Point2.hpp>
|
|
||||||
|
|
||||||
class ClawContraption
|
|
||||||
: public Solver
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
virtual const std::string getPuzzleName() const override;
|
|
||||||
virtual const int getPuzzleDay() const override;
|
|
||||||
virtual void processDataLine(const std::string& line) override;
|
|
||||||
virtual void finish() override;
|
|
||||||
private:
|
|
||||||
Point2 buttonA_;
|
|
||||||
Point2 buttonB_;
|
|
||||||
static constexpr int getNMaxButtonPushes();
|
|
||||||
static constexpr int getButtonACost();
|
|
||||||
static constexpr int getButtonBCost();
|
|
||||||
static constexpr long long int getConversionCorrection();
|
|
||||||
long long int calcTokenCost(const Point2& buttonA, const Point2& buttonB, const Point2& prize,
|
|
||||||
const long long int offset);
|
|
||||||
};
|
|
@ -1,45 +0,0 @@
|
|||||||
// Solutions to the Advent Of Code 2024.
|
|
||||||
// Copyright (C) 2025 Stefan Müller
|
|
||||||
//
|
|
||||||
// This program is free software: you can redistribute it and/or modify it under
|
|
||||||
// the terms of the GNU General Public License as published by the Free Software
|
|
||||||
// Foundation, either version 3 of the License, or (at your option) any later
|
|
||||||
// version.
|
|
||||||
//
|
|
||||||
// This program is distributed in the hope that it will be useful, but WITHOUT
|
|
||||||
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
|
||||||
// FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU General Public License along with
|
|
||||||
// this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <array>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
#include <aoc/Interval.hpp>
|
|
||||||
#include <aoc/Solver.hpp>
|
|
||||||
|
|
||||||
class DiskFragmenter
|
|
||||||
: public Solver
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
virtual const std::string getPuzzleName() const override;
|
|
||||||
virtual const int getPuzzleDay() const override;
|
|
||||||
virtual void processDataLine(const std::string& line) override;
|
|
||||||
virtual void finish() override;
|
|
||||||
private:
|
|
||||||
typedef std::vector<Interval> Intervals;
|
|
||||||
typedef std::array<size_t, 10> DigitIndexArray;
|
|
||||||
void moveFileBlocks(const std::string& line);
|
|
||||||
void moveWholeFiles(const std::string& line);
|
|
||||||
void AddNewEmptySpaces(Intervals& emptySpaces, const std::string& line, size_t& front, const size_t back,
|
|
||||||
const int nBackBlocks, size_t& position);
|
|
||||||
void UpdateEmptySpaceIndices(const Intervals& emptySpaces, DigitIndexArray& emptySpaceIndices);
|
|
||||||
void moveBackFileForward(size_t& back, size_t& backIdNumber, const int nBackBlocks, Interval& emptySpace);
|
|
||||||
void keepBackFile(const std::string& line, size_t& back, size_t& backIdNumber, const int nBackBlocks,
|
|
||||||
size_t& position);
|
|
||||||
unsigned int getDigit(const std::string& line, const size_t index) const;
|
|
||||||
long long int calcChecksumPart(const size_t idNumber, const int nBlocks, size_t& position) const;
|
|
||||||
};
|
|
@ -1,34 +0,0 @@
|
|||||||
// Solutions to the Advent Of Code 2024.
|
|
||||||
// Copyright (C) 2025 Stefan Müller
|
|
||||||
//
|
|
||||||
// This program is free software: you can redistribute it and/or modify it under
|
|
||||||
// the terms of the GNU General Public License as published by the Free Software
|
|
||||||
// Foundation, either version 3 of the License, or (at your option) any later
|
|
||||||
// version.
|
|
||||||
//
|
|
||||||
// This program is distributed in the hope that it will be useful, but WITHOUT
|
|
||||||
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
|
||||||
// FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU General Public License along with
|
|
||||||
// this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <stack>
|
|
||||||
|
|
||||||
#include <aoc/Grid.hpp>
|
|
||||||
#include <aoc/LinesSolver.hpp>
|
|
||||||
#include <aoc/Point2.hpp>
|
|
||||||
|
|
||||||
class GardenGroups
|
|
||||||
: public LinesSolver
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
GardenGroups(const int inputFileNameSuffix = 0);
|
|
||||||
virtual const std::string getPuzzleName() const override;
|
|
||||||
virtual const int getPuzzleDay() const override;
|
|
||||||
virtual void finish() override;
|
|
||||||
private:
|
|
||||||
void traverseRegion(Grid<bool>& isVisited, std::stack<Point2>& otherRegionsStack, const Point2& start);
|
|
||||||
};
|
|
@ -1,5 +1,5 @@
|
|||||||
// Solutions to the Advent Of Code 2024.
|
// Solutions to the Advent Of Code 2024.
|
||||||
// Copyright (C) 2024 Stefan Müller
|
// Copyright (C) 2024 Stefan Müller
|
||||||
//
|
//
|
||||||
// This program is free software: you can redistribute it and/or modify it under
|
// This program is free software: you can redistribute it and/or modify it under
|
||||||
// the terms of the GNU General Public License as published by the Free Software
|
// the terms of the GNU General Public License as published by the Free Software
|
||||||
|
@ -53,14 +53,6 @@ class Grid
|
|||||||
{
|
{
|
||||||
return rowNo * nColumns_ + data_.get();
|
return rowNo * nColumns_ + data_.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
void fill(const T value)
|
|
||||||
{
|
|
||||||
for (size_t i = 0; i < nRows_ * nColumns_; i++)
|
|
||||||
{
|
|
||||||
data_[i] = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
private:
|
private:
|
||||||
size_t nRows_;
|
size_t nRows_;
|
||||||
size_t nColumns_;
|
size_t nColumns_;
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
// Solutions to the Advent Of Code 2024.
|
// Solutions to the Advent Of Code 2024.
|
||||||
// Copyright (C) 2025 Stefan Müller
|
// Copyright (C) 2024 Stefan Müller
|
||||||
//
|
//
|
||||||
// This program is free software: you can redistribute it and/or modify it under
|
// This program is free software: you can redistribute it and/or modify it under
|
||||||
// the terms of the GNU General Public License as published by the Free Software
|
// the terms of the GNU General Public License as published by the Free Software
|
||||||
@ -15,13 +15,11 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
class Interval
|
#include <bitset>
|
||||||
|
|
||||||
|
class GridPathData
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
Interval(const size_t start, const size_t length)
|
bool isVisited{ false };
|
||||||
: start{ start }, length{length}
|
std::bitset<4> isLoopingInDirection{ 0 };
|
||||||
{
|
|
||||||
}
|
|
||||||
size_t start;
|
|
||||||
size_t length;
|
|
||||||
};
|
};
|
@ -15,22 +15,31 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include <aoc/Grid.hpp>
|
||||||
|
#include <aoc/GridPathData.hpp>
|
||||||
#include <aoc/LinesSolver.hpp>
|
#include <aoc/LinesSolver.hpp>
|
||||||
|
#include <aoc/Point2.hpp>
|
||||||
|
|
||||||
class GuardGallivant
|
class GuardGallivant
|
||||||
: public LinesSolver
|
: public LinesSolver
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
virtual const std::string getPuzzleName() const override;
|
virtual const std::string getPuzzleName() const override;
|
||||||
virtual const int getPuzzleDay() const override;
|
virtual const std::string getInputFileName() const override;
|
||||||
virtual void processDataLine(const std::string& line) override;
|
virtual void processDataLine(const std::string& line) override;
|
||||||
virtual void finish() override;
|
virtual void finish() override;
|
||||||
private:
|
private:
|
||||||
static constexpr size_t getStartDirectionIndex();
|
|
||||||
static constexpr char getStartChar();
|
|
||||||
static constexpr char getVisitedChar();
|
|
||||||
static constexpr char getObstructionChar();
|
|
||||||
Point2 start_{};
|
Point2 start_{};
|
||||||
void visitPosition(const Point2& current);
|
const size_t getStartDirectionIndex() const;
|
||||||
size_t turnDirection(const size_t current) const;
|
const char getStartChar() const;
|
||||||
|
const char getObstructionChar() const;
|
||||||
|
void tracePath();
|
||||||
|
void visitPosition(const Point2& current, const size_t directionIndex, Grid<GridPathData>& pathGrid);
|
||||||
|
void backtracePath(const Point2& current, const size_t directionIndex, Grid<GridPathData>& pathGrid);
|
||||||
|
void backtraceTurn(const Point2& current, const size_t reverseTurnDirectionIndex, Grid<GridPathData>& pathGrid);
|
||||||
|
size_t turnDirectionRight(const size_t currentDirectionIndex) const;
|
||||||
|
size_t turnDirectionLeft(const size_t currentDirectionIndex) const;
|
||||||
|
size_t revertDirection(const size_t currentDirectionIndex) const;
|
||||||
};
|
};
|
||||||
|
@ -24,7 +24,7 @@ class HistorianHysteria
|
|||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
virtual const std::string getPuzzleName() const override;
|
virtual const std::string getPuzzleName() const override;
|
||||||
virtual const int getPuzzleDay() const override;
|
virtual const std::string getInputFileName() const override;
|
||||||
virtual void processDataLine(const std::string& line) override;
|
virtual void processDataLine(const std::string& line) override;
|
||||||
virtual void finish() override;
|
virtual void finish() override;
|
||||||
private:
|
private:
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
// Solutions to the Advent Of Code 2024.
|
// Solutions to the Advent Of Code 2024.
|
||||||
// Copyright (C) 2024 Stefan Müller
|
// Copyright (C) 2024 Stefan Müller
|
||||||
//
|
//
|
||||||
// This program is free software: you can redistribute it and/or modify it under
|
// This program is free software: you can redistribute it and/or modify it under
|
||||||
// the terms of the GNU General Public License as published by the Free Software
|
// the terms of the GNU General Public License as published by the Free Software
|
||||||
@ -25,10 +25,10 @@ class HoofIt
|
|||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
virtual const std::string getPuzzleName() const override;
|
virtual const std::string getPuzzleName() const override;
|
||||||
virtual const int getPuzzleDay() const override;
|
virtual const std::string getInputFileName() const override;
|
||||||
virtual void finish() override;
|
virtual void finish() override;
|
||||||
private:
|
private:
|
||||||
static constexpr char getTrailheadChar();
|
const char getTrailheadChar() const;
|
||||||
static constexpr char getTrailTopChar();
|
const char getTrailTopChar() const;
|
||||||
void addUnique(const std::vector<Point2>& source, std::vector<Point2>& destination);
|
void addUnique(const std::vector<Point2>& source, std::vector<Point2>& destination);
|
||||||
};
|
};
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
// Solutions to the Advent Of Code 2024.
|
// Solutions to the Advent Of Code 2024.
|
||||||
// Copyright (C) 2024 Stefan Müller
|
// Copyright (C) 2024 Stefan Müller
|
||||||
//
|
//
|
||||||
// This program is free software: you can redistribute it and/or modify it under
|
// This program is free software: you can redistribute it and/or modify it under
|
||||||
// the terms of the GNU General Public License as published by the Free Software
|
// the terms of the GNU General Public License as published by the Free Software
|
||||||
@ -22,7 +22,7 @@ class LanParty : public Solver
|
|||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
virtual const std::string getPuzzleName() const override;
|
virtual const std::string getPuzzleName() const override;
|
||||||
virtual const int getPuzzleDay() const override;
|
virtual const std::string getInputFileName() const override;
|
||||||
virtual void processDataLine(const std::string& line) override;
|
virtual void processDataLine(const std::string& line) override;
|
||||||
virtual void finish() override;
|
virtual void finish() override;
|
||||||
private:
|
private:
|
||||||
|
@ -1,37 +0,0 @@
|
|||||||
// Solutions to the Advent Of Code 2024.
|
|
||||||
// Copyright (C) 2024-2025 Stefan Müller
|
|
||||||
//
|
|
||||||
// This program is free software: you can redistribute it and/or modify it under
|
|
||||||
// the terms of the GNU General Public License as published by the Free Software
|
|
||||||
// Foundation, either version 3 of the License, or (at your option) any later
|
|
||||||
// version.
|
|
||||||
//
|
|
||||||
// This program is distributed in the hope that it will be useful, but WITHOUT
|
|
||||||
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
|
||||||
// FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU General Public License along with
|
|
||||||
// this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <iostream>
|
|
||||||
#include <string>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
#include <aoc/Point2.hpp>
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// A vector of strings of the same length, with methods to treat it as a rectangular, two-dimensional map of char and
|
|
||||||
/// access points on it.
|
|
||||||
/// </summary>
|
|
||||||
class Lines
|
|
||||||
: public std::vector<std::string>
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
bool isInBounds(const Point2& point) const;
|
|
||||||
char getCharAt(const Point2& point) const;
|
|
||||||
void setCharAt(const Point2& point, const char value);
|
|
||||||
};
|
|
||||||
|
|
||||||
std::ostream& operator<<(std::ostream& os, const Lines& lines);
|
|
@ -1,5 +1,5 @@
|
|||||||
// Solutions to the Advent Of Code 2024.
|
// Solutions to the Advent Of Code 2024.
|
||||||
// Copyright (C) 2024-2025 Stefan Müller
|
// Copyright (C) 2024 Stefan Müller
|
||||||
//
|
//
|
||||||
// This program is free software: you can redistribute it and/or modify it under
|
// This program is free software: you can redistribute it and/or modify it under
|
||||||
// the terms of the GNU General Public License as published by the Free Software
|
// the terms of the GNU General Public License as published by the Free Software
|
||||||
@ -17,7 +17,6 @@
|
|||||||
|
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include <aoc/Lines.hpp>
|
|
||||||
#include <aoc/Point2.hpp>
|
#include <aoc/Point2.hpp>
|
||||||
#include <aoc/Solver.hpp>
|
#include <aoc/Solver.hpp>
|
||||||
|
|
||||||
@ -25,11 +24,10 @@ class LinesSolver
|
|||||||
: public Solver
|
: public Solver
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
LinesSolver(const int inputFileNameSuffix = 0);
|
|
||||||
virtual void processDataLine(const std::string& line) override;
|
virtual void processDataLine(const std::string& line) override;
|
||||||
protected:
|
protected:
|
||||||
Lines lines{};
|
std::vector<std::string> lines{};
|
||||||
bool isInBounds(const Point2& point) const;
|
bool isInBounds(const Point2& point) const;
|
||||||
char getCharAt(const Point2& point) const;
|
char getPosition(const Point2& point) const;
|
||||||
void setCharAt(const Point2& point, const char value);
|
void setPosition(const Point2& point, const char value);
|
||||||
};
|
};
|
||||||
|
@ -1,40 +0,0 @@
|
|||||||
// Solutions to the Advent Of Code 2024.
|
|
||||||
// Copyright (C) 2025 Stefan Müller
|
|
||||||
//
|
|
||||||
// This program is free software: you can redistribute it and/or modify it under
|
|
||||||
// the terms of the GNU General Public License as published by the Free Software
|
|
||||||
// Foundation, either version 3 of the License, or (at your option) any later
|
|
||||||
// version.
|
|
||||||
//
|
|
||||||
// This program is distributed in the hope that it will be useful, but WITHOUT
|
|
||||||
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
|
||||||
// FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU General Public License along with
|
|
||||||
// this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <tuple>
|
|
||||||
|
|
||||||
class Math
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
/// <summary>
|
|
||||||
/// Calculates an integer exponentiation.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="base">Base of the exponentiation.</param>
|
|
||||||
/// <param name="exponent">Exponent of the exponentiation</param>
|
|
||||||
/// <returns>'base' raised to the power of 'exponent'.</returns>
|
|
||||||
static int ipow(const int base, const int exponent);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Calculates the greatest common divisor gcd(a, b) and the coefficients x and y of Bézout's identity
|
|
||||||
/// ax + by = gcd(a, b). If a and b are coprime, then x is the modular multiplicative inverse of a modulo b, and y
|
|
||||||
/// is the modular multiplicative inverse of b modulo a.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="a"></param>
|
|
||||||
/// <param name="b"></param>
|
|
||||||
/// <returns>A tuple of the gcd(a, b), x, and y.</returns>
|
|
||||||
static std::tuple<int, int, int> extendedEuclid(const int a, const int b);
|
|
||||||
};
|
|
@ -25,7 +25,7 @@ class MullItOver
|
|||||||
public:
|
public:
|
||||||
MullItOver();
|
MullItOver();
|
||||||
virtual const std::string getPuzzleName() const override;
|
virtual const std::string getPuzzleName() const override;
|
||||||
virtual const int getPuzzleDay() const override;
|
virtual const std::string getInputFileName() const override;
|
||||||
virtual void processDataLine(const std::string& line) override;
|
virtual void processDataLine(const std::string& line) override;
|
||||||
virtual void finish() override;
|
virtual void finish() override;
|
||||||
private:
|
private:
|
||||||
|
@ -1,38 +0,0 @@
|
|||||||
// Solutions to the Advent Of Code 2024.
|
|
||||||
// Copyright (C) 2025 Stefan Müller
|
|
||||||
//
|
|
||||||
// This program is free software: you can redistribute it and/or modify it under
|
|
||||||
// the terms of the GNU General Public License as published by the Free Software
|
|
||||||
// Foundation, either version 3 of the License, or (at your option) any later
|
|
||||||
// version.
|
|
||||||
//
|
|
||||||
// This program is distributed in the hope that it will be useful, but WITHOUT
|
|
||||||
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
|
||||||
// FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU General Public License along with
|
|
||||||
// this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <map>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
#include <aoc/Solver.hpp>
|
|
||||||
|
|
||||||
class PlutonianPebbles : public Solver
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
virtual const std::string getPuzzleName() const override;
|
|
||||||
virtual const int getPuzzleDay() const override;
|
|
||||||
virtual void processDataLine(const std::string& line) override;
|
|
||||||
virtual void finish() override;
|
|
||||||
private:
|
|
||||||
static constexpr int getNBlinksPart1();
|
|
||||||
static constexpr int getNBlinksPart2();
|
|
||||||
std::map<long long int, std::vector<long long int>> blinkMap_{};
|
|
||||||
void addPebble(std::map<long long int, long long int>& pebbles, const long long int pebbleNumber,
|
|
||||||
long long int cardinality);
|
|
||||||
void addNextBlinkNumbers(const long long int pebbleNumber, std::vector<long long int>& nextBlinkNumbers);
|
|
||||||
int getNNextBlinkNumbers(const long long int pebbleNumber) const;
|
|
||||||
};
|
|
@ -1,5 +1,5 @@
|
|||||||
// Solutions to the Advent Of Code 2024.
|
// Solutions to the Advent Of Code 2024.
|
||||||
// Copyright (C) 2024 Stefan Müller
|
// Copyright (C) 2024 Stefan Müller
|
||||||
//
|
//
|
||||||
// This program is free software: you can redistribute it and/or modify it under
|
// This program is free software: you can redistribute it and/or modify it under
|
||||||
// the terms of the GNU General Public License as published by the Free Software
|
// the terms of the GNU General Public License as published by the Free Software
|
||||||
@ -15,7 +15,7 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <array>
|
#include <iostream>
|
||||||
|
|
||||||
class Point2
|
class Point2
|
||||||
{
|
{
|
||||||
@ -26,12 +26,11 @@ class Point2
|
|||||||
/// The eight cardinal and diagonal directions starting down, rotating in
|
/// The eight cardinal and diagonal directions starting down, rotating in
|
||||||
/// positive direction.
|
/// positive direction.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
static const std::array<Point2, 8> directions;
|
static const Point2 directions[8];
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The four cardinal directions starting down, rotating in positive direction.
|
/// The four cardinal directions starting down, rotating in positive direction.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
static const std::array<Point2, 4> cardinalDirections;
|
static const Point2 cardinalDirections[4];
|
||||||
static Point2 getCardinalDirection(const char directionChar);
|
|
||||||
int x, y;
|
int x, y;
|
||||||
Point2();
|
Point2();
|
||||||
Point2(const int x, const int y);
|
Point2(const int x, const int y);
|
||||||
@ -44,5 +43,6 @@ class Point2
|
|||||||
Point2& operator+=(const Point2& rhs);
|
Point2& operator+=(const Point2& rhs);
|
||||||
Point2& operator-=(const Point2& rhs);
|
Point2& operator-=(const Point2& rhs);
|
||||||
Point2& operator*=(const int rhs);
|
Point2& operator*=(const int rhs);
|
||||||
int& operator[](size_t coordinateIndex);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
std::ostream& operator<<(std::ostream& os, const Point2& rhs);
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
// Solutions to the Advent Of Code 2024.
|
// Solutions to the Advent Of Code 2024.
|
||||||
// Copyright (C) 2024 Stefan Müller
|
// Copyright (C) 2024 Stefan Müller
|
||||||
//
|
//
|
||||||
// This program is free software: you can redistribute it and/or modify it under
|
// This program is free software: you can redistribute it and/or modify it under
|
||||||
// the terms of the GNU General Public License as published by the Free Software
|
// the terms of the GNU General Public License as published by the Free Software
|
||||||
@ -15,9 +15,6 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
#include <aoc/Grid.hpp>
|
|
||||||
#include <aoc/Solver.hpp>
|
#include <aoc/Solver.hpp>
|
||||||
|
|
||||||
class PrintQueue
|
class PrintQueue
|
||||||
@ -26,16 +23,16 @@ class PrintQueue
|
|||||||
public:
|
public:
|
||||||
PrintQueue();
|
PrintQueue();
|
||||||
virtual const std::string getPuzzleName() const override;
|
virtual const std::string getPuzzleName() const override;
|
||||||
virtual const int getPuzzleDay() const override;
|
virtual const std::string getInputFileName() const override;
|
||||||
virtual void processDataLine(const std::string& line) override;
|
virtual void processDataLine(const std::string& line) override;
|
||||||
virtual void finish() override;
|
virtual void finish() override;
|
||||||
private:
|
private:
|
||||||
static constexpr int getNPages();
|
static const int nPages_{ 49 };
|
||||||
static constexpr int getMaxPageNo();
|
static const int maxPageNo_{ 99 };
|
||||||
bool isProcessingOrderingRules_;
|
bool isProcessingOrderingRules_;
|
||||||
int pageNoMapIndex_;
|
int pageNoMapIndex_;
|
||||||
std::vector<int> pageNoMap_;
|
int pageNoMap_[maxPageNo_ + 1];
|
||||||
Grid<bool> orderingRules_;
|
bool orderingRules_[nPages_][nPages_];
|
||||||
size_t getMapped(const int pageNo);
|
size_t getMapped(const int pageNo);
|
||||||
void processOrderingRule(const std::string& line);
|
void processOrderingRule(const std::string& line);
|
||||||
void processUpdatePages(const std::string& line);
|
void processUpdatePages(const std::string& line);
|
||||||
|
@ -24,10 +24,10 @@ class RedNosedReports
|
|||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
virtual const std::string getPuzzleName() const override;
|
virtual const std::string getPuzzleName() const override;
|
||||||
virtual const int getPuzzleDay() const override;
|
virtual const std::string getInputFileName() const override;
|
||||||
virtual void processDataLine(const std::string& line) override;
|
virtual void processDataLine(const std::string& line) override;
|
||||||
virtual void finish() override;
|
virtual void finish() override;
|
||||||
private:
|
private:
|
||||||
void checkLastLevel(const Slope sameSlope, const Slope otherSlope, const int delta, const int sign,
|
void checkLastLevel(const Slope sameSlope, const Slope otherSlope,
|
||||||
RedNosedReportData& data);
|
const int delta, const int sign, RedNosedReportData& data);
|
||||||
};
|
};
|
||||||
|
@ -1,39 +0,0 @@
|
|||||||
// Solutions to the Advent Of Code 2024.
|
|
||||||
// Copyright (C) 2025 Stefan Müller
|
|
||||||
//
|
|
||||||
// This program is free software: you can redistribute it and/or modify it under
|
|
||||||
// the terms of the GNU General Public License as published by the Free Software
|
|
||||||
// Foundation, either version 3 of the License, or (at your option) any later
|
|
||||||
// version.
|
|
||||||
//
|
|
||||||
// This program is distributed in the hope that it will be useful, but WITHOUT
|
|
||||||
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
|
||||||
// FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU General Public License along with
|
|
||||||
// this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <map>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
#include <aoc/Grid.hpp>
|
|
||||||
#include <aoc/LinesSolver.hpp>
|
|
||||||
#include <aoc/Point2.hpp>
|
|
||||||
|
|
||||||
class ResonantCollinearity
|
|
||||||
: public LinesSolver
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
virtual const std::string getPuzzleName() const override;
|
|
||||||
virtual const int getPuzzleDay() const override;
|
|
||||||
virtual void finish() override;
|
|
||||||
private:
|
|
||||||
static constexpr char getEmptyChar();
|
|
||||||
std::map<char, std::vector<Point2>> antennas_{};
|
|
||||||
void addNewAntenna(Grid<bool>& antinodeGrid1, Grid<bool>& antinodeGrid2, Point2&& newPosition);
|
|
||||||
void findNewAntinodes(Grid<bool>& antinodeGrid1, Grid<bool>& antinodeGrid2, Point2&& start,
|
|
||||||
const Point2& increment);
|
|
||||||
void addNewAntinode(Grid<bool>& antinodeGrid, Point2& newPosition, long long int& part);
|
|
||||||
};
|
|
@ -1,44 +0,0 @@
|
|||||||
// Solutions to the Advent Of Code 2024.
|
|
||||||
// Copyright (C) 2025 Stefan Müller
|
|
||||||
//
|
|
||||||
// This program is free software: you can redistribute it and/or modify it under
|
|
||||||
// the terms of the GNU General Public License as published by the Free Software
|
|
||||||
// Foundation, either version 3 of the License, or (at your option) any later
|
|
||||||
// version.
|
|
||||||
//
|
|
||||||
// This program is distributed in the hope that it will be useful, but WITHOUT
|
|
||||||
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
|
||||||
// FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU General Public License along with
|
|
||||||
// this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <array>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
#include <aoc/Point2.hpp>
|
|
||||||
#include <aoc/Solver.hpp>
|
|
||||||
|
|
||||||
class RestroomRedoubt
|
|
||||||
: public Solver
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
RestroomRedoubt(const int width = 101, const int height = 103, const bool runPart2 = true);
|
|
||||||
virtual const std::string getPuzzleName() const override;
|
|
||||||
virtual const int getPuzzleDay() const override;
|
|
||||||
virtual void processDataLine(const std::string& line) override;
|
|
||||||
virtual void finish() override;
|
|
||||||
private:
|
|
||||||
const int width_;
|
|
||||||
const int height_;
|
|
||||||
const int halfWidth_;
|
|
||||||
const int halfHeight_;
|
|
||||||
const bool runPart2_;
|
|
||||||
std::array<int, 4> quadrants_;
|
|
||||||
std::vector<std::pair<Point2, Point2>> robots_;
|
|
||||||
static constexpr int getNPredictionSeconds();
|
|
||||||
static constexpr int getEasterEggThreshold();
|
|
||||||
void findEasterEgg();
|
|
||||||
};
|
|
@ -20,11 +20,10 @@
|
|||||||
class Solver
|
class Solver
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
Solver(const int inputFileNameSuffix = 0);
|
Solver();
|
||||||
virtual ~Solver(){};
|
virtual ~Solver(){};
|
||||||
const std::string getInputFileName() const;
|
|
||||||
virtual const std::string getPuzzleName() const = 0;
|
virtual const std::string getPuzzleName() const = 0;
|
||||||
virtual const int getPuzzleDay() const = 0;
|
virtual const std::string getInputFileName() const = 0;
|
||||||
virtual void processDataLine(const std::string &line) = 0;
|
virtual void processDataLine(const std::string &line) = 0;
|
||||||
virtual void finish() = 0;
|
virtual void finish() = 0;
|
||||||
long long int getResultPart1() const;
|
long long int getResultPart1() const;
|
||||||
@ -32,7 +31,4 @@ class Solver
|
|||||||
protected:
|
protected:
|
||||||
long long int part1;
|
long long int part1;
|
||||||
long long int part2;
|
long long int part2;
|
||||||
private:
|
|
||||||
int inputFileNameSuffix_;
|
|
||||||
std::string clean(const std::string& original) const;
|
|
||||||
};
|
};
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
// Solutions to the Advent Of Code 2024.
|
// Solutions to the Advent Of Code 2024.
|
||||||
// Copyright (C) 2024 Stefan Müller
|
// Copyright (C) 2024 Stefan Müller
|
||||||
//
|
//
|
||||||
// This program is free software: you can redistribute it and/or modify it under
|
// This program is free software: you can redistribute it and/or modify it under
|
||||||
// the terms of the GNU General Public License as published by the Free Software
|
// the terms of the GNU General Public License as published by the Free Software
|
||||||
|
@ -1,37 +0,0 @@
|
|||||||
// Solutions to the Advent Of Code 2024.
|
|
||||||
// Copyright (C) 2025 Stefan Müller
|
|
||||||
//
|
|
||||||
// This program is free software: you can redistribute it and/or modify it under
|
|
||||||
// the terms of the GNU General Public License as published by the Free Software
|
|
||||||
// Foundation, either version 3 of the License, or (at your option) any later
|
|
||||||
// version.
|
|
||||||
//
|
|
||||||
// This program is distributed in the hope that it will be useful, but WITHOUT
|
|
||||||
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
|
||||||
// FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU General Public License along with
|
|
||||||
// this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <string>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
#include <aoc/Lines.hpp>
|
|
||||||
#include <aoc/Point2.hpp>
|
|
||||||
|
|
||||||
class WarehouseBoxPusher
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
WarehouseBoxPusher(const char boxChar, const char wallChar, const char emptyChar);
|
|
||||||
virtual ~WarehouseBoxPusher(){};
|
|
||||||
virtual void processMovements(Lines& warehouseMap, const std::string& line);
|
|
||||||
virtual void processMovement(Lines& warehouseMap, const Point2& direction);
|
|
||||||
void setRobotPosition(const Point2& robotPosition);
|
|
||||||
protected:
|
|
||||||
Point2 robotPosition_;
|
|
||||||
const char boxChar_;
|
|
||||||
const char wallChar_;
|
|
||||||
const char emptyChar_;
|
|
||||||
};
|
|
@ -1,30 +0,0 @@
|
|||||||
// Solutions to the Advent Of Code 2024.
|
|
||||||
// Copyright (C) 2025 Stefan Müller
|
|
||||||
//
|
|
||||||
// This program is free software: you can redistribute it and/or modify it under
|
|
||||||
// the terms of the GNU General Public License as published by the Free Software
|
|
||||||
// Foundation, either version 3 of the License, or (at your option) any later
|
|
||||||
// version.
|
|
||||||
//
|
|
||||||
// This program is distributed in the hope that it will be useful, but WITHOUT
|
|
||||||
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
|
||||||
// FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU General Public License along with
|
|
||||||
// this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <aoc/WarehouseBoxPusher.hpp>
|
|
||||||
|
|
||||||
class WarehouseWideBoxPusher
|
|
||||||
: public WarehouseBoxPusher
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
WarehouseWideBoxPusher(const char leftWideBoxChar, const char rightWideBoxChar, const char wallChar,
|
|
||||||
const char emptyChar);
|
|
||||||
virtual void processMovement(Lines& warehouseMap, const Point2& direction) override;
|
|
||||||
protected:
|
|
||||||
const char leftWideBoxChar_;
|
|
||||||
const char rightWideBoxChar_;
|
|
||||||
};
|
|
@ -1,48 +0,0 @@
|
|||||||
// Solutions to the Advent Of Code 2024.
|
|
||||||
// Copyright (C) 2025 Stefan Müller
|
|
||||||
//
|
|
||||||
// This program is free software: you can redistribute it and/or modify it under
|
|
||||||
// the terms of the GNU General Public License as published by the Free Software
|
|
||||||
// Foundation, either version 3 of the License, or (at your option) any later
|
|
||||||
// version.
|
|
||||||
//
|
|
||||||
// This program is distributed in the hope that it will be useful, but WITHOUT
|
|
||||||
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
|
||||||
// FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU General Public License along with
|
|
||||||
// this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <aoc/Lines.hpp>
|
|
||||||
#include <aoc/LinesSolver.hpp>
|
|
||||||
#include <aoc/WarehouseBoxPusher.hpp>
|
|
||||||
#include <aoc/WarehouseWideBoxPusher.hpp>
|
|
||||||
|
|
||||||
class WarehouseWoes
|
|
||||||
: public LinesSolver
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
WarehouseWoes(const int inputFileNameSuffix = 0);
|
|
||||||
virtual const std::string getPuzzleName() const override;
|
|
||||||
virtual const int getPuzzleDay() const override;
|
|
||||||
virtual void processDataLine(const std::string& line) override;
|
|
||||||
virtual void finish() override;
|
|
||||||
private:
|
|
||||||
bool isProcessingMap_;
|
|
||||||
bool isSearchingStartPosition_;
|
|
||||||
Lines lines2_;
|
|
||||||
WarehouseBoxPusher warehouseBoxPusher1_;
|
|
||||||
WarehouseWideBoxPusher warehouseBoxPusher2_;
|
|
||||||
static constexpr char getRobotChar();
|
|
||||||
static constexpr char getBoxChar();
|
|
||||||
static constexpr char getLeftWideBoxChar();
|
|
||||||
static constexpr char getRightWideBoxChar();
|
|
||||||
static constexpr char getWallChar();
|
|
||||||
static constexpr char getEmptyChar();
|
|
||||||
void checkStartingPosition(const std::string& line);
|
|
||||||
void addWarehouseMap2Line(const std::string& line);
|
|
||||||
long long int calcAllGpsCoordinates(Lines& warehouseMap, const char boxChar);
|
|
||||||
long long int calcGpsCoordinate(const size_t x, const size_t y);
|
|
||||||
};
|
|
@ -1,95 +0,0 @@
|
|||||||
// Solutions to the Advent Of Code 2024.
|
|
||||||
// Copyright (C) 2025 Stefan Müller
|
|
||||||
//
|
|
||||||
// This program is free software: you can redistribute it and/or modify it under
|
|
||||||
// the terms of the GNU General Public License as published by the Free Software
|
|
||||||
// Foundation, either version 3 of the License, or (at your option) any later
|
|
||||||
// version.
|
|
||||||
//
|
|
||||||
// This program is distributed in the hope that it will be useful, but WITHOUT
|
|
||||||
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
|
||||||
// FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU General Public License along with
|
|
||||||
// this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
#include <aoc/BridgeRepair.hpp>
|
|
||||||
|
|
||||||
#include <cmath>
|
|
||||||
#include <sstream>
|
|
||||||
|
|
||||||
#include <aoc/Math.hpp>
|
|
||||||
|
|
||||||
const std::string BridgeRepair::getPuzzleName() const
|
|
||||||
{
|
|
||||||
return "Bridge Repair";
|
|
||||||
}
|
|
||||||
|
|
||||||
const int BridgeRepair::getPuzzleDay() const
|
|
||||||
{
|
|
||||||
return 7;
|
|
||||||
}
|
|
||||||
|
|
||||||
void BridgeRepair::processDataLine(const std::string& line)
|
|
||||||
{
|
|
||||||
std::istringstream stream{ line };
|
|
||||||
|
|
||||||
long long int testValue;
|
|
||||||
char colon;
|
|
||||||
int calibrationNumber;
|
|
||||||
// Calibration numbers are a pair of an actual calibration number and its precomputed smallest larger power of 10.
|
|
||||||
// The precomputed numbers are used to facilitate the "concatenate" operator.
|
|
||||||
std::vector<std::pair<int, int>> calibrationNumbers{};
|
|
||||||
|
|
||||||
stream >> testValue >> colon;
|
|
||||||
while (stream >> calibrationNumber)
|
|
||||||
{
|
|
||||||
// Skips the precomputation for the first calibration numbers.
|
|
||||||
int pow10{ 0 };
|
|
||||||
if (!calibrationNumbers.empty())
|
|
||||||
{
|
|
||||||
pow10 = Math::ipow(10, static_cast<int>(floor(log10(calibrationNumber))) + 1);
|
|
||||||
}
|
|
||||||
calibrationNumbers.push_back({ calibrationNumber, pow10 });
|
|
||||||
}
|
|
||||||
|
|
||||||
if (testCalibration(testValue, calibrationNumbers, 1, false, calibrationNumbers[0].first))
|
|
||||||
{
|
|
||||||
part1 += testValue;
|
|
||||||
part2 += testValue;
|
|
||||||
}
|
|
||||||
else if (testCalibration(testValue, calibrationNumbers, 1, true, calibrationNumbers[0].first))
|
|
||||||
{
|
|
||||||
part2 += testValue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void BridgeRepair::finish()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
bool BridgeRepair::testCalibration(const long long int testValue,
|
|
||||||
const std::vector<std::pair<int, int>>& calibrationNumbers, const size_t currentIndex,
|
|
||||||
const bool allowConcatenation, const long long int accumulatedTestValue)
|
|
||||||
{
|
|
||||||
if (testValue >= accumulatedTestValue && currentIndex < calibrationNumbers.size())
|
|
||||||
{
|
|
||||||
size_t nextIndex{ currentIndex + 1 };
|
|
||||||
long long int nextCalibrationNumber{ calibrationNumbers[currentIndex].first };
|
|
||||||
// Recursively tries the ||, *, and + operators.
|
|
||||||
if ((allowConcatenation && testCalibration(testValue, calibrationNumbers, nextIndex, allowConcatenation,
|
|
||||||
accumulatedTestValue * calibrationNumbers[currentIndex].second + calibrationNumbers[currentIndex].first)) ||
|
|
||||||
testCalibration(testValue, calibrationNumbers, nextIndex, allowConcatenation,
|
|
||||||
accumulatedTestValue * calibrationNumbers[currentIndex].first) ||
|
|
||||||
testCalibration(testValue, calibrationNumbers, nextIndex, allowConcatenation,
|
|
||||||
accumulatedTestValue + calibrationNumbers[currentIndex].first))
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (testValue == accumulatedTestValue)
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
@ -1,5 +1,5 @@
|
|||||||
// Solutions to the Advent Of Code 2024.
|
// Solutions to the Advent Of Code 2024.
|
||||||
// Copyright (C) 2024-2025 Stefan Müller
|
// Copyright (C) 2024 Stefan Müller
|
||||||
//
|
//
|
||||||
// This program is free software: you can redistribute it and/or modify it under
|
// This program is free software: you can redistribute it and/or modify it under
|
||||||
// the terms of the GNU General Public License as published by the Free Software
|
// the terms of the GNU General Public License as published by the Free Software
|
||||||
@ -17,12 +17,12 @@
|
|||||||
|
|
||||||
const std::string CeresSearch::getPuzzleName() const
|
const std::string CeresSearch::getPuzzleName() const
|
||||||
{
|
{
|
||||||
return "Ceres Search";
|
return "Day 4: Ceres Search";
|
||||||
}
|
}
|
||||||
|
|
||||||
const int CeresSearch::getPuzzleDay() const
|
const std::string CeresSearch::getInputFileName() const
|
||||||
{
|
{
|
||||||
return 4;
|
return "ceres_search.txt";
|
||||||
}
|
}
|
||||||
|
|
||||||
void CeresSearch::finish()
|
void CeresSearch::finish()
|
||||||
@ -51,7 +51,7 @@ void CeresSearch::computeXmasCount(const Point2& start)
|
|||||||
{
|
{
|
||||||
auto p{ start + d };
|
auto p{ start + d };
|
||||||
auto i{ 1 };
|
auto i{ 1 };
|
||||||
while (i < 4 && isInBounds(p) && xmas_[i] == getCharAt(p))
|
while (i < 4 && isInBounds(p) && xmas_[i] == getPosition(p))
|
||||||
{
|
{
|
||||||
p += d;
|
p += d;
|
||||||
i++;
|
i++;
|
||||||
@ -70,10 +70,10 @@ void CeresSearch::computeX_MasCount(const Point2& start)
|
|||||||
auto pUR{ start + Point2::upRight };
|
auto pUR{ start + Point2::upRight };
|
||||||
auto pDL{ start + Point2::downLeft };
|
auto pDL{ start + Point2::downLeft };
|
||||||
if (isInBounds(pUL) && isInBounds(pDR)
|
if (isInBounds(pUL) && isInBounds(pDR)
|
||||||
&& ((getCharAt(pUL) == xmas_[1] && getCharAt(pDR) == xmas_[3])
|
&& ((getPosition(pUL) == xmas_[1] && getPosition(pDR) == xmas_[3])
|
||||||
|| (getCharAt(pUL) == xmas_[3] && getCharAt(pDR) == xmas_[1]))
|
|| (getPosition(pUL) == xmas_[3] && getPosition(pDR) == xmas_[1]))
|
||||||
&& ((getCharAt(pUR) == xmas_[1] && getCharAt(pDL) == xmas_[3])
|
&& ((getPosition(pUR) == xmas_[1] && getPosition(pDL) == xmas_[3])
|
||||||
|| (getCharAt(pUR) == xmas_[3] && getCharAt(pDL) == xmas_[1])))
|
|| (getPosition(pUR) == xmas_[3] && getPosition(pDL) == xmas_[1])))
|
||||||
{
|
{
|
||||||
part2++;
|
part2++;
|
||||||
}
|
}
|
||||||
|
@ -1,110 +0,0 @@
|
|||||||
// Solutions to the Advent Of Code 2024.
|
|
||||||
// Copyright (C) 2025 Stefan Müller
|
|
||||||
//
|
|
||||||
// This program is free software: you can redistribute it and/or modify it under
|
|
||||||
// the terms of the GNU General Public License as published by the Free Software
|
|
||||||
// Foundation, either version 3 of the License, or (at your option) any later
|
|
||||||
// version.
|
|
||||||
//
|
|
||||||
// This program is distributed in the hope that it will be useful, but WITHOUT
|
|
||||||
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
|
||||||
// FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU General Public License along with
|
|
||||||
// this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
#include <aoc/ClawContraption.hpp>
|
|
||||||
|
|
||||||
#include <sstream>
|
|
||||||
|
|
||||||
const std::string ClawContraption::getPuzzleName() const
|
|
||||||
{
|
|
||||||
return "Claw Contraption";
|
|
||||||
}
|
|
||||||
|
|
||||||
const int ClawContraption::getPuzzleDay() const
|
|
||||||
{
|
|
||||||
return 13;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ClawContraption::processDataLine(const std::string& line)
|
|
||||||
{
|
|
||||||
if (!line.empty())
|
|
||||||
{
|
|
||||||
std::istringstream stream{ line };
|
|
||||||
std::string token;
|
|
||||||
char c;
|
|
||||||
int x;
|
|
||||||
int y;
|
|
||||||
|
|
||||||
stream >> token;
|
|
||||||
if (token == "Button")
|
|
||||||
{
|
|
||||||
stream >> c;
|
|
||||||
std::getline(stream, token, '+');
|
|
||||||
stream >> x;
|
|
||||||
std::getline(stream, token, '+');
|
|
||||||
stream >> y;
|
|
||||||
if (c == 'A')
|
|
||||||
{
|
|
||||||
buttonA_ = { x, y };
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
buttonB_ = { x, y };
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
std::getline(stream, token, '=');
|
|
||||||
stream >> x;
|
|
||||||
std::getline(stream, token, '=');
|
|
||||||
stream >> y;
|
|
||||||
part1 += calcTokenCost(buttonA_, buttonB_, { x, y }, 0);
|
|
||||||
part2 += calcTokenCost(buttonA_, buttonB_, { x, y }, getConversionCorrection());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void ClawContraption::finish()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
constexpr int ClawContraption::getNMaxButtonPushes()
|
|
||||||
{
|
|
||||||
return 100;
|
|
||||||
}
|
|
||||||
|
|
||||||
constexpr int ClawContraption::getButtonACost()
|
|
||||||
{
|
|
||||||
return 3;
|
|
||||||
}
|
|
||||||
|
|
||||||
constexpr int ClawContraption::getButtonBCost()
|
|
||||||
{
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
constexpr long long int ClawContraption::getConversionCorrection()
|
|
||||||
{
|
|
||||||
return 10000000000000;
|
|
||||||
}
|
|
||||||
|
|
||||||
long long int ClawContraption::calcTokenCost(const Point2& buttonA, const Point2& buttonB, const Point2& prize,
|
|
||||||
const long long int offset)
|
|
||||||
{
|
|
||||||
long long int p{ (prize.y + offset) * buttonB.x - (prize.x + offset) * buttonB.y };
|
|
||||||
long long int q{ buttonA.y * buttonB.x - buttonA.x * buttonB.y };
|
|
||||||
long long int a{ p / q };
|
|
||||||
if (a * q != p || (offset == 0 && a > getNMaxButtonPushes()))
|
|
||||||
{
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
long long int r{ prize.x + offset - a * buttonA.x };
|
|
||||||
long long int b{ r / buttonB.x };
|
|
||||||
if (b * buttonB.x != r || (offset == 0 && b > getNMaxButtonPushes()))
|
|
||||||
{
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
return a * getButtonACost() + b * getButtonBCost();
|
|
||||||
}
|
|
@ -1,205 +0,0 @@
|
|||||||
// Solutions to the Advent Of Code 2024.
|
|
||||||
// Copyright (C) 2025 Stefan Müller
|
|
||||||
//
|
|
||||||
// This program is free software: you can redistribute it and/or modify it under
|
|
||||||
// the terms of the GNU General Public License as published by the Free Software
|
|
||||||
// Foundation, either version 3 of the License, or (at your option) any later
|
|
||||||
// version.
|
|
||||||
//
|
|
||||||
// This program is distributed in the hope that it will be useful, but WITHOUT
|
|
||||||
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
|
||||||
// FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU General Public License along with
|
|
||||||
// this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
#include <aoc/DiskFragmenter.hpp>
|
|
||||||
|
|
||||||
#include <algorithm>
|
|
||||||
|
|
||||||
const std::string DiskFragmenter::getPuzzleName() const
|
|
||||||
{
|
|
||||||
return "Disk Fragmenter";
|
|
||||||
}
|
|
||||||
|
|
||||||
const int DiskFragmenter::getPuzzleDay() const
|
|
||||||
{
|
|
||||||
return 9;
|
|
||||||
}
|
|
||||||
|
|
||||||
void DiskFragmenter::processDataLine(const std::string& line)
|
|
||||||
{
|
|
||||||
moveFileBlocks(line);
|
|
||||||
moveWholeFiles(line);
|
|
||||||
}
|
|
||||||
|
|
||||||
void DiskFragmenter::moveFileBlocks(const std::string& line)
|
|
||||||
{
|
|
||||||
// Index of the first unprocessed digit in the disk map.
|
|
||||||
size_t front{ 1 };
|
|
||||||
// ID number of the file 'front' refers to (when 'isFile == true'). Equivalent to 'front / 2', but calculated
|
|
||||||
// incrementally.
|
|
||||||
size_t frontIdNumber{ 1 };
|
|
||||||
// Index of the last unprocessed digit in the disk map.
|
|
||||||
size_t back{ line.size() - 1 };
|
|
||||||
// Number of remaining fragmented blocks in the file that 'back' refers to.
|
|
||||||
unsigned int nRemainingBackBlocks{ getDigit(line, back) };
|
|
||||||
// ID number of the file 'back' refers to. Equivalent to 'back / 2', but calculated incrementally.
|
|
||||||
size_t backIdNumber{ line.size() / 2 };
|
|
||||||
// Current block position.
|
|
||||||
size_t position{ getDigit(line, 0) };
|
|
||||||
// If 'true', 'front' refers to a file in the digit map, otherwise it refers to an empty space.
|
|
||||||
bool isFile{ false };
|
|
||||||
|
|
||||||
while (front < back)
|
|
||||||
{
|
|
||||||
if (isFile)
|
|
||||||
{
|
|
||||||
// Adds the checksum for the file at 'front'.
|
|
||||||
unsigned int nFileBlocks = getDigit(line, front);
|
|
||||||
part1 += calcChecksumPart(frontIdNumber, nFileBlocks, position);
|
|
||||||
frontIdNumber++;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
unsigned int nFreeBlocks = getDigit(line, front);
|
|
||||||
while (nFreeBlocks > 0)
|
|
||||||
{
|
|
||||||
if (nFreeBlocks >= nRemainingBackBlocks)
|
|
||||||
{
|
|
||||||
// Adds the checksum for all the blocks of the file at 'back'.
|
|
||||||
part1 += calcChecksumPart(backIdNumber, nRemainingBackBlocks, position);
|
|
||||||
backIdNumber--;
|
|
||||||
|
|
||||||
nFreeBlocks -= nRemainingBackBlocks;
|
|
||||||
back -= 2;
|
|
||||||
nRemainingBackBlocks = getDigit(line, back);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// Adds the checksum for the blocks of the file at 'back' that fit into the empty blocks at 'front'.
|
|
||||||
part1 += calcChecksumPart(backIdNumber, nFreeBlocks, position);
|
|
||||||
|
|
||||||
nRemainingBackBlocks -= nFreeBlocks;
|
|
||||||
nFreeBlocks = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
front++;
|
|
||||||
isFile = !isFile;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Adds the checksum for the remaining blocks of the file at 'back'.
|
|
||||||
part1 += calcChecksumPart(backIdNumber, nRemainingBackBlocks, position);
|
|
||||||
}
|
|
||||||
|
|
||||||
void DiskFragmenter::moveWholeFiles(const std::string& line)
|
|
||||||
{
|
|
||||||
// Index of the first unprocessed empty space in the disk map.
|
|
||||||
size_t front{ 1 };
|
|
||||||
// Index of the last unprocessed file in the disk map.
|
|
||||||
size_t back{ line.size() - 1 };
|
|
||||||
// ID number of the file 'back' refers to. Equivalent to 'back / 2', but calculated incrementally.
|
|
||||||
size_t backIdNumber{ line.size() / 2 };
|
|
||||||
// Current block position.
|
|
||||||
size_t position{ getDigit(line, 0) };
|
|
||||||
|
|
||||||
// Contains empty spaces as Interval.
|
|
||||||
Intervals emptySpaces{};
|
|
||||||
emptySpaces.reserve(backIdNumber / 2);
|
|
||||||
|
|
||||||
// Contains indices of the next empty space with at least as many blocks as their index. 'emptySpaceIndices[0]' is
|
|
||||||
// not used, but included for convenience of accessing the other values by index.
|
|
||||||
DigitIndexArray emptySpaceIndices;
|
|
||||||
emptySpaceIndices.fill(0);
|
|
||||||
|
|
||||||
while (back > 0)
|
|
||||||
{
|
|
||||||
int nBackBlocks = getDigit(line, back);
|
|
||||||
if (emptySpaceIndices[nBackBlocks] < emptySpaces.size() &&
|
|
||||||
emptySpaces[emptySpaceIndices[nBackBlocks]].start < position)
|
|
||||||
{
|
|
||||||
if (front >= back)
|
|
||||||
{
|
|
||||||
position -= getDigit(line, back) + getDigit(line, back - 1);
|
|
||||||
}
|
|
||||||
moveBackFileForward(back, backIdNumber, nBackBlocks, emptySpaces[emptySpaceIndices[nBackBlocks]]);
|
|
||||||
UpdateEmptySpaceIndices(emptySpaces, emptySpaceIndices);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
AddNewEmptySpaces(emptySpaces, line, front, back, nBackBlocks, position);
|
|
||||||
if (front < back)
|
|
||||||
{
|
|
||||||
moveBackFileForward(back, backIdNumber, nBackBlocks, emptySpaces.back());
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
keepBackFile(line, back, backIdNumber, nBackBlocks, position);
|
|
||||||
}
|
|
||||||
UpdateEmptySpaceIndices(emptySpaces, emptySpaceIndices);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void DiskFragmenter::AddNewEmptySpaces(Intervals& emptySpaces, const std::string& line, size_t& front,
|
|
||||||
const size_t back, const int nBackBlocks, size_t& position)
|
|
||||||
{
|
|
||||||
int nFrontBlocks{ 0 };
|
|
||||||
while (front < back && nFrontBlocks < nBackBlocks)
|
|
||||||
{
|
|
||||||
// Adds more empty spaces.
|
|
||||||
nFrontBlocks = getDigit(line, front);
|
|
||||||
emptySpaces.emplace_back(position, nFrontBlocks);
|
|
||||||
front++;
|
|
||||||
position += static_cast<size_t>(nFrontBlocks) + getDigit(line, front++);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void DiskFragmenter::UpdateEmptySpaceIndices(const Intervals& emptySpaces, DigitIndexArray& emptySpaceIndices)
|
|
||||||
{
|
|
||||||
for (size_t i = 1; i < emptySpaceIndices.size(); i++)
|
|
||||||
{
|
|
||||||
while (emptySpaceIndices[i] < emptySpaces.size() && emptySpaces[emptySpaceIndices[i]].length < i)
|
|
||||||
{
|
|
||||||
emptySpaceIndices[i]++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Moves the 'back' file into the empty space.
|
|
||||||
void DiskFragmenter::moveBackFileForward(size_t& back, size_t& backIdNumber, const int nBackBlocks,
|
|
||||||
Interval& emptySpace)
|
|
||||||
{
|
|
||||||
part2 += calcChecksumPart(backIdNumber, nBackBlocks, emptySpace.start);
|
|
||||||
emptySpace.length -= nBackBlocks;
|
|
||||||
back -= 2;
|
|
||||||
backIdNumber--;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Does not move the 'back' file, there is no space for it.
|
|
||||||
void DiskFragmenter::keepBackFile(const std::string& line, size_t& back, size_t& backIdNumber, const int nBackBlocks,
|
|
||||||
size_t& position)
|
|
||||||
{
|
|
||||||
part2 -= calcChecksumPart(backIdNumber, -nBackBlocks, position);
|
|
||||||
back--;
|
|
||||||
position -= getDigit(line, back--);
|
|
||||||
backIdNumber--;
|
|
||||||
}
|
|
||||||
|
|
||||||
void DiskFragmenter::finish()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
unsigned int DiskFragmenter::getDigit(const std::string& line, const size_t index) const
|
|
||||||
{
|
|
||||||
return line[index] - '0';
|
|
||||||
}
|
|
||||||
|
|
||||||
long long int DiskFragmenter::calcChecksumPart(const size_t idNumber, const int nBlocks, size_t& position) const
|
|
||||||
{
|
|
||||||
position += nBlocks;
|
|
||||||
// Casting the parameters is required to allow negative block count, resulting in a negative return value.
|
|
||||||
return static_cast<long long int>(idNumber) *
|
|
||||||
((nBlocks * (2 * static_cast<long long int>(position) - nBlocks - 1)) / 2);
|
|
||||||
}
|
|
@ -1,119 +0,0 @@
|
|||||||
// Solutions to the Advent Of Code 2024.
|
|
||||||
// Copyright (C) 2025 Stefan Müller
|
|
||||||
//
|
|
||||||
// This program is free software: you can redistribute it and/or modify it under
|
|
||||||
// the terms of the GNU General Public License as published by the Free Software
|
|
||||||
// Foundation, either version 3 of the License, or (at your option) any later
|
|
||||||
// version.
|
|
||||||
//
|
|
||||||
// This program is distributed in the hope that it will be useful, but WITHOUT
|
|
||||||
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
|
||||||
// FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU General Public License along with
|
|
||||||
// this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
#include <aoc/GardenGroups.hpp>
|
|
||||||
|
|
||||||
GardenGroups::GardenGroups(const int inputFileNameSuffix)
|
|
||||||
: LinesSolver{ inputFileNameSuffix }
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
const std::string GardenGroups::getPuzzleName() const
|
|
||||||
{
|
|
||||||
return "Garden Groups";
|
|
||||||
}
|
|
||||||
|
|
||||||
const int GardenGroups::getPuzzleDay() const
|
|
||||||
{
|
|
||||||
return 12;
|
|
||||||
}
|
|
||||||
|
|
||||||
void GardenGroups::finish()
|
|
||||||
{
|
|
||||||
Grid<bool> isVisited{ lines.size(), lines[0].size() };
|
|
||||||
isVisited.fill(false);
|
|
||||||
|
|
||||||
std::stack<Point2> otherRegionsStack{};
|
|
||||||
otherRegionsStack.emplace(0, 0);
|
|
||||||
while (!otherRegionsStack.empty())
|
|
||||||
{
|
|
||||||
const Point2 position{ otherRegionsStack.top() };
|
|
||||||
otherRegionsStack.pop();
|
|
||||||
if (!isVisited.cell(position))
|
|
||||||
{
|
|
||||||
traverseRegion(isVisited, otherRegionsStack, position);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void GardenGroups::traverseRegion(Grid<bool>& isVisited, std::stack<Point2>& otherRegionsStack, const Point2& start)
|
|
||||||
{
|
|
||||||
const char plantType{ getCharAt(start) };
|
|
||||||
long long int area{ 0 };
|
|
||||||
long long int perimeter{ 0 };
|
|
||||||
long long int nCorners{ 0 };
|
|
||||||
std::stack<Point2> regionStack{};
|
|
||||||
regionStack.push(start);
|
|
||||||
|
|
||||||
while (!regionStack.empty())
|
|
||||||
{
|
|
||||||
const Point2 position{ regionStack.top() };
|
|
||||||
regionStack.pop();
|
|
||||||
if (!isVisited.cell(position))
|
|
||||||
{
|
|
||||||
isVisited.cell(position) = true;
|
|
||||||
area++;
|
|
||||||
|
|
||||||
Point2 previousNeighbor{ position + Point2::cardinalDirections.back() };
|
|
||||||
bool isPreviousNeighborSameRegion{
|
|
||||||
isInBounds(previousNeighbor) && getCharAt(previousNeighbor) == plantType
|
|
||||||
};
|
|
||||||
for (const Point2& direction : Point2::cardinalDirections)
|
|
||||||
{
|
|
||||||
const Point2 neighbor{ position + direction };
|
|
||||||
const bool isNeighborSameRegion{ isInBounds(neighbor) && getCharAt(neighbor) == plantType };
|
|
||||||
|
|
||||||
if (isNeighborSameRegion)
|
|
||||||
{
|
|
||||||
if (!isVisited.cell(neighbor))
|
|
||||||
{
|
|
||||||
regionStack.push(neighbor);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Increases the corner count if two adjacent neighbors are both inside this region, but the
|
|
||||||
// diagonal neighbor in between them is outside this region.
|
|
||||||
if (isPreviousNeighborSameRegion)
|
|
||||||
{
|
|
||||||
const Point2 diagonalNeighbor{ previousNeighbor + direction };
|
|
||||||
if (!isInBounds(diagonalNeighbor) || getCharAt(diagonalNeighbor) != plantType)
|
|
||||||
{
|
|
||||||
nCorners++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (isInBounds(neighbor) && !isVisited.cell(neighbor))
|
|
||||||
{
|
|
||||||
otherRegionsStack.push(neighbor);
|
|
||||||
}
|
|
||||||
|
|
||||||
perimeter++;
|
|
||||||
|
|
||||||
// Increases the corner count if two adjacent neighbors are both outside this region.
|
|
||||||
if (!isPreviousNeighborSameRegion)
|
|
||||||
{
|
|
||||||
nCorners++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
previousNeighbor = neighbor;
|
|
||||||
isPreviousNeighborSameRegion = isNeighborSameRegion;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
part1 += area * perimeter;
|
|
||||||
part2 += area * nCorners;
|
|
||||||
}
|
|
@ -1,5 +1,5 @@
|
|||||||
// Solutions to the Advent Of Code 2024.
|
// Solutions to the Advent Of Code 2024.
|
||||||
// Copyright (C) 2024 Stefan Müller
|
// Copyright (C) 2024 Stefan Müller
|
||||||
//
|
//
|
||||||
// This program is free software: you can redistribute it and/or modify it under
|
// This program is free software: you can redistribute it and/or modify it under
|
||||||
// the terms of the GNU General Public License as published by the Free Software
|
// the terms of the GNU General Public License as published by the Free Software
|
||||||
|
File diff suppressed because one or more lines are too long
@ -13,16 +13,18 @@
|
|||||||
// You should have received a copy of the GNU General Public License along with
|
// You should have received a copy of the GNU General Public License along with
|
||||||
// this program. If not, see <http://www.gnu.org/licenses/>.
|
// this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
#include <aoc/HistorianHysteria.hpp>
|
#include <aoc/HistorianHysteria.hpp>
|
||||||
|
|
||||||
const std::string HistorianHysteria::getPuzzleName() const
|
const std::string HistorianHysteria::getPuzzleName() const
|
||||||
{
|
{
|
||||||
return "Historian Hysteria";
|
return "Day 1: Historian Hysteria";
|
||||||
}
|
}
|
||||||
|
|
||||||
const int HistorianHysteria::getPuzzleDay() const
|
const std::string HistorianHysteria::getInputFileName() const
|
||||||
{
|
{
|
||||||
return 1;
|
return "historian_hysteria.txt";
|
||||||
}
|
}
|
||||||
|
|
||||||
void HistorianHysteria::processDataLine(const std::string& line)
|
void HistorianHysteria::processDataLine(const std::string& line)
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
// Solutions to the Advent Of Code 2024.
|
// Solutions to the Advent Of Code 2024.
|
||||||
// Copyright (C) 2024-2025 Stefan Müller
|
// Copyright (C) 2024 Stefan Müller
|
||||||
//
|
//
|
||||||
// This program is free software: you can redistribute it and/or modify it under
|
// This program is free software: you can redistribute it and/or modify it under
|
||||||
// the terms of the GNU General Public License as published by the Free Software
|
// the terms of the GNU General Public License as published by the Free Software
|
||||||
@ -24,12 +24,12 @@
|
|||||||
|
|
||||||
const std::string HoofIt::getPuzzleName() const
|
const std::string HoofIt::getPuzzleName() const
|
||||||
{
|
{
|
||||||
return "Hoof It";
|
return "Day 10: Hoof It";
|
||||||
}
|
}
|
||||||
|
|
||||||
const int HoofIt::getPuzzleDay() const
|
const std::string HoofIt::getInputFileName() const
|
||||||
{
|
{
|
||||||
return 10;
|
return "hoof_it.txt";
|
||||||
}
|
}
|
||||||
|
|
||||||
void HoofIt::finish()
|
void HoofIt::finish()
|
||||||
@ -57,12 +57,12 @@ void HoofIt::finish()
|
|||||||
{
|
{
|
||||||
auto trail = queue.front();
|
auto trail = queue.front();
|
||||||
queue.pop();
|
queue.pop();
|
||||||
if (getCharAt(trail) != getTrailheadChar())
|
if (getPosition(trail) != getTrailheadChar())
|
||||||
{
|
{
|
||||||
for (auto direction : Point2::cardinalDirections)
|
for (auto direction : Point2::cardinalDirections)
|
||||||
{
|
{
|
||||||
Point2 p{ trail + direction };
|
Point2 p{ trail + direction };
|
||||||
if (isInBounds(p) && getCharAt(p) + 1 == getCharAt(trail))
|
if (isInBounds(p) && getPosition(p) + 1 == getPosition(trail))
|
||||||
{
|
{
|
||||||
if (trailEndsMap[p.y][p.x].empty())
|
if (trailEndsMap[p.y][p.x].empty())
|
||||||
{
|
{
|
||||||
@ -81,12 +81,12 @@ void HoofIt::finish()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
constexpr char HoofIt::getTrailheadChar()
|
const char HoofIt::getTrailheadChar() const
|
||||||
{
|
{
|
||||||
return '0';
|
return '0';
|
||||||
}
|
}
|
||||||
|
|
||||||
constexpr char HoofIt::getTrailTopChar()
|
const char HoofIt::getTrailTopChar() const
|
||||||
{
|
{
|
||||||
return '9';
|
return '9';
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
// Solutions to the Advent Of Code 2024.
|
// Solutions to the Advent Of Code 2024.
|
||||||
// Copyright (C) 2024 Stefan Müller
|
// Copyright (C) 2024 Stefan Müller
|
||||||
//
|
//
|
||||||
// This program is free software: you can redistribute it and/or modify it under
|
// This program is free software: you can redistribute it and/or modify it under
|
||||||
// the terms of the GNU General Public License as published by the Free Software
|
// the terms of the GNU General Public License as published by the Free Software
|
||||||
@ -17,12 +17,12 @@
|
|||||||
|
|
||||||
const std::string LanParty::getPuzzleName() const
|
const std::string LanParty::getPuzzleName() const
|
||||||
{
|
{
|
||||||
return "LAN Party";
|
return "Day 23: LAN Party";
|
||||||
}
|
}
|
||||||
|
|
||||||
const int LanParty::getPuzzleDay() const
|
const std::string LanParty::getInputFileName() const
|
||||||
{
|
{
|
||||||
return 23;
|
return "lan_party.txt";
|
||||||
}
|
}
|
||||||
|
|
||||||
void LanParty::processDataLine(const std::string& line)
|
void LanParty::processDataLine(const std::string& line)
|
||||||
@ -33,7 +33,8 @@ void LanParty::processDataLine(const std::string& line)
|
|||||||
void LanParty::finish()
|
void LanParty::finish()
|
||||||
{
|
{
|
||||||
auto vertices = lan_.getVertices();
|
auto vertices = lan_.getVertices();
|
||||||
// This works as long as "ta" is present as a computername, otherwise we would have to use vertices.find_if().
|
// This works as long as "ta" is present as a computername, otherwise we would have to use
|
||||||
|
// vertices.find_if().
|
||||||
auto iterTx = vertices.find(getFirstTxComputerName());
|
auto iterTx = vertices.find(getFirstTxComputerName());
|
||||||
while (iterTx != vertices.end() && iterTx->second->getId() <= getLastTxComputerName())
|
while (iterTx != vertices.end() && iterTx->second->getId() <= getLastTxComputerName())
|
||||||
{
|
{
|
||||||
|
@ -1,43 +0,0 @@
|
|||||||
// Solutions to the Advent Of Code 2024.
|
|
||||||
// Copyright (C) 2024-2025 Stefan Müller
|
|
||||||
//
|
|
||||||
// This program is free software: you can redistribute it and/or modify it under
|
|
||||||
// the terms of the GNU General Public License as published by the Free Software
|
|
||||||
// Foundation, either version 3 of the License, or (at your option) any later
|
|
||||||
// version.
|
|
||||||
//
|
|
||||||
// This program is distributed in the hope that it will be useful, but WITHOUT
|
|
||||||
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
|
||||||
// FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU General Public License along with
|
|
||||||
// this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
#include <aoc/Lines.hpp>
|
|
||||||
|
|
||||||
#include <algorithm>
|
|
||||||
|
|
||||||
bool Lines::isInBounds(const Point2& point) const
|
|
||||||
{
|
|
||||||
return 0 <= point.y && point.y < size() &&
|
|
||||||
0 <= point.x && point.x < at(point.y).size();
|
|
||||||
}
|
|
||||||
|
|
||||||
char Lines::getCharAt(const Point2& point) const
|
|
||||||
{
|
|
||||||
return at(point.y)[point.x];
|
|
||||||
}
|
|
||||||
|
|
||||||
void Lines::setCharAt(const Point2& point, const char value)
|
|
||||||
{
|
|
||||||
at(point.y)[point.x] = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::ostream& operator<<(std::ostream& os, const Lines& lines)
|
|
||||||
{
|
|
||||||
for (const auto& line : lines)
|
|
||||||
{
|
|
||||||
os << line << std::endl;
|
|
||||||
}
|
|
||||||
return os;
|
|
||||||
}
|
|
@ -1,5 +1,5 @@
|
|||||||
// Solutions to the Advent Of Code 2024.
|
// Solutions to the Advent Of Code 2024.
|
||||||
// Copyright (C) 2024-2025 Stefan Müller
|
// Copyright (C) 2024 Stefan Müller
|
||||||
//
|
//
|
||||||
// This program is free software: you can redistribute it and/or modify it under
|
// This program is free software: you can redistribute it and/or modify it under
|
||||||
// the terms of the GNU General Public License as published by the Free Software
|
// the terms of the GNU General Public License as published by the Free Software
|
||||||
@ -15,11 +15,6 @@
|
|||||||
|
|
||||||
#include <aoc/LinesSolver.hpp>
|
#include <aoc/LinesSolver.hpp>
|
||||||
|
|
||||||
LinesSolver::LinesSolver(const int inputFileNameSuffix)
|
|
||||||
: Solver{ inputFileNameSuffix }
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void LinesSolver::processDataLine(const std::string& line)
|
void LinesSolver::processDataLine(const std::string& line)
|
||||||
{
|
{
|
||||||
lines.push_back(line);
|
lines.push_back(line);
|
||||||
@ -27,15 +22,16 @@ void LinesSolver::processDataLine(const std::string& line)
|
|||||||
|
|
||||||
bool LinesSolver::isInBounds(const Point2& point) const
|
bool LinesSolver::isInBounds(const Point2& point) const
|
||||||
{
|
{
|
||||||
return lines.isInBounds(point);
|
return 0 <= point.y && point.y < lines.size()
|
||||||
|
&& 0 <= point.x && point.x < lines[point.y].size();
|
||||||
}
|
}
|
||||||
|
|
||||||
char LinesSolver::getCharAt(const Point2& point) const
|
char LinesSolver::getPosition(const Point2& point) const
|
||||||
{
|
{
|
||||||
return lines.getCharAt(point);
|
return lines[point.y][point.x];
|
||||||
}
|
}
|
||||||
|
|
||||||
void LinesSolver::setCharAt(const Point2& point, const char value)
|
void LinesSolver::setPosition(const Point2& point, const char value)
|
||||||
{
|
{
|
||||||
lines.setCharAt(point, value);
|
lines[point.y][point.x] = value;
|
||||||
}
|
}
|
||||||
|
47
src/Math.cpp
47
src/Math.cpp
@ -1,47 +0,0 @@
|
|||||||
// Solutions to the Advent Of Code 2024.
|
|
||||||
// Copyright (C) 2025 Stefan Müller
|
|
||||||
//
|
|
||||||
// This program is free software: you can redistribute it and/or modify it under
|
|
||||||
// the terms of the GNU General Public License as published by the Free Software
|
|
||||||
// Foundation, either version 3 of the License, or (at your option) any later
|
|
||||||
// version.
|
|
||||||
//
|
|
||||||
// This program is distributed in the hope that it will be useful, but WITHOUT
|
|
||||||
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
|
||||||
// FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU General Public License along with
|
|
||||||
// this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
#include <aoc/Math.hpp>
|
|
||||||
|
|
||||||
#include <array>
|
|
||||||
|
|
||||||
int Math::ipow(const int base, const int exponent)
|
|
||||||
{
|
|
||||||
int result = 1;
|
|
||||||
for (int i = 1; i <= exponent; i++)
|
|
||||||
{
|
|
||||||
result *= base;
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::tuple<int, int, int> Math::extendedEuclid(const int a, const int b)
|
|
||||||
{
|
|
||||||
std::array<std::pair<int, int>, 3> rst{ { { a, b }, { 1, 0 }, { 0, 1 } } };
|
|
||||||
int q;
|
|
||||||
int r;
|
|
||||||
|
|
||||||
while (rst[0].second > 0)
|
|
||||||
{
|
|
||||||
q = rst[0].first / rst[0].second;
|
|
||||||
for (auto& p : rst)
|
|
||||||
{
|
|
||||||
r = p.first - q * p.second;
|
|
||||||
p.first = p.second;
|
|
||||||
p.second = r;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return { rst[0].first, rst[1].first, rst[2].first };
|
|
||||||
}
|
|
@ -16,9 +16,7 @@
|
|||||||
#include <aoc/MullCharState.hpp>
|
#include <aoc/MullCharState.hpp>
|
||||||
|
|
||||||
MullCharState::MullCharState(const char expected)
|
MullCharState::MullCharState(const char expected)
|
||||||
: expected_{ expected }, successState_{}, failState_{}
|
: expected_{ expected }, successState_{}, failState_{} {}
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void MullCharState::next(StringStateMachine* stateMachine)
|
void MullCharState::next(StringStateMachine* stateMachine)
|
||||||
{
|
{
|
||||||
|
@ -16,9 +16,7 @@
|
|||||||
#include <aoc/MullData.hpp>
|
#include <aoc/MullData.hpp>
|
||||||
|
|
||||||
MullData::MullData()
|
MullData::MullData()
|
||||||
: isEnabled_{ true }, factor1_{ 0 }, factor2_{ 0 }, part1_{ 0 }, part2_{ 0 }
|
: isEnabled_{ true }, factor1_{ 0 }, factor2_{ 0 }, part1_{ 0 }, part2_{ 0 } {};
|
||||||
{
|
|
||||||
};
|
|
||||||
|
|
||||||
bool MullData::getIsEnabled()
|
bool MullData::getIsEnabled()
|
||||||
{
|
{
|
||||||
|
@ -16,9 +16,7 @@
|
|||||||
#include <aoc/MullFactorState.hpp>
|
#include <aoc/MullFactorState.hpp>
|
||||||
|
|
||||||
MullFactorState::MullFactorState(const char expected, const int index)
|
MullFactorState::MullFactorState(const char expected, const int index)
|
||||||
: expected_{ expected }, index_{ index }, successState_{}, failState_{}
|
: expected_{ expected }, index_{ index }, successState_{}, failState_{} {}
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void MullFactorState::enter(StringStateMachine* stateMachine)
|
void MullFactorState::enter(StringStateMachine* stateMachine)
|
||||||
{
|
{
|
||||||
|
@ -18,18 +18,18 @@
|
|||||||
#include <aoc/StringStateMachine.hpp>
|
#include <aoc/StringStateMachine.hpp>
|
||||||
|
|
||||||
MullItOver::MullItOver()
|
MullItOver::MullItOver()
|
||||||
: Solver{}, data_{}, states_ { data_ }
|
: Solver{}, data_{}, states_ {
|
||||||
{
|
data_
|
||||||
}
|
} {}
|
||||||
|
|
||||||
const std::string MullItOver::getPuzzleName() const
|
const std::string MullItOver::getPuzzleName() const
|
||||||
{
|
{
|
||||||
return "Mull It Over";
|
return "Day 3: Mull It Over";
|
||||||
}
|
}
|
||||||
|
|
||||||
const int MullItOver::getPuzzleDay() const
|
const std::string MullItOver::getInputFileName() const
|
||||||
{
|
{
|
||||||
return 3;
|
return "mull_it_over.txt";
|
||||||
}
|
}
|
||||||
|
|
||||||
void MullItOver::processDataLine(const std::string& line)
|
void MullItOver::processDataLine(const std::string& line)
|
||||||
|
@ -16,9 +16,7 @@
|
|||||||
#include <aoc/MullToggleCloseState.hpp>
|
#include <aoc/MullToggleCloseState.hpp>
|
||||||
|
|
||||||
MullToggleCloseState::MullToggleCloseState(const bool isEnabler)
|
MullToggleCloseState::MullToggleCloseState(const bool isEnabler)
|
||||||
: isEnabler_{ isEnabler }, successState_{}
|
: isEnabler_{ isEnabler }, successState_{} {}
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void MullToggleCloseState::next(StringStateMachine* stateMachine)
|
void MullToggleCloseState::next(StringStateMachine* stateMachine)
|
||||||
{
|
{
|
||||||
|
@ -1,158 +0,0 @@
|
|||||||
// Solutions to the Advent Of Code 2024.
|
|
||||||
// Copyright (C) 2025 Stefan Müller
|
|
||||||
//
|
|
||||||
// This program is free software: you can redistribute it and/or modify it under
|
|
||||||
// the terms of the GNU General Public License as published by the Free Software
|
|
||||||
// Foundation, either version 3 of the License, or (at your option) any later
|
|
||||||
// version.
|
|
||||||
//
|
|
||||||
// This program is distributed in the hope that it will be useful, but WITHOUT
|
|
||||||
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
|
||||||
// FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU General Public License along with
|
|
||||||
// this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
#include <aoc/PlutonianPebbles.hpp>
|
|
||||||
|
|
||||||
#include <memory>
|
|
||||||
#include <sstream>
|
|
||||||
|
|
||||||
const std::string PlutonianPebbles::getPuzzleName() const
|
|
||||||
{
|
|
||||||
return "Plutonian Pebbles";
|
|
||||||
}
|
|
||||||
|
|
||||||
const int PlutonianPebbles::getPuzzleDay() const
|
|
||||||
{
|
|
||||||
return 11;
|
|
||||||
}
|
|
||||||
|
|
||||||
void PlutonianPebbles::processDataLine(const std::string& line)
|
|
||||||
{
|
|
||||||
// Maps pebble numbers before the blink to their cardinality.
|
|
||||||
auto currentPebbles = std::make_shared<std::map<long long int, long long int>>();
|
|
||||||
// Maps pebble numbers after the blink to their cardinality.
|
|
||||||
std::shared_ptr<std::map<long long int, long long int>> nextBlinkPebbles;
|
|
||||||
|
|
||||||
// Adds initial pebbles numbers.
|
|
||||||
std::istringstream stream{ line };
|
|
||||||
long long int pebbleNumber;
|
|
||||||
while (stream >> pebbleNumber)
|
|
||||||
{
|
|
||||||
addPebble(*currentPebbles, pebbleNumber, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Processes the blinks.
|
|
||||||
for (int i = getNBlinksPart2(); i > 1; i--)
|
|
||||||
{
|
|
||||||
// Maps pebble numbers after the blink to their cardinality.
|
|
||||||
nextBlinkPebbles = std::make_shared<std::map<long long int, long long int>>();
|
|
||||||
|
|
||||||
for (const auto& pebble : *currentPebbles)
|
|
||||||
{
|
|
||||||
// Finds or inserts the pebble number to the blink map.
|
|
||||||
const auto [it, success] = blinkMap_.insert({ pebble.first, {} });
|
|
||||||
if (success)
|
|
||||||
{
|
|
||||||
addNextBlinkNumbers(pebble.first, it->second);
|
|
||||||
}
|
|
||||||
// Adds the new pebble numbers to the line.
|
|
||||||
for (const auto nextBlinkNumber : it->second)
|
|
||||||
{
|
|
||||||
addPebble(*nextBlinkPebbles, nextBlinkNumber, pebble.second);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (i == getNBlinksPart2() - getNBlinksPart1() + 1)
|
|
||||||
{
|
|
||||||
for (const auto& blinkNumber : *currentPebbles)
|
|
||||||
{
|
|
||||||
// The number is guaranteed to be in blinkMap_ already.
|
|
||||||
auto hit = blinkMap_.find(blinkNumber.first);
|
|
||||||
part1 += blinkNumber.second * hit->second.size();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
currentPebbles = nextBlinkPebbles;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const auto& nextBlinkNumber : *nextBlinkPebbles)
|
|
||||||
{
|
|
||||||
// In the last step we only care about cardinalities, not the actual new pebble numbers, so we use
|
|
||||||
// getNNextBlinkNumbers() instead of addNextBlinkNumbers().
|
|
||||||
auto hit = blinkMap_.find(nextBlinkNumber.first);
|
|
||||||
if (hit == blinkMap_.end())
|
|
||||||
{
|
|
||||||
part2 += nextBlinkNumber.second * getNNextBlinkNumbers(nextBlinkNumber.first);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
part2 += nextBlinkNumber.second * hit->second.size();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void PlutonianPebbles::finish()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
constexpr int PlutonianPebbles::getNBlinksPart1()
|
|
||||||
{
|
|
||||||
return 25;
|
|
||||||
}
|
|
||||||
|
|
||||||
constexpr int PlutonianPebbles::getNBlinksPart2()
|
|
||||||
{
|
|
||||||
return 75;
|
|
||||||
}
|
|
||||||
|
|
||||||
void PlutonianPebbles::addPebble(std::map<long long int, long long int>& pebbles, const long long int pebbleNumber,
|
|
||||||
long long int cardinality)
|
|
||||||
{
|
|
||||||
auto hit = pebbles.find(pebbleNumber);
|
|
||||||
if (hit == pebbles.end())
|
|
||||||
{
|
|
||||||
pebbles.insert({ pebbleNumber, cardinality });
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
hit->second += cardinality;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void PlutonianPebbles::addNextBlinkNumbers(const long long int pebbleNumber,
|
|
||||||
std::vector<long long int>& nextBlinkNumbers)
|
|
||||||
{
|
|
||||||
if (pebbleNumber == 0)
|
|
||||||
{
|
|
||||||
nextBlinkNumbers.push_back(1);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
auto s = std::to_string(pebbleNumber);
|
|
||||||
if (s.size() % 2 == 0)
|
|
||||||
{
|
|
||||||
auto half = s.size() / 2;
|
|
||||||
nextBlinkNumbers.push_back(std::stoll(s.substr(0, half)));
|
|
||||||
nextBlinkNumbers.push_back(std::stoll(s.substr(half, half)));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
nextBlinkNumbers.push_back(pebbleNumber * 2024);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int PlutonianPebbles::getNNextBlinkNumbers(const long long int pebbleNumber) const
|
|
||||||
{
|
|
||||||
auto s = std::to_string(pebbleNumber);
|
|
||||||
if (s.size() % 2 == 0)
|
|
||||||
{
|
|
||||||
return 2;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,5 +1,5 @@
|
|||||||
// Solutions to the Advent Of Code 2024.
|
// Solutions to the Advent Of Code 2024.
|
||||||
// Copyright (C) 2024 Stefan Müller
|
// Copyright (C) 2024 Stefan Müller
|
||||||
//
|
//
|
||||||
// This program is free software: you can redistribute it and/or modify it under
|
// This program is free software: you can redistribute it and/or modify it under
|
||||||
// the terms of the GNU General Public License as published by the Free Software
|
// the terms of the GNU General Public License as published by the Free Software
|
||||||
@ -23,37 +23,16 @@ const Point2 Point2::upLeft{ -1, -1 };
|
|||||||
const Point2 Point2::upRight{ 1, -1 };
|
const Point2 Point2::upRight{ 1, -1 };
|
||||||
const Point2 Point2::downLeft{ -1, 1 };
|
const Point2 Point2::downLeft{ -1, 1 };
|
||||||
const Point2 Point2::downRight{ 1, 1 };
|
const Point2 Point2::downRight{ 1, 1 };
|
||||||
const std::array<Point2, 8> Point2::directions = { Point2::down, Point2::downRight, Point2::right, Point2::upRight,
|
const Point2 Point2::directions[] = { Point2::down, Point2::downRight, Point2::right,
|
||||||
Point2::up, Point2::upLeft, Point2::left, Point2::downLeft };
|
Point2::upRight, Point2::up, Point2::upLeft, Point2::left, Point2::downLeft };
|
||||||
const std::array<Point2, 4> Point2::cardinalDirections = { Point2::down, Point2::right, Point2::up, Point2::left };
|
const Point2 Point2::cardinalDirections[] = { Point2::down, Point2::right, Point2::up,
|
||||||
|
Point2::left};
|
||||||
Point2 Point2::getCardinalDirection(const char directionChar)
|
|
||||||
{
|
|
||||||
switch (directionChar)
|
|
||||||
{
|
|
||||||
case 'v' :
|
|
||||||
return Point2::down;
|
|
||||||
case '>' :
|
|
||||||
return Point2::right;
|
|
||||||
case '^' :
|
|
||||||
return Point2::up;
|
|
||||||
case '<' :
|
|
||||||
return Point2::left;
|
|
||||||
default :
|
|
||||||
return { 0, 0 };
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
Point2::Point2()
|
Point2::Point2()
|
||||||
: Point2{ 0, 0 }
|
: Point2{ 0, 0 } {}
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
Point2::Point2(const int x, const int y)
|
Point2::Point2(const int x, const int y)
|
||||||
: x{ x }, y{ y }
|
: x{ x }, y{ y } {}
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Point2::operator==(const Point2& rhs) const
|
bool Point2::operator==(const Point2& rhs) const
|
||||||
{
|
{
|
||||||
@ -103,7 +82,8 @@ Point2& Point2::operator*=(const int rhs)
|
|||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
int& Point2::operator[](size_t coordinateIndex)
|
std::ostream& operator<<(std::ostream& os, const Point2& rhs)
|
||||||
{
|
{
|
||||||
return coordinateIndex == 0 ? x : y;
|
os << "(" << rhs.x << ", " << rhs.y << ")";
|
||||||
|
return os;
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
// Solutions to the Advent Of Code 2024.
|
// Solutions to the Advent Of Code 2024.
|
||||||
// Copyright (C) 2024 Stefan Müller
|
// Copyright (C) 2024 Stefan Müller
|
||||||
//
|
//
|
||||||
// This program is free software: you can redistribute it and/or modify it under
|
// This program is free software: you can redistribute it and/or modify it under
|
||||||
// the terms of the GNU General Public License as published by the Free Software
|
// the terms of the GNU General Public License as published by the Free Software
|
||||||
@ -20,20 +20,30 @@
|
|||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
PrintQueue::PrintQueue()
|
PrintQueue::PrintQueue()
|
||||||
: Solver{}, isProcessingOrderingRules_{ true }, pageNoMapIndex_{ 0 }, pageNoMap_(getMaxPageNo() + 1, -1),
|
: Solver{}, pageNoMapIndex_{ 0 }, isProcessingOrderingRules_{ true }, orderingRules_{}
|
||||||
orderingRules_(getNPages(), getNPages())
|
|
||||||
{
|
{
|
||||||
orderingRules_.fill(false);
|
for (size_t i = 0; i <= maxPageNo_; i++)
|
||||||
|
{
|
||||||
|
pageNoMap_[i] = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (size_t i = 0; i < nPages_; i++)
|
||||||
|
{
|
||||||
|
for (size_t j = 0; j < nPages_; j++)
|
||||||
|
{
|
||||||
|
orderingRules_[i][j] = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const std::string PrintQueue::getPuzzleName() const
|
const std::string PrintQueue::getPuzzleName() const
|
||||||
{
|
{
|
||||||
return "Print Queue";
|
return "Day 5: Print Queue";
|
||||||
}
|
}
|
||||||
|
|
||||||
const int PrintQueue::getPuzzleDay() const
|
const std::string PrintQueue::getInputFileName() const
|
||||||
{
|
{
|
||||||
return 5;
|
return "print_queue.txt";
|
||||||
}
|
}
|
||||||
|
|
||||||
void PrintQueue::processDataLine(const std::string& line)
|
void PrintQueue::processDataLine(const std::string& line)
|
||||||
@ -59,16 +69,6 @@ void PrintQueue::finish()
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
constexpr int PrintQueue::getNPages()
|
|
||||||
{
|
|
||||||
return 49;
|
|
||||||
}
|
|
||||||
|
|
||||||
constexpr int PrintQueue::getMaxPageNo()
|
|
||||||
{
|
|
||||||
return 99;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t PrintQueue::getMapped(const int pageNo)
|
size_t PrintQueue::getMapped(const int pageNo)
|
||||||
{
|
{
|
||||||
if (pageNoMap_[pageNo] < 0)
|
if (pageNoMap_[pageNo] < 0)
|
||||||
@ -80,22 +80,22 @@ size_t PrintQueue::getMapped(const int pageNo)
|
|||||||
|
|
||||||
void PrintQueue::processOrderingRule(const std::string& line)
|
void PrintQueue::processOrderingRule(const std::string& line)
|
||||||
{
|
{
|
||||||
size_t pos{ line.find("|") };
|
auto pos{ line.find("|") };
|
||||||
int before{ std::stoi(line.substr(0, pos)) };
|
auto before{ std::stoi(line.substr(0, pos)) };
|
||||||
int after{ std::stoi(line.substr(pos + 1)) };
|
auto after{ std::stoi(line.substr(pos + 1)) };
|
||||||
orderingRules_[getMapped(before)][getMapped(after)] = true;
|
orderingRules_[getMapped(before)][getMapped(after)] = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void PrintQueue::processUpdatePages(const std::string& line)
|
void PrintQueue::processUpdatePages(const std::string& line)
|
||||||
{
|
{
|
||||||
std::vector<int> pages{};
|
std::vector<size_t> pages{};
|
||||||
std::istringstream stream{ line };
|
std::stringstream stream{ line };
|
||||||
std::string token;
|
std::string token;
|
||||||
bool isCorrectOrder{ true };
|
auto isCorrectOrder{ true };
|
||||||
// We completely construct 'pages' for part 2, even if the ordering is not correct.
|
// We completely construct 'pages' for part 2, even if the ordering is not correct.
|
||||||
while (std::getline(stream, token, ','))
|
while (std::getline(stream, token, ','))
|
||||||
{
|
{
|
||||||
int page = std::stoi(token);
|
size_t page = std::stoi(token);
|
||||||
size_t i{ 0 };
|
size_t i{ 0 };
|
||||||
while (isCorrectOrder && i < pages.size())
|
while (isCorrectOrder && i < pages.size())
|
||||||
{
|
{
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
// Solutions to the Advent Of Code 2024.
|
// Solutions to the Advent Of Code 2024.
|
||||||
// Copyright (C) 2024-2025 Stefan Müller
|
// Copyright (C) 2024 Stefan Müller
|
||||||
//
|
//
|
||||||
// This program is free software: you can redistribute it and/or modify it under
|
// This program is free software: you can redistribute it and/or modify it under
|
||||||
// the terms of the GNU General Public License as published by the Free Software
|
// the terms of the GNU General Public License as published by the Free Software
|
||||||
@ -27,15 +27,7 @@
|
|||||||
#include <aoc/CeresSearch.hpp>
|
#include <aoc/CeresSearch.hpp>
|
||||||
#include <aoc/PrintQueue.hpp>
|
#include <aoc/PrintQueue.hpp>
|
||||||
#include <aoc/GuardGallivant.hpp>
|
#include <aoc/GuardGallivant.hpp>
|
||||||
#include <aoc/BridgeRepair.hpp>
|
|
||||||
#include <aoc/ResonantCollinearity.hpp>
|
|
||||||
#include <aoc/DiskFragmenter.hpp>
|
|
||||||
#include <aoc/HoofIt.hpp>
|
#include <aoc/HoofIt.hpp>
|
||||||
#include <aoc/PlutonianPebbles.hpp>
|
|
||||||
#include <aoc/GardenGroups.hpp>
|
|
||||||
#include <aoc/ClawContraption.hpp>
|
|
||||||
#include <aoc/RestroomRedoubt.hpp>
|
|
||||||
#include <aoc/WarehouseWoes.hpp>
|
|
||||||
#include <aoc/LanParty.hpp>
|
#include <aoc/LanParty.hpp>
|
||||||
|
|
||||||
void Program::run()
|
void Program::run()
|
||||||
@ -60,15 +52,7 @@ void Program::runSolvers()
|
|||||||
runSolver<CeresSearch>(solverEngine);
|
runSolver<CeresSearch>(solverEngine);
|
||||||
runSolver<PrintQueue>(solverEngine);
|
runSolver<PrintQueue>(solverEngine);
|
||||||
runSolver<GuardGallivant>(solverEngine);
|
runSolver<GuardGallivant>(solverEngine);
|
||||||
runSolver<BridgeRepair>(solverEngine);
|
|
||||||
runSolver<ResonantCollinearity>(solverEngine);
|
|
||||||
runSolver<DiskFragmenter>(solverEngine);
|
|
||||||
runSolver<HoofIt>(solverEngine);
|
runSolver<HoofIt>(solverEngine);
|
||||||
runSolver<PlutonianPebbles>(solverEngine);
|
|
||||||
runSolver<GardenGroups>(solverEngine);
|
|
||||||
runSolver<ClawContraption>(solverEngine);
|
|
||||||
runSolver<RestroomRedoubt>(solverEngine);
|
|
||||||
runSolver<WarehouseWoes>(solverEngine);
|
|
||||||
runSolver<LanParty>(solverEngine);
|
runSolver<LanParty>(solverEngine);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -15,27 +15,30 @@
|
|||||||
|
|
||||||
#include <aoc/RedNosedReports.hpp>
|
#include <aoc/RedNosedReports.hpp>
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
const std::string RedNosedReports::getPuzzleName() const
|
const std::string RedNosedReports::getPuzzleName() const
|
||||||
{
|
{
|
||||||
return "Red-Nosed Reports";
|
return "Day 2: Red-Nosed Reports";
|
||||||
}
|
}
|
||||||
|
|
||||||
const int RedNosedReports::getPuzzleDay() const
|
const std::string RedNosedReports::getInputFileName() const
|
||||||
{
|
{
|
||||||
return 2;
|
return "red-nosed_reports.txt";
|
||||||
}
|
}
|
||||||
|
|
||||||
// 'X' in the comments here and below marks the level that must be ignored to make the report safe.
|
// 'X' in the comments here and below marks the level that must be ignored to
|
||||||
|
// make the report safe.
|
||||||
// 'first' means that this series starts at the beginning of the line.
|
// 'first' means that this series starts at the beginning of the line.
|
||||||
// 'next' means that the level(s) from the next loop iteration(s) will determine which level to ignore.
|
// 'next' means that the level(s) from the next loop iteration(s) will determine
|
||||||
|
// which level to ignore.
|
||||||
void RedNosedReports::processDataLine(const std::string& line)
|
void RedNosedReports::processDataLine(const std::string& line)
|
||||||
{
|
{
|
||||||
RedNosedReportData data{};
|
RedNosedReportData data{};
|
||||||
|
|
||||||
std::istringstream stream{ line };
|
std::stringstream stream{ line };
|
||||||
std::string token;
|
std::string token;
|
||||||
std::getline(stream, token, ' ');
|
std::getline(stream, token, ' ');
|
||||||
data.levels.push_back(std::stoi(token));
|
data.levels.push_back(std::stoi(token));
|
||||||
@ -108,8 +111,8 @@ void RedNosedReports::finish()
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
void RedNosedReports::checkLastLevel(const Slope sameSlope, const Slope otherSlope, const int delta, const int sign,
|
void RedNosedReports::checkLastLevel(const Slope sameSlope, const Slope otherSlope,
|
||||||
RedNosedReportData& data)
|
const int delta, const int sign, RedNosedReportData& data)
|
||||||
{
|
{
|
||||||
if (data.slope == sameSlope)
|
if (data.slope == sameSlope)
|
||||||
{
|
{
|
||||||
@ -148,9 +151,9 @@ void RedNosedReports::checkLastLevel(const Slope sameSlope, const Slope otherSlo
|
|||||||
}
|
}
|
||||||
if (data.mustAwaitFourLevels)
|
if (data.mustAwaitFourLevels)
|
||||||
{
|
{
|
||||||
if (delta <= 3 &&
|
if (delta <= 3
|
||||||
((0 < sign * (data.levels[2] - data.levels[0]) && sign * (data.levels[2] - data.levels[0]) <= 3) ||
|
&& ((0 < sign * (data.levels[2] - data.levels[0]) && sign * (data.levels[2] - data.levels[0]) <= 3)
|
||||||
(0 < sign * (data.levels[2] - data.levels[1]) && sign * (data.levels[2] - data.levels[1]) <= 3)))
|
|| (0 < sign * (data.levels[2] - data.levels[1]) && sign * (data.levels[2] - data.levels[1]) <= 3)))
|
||||||
{
|
{
|
||||||
// X X X X
|
// X X X X
|
||||||
// first 1 5 3 4 first 8 4 6 5
|
// first 1 5 3 4 first 8 4 6 5
|
||||||
|
@ -1,101 +0,0 @@
|
|||||||
// Solutions to the Advent Of Code 2024.
|
|
||||||
// Copyright (C) 2025 Stefan Müller
|
|
||||||
//
|
|
||||||
// This program is free software: you can redistribute it and/or modify it under
|
|
||||||
// the terms of the GNU General Public License as published by the Free Software
|
|
||||||
// Foundation, either version 3 of the License, or (at your option) any later
|
|
||||||
// version.
|
|
||||||
//
|
|
||||||
// This program is distributed in the hope that it will be useful, but WITHOUT
|
|
||||||
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
|
||||||
// FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU General Public License along with
|
|
||||||
// this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
#include <aoc/ResonantCollinearity.hpp>
|
|
||||||
|
|
||||||
const std::string ResonantCollinearity::getPuzzleName() const
|
|
||||||
{
|
|
||||||
return "Resonant Collinearity";
|
|
||||||
}
|
|
||||||
|
|
||||||
const int ResonantCollinearity::getPuzzleDay() const
|
|
||||||
{
|
|
||||||
return 8;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ResonantCollinearity::finish()
|
|
||||||
{
|
|
||||||
Grid<bool> antinodeGrid1{ lines.size(), lines[0].size() };
|
|
||||||
antinodeGrid1.fill(false);
|
|
||||||
Grid<bool> antinodeGrid2{ lines.size(), lines[0].size() };
|
|
||||||
antinodeGrid2.fill(false);
|
|
||||||
|
|
||||||
for (int j = 0; j < lines.size(); j++)
|
|
||||||
{
|
|
||||||
for (int i = 0; i < lines[j].size(); i++)
|
|
||||||
{
|
|
||||||
if (lines[j][i] != getEmptyChar())
|
|
||||||
{
|
|
||||||
addNewAntenna(antinodeGrid1, antinodeGrid2, { i, j });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
constexpr char ResonantCollinearity::getEmptyChar()
|
|
||||||
{
|
|
||||||
return '.';
|
|
||||||
}
|
|
||||||
|
|
||||||
void ResonantCollinearity::addNewAntenna(Grid<bool>& antinodeGrid1, Grid<bool>& antinodeGrid2, Point2&& newPosition)
|
|
||||||
{
|
|
||||||
const auto [it, success] = antennas_.insert({ getCharAt(newPosition), { newPosition } });
|
|
||||||
if (!success)
|
|
||||||
{
|
|
||||||
// Adds the new antenna position itself as an antinode for part 2.
|
|
||||||
if (it->second.size() == 1)
|
|
||||||
{
|
|
||||||
addNewAntinode(antinodeGrid2, it->second.front(), part2);
|
|
||||||
}
|
|
||||||
addNewAntinode(antinodeGrid2, newPosition, part2);
|
|
||||||
|
|
||||||
// Adds antinodes for all pairs of the new antenna position and each of the already processed other antenna
|
|
||||||
// positions with the same frequency.
|
|
||||||
for (const auto& other : it->second)
|
|
||||||
{
|
|
||||||
Point2 diff = newPosition - other;
|
|
||||||
findNewAntinodes(antinodeGrid1, antinodeGrid2, newPosition + diff, diff);
|
|
||||||
diff = -diff;
|
|
||||||
findNewAntinodes(antinodeGrid1, antinodeGrid2, other + diff, diff);
|
|
||||||
}
|
|
||||||
|
|
||||||
it->second.push_back(newPosition);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void ResonantCollinearity::findNewAntinodes(Grid<bool>& antinodeGrid1, Grid<bool>& antinodeGrid2, Point2&& start,
|
|
||||||
const Point2& increment)
|
|
||||||
{
|
|
||||||
if (isInBounds(start))
|
|
||||||
{
|
|
||||||
addNewAntinode(antinodeGrid1, start, part1);
|
|
||||||
addNewAntinode(antinodeGrid2, start, part2);
|
|
||||||
start += increment;
|
|
||||||
while (isInBounds(start))
|
|
||||||
{
|
|
||||||
addNewAntinode(antinodeGrid2, start, part2);
|
|
||||||
start += increment;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void ResonantCollinearity::addNewAntinode(Grid<bool>& antinodeGrid, Point2& newPosition, long long int& count)
|
|
||||||
{
|
|
||||||
if (!antinodeGrid[newPosition.y][newPosition.x])
|
|
||||||
{
|
|
||||||
antinodeGrid[newPosition.y][newPosition.x] = true;
|
|
||||||
count++;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,154 +0,0 @@
|
|||||||
// Solutions to the Advent Of Code 2024.
|
|
||||||
// Copyright (C) 2025 Stefan Müller
|
|
||||||
//
|
|
||||||
// This program is free software: you can redistribute it and/or modify it under
|
|
||||||
// the terms of the GNU General Public License as published by the Free Software
|
|
||||||
// Foundation, either version 3 of the License, or (at your option) any later
|
|
||||||
// version.
|
|
||||||
//
|
|
||||||
// This program is distributed in the hope that it will be useful, but WITHOUT
|
|
||||||
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
|
||||||
// FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU General Public License along with
|
|
||||||
// this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
#include <aoc/RestroomRedoubt.hpp>
|
|
||||||
|
|
||||||
#include <algorithm>
|
|
||||||
#include <array>
|
|
||||||
#include <functional>
|
|
||||||
#include <numeric>
|
|
||||||
#include <sstream>
|
|
||||||
|
|
||||||
#include <aoc/Math.hpp>
|
|
||||||
#include <aoc/Point2.hpp>
|
|
||||||
|
|
||||||
RestroomRedoubt::RestroomRedoubt(const int width, const int height, const bool runPart2)
|
|
||||||
: width_{ width }, height_{ height }, halfWidth_{ width / 2 }, halfHeight_{ height / 2 }, runPart2_{ runPart2 }
|
|
||||||
{
|
|
||||||
quadrants_.fill(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
const std::string RestroomRedoubt::getPuzzleName() const
|
|
||||||
{
|
|
||||||
return "Restroom Redoubt";
|
|
||||||
}
|
|
||||||
|
|
||||||
const int RestroomRedoubt::getPuzzleDay() const
|
|
||||||
{
|
|
||||||
return 14;
|
|
||||||
}
|
|
||||||
|
|
||||||
void RestroomRedoubt::processDataLine(const std::string& line)
|
|
||||||
{
|
|
||||||
std::istringstream stream{ line };
|
|
||||||
char c;
|
|
||||||
Point2 p, v;
|
|
||||||
stream >> c >> c >> p.x >> c >> p.y >> c >> c >> v.x >> c >> v.y;
|
|
||||||
|
|
||||||
// Calculates the resulting position, and shifts negative results back into the target range.
|
|
||||||
Point2 position = p + v * getNPredictionSeconds();
|
|
||||||
position.x %= width_;
|
|
||||||
if (position.x < 0)
|
|
||||||
{
|
|
||||||
position.x += width_;
|
|
||||||
}
|
|
||||||
position.y %= height_;
|
|
||||||
if (position.y < 0)
|
|
||||||
{
|
|
||||||
position.y += height_;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Determines the resulting quadrant from the position.
|
|
||||||
if (position.x < halfWidth_)
|
|
||||||
{
|
|
||||||
if (position.y < halfHeight_)
|
|
||||||
{
|
|
||||||
quadrants_[0]++;
|
|
||||||
}
|
|
||||||
else if (position.y > halfHeight_)
|
|
||||||
{
|
|
||||||
quadrants_[1]++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (position.x > halfWidth_)
|
|
||||||
{
|
|
||||||
if (position.y < halfHeight_)
|
|
||||||
{
|
|
||||||
quadrants_[2]++;
|
|
||||||
}
|
|
||||||
else if (position.y > halfHeight_)
|
|
||||||
{
|
|
||||||
quadrants_[3]++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Keeps track of the robot for part 2.
|
|
||||||
robots_.push_back({ p, v });
|
|
||||||
}
|
|
||||||
|
|
||||||
void RestroomRedoubt::finish()
|
|
||||||
{
|
|
||||||
part1 = std::accumulate(quadrants_.cbegin(), quadrants_.cend(), 1, std::multiplies<int>{});
|
|
||||||
|
|
||||||
if (runPart2_)
|
|
||||||
{
|
|
||||||
findEasterEgg();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
constexpr int RestroomRedoubt::getNPredictionSeconds()
|
|
||||||
{
|
|
||||||
return 100;
|
|
||||||
}
|
|
||||||
|
|
||||||
constexpr int RestroomRedoubt::getEasterEggThreshold()
|
|
||||||
{
|
|
||||||
return 25;
|
|
||||||
}
|
|
||||||
|
|
||||||
void RestroomRedoubt::findEasterEgg()
|
|
||||||
{
|
|
||||||
std::array<int, 2> patterns{ 0, 0 };
|
|
||||||
std::array<int, 2> sizes{ width_, height_ };
|
|
||||||
for (size_t i = 0; i < 2; i++)
|
|
||||||
{
|
|
||||||
int seconds{ 0 };
|
|
||||||
while (patterns[i] == 0)
|
|
||||||
{
|
|
||||||
std::vector<int> buckets(sizes[i], 0);
|
|
||||||
for (auto& robot : robots_)
|
|
||||||
{
|
|
||||||
buckets[robot.first[i]]++;
|
|
||||||
robot.first[i] += robot.second[i];
|
|
||||||
if (robot.first[i] < 0)
|
|
||||||
{
|
|
||||||
robot.first[i] += sizes[i];
|
|
||||||
}
|
|
||||||
else if (robot.first[i] >= sizes[i])
|
|
||||||
{
|
|
||||||
robot.first[i] -= sizes[i];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
auto it = std::find_if(buckets.cbegin(), buckets.cend(), [](int x) { return x > getEasterEggThreshold(); });
|
|
||||||
if (it != buckets.cend())
|
|
||||||
{
|
|
||||||
patterns[i] = seconds;
|
|
||||||
}
|
|
||||||
seconds++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Determines the iteration where the repeating patterns of horizontal and vertical bands overlap, that is
|
|
||||||
// patterns_0 + width * x = patterns_1 + height * y
|
|
||||||
// for two positive integers 'x' and 'y'. This is equivalent to finding 'x' such that
|
|
||||||
// width * x ≡ patterns_1 - patterns_0 (modulo height)
|
|
||||||
// We can determine the modular multiplicative inverse of 'width' modulo 'height' with the extended Euclidean
|
|
||||||
// algorithm.
|
|
||||||
const auto [gcd, invHeight, invWidth] = Math::extendedEuclid(height_, width_);
|
|
||||||
// With that we can find 'x'.
|
|
||||||
int x{ (invWidth * (patterns[1] - patterns[0])) % height_ };
|
|
||||||
// And calculate when the overlap occurs.
|
|
||||||
part2 = patterns[0] + width_ * x;
|
|
||||||
}
|
|
@ -15,24 +15,8 @@
|
|||||||
|
|
||||||
#include <aoc/Solver.hpp>
|
#include <aoc/Solver.hpp>
|
||||||
|
|
||||||
#include <sstream>
|
Solver::Solver()
|
||||||
|
: part1{ 0 }, part2{ 0 } {}
|
||||||
Solver::Solver(const int inputFileNameSuffix)
|
|
||||||
: inputFileNameSuffix_{ inputFileNameSuffix }, part1{ 0 }, part2{ 0 }
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
const std::string Solver::getInputFileName() const
|
|
||||||
{
|
|
||||||
std::ostringstream stream;
|
|
||||||
stream << clean(getPuzzleName());
|
|
||||||
if (inputFileNameSuffix_ > 0)
|
|
||||||
{
|
|
||||||
stream << "_" << inputFileNameSuffix_;
|
|
||||||
}
|
|
||||||
stream << ".txt";
|
|
||||||
return stream.str();
|
|
||||||
}
|
|
||||||
|
|
||||||
long long int Solver::getResultPart1() const
|
long long int Solver::getResultPart1() const
|
||||||
{
|
{
|
||||||
@ -43,14 +27,3 @@ long long int Solver::getResultPart2() const
|
|||||||
{
|
{
|
||||||
return part2;
|
return part2;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string Solver::clean(const std::string& original) const
|
|
||||||
{
|
|
||||||
std::string cleaned{ original };
|
|
||||||
size_t start_pos = 0;
|
|
||||||
while ((start_pos = cleaned.find(" ", start_pos)) != std::string::npos)
|
|
||||||
{
|
|
||||||
cleaned.replace(start_pos, 1, "_");
|
|
||||||
}
|
|
||||||
return cleaned;
|
|
||||||
}
|
|
||||||
|
@ -19,13 +19,11 @@
|
|||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
|
||||||
SolverEngine::SolverEngine(const std::vector<std::string>& inputPaths)
|
SolverEngine::SolverEngine(const std::vector<std::string>& inputPaths)
|
||||||
: inputPaths_{ inputPaths }
|
: inputPaths_{ inputPaths } {}
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void SolverEngine::run(Solver& solver)
|
void SolverEngine::run(Solver& solver)
|
||||||
{
|
{
|
||||||
std::cout << "\n--- Day " << solver.getPuzzleDay() << ": " << solver.getPuzzleName() << " ---\n";
|
std::cout << "\n--- " << solver.getPuzzleName() << " ---\n";
|
||||||
|
|
||||||
auto fullFilePath = tryGetValidFullInputFilePath(solver.getInputFileName());
|
auto fullFilePath = tryGetValidFullInputFilePath(solver.getInputFileName());
|
||||||
if (fullFilePath != "")
|
if (fullFilePath != "")
|
||||||
@ -40,7 +38,8 @@ void SolverEngine::run(Solver& solver)
|
|||||||
|
|
||||||
solver.finish();
|
solver.finish();
|
||||||
|
|
||||||
std::cout << "Part 1: " << solver.getResultPart1() << "\nPart 2: " << solver.getResultPart2() << std::endl;
|
std::cout << "Part 1: " << solver.getResultPart1()
|
||||||
|
<< "\nPart 2: " << solver.getResultPart2() << std::endl;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
// Solutions to the Advent Of Code 2024.
|
// Solutions to the Advent Of Code 2024.
|
||||||
// Copyright (C) 2024 Stefan Müller
|
// Copyright (C) 2024 Stefan Müller
|
||||||
//
|
//
|
||||||
// This program is free software: you can redistribute it and/or modify it under
|
// This program is free software: you can redistribute it and/or modify it under
|
||||||
// the terms of the GNU General Public License as published by the Free Software
|
// the terms of the GNU General Public License as published by the Free Software
|
||||||
|
@ -1,53 +0,0 @@
|
|||||||
// Solutions to the Advent Of Code 2024.
|
|
||||||
// Copyright (C) 2025 Stefan Müller
|
|
||||||
//
|
|
||||||
// This program is free software: you can redistribute it and/or modify it under
|
|
||||||
// the terms of the GNU General Public License as published by the Free Software
|
|
||||||
// Foundation, either version 3 of the License, or (at your option) any later
|
|
||||||
// version.
|
|
||||||
//
|
|
||||||
// This program is distributed in the hope that it will be useful, but WITHOUT
|
|
||||||
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
|
||||||
// FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU General Public License along with
|
|
||||||
// this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
#include <aoc/WarehouseBoxPusher.hpp>
|
|
||||||
|
|
||||||
WarehouseBoxPusher::WarehouseBoxPusher(const char boxChar, const char wallChar, const char emptyChar)
|
|
||||||
: boxChar_{ boxChar }, wallChar_{ wallChar }, emptyChar_{ emptyChar }
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void WarehouseBoxPusher::processMovements(Lines& warehouseMap, const std::string& line)
|
|
||||||
{
|
|
||||||
for (const char c : line)
|
|
||||||
{
|
|
||||||
processMovement(warehouseMap, Point2::getCardinalDirection(c));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void WarehouseBoxPusher::processMovement(Lines& warehouseMap, const Point2& direction)
|
|
||||||
{
|
|
||||||
const Point2 next{ robotPosition_ + direction };
|
|
||||||
Point2 push{ next };
|
|
||||||
while (warehouseMap.getCharAt(push) == boxChar_)
|
|
||||||
{
|
|
||||||
push += direction;
|
|
||||||
}
|
|
||||||
if (warehouseMap.getCharAt(push) != wallChar_)
|
|
||||||
{
|
|
||||||
if (push != next)
|
|
||||||
{
|
|
||||||
warehouseMap.setCharAt(push, boxChar_);
|
|
||||||
warehouseMap.setCharAt(next, emptyChar_);
|
|
||||||
}
|
|
||||||
robotPosition_ = next;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void WarehouseBoxPusher::setRobotPosition(const Point2& robotPosition)
|
|
||||||
{
|
|
||||||
robotPosition_ = robotPosition;
|
|
||||||
}
|
|
@ -1,130 +0,0 @@
|
|||||||
// Solutions to the Advent Of Code 2024.
|
|
||||||
// Copyright (C) 2025 Stefan Müller
|
|
||||||
//
|
|
||||||
// This program is free software: you can redistribute it and/or modify it under
|
|
||||||
// the terms of the GNU General Public License as published by the Free Software
|
|
||||||
// Foundation, either version 3 of the License, or (at your option) any later
|
|
||||||
// version.
|
|
||||||
//
|
|
||||||
// This program is distributed in the hope that it will be useful, but WITHOUT
|
|
||||||
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
|
||||||
// FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU General Public License along with
|
|
||||||
// this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
#include <aoc/WarehouseWideBoxPusher.hpp>
|
|
||||||
|
|
||||||
#include <stack>
|
|
||||||
|
|
||||||
WarehouseWideBoxPusher::WarehouseWideBoxPusher(const char leftWideBoxChar, const char rightWideBoxChar,
|
|
||||||
const char wallChar, const char emptyChar)
|
|
||||||
: WarehouseBoxPusher{ 0, wallChar, emptyChar }, leftWideBoxChar_{ leftWideBoxChar },
|
|
||||||
rightWideBoxChar_{ rightWideBoxChar }
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void WarehouseWideBoxPusher::processMovement(Lines& warehouseMap, const Point2& direction)
|
|
||||||
{
|
|
||||||
const Point2 next{ robotPosition_ + direction };
|
|
||||||
|
|
||||||
if (direction.y == 0)
|
|
||||||
{
|
|
||||||
// Moves robot horizontally.
|
|
||||||
const char facingBoxChar = direction.x > 0 ? leftWideBoxChar_ : rightWideBoxChar_;
|
|
||||||
const Point2 doubleDirection{ direction.x * 2, 0 };
|
|
||||||
Point2 push{ next };
|
|
||||||
while (warehouseMap.getCharAt(push) == facingBoxChar)
|
|
||||||
{
|
|
||||||
push += doubleDirection;
|
|
||||||
}
|
|
||||||
if (warehouseMap.getCharAt(push) != wallChar_)
|
|
||||||
{
|
|
||||||
if (push != next)
|
|
||||||
{
|
|
||||||
auto& line = warehouseMap[next.y];
|
|
||||||
line.erase(line.begin() + push.x);
|
|
||||||
line.insert(line.begin() + next.x, emptyChar_);
|
|
||||||
}
|
|
||||||
robotPosition_ = next;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// Moves robot vertically.
|
|
||||||
|
|
||||||
// Checks if a wall is blocking the movement directly in front of the robot.
|
|
||||||
const char charToCheck{ warehouseMap.getCharAt(next) };
|
|
||||||
if (charToCheck == wallChar_)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool canPush{ true };
|
|
||||||
std::vector<Point2> boxesToPush{};
|
|
||||||
std::stack<Point2> boxesToCheck{};
|
|
||||||
|
|
||||||
// Checks for the first box to push and stacks its left position, if any.
|
|
||||||
if (charToCheck == leftWideBoxChar_)
|
|
||||||
{
|
|
||||||
boxesToCheck.push(next);
|
|
||||||
}
|
|
||||||
else if (charToCheck == rightWideBoxChar_)
|
|
||||||
{
|
|
||||||
boxesToCheck.emplace(next.x - 1, next.y);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Checks all pushed boxes.
|
|
||||||
while (!boxesToCheck.empty())
|
|
||||||
{
|
|
||||||
Point2 leftPush{ boxesToCheck.top() };
|
|
||||||
boxesToCheck.pop();
|
|
||||||
|
|
||||||
// Checks whether any wall is blocking the push.
|
|
||||||
Point2 rightPush{ leftPush + Point2::right };
|
|
||||||
const char leftCharToCheck{ warehouseMap.getCharAt(leftPush) };
|
|
||||||
const char rightCharToCheck{ warehouseMap.getCharAt(rightPush) };
|
|
||||||
if (leftCharToCheck == wallChar_ || rightCharToCheck == wallChar_)
|
|
||||||
{
|
|
||||||
canPush = false;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Checks if any more boxes are being pushed by this box.
|
|
||||||
if (leftCharToCheck == leftWideBoxChar_)
|
|
||||||
{
|
|
||||||
boxesToPush.push_back(leftPush);
|
|
||||||
boxesToCheck.emplace(leftPush.x, leftPush.y + direction.y);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (leftCharToCheck == rightWideBoxChar_)
|
|
||||||
{
|
|
||||||
leftPush += Point2::left;
|
|
||||||
boxesToPush.push_back(leftPush);
|
|
||||||
boxesToCheck.emplace(leftPush.x, leftPush.y + direction.y);
|
|
||||||
}
|
|
||||||
if (rightCharToCheck == leftWideBoxChar_)
|
|
||||||
{
|
|
||||||
boxesToPush.push_back(rightPush);
|
|
||||||
boxesToCheck.emplace(rightPush.x, rightPush.y + direction.y);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (canPush)
|
|
||||||
{
|
|
||||||
// Moves all pushed boxes.
|
|
||||||
auto it = boxesToPush.crbegin();
|
|
||||||
while (it != boxesToPush.crend())
|
|
||||||
{
|
|
||||||
warehouseMap.setCharAt(*it, emptyChar_);
|
|
||||||
warehouseMap.setCharAt(*it + Point2::right, emptyChar_);
|
|
||||||
warehouseMap.setCharAt(*it + direction, leftWideBoxChar_);
|
|
||||||
warehouseMap.setCharAt(*it + direction + Point2::right, rightWideBoxChar_);
|
|
||||||
it++;
|
|
||||||
}
|
|
||||||
robotPosition_ = next;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,147 +0,0 @@
|
|||||||
// Solutions to the Advent Of Code 2024.
|
|
||||||
// Copyright (C) 2025 Stefan Müller
|
|
||||||
//
|
|
||||||
// This program is free software: you can redistribute it and/or modify it under
|
|
||||||
// the terms of the GNU General Public License as published by the Free Software
|
|
||||||
// Foundation, either version 3 of the License, or (at your option) any later
|
|
||||||
// version.
|
|
||||||
//
|
|
||||||
// This program is distributed in the hope that it will be useful, but WITHOUT
|
|
||||||
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
|
||||||
// FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU General Public License along with
|
|
||||||
// this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
#include <aoc/WarehouseWoes.hpp>
|
|
||||||
|
|
||||||
#include <sstream>
|
|
||||||
|
|
||||||
WarehouseWoes::WarehouseWoes(const int inputFileNameSuffix)
|
|
||||||
: LinesSolver{ inputFileNameSuffix }, isProcessingMap_{ true }, isSearchingStartPosition_{ true }, lines2_{},
|
|
||||||
warehouseBoxPusher1_{ getBoxChar(), getWallChar(), getEmptyChar() },
|
|
||||||
warehouseBoxPusher2_{ getLeftWideBoxChar(), getRightWideBoxChar(), getWallChar(), getEmptyChar() }
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
const std::string WarehouseWoes::getPuzzleName() const
|
|
||||||
{
|
|
||||||
return "Warehouse Woes";
|
|
||||||
}
|
|
||||||
|
|
||||||
const int WarehouseWoes::getPuzzleDay() const
|
|
||||||
{
|
|
||||||
return 15;
|
|
||||||
}
|
|
||||||
|
|
||||||
void WarehouseWoes::processDataLine(const std::string& line)
|
|
||||||
{
|
|
||||||
if (!line.empty())
|
|
||||||
{
|
|
||||||
if (isProcessingMap_)
|
|
||||||
{
|
|
||||||
checkStartingPosition(line);
|
|
||||||
LinesSolver::processDataLine(line);
|
|
||||||
addWarehouseMap2Line(line);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
warehouseBoxPusher1_.processMovements(lines, line);
|
|
||||||
warehouseBoxPusher2_.processMovements(lines2_, line);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
isProcessingMap_ = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void WarehouseWoes::finish()
|
|
||||||
{
|
|
||||||
part1 = calcAllGpsCoordinates(lines, getBoxChar());
|
|
||||||
part2 = calcAllGpsCoordinates(lines2_, getLeftWideBoxChar());
|
|
||||||
}
|
|
||||||
|
|
||||||
constexpr char WarehouseWoes::getRobotChar()
|
|
||||||
{
|
|
||||||
return '@';
|
|
||||||
}
|
|
||||||
|
|
||||||
constexpr char WarehouseWoes::getBoxChar()
|
|
||||||
{
|
|
||||||
return 'O';
|
|
||||||
}
|
|
||||||
|
|
||||||
constexpr char WarehouseWoes::getLeftWideBoxChar()
|
|
||||||
{
|
|
||||||
return '[';
|
|
||||||
}
|
|
||||||
|
|
||||||
constexpr char WarehouseWoes::getRightWideBoxChar()
|
|
||||||
{
|
|
||||||
return ']';
|
|
||||||
}
|
|
||||||
|
|
||||||
constexpr char WarehouseWoes::getWallChar()
|
|
||||||
{
|
|
||||||
return '#';
|
|
||||||
}
|
|
||||||
|
|
||||||
constexpr char WarehouseWoes::getEmptyChar()
|
|
||||||
{
|
|
||||||
return '.';
|
|
||||||
}
|
|
||||||
|
|
||||||
void WarehouseWoes::checkStartingPosition(const std::string& line)
|
|
||||||
{
|
|
||||||
if (isSearchingStartPosition_)
|
|
||||||
{
|
|
||||||
auto hit = line.find(getRobotChar());
|
|
||||||
if (hit != std::string::npos)
|
|
||||||
{
|
|
||||||
int x = static_cast<int>(hit);
|
|
||||||
int y = static_cast<int>(lines.size());
|
|
||||||
warehouseBoxPusher1_.setRobotPosition({ x, y });
|
|
||||||
warehouseBoxPusher2_.setRobotPosition({ x * 2, y });
|
|
||||||
isSearchingStartPosition_ = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void WarehouseWoes::addWarehouseMap2Line(const std::string& line)
|
|
||||||
{
|
|
||||||
std::ostringstream stream{};
|
|
||||||
for (const char c : line)
|
|
||||||
{
|
|
||||||
if (c == getBoxChar())
|
|
||||||
{
|
|
||||||
stream << getLeftWideBoxChar() << getRightWideBoxChar();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
stream << c << c;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
lines2_.push_back(stream.str());
|
|
||||||
}
|
|
||||||
|
|
||||||
long long int WarehouseWoes::calcAllGpsCoordinates(Lines& warehouseMap, const char boxChar)
|
|
||||||
{
|
|
||||||
long long int result{ 0 };
|
|
||||||
for (size_t j = 0; j < warehouseMap.size(); j++)
|
|
||||||
{
|
|
||||||
for (size_t i = 0; i < warehouseMap[j].size(); i++)
|
|
||||||
{
|
|
||||||
if (warehouseMap[j][i] == boxChar)
|
|
||||||
{
|
|
||||||
result += calcGpsCoordinate(i, j);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
long long int WarehouseWoes::calcGpsCoordinate(const size_t x, const size_t y)
|
|
||||||
{
|
|
||||||
return x + 100 * y;
|
|
||||||
}
|
|
@ -1,5 +1,5 @@
|
|||||||
// Solutions to the Advent Of Code 2024.
|
// Solutions to the Advent Of Code 2024.
|
||||||
// Copyright (C) 2024 Stefan Müller
|
// Copyright (C) 2024 Stefan Müller
|
||||||
//
|
//
|
||||||
// This program is free software: you can redistribute it and/or modify it under
|
// This program is free software: you can redistribute it and/or modify it under
|
||||||
// the terms of the GNU General Public License as published by the Free Software
|
// the terms of the GNU General Public License as published by the Free Software
|
||||||
@ -24,12 +24,8 @@
|
|||||||
class TestContext
|
class TestContext
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
void run(const std::unique_ptr<Solver>&& solver, const long long int expected1, const long long int expected2,
|
void run(const std::unique_ptr<Solver>&& solver, const long long int expected1,
|
||||||
const std::vector<std::string>& inputPaths);
|
const long long int expected2, const std::vector<std::string>& inputPaths);
|
||||||
void runPart1(const std::unique_ptr<Solver>&& solver, const long long int expected,
|
|
||||||
const std::vector<std::string>& inputPaths);
|
|
||||||
void runPart2(const std::unique_ptr<Solver>&& solver, const long long int expected,
|
|
||||||
const std::vector<std::string>& inputPaths);
|
|
||||||
std::vector<std::string> getInputPaths() const;
|
std::vector<std::string> getInputPaths() const;
|
||||||
std::vector<std::string> getExampleInputPaths() const;
|
std::vector<std::string> getExampleInputPaths() const;
|
||||||
};
|
};
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
// Solutions to the Advent Of Code 2024.
|
// Solutions to the Advent Of Code 2024.
|
||||||
// Copyright (C) 2024-2025 Stefan Müller
|
// Copyright (C) 2024 Stefan Müller
|
||||||
//
|
//
|
||||||
// This program is free software: you can redistribute it and/or modify it under
|
// This program is free software: you can redistribute it and/or modify it under
|
||||||
// the terms of the GNU General Public License as published by the Free Software
|
// the terms of the GNU General Public License as published by the Free Software
|
||||||
@ -15,25 +15,15 @@
|
|||||||
|
|
||||||
#include <catch2/catch_test_macros.hpp>
|
#include <catch2/catch_test_macros.hpp>
|
||||||
|
|
||||||
#include <aocTests/TestContext.hpp>
|
|
||||||
|
|
||||||
// Solver implementations in day order.
|
|
||||||
#include <aoc/HistorianHysteria.hpp>
|
#include <aoc/HistorianHysteria.hpp>
|
||||||
#include <aoc/RedNosedReports.hpp>
|
#include <aoc/RedNosedReports.hpp>
|
||||||
#include <aoc/MullItOver.hpp>
|
#include <aoc/MullItOver.hpp>
|
||||||
#include <aoc/CeresSearch.hpp>
|
#include <aoc/CeresSearch.hpp>
|
||||||
#include <aoc/PrintQueue.hpp>
|
#include <aoc/PrintQueue.hpp>
|
||||||
#include <aoc/GuardGallivant.hpp>
|
#include <aoc/GuardGallivant.hpp>
|
||||||
#include <aoc/BridgeRepair.hpp>
|
|
||||||
#include <aoc/ResonantCollinearity.hpp>
|
|
||||||
#include <aoc/DiskFragmenter.hpp>
|
|
||||||
#include <aoc/HoofIt.hpp>
|
#include <aoc/HoofIt.hpp>
|
||||||
#include <aoc/PlutonianPebbles.hpp>
|
|
||||||
#include <aoc/GardenGroups.hpp>
|
|
||||||
#include <aoc/ClawContraption.hpp>
|
|
||||||
#include <aoc/RestroomRedoubt.hpp>
|
|
||||||
#include <aoc/WarehouseWoes.hpp>
|
|
||||||
#include <aoc/LanParty.hpp>
|
#include <aoc/LanParty.hpp>
|
||||||
|
#include <aocTests/TestContext.hpp>
|
||||||
|
|
||||||
#define REQUIRE_MESSAGE(cond, msg) if (!(cond)) { INFO(msg); REQUIRE(cond); }
|
#define REQUIRE_MESSAGE(cond, msg) if (!(cond)) { INFO(msg); REQUIRE(cond); }
|
||||||
|
|
||||||
@ -107,50 +97,11 @@ TEST_CASE("[GuardGallivantTests]")
|
|||||||
TestContext test;
|
TestContext test;
|
||||||
SECTION("FullData")
|
SECTION("FullData")
|
||||||
{
|
{
|
||||||
test.run(std::make_unique<GuardGallivant>(), 4665, 0, test.getInputPaths());
|
test.run(std::make_unique<GuardGallivant>(), 4665, 1688, test.getInputPaths());
|
||||||
}
|
}
|
||||||
SECTION("ExampleData")
|
SECTION("ExampleData")
|
||||||
{
|
{
|
||||||
test.run(std::make_unique<GuardGallivant>(), 41, 0, test.getExampleInputPaths());
|
test.run(std::make_unique<GuardGallivant>(), 41, 6, test.getExampleInputPaths());
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_CASE("[BridgeRepairTests]")
|
|
||||||
{
|
|
||||||
TestContext test;
|
|
||||||
SECTION("FullData")
|
|
||||||
{
|
|
||||||
test.run(std::make_unique<BridgeRepair>(), 12839601725877, 149956401519484, test.getInputPaths());
|
|
||||||
}
|
|
||||||
SECTION("ExampleData")
|
|
||||||
{
|
|
||||||
test.run(std::make_unique<BridgeRepair>(), 3749, 11387, test.getExampleInputPaths());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_CASE("[ResonantCollinearityTests]")
|
|
||||||
{
|
|
||||||
TestContext test;
|
|
||||||
SECTION("FullData")
|
|
||||||
{
|
|
||||||
test.run(std::make_unique<ResonantCollinearity>(), 426, 1359, test.getInputPaths());
|
|
||||||
}
|
|
||||||
SECTION("ExampleData")
|
|
||||||
{
|
|
||||||
test.run(std::make_unique<ResonantCollinearity>(), 14, 34, test.getExampleInputPaths());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_CASE("[DiskFragmenterTests]")
|
|
||||||
{
|
|
||||||
TestContext test;
|
|
||||||
SECTION("FullData")
|
|
||||||
{
|
|
||||||
test.run(std::make_unique<DiskFragmenter>(), 6401092019345, 6431472344710, test.getInputPaths());
|
|
||||||
}
|
|
||||||
SECTION("ExampleData")
|
|
||||||
{
|
|
||||||
test.run(std::make_unique<DiskFragmenter>(), 1928, 2858, test.getExampleInputPaths());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -167,91 +118,6 @@ TEST_CASE("[HoofItTests]")
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("[PlutonianPebblesTests]")
|
|
||||||
{
|
|
||||||
TestContext test;
|
|
||||||
SECTION("FullData")
|
|
||||||
{
|
|
||||||
test.run(std::make_unique<PlutonianPebbles>(), 220999, 261936432123724, test.getInputPaths());
|
|
||||||
}
|
|
||||||
SECTION("ExampleData")
|
|
||||||
{
|
|
||||||
test.run(std::make_unique<PlutonianPebbles>(), 55312, 65601038650482, test.getExampleInputPaths());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_CASE("[GardenGroupsTests]")
|
|
||||||
{
|
|
||||||
TestContext test;
|
|
||||||
SECTION("FullData")
|
|
||||||
{
|
|
||||||
test.run(std::make_unique<GardenGroups>(), 1477762, 923480, test.getInputPaths());
|
|
||||||
}
|
|
||||||
SECTION("ExampleData")
|
|
||||||
{
|
|
||||||
test.run(std::make_unique<GardenGroups>(), 1930, 1206, test.getExampleInputPaths());
|
|
||||||
}
|
|
||||||
SECTION("ExampleData2")
|
|
||||||
{
|
|
||||||
test.run(std::make_unique<GardenGroups>(2), 140, 80, test.getExampleInputPaths());
|
|
||||||
}
|
|
||||||
SECTION("ExampleData3")
|
|
||||||
{
|
|
||||||
test.run(std::make_unique<GardenGroups>(3), 772, 436, test.getExampleInputPaths());
|
|
||||||
}
|
|
||||||
SECTION("ExampleData4")
|
|
||||||
{
|
|
||||||
test.runPart2(std::make_unique<GardenGroups>(4), 236, test.getExampleInputPaths());
|
|
||||||
}
|
|
||||||
SECTION("ExampleData5")
|
|
||||||
{
|
|
||||||
test.runPart2(std::make_unique<GardenGroups>(5), 368, test.getExampleInputPaths());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_CASE("[ClawContraptionTests]")
|
|
||||||
{
|
|
||||||
TestContext test;
|
|
||||||
SECTION("FullData")
|
|
||||||
{
|
|
||||||
test.run(std::make_unique<ClawContraption>(), 36571, 85527711500010, test.getInputPaths());
|
|
||||||
}
|
|
||||||
SECTION("ExampleData")
|
|
||||||
{
|
|
||||||
test.run(std::make_unique<ClawContraption>(), 480, 875318608908, test.getExampleInputPaths());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_CASE("[RestroomRedoubtTests]")
|
|
||||||
{
|
|
||||||
TestContext test;
|
|
||||||
SECTION("FullData")
|
|
||||||
{
|
|
||||||
test.run(std::make_unique<RestroomRedoubt>(), 224969976, 7892, test.getInputPaths());
|
|
||||||
}
|
|
||||||
SECTION("ExampleData")
|
|
||||||
{
|
|
||||||
test.run(std::make_unique<RestroomRedoubt>(11, 7, false), 12, 0, test.getExampleInputPaths());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_CASE("[WarehouseWoesTests]")
|
|
||||||
{
|
|
||||||
TestContext test;
|
|
||||||
SECTION("FullData")
|
|
||||||
{
|
|
||||||
test.run(std::make_unique<WarehouseWoes>(), 1515788, 1516544, test.getInputPaths());
|
|
||||||
}
|
|
||||||
SECTION("ExampleData")
|
|
||||||
{
|
|
||||||
test.run(std::make_unique<WarehouseWoes>(), 10092, 9021, test.getExampleInputPaths());
|
|
||||||
}
|
|
||||||
SECTION("ExampleData2")
|
|
||||||
{
|
|
||||||
test.runPart1(std::make_unique<WarehouseWoes>(2), 2028, test.getExampleInputPaths());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_CASE("[LanPartyTests]")
|
TEST_CASE("[LanPartyTests]")
|
||||||
{
|
{
|
||||||
TestContext test;
|
TestContext test;
|
||||||
|
@ -29,24 +29,6 @@ void TestContext::run(const std::unique_ptr<Solver>&& solver, const long long in
|
|||||||
REQUIRE(expected2 == solver->getResultPart2());
|
REQUIRE(expected2 == solver->getResultPart2());
|
||||||
}
|
}
|
||||||
|
|
||||||
void TestContext::runPart1(const std::unique_ptr<Solver>&& solver, const long long int expected,
|
|
||||||
const std::vector<std::string>& inputPaths)
|
|
||||||
{
|
|
||||||
SolverEngine solverEngine{ inputPaths };
|
|
||||||
solverEngine.run(*solver);
|
|
||||||
|
|
||||||
REQUIRE(expected == solver->getResultPart1());
|
|
||||||
}
|
|
||||||
|
|
||||||
void TestContext::runPart2(const std::unique_ptr<Solver>&& solver, const long long int expected,
|
|
||||||
const std::vector<std::string>& inputPaths)
|
|
||||||
{
|
|
||||||
SolverEngine solverEngine{ inputPaths };
|
|
||||||
solverEngine.run(*solver);
|
|
||||||
|
|
||||||
REQUIRE(expected == solver->getResultPart2());
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<std::string> TestContext::getInputPaths() const
|
std::vector<std::string> TestContext::getInputPaths() const
|
||||||
{
|
{
|
||||||
return std::vector<std::string>{ "data", "../../../data", "../../../../data" };
|
return std::vector<std::string>{ "data", "../../../data", "../../../../data" };
|
||||||
@ -54,5 +36,6 @@ std::vector<std::string> TestContext::getInputPaths() const
|
|||||||
|
|
||||||
std::vector<std::string> TestContext::getExampleInputPaths() const
|
std::vector<std::string> TestContext::getExampleInputPaths() const
|
||||||
{
|
{
|
||||||
return std::vector<std::string>{ "data/examples", "../../../data/examples", "../../../../data/examples" };
|
return std::vector<std::string>{ "data/examples", "../../../data/examples",
|
||||||
|
"../../../../data/examples" };
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user