Compare commits
64 Commits
Author | SHA1 | Date |
---|---|---|
Stefan Müller | 5ff8fafcb5 | |
Stefan Müller | 2517c4b8cf | |
Stefan Müller | e7285e88b5 | |
Stefan Müller | b5576c66f1 | |
Stefan Müller | 75aab50d42 | |
Stefan Müller | c84d3e6a2d | |
Stefan Müller | ba4195af82 | |
Stefan Müller | b086038aa5 | |
Stefan Müller | 4cd9392bbc | |
Stefan Müller | c0a6145019 | |
Stefan Müller | dfa6f6a09c | |
Stefan Müller | 00f90a1da9 | |
Stefan Müller | 5441700572 | |
Stefan Müller | 6d622d4c22 | |
Stefan Müller | 72ce40d84a | |
Stefan Müller | d79bd2e78a | |
Stefan Müller | 7b33e8b406 | |
Stefan Müller | ba1cefc371 | |
Stefan Müller | 0ccb047312 | |
Stefan Müller | 3e7943056a | |
Stefan Müller | f66e3fc2d8 | |
Stefan Müller | 8eb76329c1 | |
Stefan Müller | 19509c6173 | |
Stefan Müller | e81df9d558 | |
Stefan Müller | 3f83f888f3 | |
Stefan Müller | c5ea70ed21 | |
Stefan Müller | 216839c98b | |
Stefan Müller | 3c2acf05e8 | |
Stefan Müller | c2570fab3d | |
Stefan Müller | 33e7b64276 | |
Stefan Müller | 859a5db921 | |
Stefan Müller | b27b14a153 | |
Stefan Müller | 3e3e1d45d3 | |
Stefan Müller | 44caf3e21c | |
Stefan Müller | 1784e41c0f | |
Stefan Müller | 5f93ad7869 | |
Stefan Müller | 7db8f948c5 | |
Stefan Müller | 8d4a5c2ed8 | |
Stefan Müller | 04e1702a2e | |
Stefan Müller | ab453b347d | |
Stefan Müller | ae30889bbb | |
Stefan Müller | 748964c871 | |
Stefan Müller | fa5616f3cc | |
Stefan Müller | baa1f8f31f | |
Stefan Müller | 53e3922654 | |
Stefan Müller | cbaffbf55e | |
Stefan Müller | aef4f28f46 | |
Stefan Müller | 37309d2817 | |
Stefan Müller | 6802da1743 | |
Stefan Müller | 9d444272e0 | |
Stefan Müller | afefbf46e3 | |
Stefan Müller | 7ac4a3519a | |
Stefan Müller | d503968cee | |
Stefan Müller | c25317c0fc | |
Stefan Müller | 53827acf9b | |
Stefan Müller | 4c0ff2f23f | |
Stefan Müller | 0bbae0a83e | |
Stefan Müller | 71c8462358 | |
Stefan Müller | 5808ec24f2 | |
Stefan Müller | eca6b8f3f9 | |
Stefan Müller | 5b0a7996c5 | |
Stefan Müller | 801f8aa643 | |
Stefan Müller | 824ec0e29b | |
Stefan Müller | 3f4a64b6e0 |
|
@ -141,6 +141,22 @@
|
|||
<Filename Value="UBigInt.pas"/>
|
||||
<IsPartOfProject Value="True"/>
|
||||
</Unit>
|
||||
<Unit>
|
||||
<Filename Value="UPolynomial.pas"/>
|
||||
<IsPartOfProject Value="True"/>
|
||||
</Unit>
|
||||
<Unit>
|
||||
<Filename Value="UPolynomialRoots.pas"/>
|
||||
<IsPartOfProject Value="True"/>
|
||||
</Unit>
|
||||
<Unit>
|
||||
<Filename Value="solvers\USnowverload.pas"/>
|
||||
<IsPartOfProject Value="True"/>
|
||||
</Unit>
|
||||
<Unit>
|
||||
<Filename Value="UCommon.pas"/>
|
||||
<IsPartOfProject Value="True"/>
|
||||
</Unit>
|
||||
</Units>
|
||||
</ProjectOptions>
|
||||
<CompilerOptions>
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
Solutions to the Advent Of Code.
|
||||
Copyright (C) 2023 Stefan Müller
|
||||
Copyright (C) 2023-2024 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
|
||||
|
@ -25,9 +25,9 @@ uses
|
|||
{$ENDIF}
|
||||
Classes, SysUtils, CustApp, Generics.Collections, USolver,
|
||||
UTrebuchet, UCubeConundrum, UGearRatios, UScratchcards, UGiveSeedFertilizer, UWaitForIt, UCamelCards,
|
||||
UHauntedWasteland, UNumberTheory, UMirageMaintenance, UPipeMaze, UCosmicExpansion, UHotSprings, UPointOfIncidence,
|
||||
UHauntedWasteland, UMirageMaintenance, UPipeMaze, UCosmicExpansion, UHotSprings, UPointOfIncidence,
|
||||
UParabolicReflectorDish, ULensLibrary, UFloorWillBeLava, UClumsyCrucible, ULavaductLagoon, UAplenty,
|
||||
UPulsePropagation, UStepCounter, USandSlabs, ULongWalk, UNeverTellMeTheOdds;
|
||||
UPulsePropagation, UStepCounter, USandSlabs, ULongWalk, UNeverTellMeTheOdds, USnowverload;
|
||||
|
||||
type
|
||||
|
||||
|
@ -54,7 +54,10 @@ var
|
|||
n: Integer;
|
||||
begin
|
||||
WriteLn('### Advent of Code 2023 ###');
|
||||
engine := TSolverEngine.Create('data');
|
||||
engine := TSolverEngine.Create(TStringArray.Create(
|
||||
'data',
|
||||
ConcatPaths(['..', '..', 'data'])
|
||||
));
|
||||
|
||||
solvers := specialize TList<Integer>.Create;
|
||||
if HasOption('p', 'puzzle') then
|
||||
|
@ -93,6 +96,7 @@ begin
|
|||
22: engine.RunAndFree(TSandSlabs.Create);
|
||||
23: engine.RunAndFree(TLongWalk.Create);
|
||||
24: engine.RunAndFree(TNeverTellMeTheOdds.Create);
|
||||
25: engine.RunAndFree(TSnowverload.Create);
|
||||
end;
|
||||
|
||||
engine.Free;
|
||||
|
|
180
README.md
180
README.md
|
@ -1,130 +1,216 @@
|
|||
# Advent of Code 2023
|
||||
# :christmas_tree: Advent of Code 2023
|
||||
|
||||
Solver for [Advent of Code 2023](https://adventofcode.com/2023/) puzzles.
|
||||
|
||||
This is a single command line application for all puzzles written in [FreePascal](https://www.freepascal.org) with [Lazarus](https://www.lazarus-ide.org/) 2.2.6 and compiled with FPC 3.2.2.
|
||||
|
||||
## Day 1: Trebuchet?!
|
||||
## Puzzle Input
|
||||
|
||||
<https://adventofcode.com/2023/day/1>
|
||||
This project does not contain the puzzle or example inputs as per the [copyright notice of Advent of Code](https://adventofcode.com/about). In order to run the compiled application, the puzzle inputs have to be downloaded from the [Advent of Code 2023](https://adventofcode.com/2023/) puzzle pages, and placed as text files into the `bin\data` directory, e.g. `bin\data\cube_conundrum.txt` or `bin\data\example\cube_conundrum.txt`. The application will output an error message with details, if it cannot find an input file.
|
||||
|
||||
## Tests
|
||||
|
||||
On day 3, I introduced unit tests to help troubleshoot issues and prevent regressions while I kept iterating over the solver class framework. These tests cover the provided example solutions and occasional partial data tests whenever I felt the need for it.
|
||||
|
||||
I also added tests for the full puzzle data once I found the solution, but these tests are not public to not spoil the Advent of Code puzzles.
|
||||
|
||||
## My Favorites
|
||||
|
||||
While I think all the puzzles were a lot of fun, some of them became my favorites, marked with a star :star:, because I enjoyed the puzzle itself or was particularly content with my solution algorithm. These are [day 9](#day-9-mirage-maintenance), [day 18](#day-18-lavaduct-lagoon), and [day 24](#day-24-never-tell-me-the-odds).
|
||||
|
||||
## Solutions
|
||||
|
||||
### Day 1: Trebuchet?!
|
||||
|
||||
:mag_right: Puzzle: <https://adventofcode.com/2023/day/1>, :white_check_mark: Solver: [`UTrebuchet.pas`](solvers/UTrebuchet.pas)
|
||||
|
||||
My solution parses each line once forward for the right number, and once backward for the left number for both parts of the puzzle.
|
||||
|
||||
## Day 2: Cube Conundrum
|
||||
### Day 2: Cube Conundrum
|
||||
|
||||
<https://adventofcode.com/2023/day/2>
|
||||
:mag_right: Puzzle: <https://adventofcode.com/2023/day/2>, :white_check_mark: Solver: [`UCubeConundrum.pas`](solvers/UCubeConundrum.pas)
|
||||
|
||||
That one seemed pretty straight forward. For each line, the solution immediately sums up games that fulfill the maxima and finds the maxima of each color.
|
||||
That one seemed pretty straight forward. For each line, the solution immediately sums up games that fulfill the maxima and finds the maximum of each color.
|
||||
|
||||
## Day 3: Gear Ratios
|
||||
### Day 3: Gear Ratios
|
||||
|
||||
<https://adventofcode.com/2023/day/3>
|
||||
:mag_right: Puzzle: <https://adventofcode.com/2023/day/3>, :white_check_mark: Solver: [`UGearRatios.pas`](solvers/UGearRatios.pas)
|
||||
|
||||
For this I modified the solver class to pass in three lines at once, shifting one line down in each iteration, processing the numbers in the middle line and looking for additional symbols in the lines before and after. The tricky part was to correctly track the data needed for processing of each line and discarding it in time, without resorting to reading all data in before processing.
|
||||
This was the first puzzle where I had to implement a solver processing multiple lines at once instead of one by one. Here, it also needs the lines directly before and after the one being processed.
|
||||
|
||||
I introduced the test framework for this puzzle while stumbling over quite a few bugs.
|
||||
The algorithm processes the numbers in the middle line and looks for additional symbols in the lines before and after. The tricky part was to correctly track the data needed for processing of each line and discarding it in time, without resorting to reading all data in before processing.
|
||||
|
||||
## Day 4: Scratchcards
|
||||
### Day 4: Scratchcards
|
||||
|
||||
<https://adventofcode.com/2023/day/4>
|
||||
:mag_right: Puzzle: <https://adventofcode.com/2023/day/4>, :white_check_mark: Solver: [`UScratchcards.pas`](solvers/UScratchcards.pas)
|
||||
|
||||
For part 1, the algorithm simply matches winning numbers against numbers we have, and multiplies the current line result by two for every match (except the first).
|
||||
|
||||
For part 2 there is a list of numbers of card copies for the upcoming cards, wher the list index is always relative to the current line. This works because the copies are always applied contiguously over upcoming cards. Once a card has been processed, its copy value is deleted from the beginning of the list (index 0).
|
||||
For part 2, there is a list of numbers of card copies for the upcoming cards, where the list index is shifted to be always relative to the current line. This works because the copies are always applied contiguously over upcoming cards. Once a card has been processed, its copy value is deleted from the beginning of the list.
|
||||
|
||||
## Day 5: If You Give A Seed A Fertilizer
|
||||
### Day 5: If You Give A Seed A Fertilizer
|
||||
|
||||
<https://adventofcode.com/2023/day/5>
|
||||
:mag_right: Puzzle: <https://adventofcode.com/2023/day/5>, :white_check_mark: Solver: [`UGiveSeedFertilizer.pas`](solvers/UGiveSeedFertilizer.pas)
|
||||
|
||||
Originally, I had implemented this by reading all data in first, constructing a list of the seven mappings, each containing a list of mapping ranges. I rewrote this when I realized that the conversion can be done line-by-line by maintaining separate lists of "unconverted" and "converted" values. Each mapping range is applied to all unconverted values, and if one matches it is converted and moved into the list of converted values. At the end of a map all converted values are moved back into the unconverted list. Unconverted values simply remain unconverted for the next map.
|
||||
|
||||
For part 2, it is not necessary (and not feasible) to convert the input ranges into individual values to run through the existing algorithm. Instead I modified the algorithm to run on ranges of input directly. This means that a successful conversion can split a range in up to three parts, where one is moved into the "converted" pile, while the others remain unconverted.
|
||||
|
||||
## Day 6: Wait For It
|
||||
### Day 6: Wait For It
|
||||
|
||||
<https://adventofcode.com/2023/day/6>
|
||||
:mag_right: Puzzle: <https://adventofcode.com/2023/day/6>, :white_check_mark: Solver: [`UWaitForIt.pas`](solvers/UWaitForIt.pas)
|
||||
|
||||
This one I solved by calculating the roots of the function *f(x) = -time ^2 * x + distance* and determining the distance between them. Part 2 was the first puzzle that required 64-bit integers for the calculations.
|
||||
This one I solved by calculating the roots of the function *f(x) = -time ^2 * x + distance* and determining the distance between them. Part 2 was the first puzzle where my solver required 64-bit integers for the calculations.
|
||||
|
||||
## Day 7: Camel Cards
|
||||
### Day 7: Camel Cards
|
||||
|
||||
<https://adventofcode.com/2023/day/7>
|
||||
:mag_right: Puzzle: <https://adventofcode.com/2023/day/7>, :white_check_mark: Solver: [`UCamelCards.pas`](solvers/UCamelCards.pas)
|
||||
|
||||
The first puzzle that I could not solve line-by-line (day 6 doesn't count). For this one I store all the card hands and assign them a "type", e.g. "four of a kind", when processing them by counting the different card values in a hand. The rest of work is done in a custom compare function. When all data is processed I just use the compare function to sort all card hands, and then multiply the resulting indices with the bids.
|
||||
The first puzzle that I could not solve line-by-line (day 6 doesn't count). For this one I store all the card hands and assign them a "type", e.g. "four of a kind", when processing them by counting the different card values in a hand. The rest of work is done in a custom compare function. When all data is processed, I just use the compare function to sort all card hands, and then multiply the resulting indices with the bids.
|
||||
|
||||
For part 2, each card hands gets a "joker type" analoguous to the "type", for which the number of joker cards is added to the highest number of a different card type.
|
||||
For part 2, each card hands gets a "joker type" analogous to the "type", for which the number of joker cards is added to the highest number of a different card type.
|
||||
|
||||
## Day 8: Haunted Wasteland
|
||||
### Day 8: Haunted Wasteland
|
||||
|
||||
<https://adventofcode.com/2023/day/8>
|
||||
:mag_right: Puzzle: <https://adventofcode.com/2023/day/8>, :white_check_mark: Solver: [`UHauntedWasteland.pas`](solvers/UHauntedWasteland.pas)
|
||||
|
||||
Again a puzzle where I had to read in all of the data before starting the algorithm. It proved difficult to verify parts of the algorithm by hand, but part 1 was still pretty straight forward.
|
||||
|
||||
Part 2 was a bit sneaky. This is the first puzzle where the result is outside the 32-bit unsigned integer range. And it is solvable only because each starting node leads into a loop with one of the target nodes, where the length of the loop is a multiple of the length of the sequence of instructions. With this knowledge, one can stop traversing the network once each target node has been reached and calculate the result directly.
|
||||
|
||||
## Day 9: Mirage Maintenance
|
||||
### Day 9: Mirage Maintenance
|
||||
|
||||
<https://adventofcode.com/2023/day/9>
|
||||
:star: :mag_right: Puzzle: <https://adventofcode.com/2023/day/9>, :white_check_mark: Solver: [`UMirageMaintenance.pas`](solvers/UMirageMaintenance.pas)
|
||||
|
||||
This one I enjoyed the most so far. The process that is discribed in the puzzle, constructing a series of differences from the previous series, and then reverting the process to extend the series, is equivalent to finding a polynomial with maximum degree of *n - 1*, where the original series are *n* equidistant values of the polynomial.
|
||||
This one I enjoyed the most so far. The process that is described in the puzzle, constructing a series of differences from the previous series, and then reverting the process to extend the series, is equivalent to finding a polynomial with maximum degree of *n - 1*, where the original series are *n* equidistant values of the polynomial.
|
||||
|
||||
So instead of using the outlined "brute force" method, I used Lagrange polynomials with *x1 = 0, x2 = 1, ..., xn = n - 1* evaluated at *x = n* (for part 1) and *x = -1* (for part 2) to find the function values for the extrapolated "points". Conveniently, the Lagrange polynomials can be precalculated for the whole puzzle (with some tricks to not run over Int64 limits) because they only depend on *x* values, which remain constant. This makes the calculation of the extrapolated values quite easy.
|
||||
|
||||
A nice explanation of the Lagrange method can be found here <http://bueler.github.io/M310F11/polybasics.pdf>.
|
||||
|
||||
## Day 10: Pipe Maze
|
||||
### Day 10: Pipe Maze
|
||||
|
||||
<https://adventofcode.com/2023/day/10>
|
||||
:mag_right: Puzzle: <https://adventofcode.com/2023/day/10>, :white_check_mark: Solver: [`UPipeMaze.pas`](solvers/UPipeMaze.pas)
|
||||
|
||||
The input data is such that there are only two pipes pointing to *S*, so it finding the loop is only a matter of following the chars as instructed. It seems best to read in the full input before trying to traverse the maze, I did not see another option. The length of the loop is always even, so my algorithm just follows the path until it is back to *S* and counts only every other step.
|
||||
The input data is such that there are only two pipes pointing to *S*, so finding the loop is only a matter of following the chars as instructed. It seems best to read in the full input before trying to traverse the maze, I did not see another option. The length of the loop is always even, so my algorithm just follows the path until it is back to *S* and counts only every other step.
|
||||
|
||||
For part 2, I tracked tiles that are "left" and "right" of the path the algorithm took, and implemented a little flood-fill algorithm that tries to fill the area in between the "left" tiles and the "right" tiles, while counting them. The "outside" group is the one where the flood-fill touches the edge of the map and is simply ignored.
|
||||
|
||||
## Day 11: Cosmic Expansion
|
||||
### Day 11: Cosmic Expansion
|
||||
|
||||
<https://adventofcode.com/2023/day/11>
|
||||
:mag_right: Puzzle: <https://adventofcode.com/2023/day/11>, :white_check_mark: Solver: [`UCosmicExpansion.pas`](solvers/UCosmicExpansion.pas)
|
||||
|
||||
While parsing the input, we track coordinates of each galaxy, and for each row and column a *1* if it is empty and a *0* if not. At the end we sum for each pair of galaxies the values for each row and column between their coordinates *+1* to get the sum of their (Manhattan) distances.
|
||||
While parsing the input, the solver tracks coordinates of each galaxy, and for each row and column a *1* if it is empty and a *0* if not. At the end we sum for each pair of galaxies the values for each row and column between their coordinates *+1* to get the sum of their (Manhattan) distances.
|
||||
|
||||
This approach was trivial to adapt for part 2, since all that was needed was another factor that had to be multiplied with the values tracked for the rows and columns before applying the *+1*.
|
||||
|
||||
## Day 13: Point of Incidence
|
||||
### Day 13: Point of Incidence
|
||||
|
||||
<https://adventofcode.com/2023/day/13>
|
||||
:mag_right: Puzzle: <https://adventofcode.com/2023/day/13>, :white_check_mark: Solver: [`UPointOfIncidence.pas`](solvers/UPointOfIncidence.pas)
|
||||
|
||||
While going through each line, the algorithm keeps updating two lists of mirror candidates, separately for horizontal and vertical mirrors. For horizontal mirrors, a new candidate is added whenever two consecutive lines are identical. After it is added, new lines are each compared against the potentially mirrored earlier line, until the candidate is discarded or the last line successfully mirrored.
|
||||
While going through each line, the algorithm keeps updating two lists of mirror candidates, one for horizontal and one for vertical mirrors. For horizontal mirrors, a new candidate is added whenever two consecutive lines are identical. After it is added, new lines are each compared against the potentially mirrored earlier line, until the candidate is discarded or the last line successfully mirrored.
|
||||
|
||||
For vertical mirrors, all candidates are added during processing of the first line, based on whether they mirror the first line or not. While processing further lines, each candidates is verified against each line or discarded.
|
||||
|
||||
To solve part 2 as well, each candidate tracks whether one character was switched or not to successfully mirror all processed lines. If a second character switch is required or no switch had occurred at the end, the candidate is discarded. By setting this tracker as if a switch had already happened even for new candidates, both parts of the puzzle can be solved simultanously.
|
||||
To solve part 2, each candidate is allowed one character switch, and tracks whether that switch happened or not to successfully mirror all processed lines. If a second character switch is required or no switch had occurred at the end, the candidate is discarded. By setting this tracker as if a switch had already happened even for new candidates, both parts of the puzzle can be solved simultaneously.
|
||||
|
||||
## Day 14: Parabolic Reflector Dish
|
||||
### Day 14: Parabolic Reflector Dish
|
||||
|
||||
<https://adventofcode.com/2023/day/14>
|
||||
:mag_right: Puzzle: <https://adventofcode.com/2023/day/14>, :white_check_mark: Solver: [`UParabolicReflectorDish.pas`](solvers/UParabolicReflectorDish.pas)
|
||||
|
||||
I spent too much time on this one. I had originally implemented a relatively naive algorithm that would do the tilting of the platform by operating directly the string list, swapping out round rocks as it went, which seemed quite slow.
|
||||
|
||||
So I reimplemented the whole thing with proper data structures consisting of lists of non-empty intervals between cube-shaped rocks, and lists of rows and columns of rounded rocks, between which the algorithm would alternate, to facilitate a faster computation without string manipulation. This improved the performance of the algorithm, but unfortunately not as much as I had expected.
|
||||
|
||||
An essential revelation to make any algorithm for this work is that the formations of the rounded rocks on the platform repeat while spinning it 1,000,000,000 times, so once a previous formation is discovered, the calculation can be severly short-cut.
|
||||
An essential revelation to make any algorithm for this work is that the formations of the rounded rocks on the platform repeat while spinning it 1,000,000,000 times, so once a previous formation is discovered, the calculation can be short-cut significantly.
|
||||
|
||||
## Day 15: Lens Library
|
||||
### Day 15: Lens Library
|
||||
|
||||
<https://adventofcode.com/2023/day/15>
|
||||
:mag_right: Puzzle: <https://adventofcode.com/2023/day/15>, :white_check_mark: Solver: [`ULensLibrary.pas`](solvers/ULensLibrary.pas)
|
||||
|
||||
Pretty straight-forward implementation of a HASHMAP with custom HASH function.
|
||||
Pretty straight-forward implementation of a Hashmap with a custom Hash function.
|
||||
|
||||
## Day 16: The Floor Will Be Lava
|
||||
### Day 16: The Floor Will Be Lava
|
||||
|
||||
<https://adventofcode.com/2023/day/16>
|
||||
:mag_right: Puzzle: <https://adventofcode.com/2023/day/16>, :white_check_mark: Solver: [`UFloorWillBeLava.pas`](solvers/UFloorWillBeLava.pas)
|
||||
|
||||
Here I calculate how a beam traverses through the grid until it is reflected out of the grid. Everytime it hits a splitter a new beam is put on a stack to be calculated later. I found the difficulty to be to find a good way to track how a beam has already travelled through the grid. This seems essential to detect when calculation for a part of the beam can be aborted, since splitters can create loops. However, two beams could pass through the same tile without meeting. I settled for tracking four energy states for each tile of the grid, one being "not energized", two describing generically the two directions a beam could travel through some tiles, and one for the combination of those two directions, and then using the energy state of the current field and the beam's direction to stop it before it leaves the boundaries of the grid.
|
||||
The solver calculates how a beam traverses through the grid until it is reflected outwards. Every time it hits a splitter, a new beam is put on a stack to be calculated later. I found the difficulty to be finding a good way to track how a beam has already traveled through the grid. This seems essential to detect when the calculation for a part of the beam can be aborted, since splitters can create loops. However, two beams could pass through the same tile in different ways without forming a loop. I settled for tracking four energy states for each tile of the grid, one being "not energized", two describing generically the two directions a beam could travel through some tiles, and one for the combination of those two directions. This energy state of the current field and the beam's direction could then be used to abandon a beam early, before it leaves the boundaries of the grid.
|
||||
|
||||
Once this was solved for one starting beam in part 1, I just iterated over all possible starting beams to find the maximum for part 2.
|
||||
|
||||
### Day 17: Clumsy Crucible
|
||||
|
||||
:mag_right: Puzzle: <https://adventofcode.com/2023/day/17>, :white_check_mark: Solver: [`UClumsyCrucible.pas`](solvers/UClumsyCrucible.pas)
|
||||
|
||||
I initially tried to solve this with a simple depth first search for the minimum path, while abandoning branches immediately when they exceed the current lowest known path value. However, this approach is way to costly, even on the small example data. It takes too long for a branch to be abandoned, and sub-paths are re-calculated many times for each of the branches.
|
||||
|
||||
Instead, the solver uses a somewhat Dijkstra-inspired algorithm, where for each location in the grid, it tracks two values, one for each 2D axis, which describe the lowest known accumulated heat loss when going from this grid location to the end point with the first step along the associated axis. The solver starts at the end point, where these two values are zero, and gradually calculates the minima for all grid points, while keeping track of grid locations that need recalculations. Once all grid points have been calculated, the result can be taken directly from the starting grid location.
|
||||
|
||||
The main modification to the classic algorithm here is that in order to calculate e.g. the horizontal current minimum for a grid point, the vertical current minimum of its vertical neighbors within a certain range have to be considered. The difference between part 1 and 2 is only the specific range to be used.
|
||||
|
||||
### Day 18: Lavaduct Lagoon
|
||||
|
||||
:star: :mag_right: Puzzle: <https://adventofcode.com/2023/day/18>, :white_check_mark: Solver: [`ULavaductLagoon.pas`](solvers/ULavaductLagoon.pas)
|
||||
|
||||
My first algorithm for part 1 was a simply tracking the trench in a top-view two-dimensional array and then flood-filling the outside of the trench to determine the full area. It worked, but there were two problems. Firstly, I had to iteratre over the list of digs twice in order to avoid resizing the array frequently. Secondly, the performance complexity of the algorthim depends largely on the size of the array, i.e. the length of the individual digs, so obviously it did not scale for part 2.
|
||||
|
||||
The final algorithm, uses the fact that either all right turns are convex or concave, locally, while all left turns are the opposite. That means that two consecutive turns in the same direction (a U-turn) enclose a rectangular area that is either inside or outside of the trench depending only on the direction of the two turns. So the algorthim simply collapses all U-turns it encounters into a straight dig instruction, thereby cutting of an area that is either added to or subtracted from the running area count.
|
||||
|
||||
These U-turn collapses are done immediately when adding digs because then the U-turns will always either be at the end of the list or just before the last collapse. One difficulty is that the in order for this to work well, the algorithm needs to ensure that consecutive digs are always perpendicular, merging any that are parallel into a single one.
|
||||
|
||||
### Day 19: Aplenty
|
||||
|
||||
:mag_right: Puzzle: <https://adventofcode.com/2023/day/19>, :white_check_mark: Solver: [`UAplenty.pas`](solvers/UAplenty.pas)
|
||||
|
||||
Since the workflows are at the beginning of the puzzle input, each machine part can be routed directly through the memorized workflows for part 1 when its line is processed. Each part starts at the `in` workflow and follows the checks and switches until it is either rejected or accepted. To benefit performance, the workflows cache links to each other for each switch, which are each set during the algorithm run after their first match.
|
||||
|
||||
For part two, a virtual "multi machine part" that represents all possible values of ratings, modelled as four integer intervals, is sent through the same workflow graph. Each time one of rules is applied to a multi machine part, it is split into up to three new multi machine parts that continue to go through the workflows on separate paths. This is similar to [my day 5 solution](#day-5-if-you-give-a-seed-a-fertilizer).
|
||||
|
||||
### Day 20: Pulse Propagation
|
||||
|
||||
:mag_right: Puzzle: <https://adventofcode.com/2023/day/20>, :white_check_mark: Solver: [`UPulsePropagation.pas`](solvers/UPulsePropagation.pas)
|
||||
|
||||
For part 1, it's quite straight forward to model and simulate the module pulses for the first 1000 button pushes.
|
||||
|
||||
Part 2 seemed pretty daunting at first (and probably is quite difficult in the general case), but investigating the graph of the module connection reveals pretty quickly that the modules form a set of four independent counters of button pushes modulo different reset values, such that `rx` receives one low pulse if and only if all four counters reset as a result of the same button push. Clearly, the first time this happens is when the button is pushed a number of times equal to the product of the four counters' reset values.
|
||||
|
||||
### Day 21: Step Counter
|
||||
|
||||
:mag_right: Puzzle: <https://adventofcode.com/2023/day/21>, :white_check_mark: Solver: [`UStepCounter.pas`](solvers/UStepCounter.pas)
|
||||
|
||||
Part 1 can comfortably be solved with a flood-fill algorithm. Counting every other traversed plot will emulate the trivial backtracking the elf can do, without having to do the actual backtracking in the algorithm.
|
||||
|
||||
For part 2, I noticed that the map is sparse enough so that all plots that are theoretically in range are also actually in reachable. This means that the algorithm only has to count empty plots within specific, different, disjoint areas on the map, and multiply them by the number of occurences of this piece of the map within the full shape of reachable plots. See [`UStepCounter.pas`, line 174](solvers/UStepCounter.pas#L174) for details.
|
||||
|
||||
Interestingly, this is the only puzzle besides [day 20](#day-20-pulse-propagation), which had no part 2 example, where my implementation cannot solve the part 2 examples, since the example map is not sparse and their step limits do not fit the algorithm's requirements.
|
||||
|
||||
### Day 22: Sand Slabs
|
||||
|
||||
:mag_right: Puzzle: <https://adventofcode.com/2023/day/22>, :white_check_mark: Solver: [`USandSlabs.pas`](solvers/USandSlabs.pas)
|
||||
|
||||
I first sort the bricks with a custom compare function, such that they can be stacked in this order on the ground without passing through each other. Then they can be processed one by one on the ground directly, while tracking the current height of the ground positions, essentially the top-view of the ground plot, and which bricks connect vertically.
|
||||
|
||||
For part 1, if a brick lands on a single supporting brick, that brick below cannot be disintegrated anymore and is removed from the count, if it could have been disintegrated before.
|
||||
|
||||
For part 2, given a starting brick, the algorithm makes use of the tracked vertical connections to find a group of bricks supported by it, such that all supports of the bricks in the group are also in the group. This group of bricks would fall if the starting brick was disintegrated, so its size is counted for each possible starting brick.
|
||||
|
||||
### Day 23: A Long Walk
|
||||
|
||||
:mag_right: Puzzle: <https://adventofcode.com/2023/day/23>, :white_check_mark: Solver: [`ULongWalk.pas`](solvers/ULongWalk.pas)
|
||||
|
||||
There is a nice *O(|V| * |E|)* algorithm for the maximum flow in a directed acyclic graph, if a topological ordering of the vertices is know. It's relatively easy to parse the edges ("paths") of the long walk from the input such that a topological ordering results, by adding the vertices ("crossings") only after all in-edges have been found.
|
||||
|
||||
For part 2, I believe there is no polynomial algorithm known for the general case, and even with the given restraints I was unable to come up with one. Instead, my solution uses a depth-first search to parse all options in the network. This was feasible for the given input with some smart data structures to limit iterations of the vertex or edge lists, and with shortcuts to determine early if a search branch can be abandoned.
|
||||
|
||||
### Day 24: Never Tell Me the Odds
|
||||
|
||||
:star: :mag_right: Puzzle: <https://adventofcode.com/2023/day/24>, :white_check_mark: Solver: [`UNeverTellMeTheOdds.pas`](solvers/UNeverTellMeTheOdds.pas)
|
||||
|
||||
While I found part 1 quite trivial, part 2 left me with the feeling that my approach might be mad. Eventually, I managed to find the ray hitting all other rays by solving the general equation system for three known and one unknown rays with some shortcuts for this particular problem, for example assuming the existence of a unique solution. However, this involved excessive manual pre-calculations, arbitrary length integer arithmetic, and a root finder for integer polynomials, all implemented by myself without additional third-party libraries.
|
||||
|
||||
## License
|
||||
|
||||
Copyright (C) 2023 Stefan Müller
|
||||
Copyright (C) 2023-2024 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.
|
||||
|
||||
|
|
|
@ -91,6 +91,8 @@ type
|
|||
class function FromBinaryString(const AValue: string): TBigInt; static;
|
||||
end;
|
||||
|
||||
TBigIntArray = array of TBigInt;
|
||||
|
||||
{ Operators }
|
||||
|
||||
operator := (const A: Int64): TBigInt;
|
||||
|
|
|
@ -0,0 +1,49 @@
|
|||
{
|
||||
Solutions to the Advent Of Code.
|
||||
Copyright (C) 2023-2024 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/>.
|
||||
}
|
||||
|
||||
unit UCommon;
|
||||
|
||||
{$mode ObjFPC}{$H+}
|
||||
|
||||
interface
|
||||
|
||||
uses
|
||||
Classes, SysUtils, Generics.Collections;
|
||||
|
||||
type
|
||||
PPoint = ^TPoint;
|
||||
|
||||
const
|
||||
CNoDirection: TPoint = (X: 0; Y: 0);
|
||||
CDirectionRight: TPoint = (X: 1; Y: 0);
|
||||
CDirectionDown: TPoint = (X: 0; Y: 1);
|
||||
CDirectionLeft: TPoint = (X: -1; Y: 0);
|
||||
CDirectionUp: TPoint = (X: 0; Y: -1);
|
||||
CDirectionRightDown: TPoint = (X: 1; Y: 1);
|
||||
CDirectionRightUp: TPoint = (X: 1; Y: -1);
|
||||
CDirectionLeftDown: TPoint = (X: -1; Y: 1);
|
||||
CDirectionLeftUp: TPoint = (X: -1; Y: -1);
|
||||
CPCardinalDirections: array[0..3] of PPoint = (@CDirectionRight, @CDirectionDown, @CDirectionLeft, @CDirectionUp);
|
||||
|
||||
type
|
||||
TIntegerList = specialize TList<Integer>;
|
||||
TPoints = specialize TList<TPoint>;
|
||||
|
||||
implementation
|
||||
|
||||
end.
|
||||
|
|
@ -22,7 +22,7 @@ unit UNumberTheory;
|
|||
interface
|
||||
|
||||
uses
|
||||
Classes, SysUtils, Generics.Collections, Math;
|
||||
Classes, SysUtils;
|
||||
|
||||
type
|
||||
|
||||
|
@ -34,52 +34,6 @@ type
|
|||
class function LeastCommonMultiple(AValue1, AValue2: Int64): Int64;
|
||||
end;
|
||||
|
||||
TInt64Array = array of Int64;
|
||||
|
||||
{ TIntegerFactor }
|
||||
|
||||
TIntegerFactor = record
|
||||
Factor: Int64;
|
||||
Exponent: Byte;
|
||||
end;
|
||||
|
||||
TIntegerFactors = specialize TList<TIntegerFactor>;
|
||||
|
||||
{ TIntegerFactorization }
|
||||
|
||||
TIntegerFactorization = class
|
||||
public
|
||||
class function PollardsRhoAlgorithm(const AValue: Int64): TInt64Array;
|
||||
class function GetNormalized(constref AIntegerFactorArray: TInt64Array): TIntegerFactors;
|
||||
end;
|
||||
|
||||
{ TDividersEnumerator }
|
||||
|
||||
TDividersEnumerator = class
|
||||
private
|
||||
FFactors: TIntegerFactors;
|
||||
FCurrentExponents: array of Byte;
|
||||
function GetCount: Integer;
|
||||
public
|
||||
constructor Create(constref AIntegerFactorArray: TInt64Array);
|
||||
destructor Destroy; override;
|
||||
function GetCurrent: Int64;
|
||||
function MoveNext: Boolean;
|
||||
procedure Reset;
|
||||
property Current: Int64 read GetCurrent;
|
||||
property Count: Integer read GetCount;
|
||||
end;
|
||||
|
||||
{ TDividers }
|
||||
|
||||
TDividers = class
|
||||
private
|
||||
FFactorArray: TInt64Array;
|
||||
public
|
||||
constructor Create(constref AIntegerFactorArray: TInt64Array);
|
||||
function GetEnumerator: TDividersEnumerator;
|
||||
end;
|
||||
|
||||
implementation
|
||||
|
||||
{ TNumberTheory }
|
||||
|
@ -104,177 +58,5 @@ begin
|
|||
Result := (Abs(AValue1) div GreatestCommonDivisor(AValue1, AValue2)) * Abs(AValue2);
|
||||
end;
|
||||
|
||||
{ TIntegerFactorization }
|
||||
|
||||
// https://en.wikipedia.org/wiki/Pollard%27s_rho_algorithm
|
||||
class function TIntegerFactorization.PollardsRhoAlgorithm(const AValue: Int64): TInt64Array;
|
||||
var
|
||||
primes: specialize TList<Int64>;
|
||||
composites: specialize TStack<Int64>;
|
||||
factor, n: Int64;
|
||||
i: Integer;
|
||||
|
||||
function G(const AX, AC: Int64): Int64;
|
||||
begin
|
||||
Result := (AX * AX + AC) mod n;
|
||||
end;
|
||||
|
||||
function FindFactor(const AStartValue, AC: Int64): Int64;
|
||||
var
|
||||
x, y, d: Int64;
|
||||
begin
|
||||
x := AStartValue;
|
||||
y := x;
|
||||
d := 1;
|
||||
while d = 1 do
|
||||
begin
|
||||
x := G(x, AC);
|
||||
y := G(G(y, AC), AC);
|
||||
d := TNumberTheory.GreatestCommonDivisor(Abs(x - y), n);
|
||||
end;
|
||||
Result := d;
|
||||
end;
|
||||
|
||||
begin
|
||||
primes := specialize TList<Int64>.Create;
|
||||
composites := specialize TStack<Int64>.Create;
|
||||
|
||||
n := Abs(AValue);
|
||||
while (n and 1) = 0 do
|
||||
begin
|
||||
primes.Add(2);
|
||||
n := n shr 1;
|
||||
end;
|
||||
|
||||
composites.Push(n);
|
||||
while composites.Count > 0 do
|
||||
begin
|
||||
n := composites.Pop;
|
||||
i := 0;
|
||||
repeat
|
||||
factor := FindFactor(2 + (i + 1) div 2, 1 - i div 2);
|
||||
if factor < n then
|
||||
begin
|
||||
composites.Push(factor);
|
||||
composites.Push(n div factor);
|
||||
end;
|
||||
Inc(i);
|
||||
until (factor < n) or (i > 3);
|
||||
if factor = n then
|
||||
primes.Add(factor);
|
||||
end;
|
||||
|
||||
Result := primes.ToArray;
|
||||
|
||||
primes.Free;
|
||||
composites.Free;
|
||||
end;
|
||||
|
||||
class function TIntegerFactorization.GetNormalized(constref AIntegerFactorArray: TInt64Array): TIntegerFactors;
|
||||
var
|
||||
i: Integer;
|
||||
factor: Int64;
|
||||
normal: TIntegerFactor;
|
||||
found: Boolean;
|
||||
begin
|
||||
Result := TIntegerFactors.Create;
|
||||
for factor in AIntegerFactorArray do
|
||||
begin
|
||||
found := False;
|
||||
for i := 0 to Result.Count - 1 do
|
||||
if Result[i].Factor = factor then
|
||||
begin
|
||||
found := True;
|
||||
normal := Result[i];
|
||||
Inc(normal.Exponent);
|
||||
Result[i] := normal;
|
||||
Break;
|
||||
end;
|
||||
if not found then
|
||||
begin
|
||||
normal.Factor := factor;
|
||||
normal.Exponent := 1;
|
||||
Result.Add(normal);
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
|
||||
{ TDividersEnumerator }
|
||||
|
||||
function TDividersEnumerator.GetCount: Integer;
|
||||
var
|
||||
factor: TIntegerFactor;
|
||||
begin
|
||||
if FFactors.Count > 0 then
|
||||
begin
|
||||
Result := 1;
|
||||
for factor in FFactors do
|
||||
Result := Result * factor.Exponent;
|
||||
Dec(Result);
|
||||
end
|
||||
else
|
||||
Result := 0;
|
||||
end;
|
||||
|
||||
constructor TDividersEnumerator.Create(constref AIntegerFactorArray: TInt64Array);
|
||||
begin
|
||||
FFactors := TIntegerFactorization.GetNormalized(AIntegerFactorArray);
|
||||
SetLength(FCurrentExponents, FFactors.Count);
|
||||
end;
|
||||
|
||||
destructor TDividersEnumerator.Destroy;
|
||||
begin
|
||||
FFactors.Free;
|
||||
end;
|
||||
|
||||
function TDividersEnumerator.GetCurrent: Int64;
|
||||
var
|
||||
i: Integer;
|
||||
begin
|
||||
Result := 1;
|
||||
for i := Low(FCurrentExponents) to High(FCurrentExponents) do
|
||||
if FCurrentExponents[i] > 0 then
|
||||
Result := Result * Round(Power(FFactors[i].Factor, FCurrentExponents[i]));
|
||||
end;
|
||||
|
||||
function TDividersEnumerator.MoveNext: Boolean;
|
||||
var
|
||||
i: Integer;
|
||||
begin
|
||||
Result := False;
|
||||
i := 0;
|
||||
while (i <= High(FCurrentExponents)) and (FCurrentExponents[i] >= FFactors[i].Exponent) do
|
||||
begin
|
||||
FCurrentExponents[i] := 0;
|
||||
Inc(i);
|
||||
end;
|
||||
|
||||
if i <= High(FCurrentExponents) then
|
||||
begin
|
||||
Inc(FCurrentExponents[i]);
|
||||
Result := True;
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure TDividersEnumerator.Reset;
|
||||
var
|
||||
i: Integer;
|
||||
begin
|
||||
for i := Low(FCurrentExponents) to High(FCurrentExponents) do
|
||||
FCurrentExponents[i] := 0;
|
||||
end;
|
||||
|
||||
{ TDividers }
|
||||
|
||||
constructor TDividers.Create(constref AIntegerFactorArray: TInt64Array);
|
||||
begin
|
||||
FFactorArray := AIntegerFactorArray;
|
||||
end;
|
||||
|
||||
function TDividers.GetEnumerator: TDividersEnumerator;
|
||||
begin
|
||||
Result := TDividersEnumerator.Create(FFactorArray);
|
||||
end;
|
||||
|
||||
end.
|
||||
|
||||
|
|
|
@ -0,0 +1,297 @@
|
|||
{
|
||||
Solutions to the Advent Of Code.
|
||||
Copyright (C) 2024 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/>.
|
||||
}
|
||||
|
||||
unit UPolynomial;
|
||||
|
||||
{$mode ObjFPC}{$H+}
|
||||
|
||||
interface
|
||||
|
||||
uses
|
||||
Classes, SysUtils, UBigInt;
|
||||
|
||||
type
|
||||
TInt64Array = array of Int64;
|
||||
|
||||
{ TBigIntPolynomial }
|
||||
|
||||
TBigIntPolynomial = object
|
||||
private
|
||||
FCoefficients: array of TBigInt;
|
||||
function GetDegree: Integer;
|
||||
function GetCoefficient(const AIndex: Integer): TBigInt;
|
||||
public
|
||||
property Degree: Integer read GetDegree;
|
||||
property Coefficient[const AIndex: Integer]: TBigInt read GetCoefficient;
|
||||
function CalcValueAt(const AX: Int64): TBigInt;
|
||||
function CalcSignVariations: Integer;
|
||||
|
||||
// Returns 2^n * f(x), given a polynomial f(x) and exponent n.
|
||||
function ScaleByPowerOfTwo(const AExponent: Cardinal): TBigIntPolynomial;
|
||||
|
||||
// Returns f(s * x), given a polynomial f(x) and scale factor s.
|
||||
function ScaleVariable(const AScaleFactor: TBigInt): TBigIntPolynomial;
|
||||
|
||||
// Returns f(2^n * x), given a polynomial f(x) and an exponent n.
|
||||
function ScaleVariableByPowerOfTwo(const AExponent: Cardinal): TBigIntPolynomial;
|
||||
|
||||
// Returns f(x / 2), given a polynomial f(x).
|
||||
function ScaleVariableByHalf: TBigIntPolynomial;
|
||||
|
||||
// Returns f(x + 1), given a polynomial f(x).
|
||||
function TranslateVariableByOne: TBigIntPolynomial;
|
||||
|
||||
// Returns a polynomial with the reverse order of coefficients, i.e. the polynomial
|
||||
// a_0 * x^n + a_1 * x^(n - 1) + ... + a_(n - 1) * x + a_n,
|
||||
// given a polynomial
|
||||
// a_n * x^n + a_(n - 1) * x^(n - 1) + ... + a_1 * x + a_0.
|
||||
function RevertOrderOfCoefficients: TBigIntPolynomial;
|
||||
|
||||
// Returns a polynomial with all coefficents shifted down one position, and the constant term removed. This should
|
||||
// only be used when the constant term is zero and is then equivalent to a division of polynomial f(x) by x.
|
||||
function DivideByVariable: TBigIntPolynomial;
|
||||
function IsEqualTo(const AOther: TBigIntPolynomial): Boolean;
|
||||
function ToString: string;
|
||||
class function Create(const ACoefficients: array of TBigInt): TBigIntPolynomial; static;
|
||||
end;
|
||||
|
||||
{ Operators }
|
||||
|
||||
operator = (const A, B: TBigIntPolynomial): Boolean;
|
||||
operator <> (const A, B: TBigIntPolynomial): Boolean;
|
||||
|
||||
implementation
|
||||
|
||||
{ TBigIntPolynomial }
|
||||
|
||||
function TBigIntPolynomial.GetDegree: Integer;
|
||||
begin
|
||||
Result := Length(FCoefficients) - 1;
|
||||
end;
|
||||
|
||||
function TBigIntPolynomial.GetCoefficient(const AIndex: Integer): TBigInt;
|
||||
begin
|
||||
Result := FCoefficients[AIndex];
|
||||
end;
|
||||
|
||||
function TBigIntPolynomial.CalcValueAt(const AX: Int64): TBigInt;
|
||||
var
|
||||
i: Integer;
|
||||
begin
|
||||
Result := TBigInt.Zero;
|
||||
for i := High(FCoefficients) downto 0 do
|
||||
Result := Result * AX + FCoefficients[i];
|
||||
end;
|
||||
|
||||
function TBigIntPolynomial.CalcSignVariations: Integer;
|
||||
var
|
||||
current, last, i: Integer;
|
||||
begin
|
||||
Result := 0;
|
||||
last := 0;
|
||||
for i := 0 to Length(FCoefficients) - 1 do
|
||||
begin
|
||||
current := FCoefficients[i].Sign;
|
||||
if (current <> 0) and (last <> current) then
|
||||
begin
|
||||
if last <> 0 then
|
||||
Inc(Result);
|
||||
last := current
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
|
||||
function TBigIntPolynomial.ScaleByPowerOfTwo(const AExponent: Cardinal): TBigIntPolynomial;
|
||||
var
|
||||
len, i: Integer;
|
||||
begin
|
||||
len := Length(FCoefficients);
|
||||
SetLength(Result.FCoefficients, len);
|
||||
for i := 0 to len - 1 do
|
||||
Result.FCoefficients[i] := FCoefficients[i] << AExponent;
|
||||
end;
|
||||
|
||||
function TBigIntPolynomial.ScaleVariable(const AScaleFactor: TBigInt): TBigIntPolynomial;
|
||||
var
|
||||
len, i: Integer;
|
||||
factor: TBigInt;
|
||||
begin
|
||||
if AScaleFactor <> TBigInt.Zero then
|
||||
begin
|
||||
len := Length(FCoefficients);
|
||||
SetLength(Result.FCoefficients, len);
|
||||
Result.FCoefficients[0] := FCoefficients[0];
|
||||
factor := AScaleFactor;
|
||||
for i := 1 to len - 1 do begin
|
||||
Result.FCoefficients[i] := FCoefficients[i] * factor;
|
||||
factor := factor * AScaleFactor;
|
||||
end;
|
||||
end
|
||||
else begin
|
||||
SetLength(Result.FCoefficients, 1);
|
||||
Result.FCoefficients[0] := TBigInt.Zero;
|
||||
end;
|
||||
end;
|
||||
|
||||
function TBigIntPolynomial.ScaleVariableByPowerOfTwo(const AExponent: Cardinal): TBigIntPolynomial;
|
||||
var
|
||||
len, i: Integer;
|
||||
shift: Cardinal;
|
||||
begin
|
||||
len := Length(FCoefficients);
|
||||
SetLength(Result.FCoefficients, len);
|
||||
Result.FCoefficients[0] := FCoefficients[0];
|
||||
shift := AExponent;
|
||||
for i := 1 to len - 1 do begin
|
||||
Result.FCoefficients[i] := FCoefficients[i] << shift;
|
||||
Inc(shift, AExponent);
|
||||
end;
|
||||
end;
|
||||
|
||||
function TBigIntPolynomial.ScaleVariableByHalf: TBigIntPolynomial;
|
||||
var
|
||||
len, i: Integer;
|
||||
begin
|
||||
len := Length(FCoefficients);
|
||||
SetLength(Result.FCoefficients, len);
|
||||
Result.FCoefficients[0] := FCoefficients[0];
|
||||
for i := 1 to len - 1 do
|
||||
Result.FCoefficients[i] := FCoefficients[i] >> i;
|
||||
end;
|
||||
|
||||
function TBigIntPolynomial.TranslateVariableByOne: TBigIntPolynomial;
|
||||
var
|
||||
len, i, j: Integer;
|
||||
factors: array of Cardinal;
|
||||
begin
|
||||
len := Length(FCoefficients);
|
||||
SetLength(Result.FCoefficients, len);
|
||||
SetLength(factors, len);
|
||||
for i := 0 to len - 1 do
|
||||
begin
|
||||
Result.FCoefficients[i] := TBigInt.Zero;
|
||||
factors[i] := 1;
|
||||
end;
|
||||
|
||||
// Calculates new coefficients.
|
||||
for i := 0 to len - 1 do
|
||||
begin
|
||||
for j := 0 to len - i - 1 do
|
||||
begin
|
||||
if (i <> 0) and (j <> 0) then
|
||||
factors[j] := factors[j] + factors[j - 1];
|
||||
Result.FCoefficients[i] := Result.FCoefficients[i] + factors[j] * FCoefficients[j + i];
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
|
||||
function TBigIntPolynomial.RevertOrderOfCoefficients: TBigIntPolynomial;
|
||||
var
|
||||
len, skip, i: Integer;
|
||||
begin
|
||||
// Counts the trailing zeros to skip.
|
||||
len := Length(FCoefficients);
|
||||
skip := 0;
|
||||
while (skip < len) and (FCoefficients[skip] = 0) do
|
||||
Inc(skip);
|
||||
|
||||
// Copies the other coefficients in reverse order.
|
||||
SetLength(Result.FCoefficients, len - skip);
|
||||
for i := skip to len - 1 do
|
||||
Result.FCoefficients[len - i - 1] := FCoefficients[i];
|
||||
end;
|
||||
|
||||
function TBigIntPolynomial.DivideByVariable: TBigIntPolynomial;
|
||||
var
|
||||
len: Integer;
|
||||
begin
|
||||
len := Length(FCoefficients);
|
||||
if len > 1 then
|
||||
Result.FCoefficients := Copy(FCoefficients, 1, len - 1)
|
||||
else begin
|
||||
SetLength(Result.FCoefficients, 1);
|
||||
Result.FCoefficients[0] := TBigInt.Zero;
|
||||
end;
|
||||
end;
|
||||
|
||||
function TBigIntPolynomial.IsEqualTo(const AOther: TBigIntPolynomial): Boolean;
|
||||
var
|
||||
i: Integer;
|
||||
begin
|
||||
if Length(FCoefficients) = Length(AOther.FCoefficients) then
|
||||
begin
|
||||
Result := True;
|
||||
for i := 0 to Length(FCoefficients) - 1 do
|
||||
if FCoefficients[i] <> AOther.FCoefficients[i] then
|
||||
begin
|
||||
Result := False;
|
||||
Break;
|
||||
end;
|
||||
end
|
||||
else
|
||||
Result := False;
|
||||
end;
|
||||
|
||||
function TBigIntPolynomial.ToString: string;
|
||||
var
|
||||
i: Integer;
|
||||
begin
|
||||
Result := FCoefficients[0].ToString;
|
||||
for i := 1 to Length(FCoefficients) - 1 do
|
||||
if i > 1 then
|
||||
Result := Result + ' + ' + FCoefficients[i].ToString + ' * x^' + IntToStr(i)
|
||||
else
|
||||
Result := Result + ' + ' + FCoefficients[i].ToString + ' * x';
|
||||
end;
|
||||
|
||||
class function TBigIntPolynomial.Create(const ACoefficients: array of TBigInt): TBigIntPolynomial;
|
||||
var
|
||||
high, i: integer;
|
||||
begin
|
||||
high := -1;
|
||||
for i := Length(ACoefficients) - 1 downto 0 do
|
||||
if ACoefficients[i] <> 0 then
|
||||
begin
|
||||
high := i;
|
||||
Break;
|
||||
end;
|
||||
if high >= 0 then
|
||||
begin
|
||||
SetLength(Result.FCoefficients, high + 1);
|
||||
for i := 0 to high do
|
||||
Result.FCoefficients[i] := ACoefficients[i];
|
||||
end
|
||||
else begin
|
||||
SetLength(Result.FCoefficients, 1);
|
||||
Result.FCoefficients[0] := TBigInt.Zero;
|
||||
end;
|
||||
end;
|
||||
|
||||
{ Operators }
|
||||
|
||||
operator = (const A, B: TBigIntPolynomial): Boolean;
|
||||
begin
|
||||
Result := A.IsEqualTo(B);
|
||||
end;
|
||||
|
||||
operator <> (const A, B: TBigIntPolynomial): Boolean;
|
||||
begin
|
||||
Result := not A.IsEqualTo(B);
|
||||
end;
|
||||
|
||||
end.
|
||||
|
|
@ -0,0 +1,205 @@
|
|||
{
|
||||
Solutions to the Advent Of Code.
|
||||
Copyright (C) 2024 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/>.
|
||||
}
|
||||
|
||||
unit UPolynomialRoots;
|
||||
|
||||
{$mode ObjFPC}{$H+}
|
||||
|
||||
interface
|
||||
|
||||
uses
|
||||
Classes, SysUtils, Generics.Collections, UPolynomial, UBigInt;
|
||||
|
||||
type
|
||||
|
||||
{ TIsolatingInterval }
|
||||
|
||||
// Represents an isolating interval of the form [C / 2^K, (C + H) / 2^K] in respect to [0, 1] or [A, B] in respect to
|
||||
// [0, 2^boundexp], with A = C * 2^boundexp / 2^K and B = (C + H) * 2^boundexp / 2^K.
|
||||
TIsolatingInterval = record
|
||||
C: TBigInt;
|
||||
K, H, BoundExp: Cardinal;
|
||||
A, B: TBigInt;
|
||||
end;
|
||||
|
||||
TIsolatingIntervals = specialize TList<TIsolatingInterval>;
|
||||
|
||||
TIsolatingIntervalArray = array of TIsolatingInterval;
|
||||
|
||||
{ TPolynomialRoots }
|
||||
|
||||
TPolynomialRoots = class
|
||||
private
|
||||
// Returns the exponent (base two) of an upper bound for the roots of the given polynomial, i.e. all real roots of
|
||||
// the given polynomial are less or equal than 2^b, where b is the returned positive integer.
|
||||
class function CalcUpperRootBound(constref APolynomial: TBigIntPolynomial): Cardinal;
|
||||
class function CreateIsolatingInterval(constref AC: TBigInt; const AK, AH: Cardinal; constref ABoundExp: Cardinal):
|
||||
TIsolatingInterval;
|
||||
public
|
||||
// Returns root-isolating intervals for non-negative, non-multiple roots.
|
||||
class function BisectIsolation(constref APolynomial: TBigIntPolynomial): TIsolatingIntervalArray;
|
||||
// Returns root-isolating intervals for non-multiple roots in the interval [0, 2^boundexp].
|
||||
class function BisectIsolation(constref APolynomial: TBigIntPolynomial; constref ABoundExp: Cardinal;
|
||||
const AFindIntegers: Boolean = False): TIsolatingIntervalArray;
|
||||
// Returns non-negative, non-multiple, integer roots in the interval [0, 2^boundexp].
|
||||
class function BisectInteger(constref APolynomial: TBigIntPolynomial; constref ABoundExp: Cardinal):
|
||||
TBigIntArray;
|
||||
end;
|
||||
|
||||
implementation
|
||||
|
||||
{ TPolynomialRoots }
|
||||
|
||||
class function TPolynomialRoots.CalcUpperRootBound(constref APolynomial: TBigIntPolynomial): Cardinal;
|
||||
var
|
||||
i, sign: Integer;
|
||||
an, ai, max: TBigInt;
|
||||
numeratorBit, denominatorBit: Int64;
|
||||
begin
|
||||
// We need a_n > 0 here, so we use -sign(a_n) instead of actually flipping the polynomial.
|
||||
// Sign is not 0 because a_n is not 0.
|
||||
an := APolynomial.Coefficient[APolynomial.Degree];
|
||||
sign := -an.Sign;
|
||||
|
||||
// This is a simplification of Cauchy's bound to avoid division and make it a power of two.
|
||||
// https://en.wikipedia.org/wiki/Geometrical_properties_of_polynomial_roots#Bounds_of_positive_real_roots
|
||||
max := TBigInt.Zero;
|
||||
for i := 0 to APolynomial.Degree - 1 do begin
|
||||
ai := sign * APolynomial.Coefficient[i];
|
||||
if max < ai then
|
||||
max := ai;
|
||||
end;
|
||||
numeratorBit := max.GetMostSignificantBitIndex + 1;
|
||||
denominatorBit := an.GetMostSignificantBitIndex;
|
||||
Result := numeratorBit - denominatorBit;
|
||||
end;
|
||||
|
||||
class function TPolynomialRoots.CreateIsolatingInterval(constref AC: TBigInt; const AK, AH: Cardinal;
|
||||
constref ABoundExp: Cardinal): TIsolatingInterval;
|
||||
begin
|
||||
Result.C := AC;
|
||||
Result.K := AK;
|
||||
Result.H := AH;
|
||||
Result.BoundExp := ABoundExp;
|
||||
if ABoundExp >= AK then
|
||||
begin
|
||||
Result.A := AC << (ABoundExp - AK);
|
||||
Result.B := (AC + AH) << (ABoundExp - AK);
|
||||
end
|
||||
else begin
|
||||
Result.A := AC << (ABoundExp - AK);
|
||||
Result.B := (AC + AH) << (ABoundExp - AK);
|
||||
end;
|
||||
end;
|
||||
|
||||
class function TPolynomialRoots.BisectIsolation(constref APolynomial: TBigIntPolynomial): TIsolatingIntervalArray;
|
||||
var
|
||||
boundExp: Cardinal;
|
||||
begin
|
||||
boundExp := CalcUpperRootBound(APolynomial);
|
||||
Result := BisectIsolation(APolynomial, boundExp);
|
||||
end;
|
||||
|
||||
// This is adapted from https://en.wikipedia.org/wiki/Real-root_isolation#Bisection_method
|
||||
class function TPolynomialRoots.BisectIsolation(constref APolynomial: TBigIntPolynomial; constref ABoundExp: Cardinal;
|
||||
const AFindIntegers: Boolean): TIsolatingIntervalArray;
|
||||
type
|
||||
TWorkItem = record
|
||||
C: TBigInt;
|
||||
K: Cardinal;
|
||||
P: TBigIntPolynomial;
|
||||
end;
|
||||
TWorkStack = specialize TStack<TWorkItem>;
|
||||
var
|
||||
item: TWorkItem;
|
||||
stack: TWorkStack;
|
||||
n, v: Integer;
|
||||
varq: TBigIntPolynomial;
|
||||
iso: TIsolatingIntervals;
|
||||
begin
|
||||
iso := TIsolatingIntervals.Create;
|
||||
stack := TWorkStack.Create;
|
||||
|
||||
item.C := 0;
|
||||
item.K := 0;
|
||||
item.P := APolynomial.ScaleVariableByPowerOfTwo(ABoundExp);
|
||||
stack.Push(item);
|
||||
n := item.P.Degree;
|
||||
|
||||
while stack.Count > 0 do
|
||||
begin
|
||||
item := stack.Pop;
|
||||
if item.P.Coefficient[0] = TBigInt.Zero then
|
||||
begin
|
||||
// Found an integer root at 0.
|
||||
item.P := item.P.DivideByVariable;
|
||||
Dec(n);
|
||||
iso.Add(CreateIsolatingInterval(item.C, item.K, 0, ABoundExp));
|
||||
end;
|
||||
|
||||
varq := item.P.RevertOrderOfCoefficients.TranslateVariableByOne;
|
||||
v := varq.CalcSignVariations;
|
||||
if (v > 1)
|
||||
or ((v = 1) and AFindIntegers and (item.K < ABoundExp)) then
|
||||
begin
|
||||
// Bisects, first new work item is (2c, k + 1, 2^n * q(x/2)).
|
||||
item.C := item.C << 1;
|
||||
Inc(item.K);
|
||||
item.P := item.P.ScaleVariableByHalf.ScaleByPowerOfTwo(n);
|
||||
stack.Push(item);
|
||||
// ... second new work item is (2c + 1, k + 1, 2^n * q((x+1)/2)).
|
||||
item.C := item.C + 1;
|
||||
item.P := item.P.TranslateVariableByOne;
|
||||
stack.Push(item);
|
||||
end
|
||||
else if v = 1 then
|
||||
begin
|
||||
// Found isolating interval.
|
||||
iso.Add(CreateIsolatingInterval(item.C, item.K, 1, ABoundExp));
|
||||
end;
|
||||
end;
|
||||
Result := iso.ToArray;
|
||||
iso.Free;
|
||||
stack.Free;
|
||||
end;
|
||||
|
||||
class function TPolynomialRoots.BisectInteger(constref APolynomial: TBigIntPolynomial; constref ABoundExp: Cardinal):
|
||||
TBigIntArray;
|
||||
var
|
||||
intervals: TIsolatingIntervalArray;
|
||||
i: TIsolatingInterval;
|
||||
r: specialize TList<TBigInt>;
|
||||
value: Int64;
|
||||
begin
|
||||
// Calculates isolating intervals.
|
||||
intervals := BisectIsolation(APolynomial, ABoundExp, True);
|
||||
r := specialize TList<TBigInt>.Create;
|
||||
|
||||
for i in intervals do
|
||||
if i.H = 0 then
|
||||
r.Add(i.A)
|
||||
else if i.A.TryToInt64(value) and (APolynomial.CalcValueAt(value) = 0) then
|
||||
r.Add(value)
|
||||
else if i.B.TryToInt64(value) and (APolynomial.CalcValueAt(value) = 0) then
|
||||
r.Add(value);
|
||||
|
||||
Result := r.ToArray;
|
||||
r.Free;
|
||||
end;
|
||||
|
||||
end.
|
||||
|
66
USolver.pas
66
USolver.pas
|
@ -63,12 +63,15 @@ type
|
|||
|
||||
TSolverEngine = class
|
||||
private
|
||||
FRelativeDataPath: string;
|
||||
FRelativeDataPaths: TStringArray;
|
||||
public
|
||||
constructor Create(const ARelativeDataPath: string);
|
||||
constructor Create(constref ARelativeDataPaths: TStringArray);
|
||||
procedure ProcessData(const ASolver: ISolver);
|
||||
procedure Run(const ASolver: ISolver);
|
||||
procedure RunAndFree(const ASolver: ISolver);
|
||||
function HasValidDataPath(const ASolver: ISolver): Boolean;
|
||||
function TryGetFirstValidDataPath(const ASolver: ISolver; out ODataFilePath: string): Boolean;
|
||||
function GetInvalidDataPathMessage(const ASolver: ISolver): string;
|
||||
end;
|
||||
|
||||
implementation
|
||||
|
@ -93,19 +96,22 @@ end;
|
|||
|
||||
{ TSolverEngine }
|
||||
|
||||
constructor TSolverEngine.Create(const ARelativeDataPath: string);
|
||||
constructor TSolverEngine.Create(constref ARelativeDataPaths: TStringArray);
|
||||
begin
|
||||
FRelativeDataPath := ARelativeDataPath;
|
||||
if (ARelativeDataPaths = nil) or (Length(ARelativeDataPaths) = 0) then
|
||||
raise EArgumentOutOfRangeException.Create('Must specify at least one data path.');
|
||||
FRelativeDataPaths := ARelativeDataPaths;
|
||||
end;
|
||||
|
||||
procedure TSolverEngine.ProcessData(const ASolver: ISolver);
|
||||
var
|
||||
data: TextFile;
|
||||
s: string;
|
||||
dataFilePath, s: string;
|
||||
begin
|
||||
ASolver.Init;
|
||||
|
||||
AssignFile(data, ConcatPaths([FRelativeDataPath, ASolver.DataFileName]));
|
||||
TryGetFirstValidDataPath(ASolver, dataFilePath);
|
||||
AssignFile(data, dataFilePath);
|
||||
try
|
||||
reset(data);
|
||||
while (not EOF(data)) do
|
||||
|
@ -124,9 +130,14 @@ procedure TSolverEngine.Run(const ASolver: ISolver);
|
|||
begin
|
||||
WriteLn;
|
||||
WriteLn('--- ', ASolver.PuzzleName, ' ---');
|
||||
ProcessData(ASolver);
|
||||
WriteLn('Part 1: ', ASolver.ResultPart1);
|
||||
WriteLn('Part 2: ', ASolver.ResultPart2);
|
||||
if HasValidDataPath(ASolver) then
|
||||
begin
|
||||
ProcessData(ASolver);
|
||||
WriteLn('Part 1: ', ASolver.ResultPart1);
|
||||
WriteLn('Part 2: ', ASolver.ResultPart2);
|
||||
end
|
||||
else
|
||||
WriteLn(GetInvalidDataPathMessage(ASolver));
|
||||
end;
|
||||
|
||||
procedure TSolverEngine.RunAndFree(const ASolver: ISolver);
|
||||
|
@ -135,5 +146,42 @@ begin
|
|||
ASolver.Free;
|
||||
end;
|
||||
|
||||
function TSolverEngine.HasValidDataPath(const ASolver: ISolver): Boolean;
|
||||
var
|
||||
s: string;
|
||||
begin
|
||||
Result := TryGetFirstValidDataPath(ASolver, s);
|
||||
end;
|
||||
|
||||
function TSolverEngine.TryGetFirstValidDataPath(const ASolver: ISolver; out ODataFilePath: string): Boolean;
|
||||
var
|
||||
path: string;
|
||||
begin
|
||||
for path in FRelativeDataPaths do
|
||||
begin
|
||||
ODataFilePath := ConcatPaths([path, ASolver.DataFileName]);
|
||||
if FileExists(ODataFilePath) then
|
||||
begin
|
||||
Result := True;
|
||||
Exit;
|
||||
end;
|
||||
end;
|
||||
|
||||
Result := False;
|
||||
ODataFilePath := '';
|
||||
end;
|
||||
|
||||
function TSolverEngine.GetInvalidDataPathMessage(const ASolver: ISolver): string;
|
||||
var
|
||||
i: Integer;
|
||||
begin
|
||||
Result := 'Cannot find puzzle input file '''
|
||||
+ ExpandFileName(ConcatPaths([FRelativeDataPaths[0], ASolver.DataFileName]));
|
||||
for i := 1 to Length(FRelativeDataPaths) - 1 do
|
||||
Result := Result + ''', or '''
|
||||
+ ExpandFileName(ConcatPaths([FRelativeDataPaths[i], ASolver.DataFileName]));
|
||||
Result := Result + '''. Please download the file content from https://adventofcode.com/2023/';
|
||||
end;
|
||||
|
||||
end.
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
Solutions to the Advent Of Code.
|
||||
Copyright (C) 2023 Stefan Müller
|
||||
Copyright (C) 2024 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
|
||||
|
@ -22,14 +22,78 @@ unit UClumsyCrucible;
|
|||
interface
|
||||
|
||||
uses
|
||||
Classes, SysUtils, USolver;
|
||||
Classes, SysUtils, Generics.Collections, Math, USolver, UCommon;
|
||||
|
||||
type
|
||||
|
||||
{ TAxisData }
|
||||
|
||||
TAxisData = record
|
||||
// The current minimum total heat loss to get from this node to the end if the first step is on this axis.
|
||||
Minimum: Cardinal;
|
||||
// True, if and only if the minimum has been set, i.e. a path has been found from this node to the end, with the
|
||||
// first step on this axis.
|
||||
IsTraversed,
|
||||
// True. if a close node was updated (traversed) on the other axis, such that this minimum might be outdated. This
|
||||
// means that if this node is on the work list, NeedsUpdate is True for at least one of the axes.
|
||||
NeedsUpdate: Boolean;
|
||||
end;
|
||||
|
||||
TAxisId = (axHorizontal, axVertical);
|
||||
|
||||
const
|
||||
CAxisDirections: array[TAxisId] of array[0..1] of PPoint
|
||||
= ((@CDirectionRight, @CDirectionLeft), (@CDirectionDown, @CDirectionUp));
|
||||
COtherAxes: array[TAxisId] of TAxisId = (axVertical, axHorizontal);
|
||||
|
||||
type
|
||||
{ TNode }
|
||||
|
||||
TNode = record
|
||||
Axes: array[TAxisId] of TAxisData;
|
||||
LocalHeatLoss: Byte;
|
||||
end;
|
||||
PNode = ^TNode;
|
||||
|
||||
TNodeArray = array of TNode;
|
||||
TNodeArrays = specialize TList<TNodeArray>;
|
||||
|
||||
TWorkQueue = specialize TQueue<TPoint>;
|
||||
|
||||
{ TNodeMap }
|
||||
|
||||
TNodeMap = class
|
||||
private
|
||||
// Each item in FNodes is a horizontal row of nodes.
|
||||
FNodes: TNodeArrays;
|
||||
FWidth: Integer;
|
||||
FMinStraight, FMaxStraight: Integer;
|
||||
function GetHeight: Integer;
|
||||
function GetNode(APosition: TPoint): TNode;
|
||||
function GetPNode(APosition: TPoint): PNode;
|
||||
function IsPositionInMap(constref APosition: TPoint): Boolean;
|
||||
procedure ClampPositionToMap(var APosition: TPoint);
|
||||
procedure InitWorkQueue(constref AWorkQueue: TWorkQueue);
|
||||
procedure InvalidateNeighbors(constref AWorkQueue: TWorkQueue; const AAxis: TAxisId; constref APosition: TPoint);
|
||||
function FindStepNodeMinimum(const AAxis: TAxisId; constref APosition: TPoint): Cardinal;
|
||||
public
|
||||
property Width: Integer read FWidth;
|
||||
property Height: Integer read GetHeight;
|
||||
constructor Create;
|
||||
destructor Destroy; override;
|
||||
procedure AddNodes(const ALine: string);
|
||||
function FindMinimumPathLength(const AMinStraight, AMaxStraight: Integer): Cardinal;
|
||||
procedure Reset;
|
||||
end;
|
||||
|
||||
{ TClumsyCrucible }
|
||||
|
||||
TClumsyCrucible = class(TSolver)
|
||||
private
|
||||
FMap: TNodeMap;
|
||||
public
|
||||
constructor Create;
|
||||
destructor Destroy; override;
|
||||
procedure ProcessDataLine(const ALine: string); override;
|
||||
procedure Finish; override;
|
||||
function GetDataFileName: string; override;
|
||||
|
@ -38,16 +102,246 @@ type
|
|||
|
||||
implementation
|
||||
|
||||
const
|
||||
CMinStraight = 1;
|
||||
CMaxStraight = 3;
|
||||
CUltraMinStraight = 4;
|
||||
CUltraMaxStraight = 10;
|
||||
|
||||
{ TNodeMap }
|
||||
|
||||
function TNodeMap.GetHeight: Integer;
|
||||
begin
|
||||
Result := FNodes.Count;
|
||||
end;
|
||||
|
||||
function TNodeMap.GetNode(APosition: TPoint): TNode;
|
||||
begin
|
||||
Result := FNodes[APosition.Y][APosition.X];
|
||||
end;
|
||||
|
||||
function TNodeMap.GetPNode(APosition: TPoint): PNode;
|
||||
begin
|
||||
Result := @FNodes[APosition.Y][APosition.X];
|
||||
end;
|
||||
|
||||
function TNodeMap.IsPositionInMap(constref APosition: TPoint): Boolean;
|
||||
begin
|
||||
Result := (0 <= APosition.X) and (APosition.X < Width) and (0 <= APosition.Y) and (APosition.Y < Height);
|
||||
end;
|
||||
|
||||
procedure TNodeMap.ClampPositionToMap(var APosition: TPoint);
|
||||
begin
|
||||
if APosition.X < -1 then
|
||||
APosition.X := -1
|
||||
else if APosition.X > Width then
|
||||
APosition.X := Width;
|
||||
if APosition.Y < -1 then
|
||||
APosition.Y := -1
|
||||
else if APosition.Y > Height then
|
||||
APosition.Y := Height;
|
||||
end;
|
||||
|
||||
procedure TNodeMap.InitWorkQueue(constref AWorkQueue: TWorkQueue);
|
||||
var
|
||||
position: TPoint;
|
||||
last: PNode;
|
||||
axis: TAxisId;
|
||||
begin
|
||||
// Initializes the end node and the work queue with its neighbors.
|
||||
position := Point(Width - 1, Height - 1);
|
||||
last := GetPNode(position);
|
||||
for axis in TAxisId do
|
||||
begin
|
||||
last^.Axes[axis].Minimum := 0;
|
||||
last^.Axes[axis].IsTraversed := True;
|
||||
end;
|
||||
InvalidateNeighbors(AWorkQueue, axHorizontal, position);
|
||||
InvalidateNeighbors(AWorkQueue, axVertical, position);
|
||||
end;
|
||||
|
||||
procedure TNodeMap.InvalidateNeighbors(constref AWorkQueue: TWorkQueue; const AAxis: TAxisId; constref APosition:
|
||||
TPoint);
|
||||
var
|
||||
otherAxis: TAxisId;
|
||||
nodeMinimum: Cardinal;
|
||||
direction: PPoint;
|
||||
neighborPos, stop: TPoint;
|
||||
neighbor: PNode;
|
||||
begin
|
||||
otherAxis := COtherAxes[AAxis];
|
||||
nodeMinimum := GetNode(APosition).Axes[otherAxis].Minimum;
|
||||
|
||||
for direction in CAxisDirections[AAxis] do
|
||||
begin
|
||||
neighborPos := Point(APosition.X + direction^.X * FMinStraight, APosition.Y + direction^.Y * FMinStraight);
|
||||
if IsPositionInMap(neighborPos) then
|
||||
begin
|
||||
stop := Point(APosition.X + direction^.X * (FMaxStraight + 1), APosition.Y + direction^.Y * (FMaxStraight + 1));
|
||||
ClampPositionToMap(stop);
|
||||
while neighborPos <> stop do
|
||||
begin
|
||||
neighbor := GetPNode(neighborPos);
|
||||
if not neighbor^.Axes[AAxis].NeedsUpdate
|
||||
and (not neighbor^.Axes[AAxis].IsTraversed or (neighbor^.Axes[AAxis].Minimum > nodeMinimum)) then
|
||||
begin
|
||||
neighbor^.Axes[AAxis].NeedsUpdate := True;
|
||||
if not neighbor^.Axes[otherAxis].NeedsUpdate then
|
||||
AWorkQueue.Enqueue(neighborPos);
|
||||
end;
|
||||
neighborPos := neighborPos + direction^;
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
|
||||
function TNodeMap.FindStepNodeMinimum(const AAxis: TAxisId; constref APosition: TPoint): Cardinal;
|
||||
var
|
||||
otherAxis: TAxisId;
|
||||
direction: PPoint;
|
||||
acc: Cardinal;
|
||||
neighborPos, start, stop: TPoint;
|
||||
isStartReached: Boolean;
|
||||
neighbor: TNode;
|
||||
begin
|
||||
otherAxis := COtherAxes[AAxis];
|
||||
Result := Cardinal.MaxValue;
|
||||
|
||||
for direction in CAxisDirections[AAxis] do
|
||||
begin
|
||||
acc := 0;
|
||||
isStartReached := False;
|
||||
neighborPos := APosition + direction^;
|
||||
start := Point(APosition.X + direction^.X * FMinStraight, APosition.Y + direction^.Y * FMinStraight);
|
||||
if IsPositionInMap(start) then
|
||||
begin
|
||||
stop := Point(APosition.X + direction^.X * (FMaxStraight + 1), APosition.Y + direction^.Y * (FMaxStraight + 1));
|
||||
ClampPositionToMap(stop);
|
||||
while neighborPos <> stop do
|
||||
begin
|
||||
if neighborPos = start then
|
||||
isStartReached := True;
|
||||
neighbor := GetNode(neighborPos);
|
||||
Inc(acc, neighbor.LocalHeatLoss);
|
||||
if isStartReached and neighbor.Axes[otherAxis].IsTraversed then
|
||||
Result := Min(Result, neighbor.Axes[otherAxis].Minimum + acc);
|
||||
neighborPos := neighborPos + direction^;
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
|
||||
constructor TNodeMap.Create;
|
||||
begin
|
||||
FNodes := TNodeArrays.Create;
|
||||
end;
|
||||
|
||||
destructor TNodeMap.Destroy;
|
||||
begin
|
||||
FNodes.Free;
|
||||
inherited Destroy;
|
||||
end;
|
||||
|
||||
procedure TNodeMap.AddNodes(const ALine: string);
|
||||
var
|
||||
i: Integer;
|
||||
nodes: TNodeArray;
|
||||
axis: TAxisId;
|
||||
begin
|
||||
FWidth := Length(ALine);
|
||||
SetLength(nodes, FWidth);
|
||||
for i := 0 to FWidth - 1 do
|
||||
begin
|
||||
nodes[i].LocalHeatLoss := StrToInt(ALine[i + 1]);
|
||||
for axis in TAxisId do
|
||||
begin
|
||||
nodes[i].Axes[axis].IsTraversed := False;
|
||||
nodes[i].Axes[axis].NeedsUpdate := False;
|
||||
end;
|
||||
end;
|
||||
FNodes.Add(nodes);
|
||||
end;
|
||||
|
||||
function TNodeMap.FindMinimumPathLength(const AMinStraight, AMaxStraight: Integer): Cardinal;
|
||||
var
|
||||
queue: TWorkQueue;
|
||||
position: TPoint;
|
||||
node: PNode;
|
||||
axis: TAxisId;
|
||||
start: TNode;
|
||||
newMinimum: Cardinal;
|
||||
begin
|
||||
FMinStraight := AMinStraight;
|
||||
FMaxStraight := AMaxStraight;
|
||||
|
||||
queue := TWorkQueue.Create;
|
||||
InitWorkQueue(queue);
|
||||
|
||||
// Processes work queue.
|
||||
while queue.Count > 0 do
|
||||
begin
|
||||
position := queue.Dequeue;
|
||||
node := GetPNode(position);
|
||||
|
||||
for axis in TAxisId do
|
||||
if node^.Axes[axis].NeedsUpdate then
|
||||
begin
|
||||
node^.Axes[axis].NeedsUpdate := False;
|
||||
// Finds minimum for one step from this node along this axis.
|
||||
newMinimum := FindStepNodeMinimum(axis, position);
|
||||
if not node^.Axes[axis].IsTraversed or (node^.Axes[axis].Minimum > newMinimum) then
|
||||
begin
|
||||
// Updates this axis minimum and queues update for neighbors on the other axis.
|
||||
node^.Axes[axis].IsTraversed := True;
|
||||
node^.Axes[axis].Minimum := newMinimum;
|
||||
InvalidateNeighbors(queue, COtherAxes[axis], position);
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
|
||||
queue.Free;
|
||||
|
||||
start := GetNode(Point(0, 0));
|
||||
Result := Min(start.Axes[axHorizontal].Minimum, start.Axes[axVertical].Minimum);
|
||||
end;
|
||||
|
||||
procedure TNodeMap.Reset;
|
||||
var
|
||||
i, j: Integer;
|
||||
axis: TAxisId;
|
||||
begin
|
||||
for i := 0 to Width - 1 do
|
||||
for j := 0 to Height - 1 do
|
||||
for axis in TAxisId do
|
||||
begin
|
||||
FNodes[j][i].Axes[axis].IsTraversed := False;
|
||||
FNodes[j][i].Axes[axis].NeedsUpdate := False;
|
||||
end;
|
||||
end;
|
||||
|
||||
{ TClumsyCrucible }
|
||||
|
||||
constructor TClumsyCrucible.Create;
|
||||
begin
|
||||
FMap := TNodeMap.Create;
|
||||
end;
|
||||
|
||||
destructor TClumsyCrucible.Destroy;
|
||||
begin
|
||||
FMap.Free;
|
||||
inherited Destroy;
|
||||
end;
|
||||
|
||||
procedure TClumsyCrucible.ProcessDataLine(const ALine: string);
|
||||
begin
|
||||
|
||||
FMap.AddNodes(ALine);
|
||||
end;
|
||||
|
||||
procedure TClumsyCrucible.Finish;
|
||||
begin
|
||||
|
||||
FPart1 := FMap.FindMinimumPathLength(CMinStraight, CMaxStraight);
|
||||
FMap.Reset;
|
||||
FPart2 := FMap.FindMinimumPathLength(CUltraMinStraight, CUltraMaxStraight);
|
||||
end;
|
||||
|
||||
function TClumsyCrucible.GetDataFileName: string;
|
||||
|
|
|
@ -22,7 +22,7 @@ unit UCosmicExpansion;
|
|||
interface
|
||||
|
||||
uses
|
||||
Classes, SysUtils, Generics.Collections, Math, USolver;
|
||||
Classes, SysUtils, Generics.Collections, Math, USolver, UCommon;
|
||||
|
||||
const
|
||||
CGalaxyChar = '#';
|
||||
|
@ -36,8 +36,8 @@ type
|
|||
TCosmicExpansion = class(TSolver)
|
||||
private
|
||||
FExpansionFactor: Integer;
|
||||
FColumnExpansion, FRowExpansion: specialize TList<Integer>;
|
||||
FGalaxies: specialize TList<TPoint>;
|
||||
FColumnExpansion, FRowExpansion: TIntegerList;
|
||||
FGalaxies: TPoints;
|
||||
procedure InitColumnExpansion(const ASize: Integer);
|
||||
public
|
||||
constructor Create(const AExpansionFactor: Integer = 999999);
|
||||
|
@ -67,9 +67,9 @@ end;
|
|||
constructor TCosmicExpansion.Create(const AExpansionFactor: Integer);
|
||||
begin
|
||||
FExpansionFactor := AExpansionFactor;
|
||||
FColumnExpansion := specialize TList<Integer>.Create;
|
||||
FRowExpansion := specialize TList<Integer>.Create;
|
||||
FGalaxies := specialize TList<TPoint>.Create;
|
||||
FColumnExpansion := TIntegerList.Create;
|
||||
FRowExpansion := TIntegerList.Create;
|
||||
FGalaxies := TPoints.Create;
|
||||
end;
|
||||
|
||||
destructor TCosmicExpansion.Destroy;
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
Solutions to the Advent Of Code.
|
||||
Copyright (C) 2023 Stefan Müller
|
||||
Copyright (C) 2023-2024 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
|
||||
|
@ -22,7 +22,7 @@ unit UFloorWillBeLava;
|
|||
interface
|
||||
|
||||
uses
|
||||
Classes, SysUtils, Generics.Collections, USolver;
|
||||
Classes, SysUtils, Generics.Collections, USolver, UCommon;
|
||||
|
||||
type
|
||||
|
||||
|
@ -37,7 +37,7 @@ type
|
|||
{ TTransition }
|
||||
|
||||
TTransition = record
|
||||
IncomingDirection, OutgoingDirection, SplitDirection: TPoint;
|
||||
IncomingDirection, OutgoingDirection, SplitDirection: PPoint;
|
||||
Tile: Char;
|
||||
EnergyChange: TEnergyState;
|
||||
end;
|
||||
|
@ -73,32 +73,31 @@ type
|
|||
end;
|
||||
|
||||
const
|
||||
CNoDirection: TPoint = (X: 0; Y: 0);
|
||||
CEmptyChar = '.';
|
||||
CTransitions: array of TTransition = (
|
||||
(IncomingDirection: (X: 1; Y: 0); OutgoingDirection: (X: 0; Y: -1); SplitDirection: (X: 0; Y: 0); Tile: '/';
|
||||
(IncomingDirection: @CDirectionRight; OutgoingDirection: @CDirectionUp; SplitDirection: @CNoDirection; Tile: '/';
|
||||
EnergyChange: esWestOrHorizontal),
|
||||
(IncomingDirection: (X: 0; Y: 1); OutgoingDirection: (X: -1; Y: 0); SplitDirection: (X: 0; Y: 0); Tile: '/';
|
||||
(IncomingDirection: @CDirectionDown; OutgoingDirection: @CDirectionLeft; SplitDirection: @CNoDirection; Tile: '/';
|
||||
EnergyChange: esWestOrHorizontal),
|
||||
(IncomingDirection: (X: -1; Y: 0); OutgoingDirection: (X: 0; Y: 1); SplitDirection: (X: 0; Y: 0); Tile: '/';
|
||||
(IncomingDirection: @CDirectionLeft; OutgoingDirection: @CDirectionDown; SplitDirection: @CNoDirection; Tile: '/';
|
||||
EnergyChange: esEastOrVertical),
|
||||
(IncomingDirection: (X: 0; Y: -1); OutgoingDirection: (X: 1; Y: 0); SplitDirection: (X: 0; Y: 0); Tile: '/';
|
||||
(IncomingDirection: @CDirectionUp; OutgoingDirection: @CDirectionRight; SplitDirection: @CNoDirection; Tile: '/';
|
||||
EnergyChange: esEastOrVertical),
|
||||
(IncomingDirection: (X: 1; Y: 0); OutgoingDirection: (X: 0; Y: 1); SplitDirection: (X: 0; Y: 0); Tile: '\';
|
||||
(IncomingDirection: @CDirectionRight; OutgoingDirection: @CDirectionDown; SplitDirection: @CNoDirection; Tile: '\';
|
||||
EnergyChange: esWestOrHorizontal),
|
||||
(IncomingDirection: (X: 0; Y: 1); OutgoingDirection: (X: 1; Y: 0); SplitDirection: (X: 0; Y: 0); Tile: '\';
|
||||
(IncomingDirection: @CDirectionDown; OutgoingDirection: @CDirectionRight; SplitDirection: @CNoDirection; Tile: '\';
|
||||
EnergyChange: esEastOrVertical),
|
||||
(IncomingDirection: (X: -1; Y: 0); OutgoingDirection: (X: 0; Y: -1); SplitDirection: (X: 0; Y: 0); Tile: '\';
|
||||
(IncomingDirection: @CDirectionLeft; OutgoingDirection: @CDirectionUp; SplitDirection: @CNoDirection; Tile: '\';
|
||||
EnergyChange: esEastOrVertical),
|
||||
(IncomingDirection: (X: 0; Y: -1); OutgoingDirection: (X: -1; Y: 0); SplitDirection: (X: 0; Y: 0); Tile: '\';
|
||||
(IncomingDirection: @CDirectionUp; OutgoingDirection: @CDirectionLeft; SplitDirection: @CNoDirection; Tile: '\';
|
||||
EnergyChange: esWestOrHorizontal),
|
||||
(IncomingDirection: (X: 1; Y: 0); OutgoingDirection: (X: 0; Y: -1); SplitDirection: (X: 0; Y: 1); Tile: '|';
|
||||
(IncomingDirection: @CDirectionRight; OutgoingDirection: @CDirectionUp; SplitDirection: @CDirectionDown; Tile: '|';
|
||||
EnergyChange: esBoth),
|
||||
(IncomingDirection: (X: -1; Y: 0); OutgoingDirection: (X: 0; Y: -1); SplitDirection: (X: 0; Y: 1); Tile: '|';
|
||||
(IncomingDirection: @CDirectionLeft; OutgoingDirection: @CDirectionUp; SplitDirection: @CDirectionDown; Tile: '|';
|
||||
EnergyChange: esBoth),
|
||||
(IncomingDirection: (X: 0; Y: 1); OutgoingDirection: (X: -1; Y: 0); SplitDirection: (X: 1; Y: 0); Tile: '-';
|
||||
(IncomingDirection: @CDirectionDown; OutgoingDirection: @CDirectionLeft; SplitDirection: @CDirectionRight; Tile: '-';
|
||||
EnergyChange: esBoth),
|
||||
(IncomingDirection: (X: 0; Y: -1); OutgoingDirection: (X: -1; Y: 0); SplitDirection: (X: 1; Y: 0); Tile: '-';
|
||||
(IncomingDirection: @CDirectionUp; OutgoingDirection: @CDirectionLeft; SplitDirection: @CDirectionRight; Tile: '-';
|
||||
EnergyChange: esBoth)
|
||||
);
|
||||
|
||||
|
@ -193,11 +192,11 @@ begin
|
|||
begin
|
||||
// Checks the current position for direction changes and splits.
|
||||
for transition in CTransitions do
|
||||
if (transition.IncomingDirection = ABeam.Direction) and (transition.Tile = GetTile(ABeam.Position)) then
|
||||
if (transition.IncomingDirection^ = ABeam.Direction) and (transition.Tile = GetTile(ABeam.Position)) then
|
||||
begin
|
||||
if transition.SplitDirection <> CNoDirection then
|
||||
stack.Push(GetNewBeam(ABeam.Position + transition.SplitDirection, transition.SplitDirection));
|
||||
ABeam.Direction := transition.OutgoingDirection;
|
||||
if transition.SplitDirection^ <> CNoDirection then
|
||||
stack.Push(GetNewBeam(ABeam.Position + transition.SplitDirection^, transition.SplitDirection^));
|
||||
ABeam.Direction := transition.OutgoingDirection^;
|
||||
energyChange := transition.EnergyChange;
|
||||
Break;
|
||||
end;
|
||||
|
@ -269,7 +268,7 @@ end;
|
|||
|
||||
function TFloorWillBeLava.GetDataFileName: string;
|
||||
begin
|
||||
Result := 'floor_will_be_lava.txt';
|
||||
Result := 'the_floor_will_be_lava.txt';
|
||||
end;
|
||||
|
||||
function TFloorWillBeLava.GetPuzzleName: string;
|
||||
|
|
|
@ -255,7 +255,7 @@ end;
|
|||
|
||||
function TGiveSeedFertilizer.GetDataFileName: string;
|
||||
begin
|
||||
Result := 'give_seed_fertilizer.txt';
|
||||
Result := 'if_you_give_a_seed_a_fertilizer.txt';
|
||||
end;
|
||||
|
||||
function TGiveSeedFertilizer.GetPuzzleName: string;
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
Solutions to the Advent Of Code.
|
||||
Copyright (C) 2023 Stefan Müller
|
||||
Copyright (C) 2024 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
|
||||
|
@ -22,14 +22,41 @@ unit ULavaductLagoon;
|
|||
interface
|
||||
|
||||
uses
|
||||
Classes, SysUtils, USolver;
|
||||
Classes, SysUtils, StrUtils, Generics.Collections, Math, USolver;
|
||||
|
||||
type
|
||||
|
||||
{ TDig }
|
||||
|
||||
TDig = class
|
||||
Direction, Length: Integer;
|
||||
end;
|
||||
|
||||
TDigs = specialize TObjectList<TDig>;
|
||||
|
||||
{ TDigSite }
|
||||
|
||||
TDigSite = class
|
||||
private
|
||||
FDigs: TDigs;
|
||||
FArea, FTrench: Int64;
|
||||
function CheckMergeDigs(const ADigIndex: Integer): Cardinal;
|
||||
public
|
||||
procedure AddDig(constref ADig: TDig);
|
||||
procedure CollapseUTurns;
|
||||
function CalcFinalArea: Int64;
|
||||
constructor Create;
|
||||
destructor Destroy; override;
|
||||
end;
|
||||
|
||||
{ TLavaductLagoon }
|
||||
|
||||
TLavaductLagoon = class(TSolver)
|
||||
FSite1, FSite2: TDigSite;
|
||||
procedure AddDig(const ALine: string);
|
||||
public
|
||||
constructor Create;
|
||||
destructor Destroy; override;
|
||||
procedure ProcessDataLine(const ALine: string); override;
|
||||
procedure Finish; override;
|
||||
function GetDataFileName: string; override;
|
||||
|
@ -38,16 +65,173 @@ type
|
|||
|
||||
implementation
|
||||
|
||||
{ TDigSite }
|
||||
|
||||
function TDigSite.CheckMergeDigs(const ADigIndex: Integer): Cardinal;
|
||||
begin
|
||||
Result := 0;
|
||||
if (0 <= ADigIndex) and (ADigIndex < FDigs.Count - 1) then
|
||||
begin
|
||||
// Appends two consecutive digs, if they go in the same direction.
|
||||
if FDigs[ADigIndex].Direction = FDigs[ADigIndex + 1].Direction then
|
||||
begin
|
||||
FDigs[ADigIndex].Length := FDigs[ADigIndex].Length + FDigs[ADigIndex + 1].Length;
|
||||
FDigs.Delete(ADigIndex + 1);
|
||||
Inc(Result);
|
||||
end
|
||||
// Otherwise, checks if the directions are opposite.
|
||||
else if Abs(FDigs[ADigIndex].Direction - FDigs[ADigIndex + 1].Direction) = 2 then
|
||||
begin
|
||||
// Recurses, if the opposite digs cancel each other out.
|
||||
if FDigs[ADigIndex].Length = FDigs[ADigIndex + 1].Length then
|
||||
begin
|
||||
FDigs.DeleteRange(ADigIndex, 2);
|
||||
Inc(Result, 2);
|
||||
Inc(Result, CheckMergeDigs(ADigIndex - 1));
|
||||
end
|
||||
else begin
|
||||
// Otherwise, subtracts the opposite directions.
|
||||
if FDigs[ADigIndex].Length > FDigs[ADigIndex + 1].Length then
|
||||
FDigs[ADigIndex].Length := FDigs[ADigIndex].Length - FDigs[ADigIndex + 1].Length
|
||||
else begin
|
||||
FDigs[ADigIndex].Length := FDigs[ADigIndex + 1].Length - FDigs[ADigIndex].Length;
|
||||
FDigs[ADigIndex].Direction := FDigs[ADigIndex + 1].Direction;
|
||||
end;
|
||||
FDigs.Delete(ADigIndex + 1);
|
||||
Inc(Result);
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure TDigSite.AddDig(constref ADig: TDig);
|
||||
begin
|
||||
FDigs.Add(ADig);
|
||||
Inc(FTrench, ADig.Length);
|
||||
|
||||
// The new dig might have to be merged with the preceeding dig.
|
||||
CheckMergeDigs(FDigs.Count - 2);
|
||||
end;
|
||||
|
||||
procedure TDigSite.CollapseUTurns;
|
||||
var
|
||||
i, side, backtrack, shorter: Integer;
|
||||
begin
|
||||
// If there is a U-turn, it must involve the last dig, otherwise we would have collapsed it in an earlier call
|
||||
// already. Therefore we check the last three digs first.
|
||||
i := FDigs.Count - 3;
|
||||
while (0 <= i) and (i + 2 < FDigs.Count) do
|
||||
begin
|
||||
// We check if three consecutive digs starting at i form a U-turn. It's enough to check whether the first and the
|
||||
// third have different directions because they must be parallel.
|
||||
if FDigs[i].Direction <> FDigs[i + 2].Direction then
|
||||
begin
|
||||
// Either right or left U-turns enclose an area inside the trench. We do not need to know which one is which and
|
||||
// just assume here that the right U-turns enclose an area outside and therefore negate it. If it's the other way
|
||||
// around, then we can simply negate the end result.
|
||||
side := FDigs[i + 1].Length;
|
||||
if (FDigs[i].Direction + 1 = FDigs[i + 1].Direction)
|
||||
or (FDigs[i + 1].Direction + 1 = FDigs[i + 2].Direction) then
|
||||
side := -side;
|
||||
|
||||
// Updates the shortened dig and collapses the U-turn.
|
||||
backtrack := 0;
|
||||
shorter := Min(FDigs[i].Length, FDigs[i + 2].Length);
|
||||
Inc(FArea, side * shorter);
|
||||
if FDigs[i + 2].Length = shorter then
|
||||
begin
|
||||
FDigs.Delete(i + 2);
|
||||
Inc(backtrack);
|
||||
Inc(backtrack, CheckMergeDigs(i + 1));
|
||||
end
|
||||
else
|
||||
Dec(FDigs[i + 2].Length, shorter);
|
||||
|
||||
if FDigs[i].Length = shorter then
|
||||
begin
|
||||
FDigs.Delete(i);
|
||||
Inc(backtrack);
|
||||
Inc(backtrack, CheckMergeDigs(i - 1));
|
||||
end
|
||||
else
|
||||
Dec(FDigs[i].Length, shorter);
|
||||
Dec(i, backtrack);
|
||||
end
|
||||
else
|
||||
Inc(i);
|
||||
end;
|
||||
end;
|
||||
|
||||
function TDigSite.CalcFinalArea: Int64;
|
||||
begin
|
||||
// If the area is negative, then outside and inside have to be "swapped", which Abs() achieves here.
|
||||
// When collapsing the U-turns, only the area up to the imaginary middle line of the dug trench is considered, so half
|
||||
// of the full length of the dug trench + 1 has to be added to include the whole trench in the area calculation.
|
||||
Result := Abs(FArea) + FTrench div 2 + 1;
|
||||
end;
|
||||
|
||||
constructor TDigSite.Create;
|
||||
begin
|
||||
FDigs := TDigs.Create;
|
||||
end;
|
||||
|
||||
destructor TDigSite.Destroy;
|
||||
begin
|
||||
FDigs.Free;
|
||||
inherited Destroy;
|
||||
end;
|
||||
|
||||
{ TLavaductLagoon }
|
||||
|
||||
procedure TLavaductLagoon.AddDig(const ALine: string);
|
||||
var
|
||||
split: TStringArray;
|
||||
dig: TDig;
|
||||
begin
|
||||
dig := TDig.Create;
|
||||
split := ALine.Split([' ']);
|
||||
case split[0] of
|
||||
'R': dig.Direction := 0;
|
||||
'D': dig.Direction := 1;
|
||||
'L': dig.Direction := 2;
|
||||
'U': dig.Direction := 3;
|
||||
end;
|
||||
dig.Length := StrToInt(split[1]);
|
||||
FSite1.AddDig(dig);
|
||||
|
||||
dig := TDig.Create;
|
||||
dig.Direction := StrToInt(split[2][8]);
|
||||
dig.Length := Hex2Dec(Copy(split[2], 3, 5));
|
||||
FSite2.AddDig(dig);
|
||||
end;
|
||||
|
||||
constructor TLavaductLagoon.Create;
|
||||
begin
|
||||
FSite1 := TDigSite.Create;
|
||||
FSite2 := TDigSite.Create;
|
||||
end;
|
||||
|
||||
destructor TLavaductLagoon.Destroy;
|
||||
begin
|
||||
FSite1.Free;
|
||||
FSite2.Free;
|
||||
inherited Destroy;
|
||||
end;
|
||||
|
||||
procedure TLavaductLagoon.ProcessDataLine(const ALine: string);
|
||||
begin
|
||||
|
||||
AddDig(ALine);
|
||||
FSite1.CollapseUTurns;
|
||||
FSite2.CollapseUTurns;
|
||||
end;
|
||||
|
||||
procedure TLavaductLagoon.Finish;
|
||||
begin
|
||||
|
||||
// If the area is negative, then outside and inside have to be "swapped", which Abs() achieves here.
|
||||
// When collapsing the U-turns, only the area up to the imaginary middle line of the dug trench is considered, so half
|
||||
// of the full length of the dug trench + 1 has to be added to include the whole trench in the area calculation.
|
||||
FPart1 := FSite1.CalcFinalArea;
|
||||
FPart2 := FSite2.CalcFinalArea;
|
||||
end;
|
||||
|
||||
function TLavaductLagoon.GetDataFileName: string;
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
Solutions to the Advent Of Code.
|
||||
Copyright (C) 2023 Stefan Müller
|
||||
Copyright (C) 2023-2024 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
|
||||
|
@ -22,23 +22,26 @@ unit ULongWalk;
|
|||
interface
|
||||
|
||||
uses
|
||||
Classes, SysUtils, Generics.Collections, USolver;
|
||||
Classes, SysUtils, Generics.Collections, USolver, UCommon;
|
||||
|
||||
type
|
||||
TPoints = specialize TList<TPoint>;
|
||||
|
||||
TCrossing = class;
|
||||
|
||||
TPathSelectionState = (pssNone, pssIncluded, pssExcluded);
|
||||
|
||||
{ TPath }
|
||||
|
||||
TPath = class
|
||||
private
|
||||
FEnd: TCrossing;
|
||||
FStart, FEnd: TCrossing;
|
||||
FLength: Integer;
|
||||
FSelected: TPathSelectionState;
|
||||
public
|
||||
property StartCrossing: TCrossing read FStart;
|
||||
property EndCrossing: TCrossing read FEnd;
|
||||
property Length: Integer read FLength;
|
||||
constructor Create(const ALength: Integer; const AEnd: TCrossing);
|
||||
property Selected: TPathSelectionState read FSelected write FSelected;
|
||||
constructor Create(const ALength: Integer; const AStart, AEnd: TCrossing);
|
||||
end;
|
||||
|
||||
TPaths = specialize TObjectList<TPath>;
|
||||
|
@ -57,20 +60,51 @@ type
|
|||
TCrossing = class
|
||||
private
|
||||
FPosition: TPoint;
|
||||
FOutPaths: TPaths;
|
||||
FOutPaths, FPaths: TPaths;
|
||||
FDistance: Integer;
|
||||
FNotExcludedDegree: Integer;
|
||||
public
|
||||
property Position: TPoint read FPosition;
|
||||
property OutPaths: TPaths read FOutPaths;
|
||||
property Paths: TPaths read FPaths;
|
||||
property Distance: Integer read FDistance write FDistance;
|
||||
property NotExcludedDegree: Integer read FNotExcludedDegree write FNotExcludedDegree;
|
||||
function CalcNextPickIndex(const AMinIndex: Integer): Integer;
|
||||
constructor Create(constref APosition: TPoint);
|
||||
destructor Destroy; override;
|
||||
procedure AddOutPath(const AOutPath: TPath);
|
||||
procedure AddInPath(const AInPath: TPath);
|
||||
end;
|
||||
|
||||
TCrossings = specialize TObjectList<TCrossing>;
|
||||
TCrossingStack = specialize TStack<TCrossing>;
|
||||
|
||||
TPathChoiceResult = (pcrContinue, pcrTargetReached, pcrTargetUnreachable, pcrNoMinimum);
|
||||
|
||||
{ TPathChoice }
|
||||
|
||||
TPathChoice = class
|
||||
private
|
||||
FPrevious: TPathChoice;
|
||||
FPickIndex: Integer;
|
||||
FPick: TPath;
|
||||
FEndCrossing: TCrossing;
|
||||
FAutoExcludes: TPaths;
|
||||
FExcludeCost: Int64;
|
||||
FIncludeCost: Int64;
|
||||
public
|
||||
property PickIndex: Integer read FPickIndex;
|
||||
property EndCrossing: TCrossing read FEndCrossing;
|
||||
property IncludeCost: Int64 read FIncludeCost;
|
||||
function Apply(constref ATargetCrossing: TCrossing; const AExcludeCostLimit: Int64): TPathChoiceResult;
|
||||
procedure Revert;
|
||||
constructor Create(const AStartCrossing: TCrossing);
|
||||
constructor Create(const APickIndex: Integer; const APrevious: TPathChoice = nil);
|
||||
destructor Destroy; override;
|
||||
end;
|
||||
|
||||
TPathChoiceStack = specialize TStack<TPathChoice>;
|
||||
|
||||
{ TLongWalk }
|
||||
|
||||
TLongWalk = class(TSolver)
|
||||
|
@ -78,12 +112,15 @@ type
|
|||
FLines: TStringList;
|
||||
FPaths: TPaths;
|
||||
FCrossings, FWaitingForOtherInPath: TCrossings;
|
||||
FStart: TCrossing;
|
||||
FPathLengthSum: Int64;
|
||||
function GetPosition(constref APoint: TPoint): Char;
|
||||
procedure ProcessPaths;
|
||||
procedure StepPath(const AStartPositionQueue: TPathStartQueue);
|
||||
function FindOrCreateCrossing(constref APosition: TPoint; const AStartPositionQueue: TPathStartQueue): TCrossing;
|
||||
// Treats the graph as directed for part 1.
|
||||
procedure FindLongestPath;
|
||||
// Treats the graph as undirected for part 2.
|
||||
procedure FindLongestPathIgnoreSlopes;
|
||||
public
|
||||
constructor Create;
|
||||
destructor Destroy; override;
|
||||
|
@ -93,44 +130,173 @@ type
|
|||
function GetPuzzleName: string; override;
|
||||
end;
|
||||
|
||||
TDirection = (dirRight, dirDown, dirLeft, dirUp);
|
||||
|
||||
const
|
||||
CPathChar = '.';
|
||||
CForestChar = '#';
|
||||
CRightSlopeChar = '>';
|
||||
CDownSlopeChar = 'v';
|
||||
CDirections: array[TDirection] of TPoint = ((X: 1; Y: 0), (X: 0; Y: 1), (X: -1; Y: 0), (X: 0; Y: -1));
|
||||
CStartReverseDirection: TPoint = (X: 0; Y: -1);
|
||||
|
||||
implementation
|
||||
|
||||
{ TPath }
|
||||
|
||||
constructor TPath.Create(const ALength: Integer; const AEnd: TCrossing);
|
||||
constructor TPath.Create(const ALength: Integer; const AStart, AEnd: TCrossing);
|
||||
begin
|
||||
FLength := ALength;
|
||||
FStart := AStart;
|
||||
FEnd := AEnd;
|
||||
FSelected := pssNone;
|
||||
end;
|
||||
|
||||
{ TCrossing }
|
||||
|
||||
function TCrossing.CalcNextPickIndex(const AMinIndex: Integer): Integer;
|
||||
begin
|
||||
Result := AMinIndex;
|
||||
while (Result < FPaths.Count) and (FPaths[Result].Selected <> pssNone) do
|
||||
Inc(Result);
|
||||
end;
|
||||
|
||||
constructor TCrossing.Create(constref APosition: TPoint);
|
||||
begin
|
||||
FPosition := APosition;
|
||||
FOutPaths := TPaths.Create(False);
|
||||
FPaths := TPaths.Create(False);
|
||||
FDistance := 0;
|
||||
FNotExcludedDegree := 0;
|
||||
end;
|
||||
|
||||
destructor TCrossing.Destroy;
|
||||
begin
|
||||
FOutPaths.Free;
|
||||
FPaths.Free;
|
||||
inherited Destroy;
|
||||
end;
|
||||
|
||||
procedure TCrossing.AddOutPath(const AOutPath: TPath);
|
||||
begin
|
||||
FOutPaths.Add(AOutPath);
|
||||
FPaths.Add(AOutPath);
|
||||
Inc(FNotExcludedDegree);
|
||||
end;
|
||||
|
||||
procedure TCrossing.AddInPath(const AInPath: TPath);
|
||||
begin
|
||||
FPaths.Add(AInPath);
|
||||
Inc(FNotExcludedDegree);
|
||||
end;
|
||||
|
||||
{ TPathChoice }
|
||||
|
||||
function TPathChoice.Apply(constref ATargetCrossing: TCrossing; const AExcludeCostLimit: Int64): TPathChoiceResult;
|
||||
var
|
||||
path: TPath;
|
||||
excludeStack: TCrossingStack;
|
||||
crossing, otherCrossing: TCrossing;
|
||||
begin
|
||||
Result := pcrContinue;
|
||||
|
||||
// Includes the selected path (edge) and checks whether target has been reached.
|
||||
FPick.Selected := pssIncluded;
|
||||
if FEndCrossing = ATargetCrossing then
|
||||
Result := pcrTargetReached
|
||||
else if FPrevious <> nil then
|
||||
begin
|
||||
// If the target has not been reached, starts at the starting crossing (which is the same as FPRevious.EndCrossing)
|
||||
// and recursively excludes other connected paths (edges).
|
||||
excludeStack := TCrossingStack.Create;
|
||||
excludeStack.Push(FPrevious.EndCrossing);
|
||||
|
||||
while excludeStack.Count > 0 do
|
||||
begin
|
||||
crossing := excludeStack.Pop;
|
||||
for path in crossing.Paths do
|
||||
if path.Selected = pssNone then
|
||||
begin
|
||||
// Checks whether the path (edge) to the target crossing has been excluded and if so exits. The input data
|
||||
// should be such that there is only one such path.
|
||||
// The last crossing is always an end, never a start of a path (edge).
|
||||
if path.EndCrossing = ATargetCrossing then
|
||||
begin
|
||||
Result := pcrTargetUnreachable;
|
||||
excludeStack.Free;
|
||||
Exit;
|
||||
end
|
||||
else begin
|
||||
// Excludes the path (edge).
|
||||
path.Selected := pssExcluded;
|
||||
crossing.NotExcludedDegree := crossing.NotExcludedDegree - 1;
|
||||
FAutoExcludes.Add(path);
|
||||
FExcludeCost := FExcludeCost + path.Length;
|
||||
|
||||
// Checks if this choice is worse than the current best.
|
||||
if FExcludeCost >= AExcludeCostLimit then
|
||||
begin
|
||||
Result := pcrNoMinimum;
|
||||
excludeStack.Free;
|
||||
Exit;
|
||||
end;
|
||||
|
||||
// Finds the crossing on the other side, updates it, and possibly pushes it for recursion.
|
||||
if crossing = path.StartCrossing then
|
||||
otherCrossing := path.EndCrossing
|
||||
else
|
||||
otherCrossing := path.StartCrossing;
|
||||
|
||||
otherCrossing.NotExcludedDegree := otherCrossing.NotExcludedDegree - 1;
|
||||
if otherCrossing.NotExcludedDegree < 2 then
|
||||
excludeStack.Push(otherCrossing);
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
|
||||
excludeStack.Free;
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure TPathChoice.Revert;
|
||||
var
|
||||
path: TPath;
|
||||
begin
|
||||
FPick.Selected := pssNone;
|
||||
for path in FAutoExcludes do begin
|
||||
path.Selected := pssNone;
|
||||
path.StartCrossing.NotExcludedDegree := path.StartCrossing.NotExcludedDegree + 1;
|
||||
path.EndCrossing.NotExcludedDegree := path.EndCrossing.NotExcludedDegree + 1;
|
||||
end;
|
||||
end;
|
||||
|
||||
constructor TPathChoice.Create(const AStartCrossing: TCrossing);
|
||||
begin
|
||||
FPrevious := nil;
|
||||
FPickIndex := 0;
|
||||
FPick := AStartCrossing.Paths[FPickIndex];
|
||||
FEndCrossing := FPick.EndCrossing;
|
||||
FExcludeCost := 0;
|
||||
FIncludeCost := FPick.FLength;
|
||||
FAutoExcludes := TPaths.Create(False);
|
||||
end;
|
||||
|
||||
constructor TPathChoice.Create(const APickIndex: Integer; const APrevious: TPathChoice);
|
||||
begin
|
||||
FPrevious := APrevious;
|
||||
FPickIndex := APickIndex;
|
||||
FPick := FPrevious.EndCrossing.Paths[FPickIndex];
|
||||
|
||||
if FPick.StartCrossing = FPrevious.EndCrossing then
|
||||
FEndCrossing := FPick.EndCrossing
|
||||
else
|
||||
FEndCrossing := FPick.StartCrossing;
|
||||
|
||||
FExcludeCost := FPrevious.FExcludeCost;
|
||||
FIncludeCost := FPrevious.FIncludeCost + FPick.FLength;
|
||||
FAutoExcludes := TPaths.Create(False);
|
||||
end;
|
||||
|
||||
destructor TPathChoice.Destroy;
|
||||
begin
|
||||
FAutoExcludes.Free;
|
||||
inherited Destroy;
|
||||
end;
|
||||
|
||||
{ TLongWalk }
|
||||
|
@ -142,23 +308,24 @@ end;
|
|||
|
||||
procedure TLongWalk.ProcessPaths;
|
||||
var
|
||||
stack: TPathStartQueue;
|
||||
queue: TPathStartQueue;
|
||||
pathStart: TPathStart;
|
||||
begin
|
||||
stack := TPathStartQueue.Create;
|
||||
pathStart.Position := FStart.Position;
|
||||
pathStart.Crossing := FStart;
|
||||
pathStart.ReverseDirection := CStartReverseDirection;
|
||||
stack.Enqueue(pathStart);
|
||||
while stack.Count > 0 do
|
||||
StepPath(stack);
|
||||
stack.Free;
|
||||
queue := TPathStartQueue.Create;
|
||||
pathStart.Crossing := FCrossings.First;
|
||||
pathStart.Position := FCrossings.First.Position;
|
||||
pathStart.ReverseDirection := CDirectionUp;
|
||||
queue.Enqueue(pathStart);
|
||||
while queue.Count > 0 do
|
||||
StepPath(queue);
|
||||
queue.Free;
|
||||
end;
|
||||
|
||||
procedure TLongWalk.StepPath(const AStartPositionQueue: TPathStartQueue);
|
||||
var
|
||||
start: TPathStart;
|
||||
new, direction: TPoint;
|
||||
new: TPoint;
|
||||
pdirection: PPoint;
|
||||
c: Char;
|
||||
len: Integer;
|
||||
oneMore, stop: Boolean;
|
||||
|
@ -166,20 +333,20 @@ var
|
|||
path: TPath;
|
||||
begin
|
||||
start := AStartPositionQueue.Dequeue;
|
||||
len := 1;
|
||||
if start.Crossing <> FStart then
|
||||
len := 0;
|
||||
if start.Crossing <> FCrossings.First then
|
||||
Inc(len);
|
||||
oneMore := False;
|
||||
stop := False;
|
||||
repeat
|
||||
for direction in CDirections do
|
||||
if direction <> start.ReverseDirection then
|
||||
for pdirection in CPCardinalDirections do
|
||||
if pdirection^ <> start.ReverseDirection then
|
||||
begin
|
||||
new := start.Position + direction;
|
||||
new := start.Position + pdirection^;
|
||||
c := GetPosition(new);
|
||||
if c <> CForestChar then
|
||||
begin
|
||||
start.ReverseDirection := Point(-direction.X, -direction.Y);
|
||||
start.ReverseDirection := Point(-pdirection^.X, -pdirection^.Y);
|
||||
start.Position := new;
|
||||
|
||||
if oneMore or (new.Y = FLines.Count - 1) then
|
||||
|
@ -195,9 +362,11 @@ begin
|
|||
until stop;
|
||||
|
||||
crossing := FindOrCreateCrossing(start.Position, AStartPositionQueue);
|
||||
path := TPath.Create(len, crossing);
|
||||
path := TPath.Create(len, start.Crossing, crossing);
|
||||
FPathLengthSum := FPathLengthSum + path.FLength;
|
||||
FPaths.Add(path);
|
||||
start.Crossing.AddOutPath(path);
|
||||
crossing.AddInPath(path);
|
||||
end;
|
||||
|
||||
// Crossing with multiple (two) entries will only be added to FCrossings once both in-paths have been processed. This
|
||||
|
@ -233,8 +402,8 @@ begin
|
|||
Result := TCrossing.Create(APosition);
|
||||
|
||||
// Checks if the new crossing has multiple entries.
|
||||
if (GetPosition(APosition + CDirections[dirLeft]) = CRightSlopeChar)
|
||||
and (GetPosition(APosition + CDirections[dirUp]) = CDownSlopeChar) then
|
||||
if (GetPosition(APosition + CDirectionLeft) = CRightSlopeChar)
|
||||
and (GetPosition(APosition + CDirectionUp) = CDownSlopeChar) then
|
||||
FWaitingForOtherInPath.Add(Result)
|
||||
else
|
||||
FCrossings.Add(Result);
|
||||
|
@ -244,22 +413,24 @@ begin
|
|||
// Adds the exits of this crossing to the stack as starts for new paths.
|
||||
pathStart.Crossing := Result;
|
||||
|
||||
pathStart.Position := APosition + CDirections[dirRight];
|
||||
pathStart.Position := APosition + CDirectionRight;
|
||||
if GetPosition(pathStart.Position) = CRightSlopeChar then
|
||||
begin
|
||||
pathStart.ReverseDirection := CDirections[dirLeft];
|
||||
pathStart.ReverseDirection := CDirectionLeft;
|
||||
AStartPositionQueue.Enqueue(pathStart);
|
||||
end;
|
||||
|
||||
pathStart.Position := APosition + CDirections[dirDown];
|
||||
pathStart.Position := APosition + CDirectionDown;
|
||||
if GetPosition(pathStart.Position) = CDownSlopeChar then
|
||||
begin
|
||||
pathStart.ReverseDirection := CDirections[dirUp];
|
||||
pathStart.ReverseDirection := CDirectionUp;
|
||||
AStartPositionQueue.Enqueue(pathStart);
|
||||
end;
|
||||
end
|
||||
end;
|
||||
|
||||
// In a directed graph with a topological ordering on the crossings (vertices), the maximum distance can be computed
|
||||
// simply by traversing the crossings in that order and calculating the maximum locally.
|
||||
procedure TLongWalk.FindLongestPath;
|
||||
var
|
||||
crossing: TCrossing;
|
||||
|
@ -269,17 +440,82 @@ begin
|
|||
begin
|
||||
for path in crossing.OutPaths do
|
||||
if path.EndCrossing.Distance < crossing.Distance + path.Length then
|
||||
path.EndCrossing.Distance := crossing.Distance + path.Length;
|
||||
path.EndCrossing.Distance := crossing.Distance + path.Length + 1;
|
||||
end;
|
||||
FPart1 := FCrossings.Last.Distance;
|
||||
end;
|
||||
|
||||
// For the undirected graph, we are running a DFS for the second to last crossing (vertex) with backtracking to find the
|
||||
// minimum of excluded crossings and paths.
|
||||
procedure TLongWalk.FindLongestPathIgnoreSlopes;
|
||||
var
|
||||
pickIndex: Integer;
|
||||
choice: TPathChoice;
|
||||
stack: TPathChoiceStack;
|
||||
minExcludeCost, newExcludeCost: Int64;
|
||||
begin
|
||||
minExcludeCost := FPathLengthSum + FCrossings.Count - 1 - FPart1;
|
||||
|
||||
// Prepares the first pick, which is the only path connected to the first crossing.
|
||||
stack := TPathChoiceStack.Create;
|
||||
choice := TPathChoice.Create(FCrossings.First);
|
||||
choice.Apply(FCrossings.Last, minExcludeCost);
|
||||
stack.Push(choice);
|
||||
|
||||
// Runs a DFS for last crossing with backtracking, trying to find the minimum cost of excluded paths (i.e. edges).
|
||||
pickIndex := -1;
|
||||
while stack.Count > 0 do
|
||||
begin
|
||||
// Chooses next path.
|
||||
pickIndex := stack.Peek.EndCrossing.CalcNextPickIndex(pickIndex + 1);
|
||||
if pickIndex < stack.Peek.EndCrossing.Paths.Count then
|
||||
begin
|
||||
choice := TPathChoice.Create(pickIndex, stack.Peek);
|
||||
case choice.Apply(FCrossings.Last, minExcludeCost) of
|
||||
// Continues DFS, target has not yet been reached.
|
||||
pcrContinue: begin
|
||||
stack.Push(choice);
|
||||
pickIndex := -1;
|
||||
Continue;
|
||||
end;
|
||||
// Updates minimum and backtracks last choice, after target has been reached.
|
||||
pcrTargetReached: begin
|
||||
// Calculates new exclude cost based on path length sum and the choice's include cost. This effectively
|
||||
// accounts for the "undecided" paths (edges) as well. Note that this does not actually need the choice's
|
||||
// exclude costs, these are only required for the early exit in TPathChoice.Apply().
|
||||
newExcludeCost := FCrossings.Count - stack.Count - 2 + FPathLengthSum - choice.IncludeCost;
|
||||
if minExcludeCost > newExcludeCost then
|
||||
minExcludeCost := newExcludeCost;
|
||||
|
||||
choice.Revert;
|
||||
choice.Free;
|
||||
end;
|
||||
// Backtracks last choice, after target has been excluded or exclude costs ran over the current best.
|
||||
pcrTargetUnreachable, pcrNoMinimum: begin
|
||||
choice.Revert;
|
||||
choice.Free;
|
||||
end;
|
||||
end;
|
||||
end
|
||||
else begin
|
||||
choice := stack.Pop;
|
||||
pickIndex := choice.PickIndex;
|
||||
choice.Revert;
|
||||
choice.Free;
|
||||
end;
|
||||
end;
|
||||
stack.Free;
|
||||
|
||||
FPart2 := FPathLengthSum - minExcludeCost + FCrossings.Count - 1;
|
||||
end;
|
||||
|
||||
constructor TLongWalk.Create;
|
||||
begin
|
||||
FLines := TStringList.Create;
|
||||
FPaths := TPaths.Create;
|
||||
FCrossings := TCrossings.Create;
|
||||
FWaitingForOtherInPath := TCrossings.Create(False);
|
||||
FPathLengthSum := 0;
|
||||
end;
|
||||
|
||||
destructor TLongWalk.Destroy;
|
||||
|
@ -294,10 +530,7 @@ end;
|
|||
procedure TLongWalk.ProcessDataLine(const ALine: string);
|
||||
begin
|
||||
if FLines.Count = 0 then
|
||||
begin
|
||||
FStart := TCrossing.Create(Point(ALine.IndexOf(CPathChar) + 1, 0));
|
||||
FCrossings.Add(FStart);
|
||||
end;
|
||||
FCrossings.Add(TCrossing.Create(Point(ALine.IndexOf(CPathChar) + 1, 0)));
|
||||
FLines.Add(ALine);
|
||||
end;
|
||||
|
||||
|
@ -305,11 +538,12 @@ procedure TLongWalk.Finish;
|
|||
begin
|
||||
ProcessPaths;
|
||||
FindLongestPath;
|
||||
FindLongestPathIgnoreSlopes;
|
||||
end;
|
||||
|
||||
function TLongWalk.GetDataFileName: string;
|
||||
begin
|
||||
Result := 'long_walk.txt';
|
||||
Result := 'a_long_walk.txt';
|
||||
end;
|
||||
|
||||
function TLongWalk.GetPuzzleName: string;
|
||||
|
|
|
@ -22,7 +22,7 @@ unit UNeverTellMeTheOdds;
|
|||
interface
|
||||
|
||||
uses
|
||||
Classes, SysUtils, Generics.Collections, Math, matrix, USolver, UNumberTheory, UBigInt;
|
||||
Classes, SysUtils, Generics.Collections, Math, USolver, UBigInt, UPolynomial, UPolynomialRoots;
|
||||
|
||||
type
|
||||
|
||||
|
@ -30,26 +30,15 @@ type
|
|||
|
||||
THailstone = class
|
||||
public
|
||||
Position, Velocity: Tvector3_extended;
|
||||
P0, P1, P2: Int64;
|
||||
V0, V1, V2: Integer;
|
||||
constructor Create(const ALine: string);
|
||||
constructor Create(const APosition, AVelocity: Tvector3_extended);
|
||||
constructor Create;
|
||||
end;
|
||||
|
||||
THailstones = specialize TObjectList<THailstone>;
|
||||
|
||||
{ TFirstCollisionPolynomial }
|
||||
|
||||
TFirstCollisionPolynomial = class
|
||||
private
|
||||
FA: array[0..10] of TBigInt;
|
||||
FH: array[0..6] of TBigInt;
|
||||
procedure NormalizeCoefficients;
|
||||
public
|
||||
procedure Init(constref AHailstone1, AHailstone2, AHailstone3: THailstone; const t_0, t_1, t_2: Int64);
|
||||
function EvaluateAt(const AT0: Int64): TBigInt;
|
||||
function CalcPositiveIntegerRoot: Int64;
|
||||
function CalcT1(const AT0: Int64): Int64;
|
||||
end;
|
||||
TInt64Array = array of Int64;
|
||||
|
||||
{ TNeverTellMeTheOdds }
|
||||
|
||||
|
@ -57,10 +46,13 @@ type
|
|||
private
|
||||
FMin, FMax: Int64;
|
||||
FHailstones: THailstones;
|
||||
FA: array[0..10] of Int64;
|
||||
FH: array[0..6] of Int64;
|
||||
function AreIntersecting(constref AHailstone1, AHailstone2: THailstone): Boolean;
|
||||
procedure FindRockThrow(const AIndex1, AIndex2, AIndex3: Integer);
|
||||
function FindRockThrow(const AIndex0, AIndex1, AIndex2: Integer): Int64;
|
||||
procedure CalcCollisionPolynomials(constref AHailstone0, AHailstone1, AHailstone2: THailstone; out OPolynomial0,
|
||||
OPolynomial1: TBigIntPolynomial);
|
||||
function CalcRockThrowCollisionOptions(constref AHailstone0, AHailstone1, AHailstone2: THailstone): TInt64Array;
|
||||
function ValidateRockThrow(constref AHailstone0, AHailstone1, AHailstone2: THailstone; const AT0, AT1: Int64):
|
||||
Int64;
|
||||
public
|
||||
constructor Create(const AMin: Int64 = 200000000000000; const AMax: Int64 = 400000000000000);
|
||||
destructor Destroy; override;
|
||||
|
@ -70,10 +62,6 @@ type
|
|||
function GetPuzzleName: string; override;
|
||||
end;
|
||||
|
||||
const
|
||||
CIterationThreshold = 0.00001;
|
||||
CEpsilon = 0.0000000001;
|
||||
|
||||
implementation
|
||||
|
||||
{ THailstone }
|
||||
|
@ -83,437 +71,17 @@ var
|
|||
split: TStringArray;
|
||||
begin
|
||||
split := ALine.Split([',', '@']);
|
||||
Position.init(
|
||||
StrToFloat(Trim(split[0])),
|
||||
StrToFloat(Trim(split[1])),
|
||||
StrToFloat(Trim(split[2])));
|
||||
Velocity.init(
|
||||
StrToFloat(Trim(split[3])),
|
||||
StrToFloat(Trim(split[4])),
|
||||
StrToFloat(Trim(split[5])));
|
||||
P0 := StrToInt64(Trim(split[0]));
|
||||
P1 := StrToInt64(Trim(split[1]));
|
||||
P2 := StrToInt64(Trim(split[2]));
|
||||
V0 := StrToInt(Trim(split[3]));
|
||||
V1 := StrToInt(Trim(split[4]));
|
||||
V2 := StrToInt(Trim(split[5]));
|
||||
end;
|
||||
|
||||
constructor THailstone.Create(const APosition, AVelocity: Tvector3_extended);
|
||||
constructor THailstone.Create;
|
||||
begin
|
||||
Position := APosition;
|
||||
Velocity := AVelocity;
|
||||
end;
|
||||
|
||||
{ TFirstCollisionPolynomial }
|
||||
|
||||
procedure TFirstCollisionPolynomial.NormalizeCoefficients;
|
||||
var
|
||||
shift: Integer;
|
||||
i: Low(FA)..High(FA);
|
||||
//gcd: TBigInt;
|
||||
begin
|
||||
// Eliminates zero constant term.
|
||||
shift := 0;
|
||||
while (shift <= High(FA)) and (FA[shift] = 0) do
|
||||
Inc(shift);
|
||||
|
||||
if shift <= High(FA) then
|
||||
begin
|
||||
if shift > 0 then
|
||||
begin
|
||||
for i := Low(FA) to High(FA) - shift do
|
||||
FA[i] := FA[i + shift];
|
||||
for i := High(FA) - shift + 1 to High(FA) do
|
||||
FA[i] := 0;
|
||||
end;
|
||||
|
||||
//// Finds GCD of all coefficients.
|
||||
//gcd := FA[Low(FA)];
|
||||
//for i := Low(FA) + 1 to High(FA) do
|
||||
// if FA[i] <> 0 then
|
||||
// gcd := TNumberTheory.GreatestCommonDivisor(gcd, FA[i]);
|
||||
//WriteLn('GCD: ', gcd);
|
||||
//
|
||||
//for i := Low(FA) to High(FA) do
|
||||
// FA[i] := FA[i] div gcd;
|
||||
end;
|
||||
|
||||
//WriteLn('(', FA[10], ') * x^10 + (', FA[9], ') * x^9 + (', FA[8], ') * x^8 + (', FA[7], ') * x^7 + (',
|
||||
// FA[6], ') * x^6 + (', FA[5], ') * x^5 + (', FA[4], ') * x^4 + (', FA[3], ') * x^3 + (', FA[2], ') * x^2 + (',
|
||||
// FA[1], ') * x + (', FA[0], ')');
|
||||
end;
|
||||
|
||||
procedure TFirstCollisionPolynomial.Init(constref AHailstone1, AHailstone2, AHailstone3: THailstone; const t_0, t_1,
|
||||
t_2: Int64);
|
||||
var
|
||||
P_00, P_01, P_02, P_10, P_11, P_12, P_20, P_21, P_22,
|
||||
V_00, V_01, V_02, V_10, V_11, V_12, V_20, V_21, V_22: Int64;
|
||||
k: array[0..139] of TBigInt;
|
||||
// For debug calculations
|
||||
act, a_1, a_2, b_0, b_1, c_0, c_1, d_0, d_1, e_0, e_1, f_0, f_1, f_2: Int64;
|
||||
begin
|
||||
// Solving this non-linear equation system, with velocities V_i and start positions P_i:
|
||||
// V_0 * t_0 + P_0 = V_x * t_0 + P_x
|
||||
// V_1 * t_1 + P_1 = V_x * t_1 + P_x
|
||||
// V_2 * t_2 + P_2 = V_x * t_2 + P_x
|
||||
// Which gives:
|
||||
// P_x = (V_0 - V_x) * t_0 + P_0
|
||||
// V_x = (V_0 * t_0 - V_1 * t_1 + P_0 - P_1) / (t_0 - t_1)
|
||||
// And with vertex components:
|
||||
// 1: 0 = (t_1 - t_0) * (V_00 * t_0 - V_20 * t_2 + P_00 - P_20) - (t_2 - t_0) * (V_00 * t_0 - V_10 * t_1 + P_00 - P_10)
|
||||
// 2: t_1 = (((V_01 - V_21) * t_2 + P_11 - P_21) * t_0 + (P_01 - P_11) * t_2)
|
||||
// / ((V_01 - V_11) * t_0 + (V_11 - V_21) * t_2 + P_01 - P_21)
|
||||
// 3: t_2 = (((V_02 - V_12) * t_1 + P_22 - P_12) * t_0 + (P_02 - P_22) * t_1)
|
||||
// / ((V_02 - V_22) * t_0 + (V_22 - V_12) * t_1 + P_02 - P_12)
|
||||
// for t_0, t_1, t_2 not pairwise equal.
|
||||
// With some substitutions depending only on t_0 this gives
|
||||
// 1: 0 = (t_1 - t_0) * (f_2 - V_20 * t_2) - (t_2 - t_0) * (f_1 - V_10 * t_1)
|
||||
// 2: t_1 = (b_0 + b_1 * t_2) / (c_0 + c_1 * t_2)
|
||||
// 3: t_2 = (d_0 + d_1 * t_1) / (e_0 + e_1 * t_1)
|
||||
// And 3 in 2 gives:
|
||||
// 4: g_2 * t_1^2 - g_1 * t_1 - g_0 = 0
|
||||
// Then, with 4 and 3 in 1 and lengthy calculations with many substitutions (see constants k below, now independent of
|
||||
// t_0), the following polynomial can be constructed, with t_0 being a positive integer root of this polynomial.
|
||||
// y = a_10 * x^10 + a_9 * x^9 + ... + a_0
|
||||
|
||||
P_00 := Round(AHailstone1.Position.data[0]);
|
||||
P_01 := Round(AHailstone1.Position.data[1]);
|
||||
P_02 := Round(AHailstone1.Position.data[2]);
|
||||
P_10 := Round(AHailstone2.Position.data[0]);
|
||||
P_11 := Round(AHailstone2.Position.data[1]);
|
||||
P_12 := Round(AHailstone2.Position.data[2]);
|
||||
P_20 := Round(AHailstone3.Position.data[0]);
|
||||
P_21 := Round(AHailstone3.Position.data[1]);
|
||||
P_22 := Round(AHailstone3.Position.data[2]);
|
||||
V_00 := Round(AHailstone1.Velocity.data[0]);
|
||||
V_01 := Round(AHailstone1.Velocity.data[1]);
|
||||
V_02 := Round(AHailstone1.Velocity.data[2]);
|
||||
V_10 := Round(AHailstone2.Velocity.data[0]);
|
||||
V_11 := Round(AHailstone2.Velocity.data[1]);
|
||||
V_12 := Round(AHailstone2.Velocity.data[2]);
|
||||
V_20 := Round(AHailstone3.Velocity.data[0]);
|
||||
V_21 := Round(AHailstone3.Velocity.data[1]);
|
||||
V_22 := Round(AHailstone3.Velocity.data[2]);
|
||||
|
||||
k[0] := P_00 - P_20;
|
||||
k[1] := P_00 - P_10;
|
||||
k[2] := P_11 - P_21;
|
||||
k[3] := P_01 - P_11;
|
||||
k[4] := P_01 - P_21;
|
||||
k[5] := P_22 - P_12;
|
||||
k[6] := P_02 - P_22;
|
||||
k[7] := P_02 - P_12;
|
||||
k[8] := V_11 - V_21;
|
||||
k[9] := V_22 - V_12;
|
||||
k[10] := V_01 - V_21;
|
||||
k[11] := V_01 - V_11;
|
||||
k[12] := V_02 - V_12;
|
||||
k[13] := V_02 - V_22;
|
||||
|
||||
FH[0] := k[11] * k[9] + k[8] * k[12];
|
||||
FH[1] := k[4] * k[9] + k[8] * k[6];
|
||||
FH[2] := k[11] * k[13] - k[10] * k[12];
|
||||
FH[3] := k[11] * k[7] + k[4] * k[13] + k[8] * k[5] - k[2] * k[9] - k[10] * k[6] - k[3] * k[12];
|
||||
FH[4] := k[4] * k[7] - k[3] * k[6];
|
||||
FH[5] := k[10] * k[5] + k[2] * k[13];
|
||||
FH[6] := k[3] * k[5] + k[2] * k[7];
|
||||
|
||||
k[14] := V_00 * k[9] - V_20 * k[12];
|
||||
k[15] := k[0] * k[9] - V_20 * k[6];
|
||||
k[16] := V_00 * k[13];
|
||||
k[17] := V_00 * k[7] + k[0] * k[13] - V_20 * k[5];
|
||||
k[18] := k[0] * k[7];
|
||||
k[19] := k[5] - k[7];
|
||||
k[20] := 2 * FH[2] * FH[3];
|
||||
k[21] := FH[3] * FH[3];
|
||||
k[22] := k[21] + 2 * FH[2] * FH[4];
|
||||
k[23] := 2 * FH[3] * FH[4];
|
||||
k[24] := 2 * FH[0] * FH[1];
|
||||
k[25] := FH[0] * FH[0]; // KILL?
|
||||
k[26] := FH[5] * k[25]; // KILL?
|
||||
k[126] := FH[5] * FH[0];
|
||||
k[127] := FH[5] * FH[1] + FH[6] * FH[0];
|
||||
k[128] := FH[6] * FH[1];
|
||||
k[27] := FH[5] * k[24] + FH[6] * k[25]; // KILL?
|
||||
k[28] := FH[1] * FH[1]; // KILL?
|
||||
k[29] := FH[5] * k[28] + FH[6] * k[24]; // KILL?
|
||||
k[30] := FH[6] * k[28]; // KILL?
|
||||
k[31] := FH[2] * FH[2];
|
||||
k[132] := k[20] + 4 * k[126];
|
||||
k[133] := k[22] + 4 * k[127];
|
||||
k[134] := k[23] + 4 * k[128];
|
||||
k[32] := k[31] + 4 * k[26]; // KILL?
|
||||
k[33] := k[20] + 4 * k[27]; // KILL?
|
||||
k[34] := k[22] + 4 * k[29]; // KILL?
|
||||
k[35] := k[23] + 4 * k[30]; // KILL?
|
||||
k[36] := k[31] + 2 * k[26]; // KILL?
|
||||
k[37] := k[20] + 2 * k[27]; // KILL?
|
||||
k[38] := k[22] + 2 * k[29]; // KILL?
|
||||
k[39] := k[23] + 2 * k[30]; // KILL?
|
||||
k[137] := k[20] + 2 * k[126];
|
||||
k[138] := k[22] + 2 * k[127];
|
||||
k[139] := k[23] + 2 * k[128];
|
||||
k[40] := k[14] + V_10 * (k[12] - k[9]);
|
||||
k[41] := k[15] + V_10 * k[6];
|
||||
k[42] := k[16] - k[14] - V_10 * k[13] - (k[12] - k[9]) * V_00;
|
||||
k[43] := k[17] - k[15] + V_10 * k[19] - (k[12] - k[9]) * k[1] - k[6] * V_00;
|
||||
k[44] := k[18] - k[6] * k[1];
|
||||
k[45] := k[42] * FH[0] - k[40] * FH[2];
|
||||
k[46] := k[42] * FH[1] + k[43] * FH[0] - k[41] * FH[2] - k[40] * FH[3];
|
||||
k[47] := k[43] * FH[1] + k[44] * FH[0] - k[41] * FH[3] - k[40] * FH[4];
|
||||
k[48] := k[44] * FH[1] - k[41] * FH[4];
|
||||
k[49] := k[42] * FH[2];
|
||||
k[50] := k[40] * k[31] - k[49] * FH[0];
|
||||
k[51] := k[42] * FH[3] + k[43] * FH[2];
|
||||
k[52] := k[40] * k[137] + k[41] * k[31] - k[51] * FH[0] - k[49] * FH[1];
|
||||
k[53] := k[42] * FH[4] + k[43] * FH[3] + k[44] * FH[2];
|
||||
k[54] := k[40] * k[138] + k[41] * k[137] - k[53] * FH[0] - k[51] * FH[1];
|
||||
k[55] := k[43] * FH[4] + k[44] * FH[3];
|
||||
k[56] := k[40] * k[139] + k[41] * k[138] - k[55] * FH[0] - k[53] * FH[1];
|
||||
k[57] := k[44] * FH[4];
|
||||
k[58] := FH[4] * FH[4];
|
||||
k[59] := k[40] * k[58] + k[41] * k[139] - k[57] * FH[0] - k[55] * FH[1];
|
||||
k[60] := k[41] * k[58] - k[57] * FH[1];
|
||||
k[61] := k[13] * V_00 - k[16];
|
||||
k[62] := 2 * k[25] * k[61];
|
||||
k[63] := k[13] * k[1] - k[19] * V_00 - k[17];
|
||||
k[64] := 2 * (k[24] * k[61] + k[25] * k[63]);
|
||||
k[65] := - k[19] * k[1] - k[18];
|
||||
k[66] := 2 * (k[28] * k[61] + k[24] * k[63] + k[25] * k[65]);
|
||||
k[67] := 2 * (k[28] * k[63] + k[24] * k[65]);
|
||||
k[68] := 2 * k[28] * k[65];
|
||||
k[69] := k[50] + k[62];
|
||||
k[70] := k[52] + k[64];
|
||||
k[71] := k[54] + k[66];
|
||||
k[72] := k[56] + k[67];
|
||||
k[73] := k[59] + k[68];
|
||||
k[74] := k[45] * k[45];
|
||||
k[75] := 2 * k[45] * k[46];
|
||||
k[76] := k[46] * k[46] + 2 * k[45] * k[47];
|
||||
k[77] := 2 * (k[45] * k[48] + k[46] * k[47]);
|
||||
k[78] := k[47] * k[47] + 2 * k[46] * k[48];
|
||||
k[79] := 2 * k[47] * k[48];
|
||||
k[80] := k[48] * k[48];
|
||||
|
||||
FA[0] := k[58] * k[80] - k[60] * k[60];
|
||||
FA[1] := k[134] * k[80] + k[58] * k[79] - 2 * k[73] * k[60];
|
||||
FA[2] := k[133] * k[80] + k[134] * k[79] + k[58] * k[78] - k[73] * k[73] - 2 * k[72] * k[60];
|
||||
FA[3] := k[133] * k[79] + k[134] * k[78] + k[58] * k[77] + k[132] * k[80]
|
||||
- 2 * (k[71] * k[60] + k[72] * k[73]);
|
||||
FA[4] := k[31] * k[80] + k[133] * k[78] + k[134] * k[77] + k[58] * k[76] + k[132] * k[79] - k[72] * k[72]
|
||||
- 2 * (k[70] * k[60] + k[71] * k[73]);
|
||||
FA[5] := k[31] * k[79] + k[133] * k[77] + k[134] * k[76] + k[58] * k[75] + k[132] * k[78]
|
||||
- 2 * (k[69] * k[60] + k[70] * k[73] + k[71] * k[72]);
|
||||
FA[6] := k[31] * k[78] + k[133] * k[76] + k[134] * k[75] + k[58] * k[74] + k[132] * k[77] - k[71] * k[71]
|
||||
- 2 * (k[69] * k[73] + k[70] * k[72]);
|
||||
FA[7] := k[31] * k[77] + k[133] * k[75] + k[134] * k[74] + k[132] * k[76] - 2 * (k[69] * k[72] + k[70] * k[71]);
|
||||
FA[8] := k[31] * k[76] + k[132] * k[75] + k[133] * k[74] - k[70] * k[70] - 2 * k[69] * k[71];
|
||||
FA[9] := k[31] * k[75] + k[132] * k[74] - 2 * k[69] * k[70];
|
||||
FA[10] := k[31] * k[74] - k[69] * k[69];
|
||||
|
||||
// Debug calculations
|
||||
//a_1 := V_00 * t_0 + P_00 - P_20;
|
||||
//a_2 := V_00 * t_0 + P_00 - P_10;
|
||||
//b_0 := (P_11 - P_21) * t_0;
|
||||
//b_1 := (V_01 - V_21) * t_0 + P_01 - P_11;
|
||||
//c_0 := (V_01 - V_11) * t_0 + P_01 - P_21;
|
||||
//c_1 := V_11 - V_21;
|
||||
//d_0 := (P_22 - P_12) * t_0;
|
||||
//d_1 := (V_02 - V_12) * t_0 + P_02 - P_22;
|
||||
//e_0 := (V_02 - V_22) * t_0 + P_02 - P_12;
|
||||
//e_1 := V_22 - V_12;
|
||||
//f_2 := c_0 * e_1 + c_1 * d_1;
|
||||
//f_1 := c_0 * e_0 + c_1 * d_0 - b_0 * e_1 - b_1 * d_1;
|
||||
//f_0 := b_1 * d_0 + b_0 * e_0;
|
||||
//
|
||||
//act := f_2 * t_1 * t_1 + f_1 * t_1 - f_0;
|
||||
//Write('debug10: ', 0 = act, ' ');
|
||||
//
|
||||
//if f_2 <> 0 then
|
||||
//begin
|
||||
// act := Round(- f_1 / (2 * f_2) + Sqrt((f_1 / (2 * f_2)) * (f_1 / (2 * f_2)) + f_0 / f_2));
|
||||
// Write('debug15: ', t_1 = act);
|
||||
// act := Round(- f_1 / (2 * f_2) - Sqrt((f_1 / (2 * f_2)) * (f_1 / (2 * f_2)) + f_0 / f_2));
|
||||
// Write(' OR ', t_1 = act, ' ');
|
||||
//end;
|
||||
//
|
||||
//act := (e_0 + e_1 * t_1) * t_2 - (d_0 + d_1 * t_1);
|
||||
//Write('debug20: ', 0 = act, ' ');
|
||||
//
|
||||
//act := (a_1 * e_1 - V_20 * d_1 + V_10 * (d_1 - e_1 * t_0)) * t_1 * t_1
|
||||
// + (a_1 * e_0 - V_20 * d_0 - t_0 * (a_1 * e_1 - V_20 * d_1) - (d_1 - e_1 * t_0) * a_2 + V_10 * (d_0 - e_0 * t_0)) * t_1
|
||||
// + t_0 * (V_20 * d_0 - a_1 * e_0) + (e_0 * t_0 - d_0) * a_2;
|
||||
//Write('debug30: ', 0 = act, ' ');
|
||||
//
|
||||
//act := Round((a_1 * e_1 - V_20 * d_1 + V_10 * (d_1 - e_1 * t_0)) * (f_1 * f_1 + 2 * f_0 * f_2 - f_1 * Sqrt(f_1 * f_1 + 4 * f_0 * f_2))
|
||||
// + (a_1 * e_0 - V_20 * d_0 - t_0 * (a_1 * e_1 - V_20 * d_1) - (d_1 - e_1 * t_0) * a_2 + V_10 * (d_0 - e_0 * t_0)) * (- f_1 * f_2 + f_2 * Sqrt(f_1 * f_1 + 4 * f_0 * f_2))
|
||||
// + t_0 * (V_20 * d_0 - a_1 * e_0) * 2 * f_2 * f_2 + (e_0 * t_0 - d_0) * a_2 * 2 * f_2 * f_2);
|
||||
//Write('debug40: ', 0 = act, ' ');
|
||||
//
|
||||
//Write('debug41: ',
|
||||
// a_1 * k[9] - V_20 * d_1
|
||||
// = k[14] * t_0 + k[15], ' ');
|
||||
//Write('debug42: ',
|
||||
// d_1 - k[9] * t_0
|
||||
// = (k[12] - k[9]) * t_0 + k[6], ' ');
|
||||
//Write('debug43: ',
|
||||
// a_1 * e_0 - V_20 * d_0
|
||||
// = k[16] * t_0 * t_0 + k[17] * t_0 + k[18], ' ');
|
||||
//Write('debug44: ',
|
||||
// d_0 - e_0 * t_0
|
||||
// = - k[13] * t_0 * t_0 + k[19] * t_0, ' ');
|
||||
//Write('debug45: ',
|
||||
// f_1 * f_1
|
||||
// = FH[2] * FH[2] * t_0 * t_0 * t_0 * t_0 + k[20] * t_0 * t_0 * t_0 + k[22] * t_0 * t_0 + k[23] * t_0 + FH[4] * FH[4], ' ');
|
||||
//Write('debug46: ',
|
||||
// f_2 * f_2
|
||||
// = FH[0] * FH[0] * t_0 * t_0 + k[24] * t_0 + FH[1] * FH[1], ' ');
|
||||
//Write('debug47: ',
|
||||
// f_0 * f_2
|
||||
// = k[126] * t_0 * t_0 * t_0 + k[127] * t_0 * t_0 + k[128] * t_0, ' ');
|
||||
//Write('debug48: ',
|
||||
// f_1 * f_1 + 4 * f_0 * f_2
|
||||
// = k[31] * t_0 * t_0 * t_0 * t_0 + k[132] * t_0 * t_0 * t_0 + k[133] * t_0 * t_0 + k[134] * t_0 + k[58], ' ');
|
||||
//Write('debug49: ',
|
||||
// f_1 * f_1 + 2 * f_0 * f_2
|
||||
// = k[31] * t_0 * t_0 * t_0 * t_0 + k[137] * t_0 * t_0 * t_0 + k[138] * t_0 * t_0 + k[139] * t_0 + k[58], ' ');
|
||||
//
|
||||
//act := Round((k[14] * t_0 + k[15] + V_10 * ((k[12] - k[9]) * t_0 + k[6])) * (k[31] * t_0 * t_0 * t_0 * t_0 + k[137] * t_0 * t_0 * t_0 + k[138] * t_0 * t_0 + k[139] * t_0 + k[58] - f_1 * sqrt(f_1 * f_1 + 4 * f_0 * f_2))
|
||||
// + (k[16] * t_0 * t_0 + k[17] * t_0 + k[18] - t_0 * (k[14] * t_0 + k[15]) - ((k[12] - k[9]) * t_0 + k[6]) * a_2 - V_10 * (k[13] * t_0 * t_0 - k[19] * t_0)) * (- f_1 * f_2 + f_2 * sqrt(f_1 * f_1 + 4 * f_0 * f_2))
|
||||
// - 2 * t_0 * (k[16] * t_0 * t_0 + k[17] * t_0 + k[18]) * (FH[0] * FH[0] * t_0 * t_0 + k[24] * t_0 + FH[1] * FH[1]) + 2 * (k[13] * t_0 * t_0 - k[19] * t_0) * a_2 * (FH[0] * FH[0] * t_0 * t_0 + k[24] * t_0 + FH[1] * FH[1]));
|
||||
//Write('debug50: ', 0 = act, ' ');
|
||||
//
|
||||
//Write('debug53: ',
|
||||
// 0 = Round((k[40] * t_0 + k[41]) * (k[31] * t_0 * t_0 * t_0 * t_0 + k[137] * t_0 * t_0 * t_0 + k[138] * t_0 * t_0 + k[139] * t_0 + k[58] - f_1 * sqrt(f_1 * f_1 + 4 * f_0 * f_2))
|
||||
// + ((k[16] - k[14] - V_10 * k[13] - (k[12] - k[9]) * V_00) * t_0 * t_0 + (k[17] - k[15] + V_10 * k[19] - (k[12] - k[9]) * k[1] - k[6] * V_00) * t_0 + k[18] - k[6] * k[1]) * (- f_1 * f_2 + f_2 * sqrt(f_1 * f_1 + 4 * f_0 * f_2))
|
||||
// - 2 * t_0 * (k[16] * t_0 * t_0 + k[17] * t_0 + k[18]) * (FH[0] * FH[0] * t_0 * t_0 + k[24] * t_0 + FH[1] * FH[1]) + 2 * (k[13] * t_0 * t_0 - k[19] * t_0) * a_2 * (FH[0] * FH[0] * t_0 * t_0 + k[24] * t_0 + FH[1] * FH[1])),
|
||||
// ' ');
|
||||
//
|
||||
//Write('debug55: ',
|
||||
// 0 = Round((k[40] * t_0 + k[41]) * (k[31] * t_0 * t_0 * t_0 * t_0 + k[137] * t_0 * t_0 * t_0 + k[138] * t_0 * t_0 + k[139] * t_0 + k[58])
|
||||
// - (k[40] * t_0 + k[41]) * f_1 * sqrt(f_1 * f_1 + 4 * f_0 * f_2)
|
||||
// + (k[42] * t_0 * t_0 + k[43] * t_0 + k[44]) * (- f_1 * f_2 + f_2 * sqrt(f_1 * f_1 + 4 * f_0 * f_2))
|
||||
// - 2 * t_0 * (k[16] * t_0 * t_0 + k[17] * t_0 + k[18]) * (FH[0] * FH[0] * t_0 * t_0 + k[24] * t_0 + FH[1] * FH[1]) + 2 * (k[13] * t_0 * t_0 - k[19] * t_0) * a_2 * (FH[0] * FH[0] * t_0 * t_0 + k[24] * t_0 + FH[1] * FH[1])),
|
||||
// ' ');
|
||||
//
|
||||
//Write('debug70: ',
|
||||
// 0 = Round(((k[42] * t_0 * t_0 + k[43] * t_0 + k[44]) * (FH[0] * t_0 + FH[1]) - (k[40] * t_0 + k[41]) * (FH[2] * t_0 * t_0 + FH[3] * t_0 + FH[4])) * sqrt(f_1 * f_1 + 4 * f_0 * f_2))
|
||||
// + (k[40] * t_0 + k[41]) * (k[31] * t_0 * t_0 * t_0 * t_0 + k[137] * t_0 * t_0 * t_0 + k[138] * t_0 * t_0 + k[139] * t_0 + k[58])
|
||||
// - (k[42] * t_0 * t_0 + k[43] * t_0 + k[44]) * (FH[2] * t_0 * t_0 + FH[3] * t_0 + FH[4]) * (FH[0] * t_0 + FH[1])
|
||||
// - 2 * t_0 * (k[16] * t_0 * t_0 + k[17] * t_0 + k[18]) * (FH[0] * FH[0] * t_0 * t_0 + k[24] * t_0 + FH[1] * FH[1]) + 2 * (k[13] * t_0 * t_0 - k[19] * t_0) * (V_00 * t_0 + k[1]) * (FH[0] * FH[0] * t_0 * t_0 + k[24] * t_0 + FH[1] * FH[1]),
|
||||
// ' ');
|
||||
//
|
||||
// Write('debug73: ',
|
||||
// 0 = Round((
|
||||
// (k[42] * FH[0] - k[40] * FH[2]) * t_0 * t_0 * t_0
|
||||
// + (k[42] * FH[1] + k[43] * FH[0] - k[41] * FH[2] - k[40] * FH[3]) * t_0 * t_0
|
||||
// + (k[43] * FH[1] + k[44] * FH[0] - k[41] * FH[3] - k[40] * FH[4]) * t_0
|
||||
// + k[44] * FH[1] - k[41] * FH[4]
|
||||
// ) * sqrt(f_1 * f_1 + 4 * f_0 * f_2))
|
||||
// + (k[40] * k[31] - k[42] * FH[2] * FH[0]) * t_0 * t_0 * t_0 * t_0 * t_0
|
||||
// + (k[40] * k[137] + k[41] * k[31] - k[42] * FH[3] * FH[0] - k[43] * FH[2] * FH[0] - k[42] * FH[2] * FH[1]) * t_0 * t_0 * t_0 * t_0
|
||||
// + (k[40] * k[138] + k[41] * k[137] - k[42] * FH[4] * FH[0] - k[43] * FH[3] * FH[0] - k[44] * FH[2] * FH[0] - k[42] * FH[3] * FH[1] - k[43] * FH[2] * FH[1]) * t_0 * t_0 * t_0
|
||||
// + (k[40] * k[139] + k[41] * k[138] - k[43] * FH[4] * FH[0] - k[44] * FH[3] * FH[0] - k[42] * FH[4] * FH[1] - k[43] * FH[3] * FH[1] - k[44] * FH[2] * FH[1]) * t_0 * t_0
|
||||
// + (k[40] * k[58] + k[41] * k[139] - k[44] * FH[4] * FH[0] - k[43] * FH[4] * FH[1] - k[44] * FH[3] * FH[1]) * t_0
|
||||
// + k[41] * k[58] - k[44] * FH[4] * FH[1]
|
||||
// + 2 * (k[13] * V_00 * FH[0] * FH[0] - k[16] * FH[0] * FH[0]) * t_0 * t_0 * t_0 * t_0 * t_0
|
||||
// + 2 * (k[13] * V_00 * k[24] + k[13] * k[1] * FH[0] * FH[0] - k[19] * V_00 * FH[0] * FH[0] - k[16] * k[24] - k[17] * FH[0] * FH[0]) * t_0 * t_0 * t_0 * t_0
|
||||
// + 2 * (k[13] * V_00 * FH[1] * FH[1] + k[13] * k[1] * k[24] - k[19] * V_00 * k[24] - k[19] * k[1] * FH[0] * FH[0] - k[16] * FH[1] * FH[1] - k[17] * k[24] - k[18] * FH[0] * FH[0]) * t_0 * t_0 * t_0
|
||||
// + 2 * (k[13] * k[1] * FH[1] * FH[1] - k[19] * V_00 * FH[1] * FH[1] - k[19] * k[1] * k[24] - k[17] * FH[1] * FH[1] - k[18] * k[24]) * t_0 * t_0
|
||||
// + 2 * (- k[19] * k[1] * FH[1] * FH[1] - k[18] * FH[1] * FH[1]) * t_0,
|
||||
// ' ');
|
||||
//
|
||||
// Write('debug78: ',
|
||||
// 0 = Round((k[45] * t_0 * t_0 * t_0 + k[46] * t_0 * t_0 + k[47] * t_0 + k[48]) * sqrt(f_1 * f_1 + 4 * f_0 * f_2))
|
||||
// + (k[50] + k[62]) * t_0 * t_0 * t_0 * t_0 * t_0 + (k[52] + k[64]) * t_0 * t_0 * t_0 * t_0 + (k[54] + k[66]) * t_0 * t_0 * t_0 + (k[56] + k[67]) * t_0 * t_0 + (k[59] + k[68]) * t_0 + k[60],
|
||||
// ' ');
|
||||
//
|
||||
// Write('debug80: ',
|
||||
// 0 = Round((k[45] * t_0 * t_0 * t_0 + k[46] * t_0 * t_0 + k[47] * t_0 + k[48]) * sqrt(k[31] * t_0 * t_0 * t_0 * t_0 + k[132] * t_0 * t_0 * t_0 + k[133] * t_0 * t_0 + k[134] * t_0 + k[58])
|
||||
// + k[69] * t_0 * t_0 * t_0 * t_0 * t_0 + k[70] * t_0 * t_0 * t_0 * t_0 + k[71] * t_0 * t_0 * t_0 + k[72] * t_0 * t_0 + k[73] * t_0 + k[60]),
|
||||
// ' ');
|
||||
// WriteLn;
|
||||
// WriteLn(' 0 = ((', k[45], ') * x^3 + (', k[46], ') * x^2 + (', k[47], ') * x + (', k[48], ')) * sqrt((', k[31], ') * x^4 + (', k[132], ') * x^3 + (', k[133], ') * x^2 + (', k[134], ') * x + (', k[58], ')) + (',
|
||||
// k[69], ') * x^5 + (', k[70], ') * x^4 + (', k[71], ') * x^3 + (', k[72], ') * x^2 + (', k[73], ') * x + (', k[60], ')');
|
||||
|
||||
Write('debug83: ',
|
||||
(k[45] * t_0 * t_0 * t_0 + k[46] * t_0 * t_0 + k[47] * t_0 + k[48]) * (k[45] * t_0 * t_0 * t_0 + k[46] * t_0 * t_0 + k[47] * t_0 + k[48]) * (k[31] * t_0 * t_0 * t_0 * t_0 + k[132] * t_0 * t_0 * t_0 + k[133] * t_0 * t_0 + k[134] * t_0 + k[58]) =
|
||||
(k[69] * t_0 * t_0 * t_0 * t_0 * t_0 + k[70] * t_0 * t_0 * t_0 * t_0 + k[71] * t_0 * t_0 * t_0 + k[72] * t_0 * t_0 + k[73] * t_0 + k[60]) * (k[69] * t_0 * t_0 * t_0 * t_0 * t_0 + k[70] * t_0 * t_0 * t_0 * t_0 + k[71] * t_0 * t_0 * t_0 + k[72] * t_0 * t_0 + k[73] * t_0 + k[60]),
|
||||
' ');
|
||||
Write('debug85: ',
|
||||
0 =
|
||||
(
|
||||
k[45] * k[45] * t_0 * t_0 * t_0 * t_0 * t_0 * t_0
|
||||
+ 2 * k[45] * k[46] * t_0 * t_0 * t_0 * t_0 * t_0
|
||||
+ k[46] * k[46] * t_0 * t_0 * t_0 * t_0
|
||||
+ 2 * k[45] * k[47] * t_0 * t_0 * t_0 * t_0
|
||||
+ 2 * k[45] * k[48] * t_0 * t_0 * t_0
|
||||
+ 2 * k[46] * k[47] * t_0 * t_0 * t_0
|
||||
+ k[47] * k[47] * t_0 * t_0
|
||||
+ 2 * k[46] * k[48] * t_0 * t_0
|
||||
+ 2 * k[47] * k[48] * t_0
|
||||
+ k[48] * k[48]
|
||||
) * (k[31] * t_0 * t_0 * t_0 * t_0 + k[132] * t_0 * t_0 * t_0 + k[133] * t_0 * t_0 + k[134] * t_0 + k[58])
|
||||
- k[69] * k[69] * t_0 * t_0 * t_0 * t_0 * t_0 * t_0 * t_0 * t_0 * t_0 * t_0
|
||||
- 2 * k[69] * k[70] * t_0 * t_0 * t_0 * t_0 * t_0 * t_0 * t_0 * t_0 * t_0
|
||||
- (k[70] * k[70] + 2 * k[69] * k[71]) * t_0 * t_0 * t_0 * t_0 * t_0 * t_0 * t_0 * t_0
|
||||
- 2 * (k[69] * k[72] + k[70] * k[71]) * t_0 * t_0 * t_0 * t_0 * t_0 * t_0 * t_0
|
||||
- (k[71] * k[71] + 2 * k[69] * k[73] + 2 * k[70] * k[72]) * t_0 * t_0 * t_0 * t_0 * t_0 * t_0
|
||||
- 2 * (k[69] * k[60] + k[70] * k[73] + k[71] * k[72]) * t_0 * t_0 * t_0 * t_0 * t_0
|
||||
- (k[72] * k[72] + 2 * k[70] * k[60] + 2 * k[71] * k[73]) * t_0 * t_0 * t_0 * t_0
|
||||
- 2 * (k[71] * k[60] + k[72] * k[73]) * t_0 * t_0 * t_0
|
||||
- (k[73] * k[73] + 2 * k[72] * k[60]) * t_0 * t_0
|
||||
- 2 * k[73] * k[60] * t_0
|
||||
- k[60] * k[60],
|
||||
' ');
|
||||
|
||||
WriteLn('debug96: ', EvaluateAt(t_0) = 0);
|
||||
|
||||
NormalizeCoefficients;
|
||||
|
||||
WriteLn('debug99: ', EvaluateAt(t_0) = 0, ' ');
|
||||
end;
|
||||
|
||||
function TFirstCollisionPolynomial.EvaluateAt(const AT0: Int64): TBigInt;
|
||||
var
|
||||
i: Low(FA)..High(FA);
|
||||
begin
|
||||
Result := TBigInt.Zero;
|
||||
for i := High(FA) downto Low(FA) do
|
||||
Result := Result * AT0 + FA[i];
|
||||
end;
|
||||
|
||||
function TFirstCollisionPolynomial.CalcPositiveIntegerRoot: Int64;
|
||||
var
|
||||
dividers: TDividers;
|
||||
factors: TInt64Array;
|
||||
divider: Int64;
|
||||
begin
|
||||
Result := 0;
|
||||
//factors := TIntegerFactorization.PollardsRhoAlgorithm(FA[0]);
|
||||
//dividers := TDividers.Create(factors);
|
||||
//
|
||||
//try
|
||||
//for divider in dividers do
|
||||
//begin
|
||||
// //WriteLn('Check if ', divider, ' is a root...');
|
||||
// if EvaluateAt(divider) = 0 then
|
||||
// begin
|
||||
// Result := divider;
|
||||
// Break;
|
||||
// end;
|
||||
//end;
|
||||
//
|
||||
//finally
|
||||
// dividers.Free;
|
||||
//end;
|
||||
end;
|
||||
|
||||
function TFirstCollisionPolynomial.CalcT1(const AT0: Int64): Int64;
|
||||
var
|
||||
g_0, g_1, g_2: Int64;
|
||||
g: Extended;
|
||||
begin
|
||||
//g_2 := FH[0] * AT0 + FH[1];
|
||||
//g_1 := FH[2] * AT0 * AT0 + FH[3] * AT0 + FH[4];
|
||||
//g_0 := FH[5] * AT0 * AT0 + FH[6] * AT0;
|
||||
//g := - g_1 / (2 * g_2);
|
||||
//Result := Round(g + sqrt(g * g + g_0));
|
||||
end;
|
||||
|
||||
{ TNeverTellMeTheOdds }
|
||||
|
@ -523,78 +91,401 @@ var
|
|||
m1, m2, x, y: Double;
|
||||
begin
|
||||
Result := False;
|
||||
m1 := AHailstone1.Velocity.data[1] / AHailstone1.Velocity.data[0];
|
||||
m2 := AHailstone2.Velocity.data[1] / AHailstone2.Velocity.data[0];
|
||||
m1 := AHailstone1.V1 / AHailstone1.V0;
|
||||
m2 := AHailstone2.V1 / AHailstone2.V0;
|
||||
if m1 <> m2 then
|
||||
begin
|
||||
x := (AHailstone2.Position.data[1] - m2 * AHailstone2.Position.data[0]
|
||||
- AHailstone1.Position.data[1] + m1 * AHailstone1.Position.data[0])
|
||||
x := (AHailstone2.P1 - m2 * AHailstone2.P0
|
||||
- AHailstone1.P1 + m1 * AHailstone1.P0)
|
||||
/ (m1 - m2);
|
||||
if (FMin <= x) and (x <= FMax)
|
||||
and (x * Sign(AHailstone1.Velocity.data[0]) >= AHailstone1.Position.data[0] * Sign(AHailstone1.Velocity.data[0]))
|
||||
and (x * Sign(AHailstone2.Velocity.data[0]) >= AHailstone2.Position.data[0] * Sign(AHailstone2.Velocity.data[0]))
|
||||
and (x * Sign(AHailstone1.V0) >= AHailstone1.P0 * Sign(AHailstone1.V0))
|
||||
and (x * Sign(AHailstone2.V0) >= AHailstone2.P0 * Sign(AHailstone2.V0))
|
||||
then
|
||||
begin
|
||||
y := m1 * (x - AHailstone1.Position.data[0]) + AHailstone1.Position.data[1];
|
||||
y := m1 * (x - AHailstone1.P0) + AHailstone1.P1;
|
||||
if (FMin <= y) and (y <= FMax) then
|
||||
Result := True
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
|
||||
// For debug calculations:
|
||||
Const
|
||||
T : array[0..4] of Byte = (5, 3, 4, 6, 1);
|
||||
|
||||
procedure TNeverTellMeTheOdds.FindRockThrow(const AIndex1, AIndex2, AIndex3: Integer);
|
||||
function TNeverTellMeTheOdds.FindRockThrow(const AIndex0, AIndex1, AIndex2: Integer): Int64;
|
||||
var
|
||||
//i, j, k: Integer;
|
||||
//x0, x1, x2: Extended;
|
||||
f: TFirstCollisionPolynomial;
|
||||
t0, t1: Int64;
|
||||
p, v: Tvector3_extended;
|
||||
test: TBigInt;
|
||||
t0, t1: TInt64Array;
|
||||
i, j: Int64;
|
||||
begin
|
||||
WriteLn;
|
||||
WriteLn(AIndex1, ' ', AIndex2, ' ', AIndex3);
|
||||
f := TFirstCollisionPolynomial.Create;
|
||||
f.Init(FHailstones[AIndex1], FHailstones[AIndex2], FHailstones[AIndex3], T[AIndex1], T[AIndex2], T[AIndex3]);
|
||||
//t0 := f.CalcPositiveIntegerRoot;
|
||||
//WriteLn('t0: ', t0, ' ', t0 = T[AIndex1]);
|
||||
//t1 := f.CalcT1(t0);
|
||||
//WriteLn(', t1: ', t1);
|
||||
f.Free;
|
||||
t0 := CalcRockThrowCollisionOptions(FHailstones[AIndex0], FHailstones[AIndex1], FHailstones[AIndex2]);
|
||||
t1 := CalcRockThrowCollisionOptions(FHailstones[AIndex1], FHailstones[AIndex0], FHailstones[AIndex2]);
|
||||
|
||||
//// V_x = (V_0 * t_0 - V_1 * t_1 + P_0 - P_1) / (t_0 - t_1)
|
||||
//v := (FHailstones[AIndex1].Velocity * t0 - FHailstones[AIndex2].Velocity * t1
|
||||
// + FHailstones[AIndex1].Position - FHailstones[AIndex2].Position) / (t0 - t1);
|
||||
//// P_x = (V_0 - V_x) * t_0 + P_0
|
||||
//p := (FHailstones[AIndex1].Velocity - v) * t0 + FHailstones[AIndex1].Position;
|
||||
//FPart2 := Round(p.data[0]) + Round(p.data[1]) + Round(p.data[2]);
|
||||
Result := 0;
|
||||
for i in t0 do
|
||||
begin
|
||||
for j in t1 do
|
||||
begin
|
||||
Result := ValidateRockThrow(FHailstones[AIndex0], FHailstones[AIndex1], FHailstones[AIndex2], i, j);
|
||||
if Result > 0 then
|
||||
Break;
|
||||
end;
|
||||
if Result > 0 then
|
||||
Break;
|
||||
end;
|
||||
end;
|
||||
|
||||
//for i := 0 to FHailstones.Count - 3 do
|
||||
// for j := i + 1 to FHailstones.Count - 2 do
|
||||
// for k:= j + 1 to FHailstones.Count - 1 do
|
||||
// begin
|
||||
// WriteLn(i, j, k);
|
||||
// solver := TRockThrowSolver.Create(FHailstones[i], FHailstones[j], FHailstones[k], 0);
|
||||
// case i of
|
||||
// 0: x0 := 5;
|
||||
// 1: x0 := 3;
|
||||
// 2: x0 := 4;
|
||||
// end;
|
||||
// f := solver.CalcValue(x0);
|
||||
// solver.Free;
|
||||
// end;
|
||||
procedure TNeverTellMeTheOdds.CalcCollisionPolynomials(constref AHailstone0, AHailstone1, AHailstone2: THailstone; out
|
||||
OPolynomial0, OPolynomial1: TBigIntPolynomial);
|
||||
var
|
||||
k: array[0..74] of TBigInt;
|
||||
begin
|
||||
// Solving this non-linear equation system, with velocities V_i and start positions P_i:
|
||||
// V_0 * t_0 + P_0 = V_x * t_0 + P_x
|
||||
// V_1 * t_1 + P_1 = V_x * t_1 + P_x
|
||||
// V_2 * t_2 + P_2 = V_x * t_2 + P_x
|
||||
// Which gives:
|
||||
// P_x = (V_0 - V_x) * t_0 + P_0
|
||||
// V_x = (V_0 * t_0 - V_1 * t_1 + P_0 - P_1) / (t_0 - t_1)
|
||||
// And with vertex components:
|
||||
// 1: 0 = (t_1 - t_0) * (V_00 * t_0 - V_20 * t_2 + P_00 - P_20)
|
||||
// - (t_2 - t_0) * (V_00 * t_0 - V_10 * t_1 + P_00 - P_10)
|
||||
// 2: t_1 = (((V_01 - V_21) * t_2 + P_11 - P_21) * t_0 + (P_01 - P_11) * t_2)
|
||||
// / ((V_01 - V_11) * t_0 + (V_11 - V_21) * t_2 + P_01 - P_21)
|
||||
// 3: t_2 = (((V_02 - V_12) * t_1 + P_22 - P_12) * t_0 + (P_02 - P_22) * t_1)
|
||||
// / ((V_02 - V_22) * t_0 + (V_22 - V_12) * t_1 + P_02 - P_12)
|
||||
// for t_0, t_1, t_2 not pairwise equal.
|
||||
// With some substitutions depending only on t_0 this gives
|
||||
// 1: 0 = (t_1 - t_0) * (a_1 - V_20 * t_2) - (t_2 - t_0) * (a_2 - V_10 * t_1)
|
||||
// 2: t_1 = (b_0 + b_1 * t_2) / (c_0 + c_1 * t_2)
|
||||
// 3: t_2 = (d_0 + d_1 * t_1) / (e_0 + e_1 * t_1)
|
||||
// And 3 in 2 gives:
|
||||
// 4: f_2 * t_1^2 + f_1 * t_1 - f_0 = 0
|
||||
// Then, with 4 and 3 in 1 and many substitutions (see constants k below, now independent of t_0), the equation
|
||||
// 5: 0 = p_0(t_0) + p_1(t_0) * sqrt(p_2(t_0))
|
||||
// can be constructed, where p_0, p_1, and p_2 are polynomials in t_0. Since we are searching for an integer solution,
|
||||
// we assume that there is an integer t_0 that is a root of both p_0 and p_1, which would solve the equation.
|
||||
|
||||
//for i := 80 to 120 do
|
||||
//begin
|
||||
// solver := TRockThrowSolver.Create(FHailstones[0], FHailstones[1], FHailstones[2], 0);
|
||||
// x0 := i / 20;
|
||||
// f := solver.CalcValue(x0);
|
||||
// WriteLn(x0, ' ', f.Valid, ' ', f.Value);
|
||||
// solver.Free;
|
||||
//end;
|
||||
// Subsitutions depending on t_0:
|
||||
// a_1 = V_00 * t_0 + P_00 - P_20
|
||||
// a_2 = V_00 * t_0 + P_00 - P_10
|
||||
// b_0 = (P_11 - P_21) * t_0
|
||||
// b_1 = (V_01 - V_21) * t_0 + P_01 - P_11
|
||||
// c_0 = (V_01 - V_11) * t_0 + P_01 - P_21
|
||||
// c_1 = V_11 - V_21
|
||||
// d_0 = (P_22 - P_12) * t_0
|
||||
// d_1 = (V_02 - V_12) * t_0 + P_02 - P_22
|
||||
// e_0 = (V_02 - V_22) * t_0 + P_02 - P_12
|
||||
// e_1 = V_22 - V_12
|
||||
// f_0 = b_1 * d_0 + b_0 * e_0
|
||||
// f_1 = c_0 * e_0 + c_1 * d_0 - b_0 * e_1 - b_1 * d_1
|
||||
// f_2 = c_0 * e_1 + c_1 * d_1
|
||||
|
||||
// Calculations for equation 5 (4 and 3 in 1).
|
||||
// 1: 0 = (t_1 - t_0) * (a_1 - V_20 * t_2) - (t_2 - t_0) * (a_2 - V_10 * t_1)
|
||||
// 3: (e_0 + e_1 * t_1) * t_2 = (d_0 + d_1 * t_1)
|
||||
// 0 = (t_1 - t_0) * (a_1 - V_20 * t_2) - (t_2 - t_0) * (a_2 - V_10 * t_1)
|
||||
// = (t_1 - t_0) * (a_1 * (e_0 + e_1 * t_1) - V_20 * (e_0 + e_1 * t_1) * t_2) - ((e_0 + e_1 * t_1) * t_2 - (e_0 + e_1 * t_1) * t_0) * (a_2 - V_10 * t_1)
|
||||
// = (t_1 - t_0) * (a_1 * (e_0 + e_1 * t_1) - V_20 * (d_0 + d_1 * t_1)) - ((d_0 + d_1 * t_1) - (e_0 + e_1 * t_1) * t_0) * (a_2 - V_10 * t_1)
|
||||
// = (t_1 - t_0) * (a_1 * e_0 + a_1 * e_1 * t_1 - V_20 * d_0 - V_20 * d_1 * t_1) - (d_0 + d_1 * t_1 - e_0 * t_0 - e_1 * t_1 * t_0) * (a_2 - V_10 * t_1)
|
||||
// = (a_1 * e_1 - V_20 * d_1) * t_1^2 + (a_1 * e_0 - V_20 * d_0 - t_0 * (a_1 * e_1 - V_20 * d_1)) * t_1 - t_0 * (a_1 * e_0 - V_20 * d_0)
|
||||
// - ( - V_10 * (d_1 - e_1 * t_0) * t_1^2 + ((d_1 - e_1 * t_0) * a_2 - V_10 * (d_0 - e_0 * t_0)) * t_1 + (d_0 - e_0 * t_0) * a_2)
|
||||
// = (a_1 * e_1 - V_20 * d_1 + V_10 * (d_1 - e_1 * t_0)) * t_1^2
|
||||
// + (a_1 * e_0 - V_20 * d_0 - t_0 * (a_1 * e_1 - V_20 * d_1) - (d_1 - e_1 * t_0) * a_2 + V_10 * (d_0 - e_0 * t_0)) * t_1
|
||||
// + t_0 * (V_20 * d_0 - a_1 * e_0) + (e_0 * t_0 - d_0) * a_2
|
||||
// Inserting 4, solved for t_0: t_1 = - f_1 / (2 * f_2) + sqrt((f_1 / (2 * f_2))^2 + f_0 / f_2)
|
||||
// = (a_1 * e_1 - V_20 * d_1 + V_10 * (d_1 - e_1 * t_0)) * (f_1^2 + 2 * f_0 * f_2 - f_1 * sqrt(f_1^2 + 4 * f_0 * f_2))
|
||||
// + (a_1 * e_0 - V_20 * d_0 - t_0 * (a_1 * e_1 - V_20 * d_1) - (d_1 - e_1 * t_0) * a_2 + V_10 * (d_0 - e_0 * t_0)) * (- f_1 * f_2 + f_2 * sqrt(f_1^2 + 4 * f_0 * f_2))
|
||||
// + t_0 * (V_20 * d_0 - a_1 * e_0) * 2 * f_2^2 + (e_0 * t_0 - d_0) * a_2 * 2 * f_2^2
|
||||
|
||||
// a_1 = V_00 * t_0 + k_0
|
||||
// a_2 = V_00 * t_0 + k_1
|
||||
// b_0 = k_2 * t_0
|
||||
// b_1 = k_10 * t_0 + k_3
|
||||
// c_0 = k_11 * t_0 + k_4
|
||||
// d_0 = k_5 * t_0
|
||||
// d_1 = k_12 * t_0 + k_6
|
||||
// e_0 = k_13 * t_0 + k_7
|
||||
// f_2 = (k_11 * t_0 + k_4) * k_9 + k_8 * (k_12 * t_0 + k_6)
|
||||
// = (k_11 * k_9 + k_8 * k_12) * t_0 + k_4 * k_9 + k_8 * k_6
|
||||
// = k_14 * t_0 + k_15
|
||||
// f_1 = (k_11 * t_0 + k_4) * (k_13 * t_0 + k_7) + k_8 * k_5 * t_0 - k_2 * t_0 * k_9 - (k_10 * t_0 + k_3) * (k_12 * t_0 + k_6)
|
||||
// = (k_11 * k_13 - k_10 * k_12) * t_0^2 + (k_11 * k_7 + k_4 * k_12 + k_8 * k_5 - k_2 * k_9 - k_10 * k_6 - k_3 * k_12) * t_0 + k_4 * k_7 - k_3 * k_6
|
||||
// = k_16 * t_0^2 + k_17 * t_0 + k_18
|
||||
// f_0 = (k_10 * t_0 + k_3) * k_5 * t_0 + k_2 * t_0 * (k_13 * t_0 + k_7)
|
||||
// = (k_10 * k_5 + k_2 * k_13) * t_0^2 + (k_3 * k_5 + k_2 * k_7) * t_0
|
||||
// = k_19 * t_0^2 + k_20 * t_0
|
||||
|
||||
k[0] := AHailstone0.P0 - AHailstone2.P0;
|
||||
k[1] := AHailstone0.P0 - AHailstone1.P0;
|
||||
k[2] := AHailstone1.P1 - AHailstone2.P1;
|
||||
k[3] := AHailstone0.P1 - AHailstone1.P1;
|
||||
k[4] := AHailstone0.P1 - AHailstone2.P1;
|
||||
k[5] := AHailstone2.P2 - AHailstone1.P2;
|
||||
k[6] := AHailstone0.P2 - AHailstone2.P2;
|
||||
k[7] := AHailstone0.P2 - AHailstone1.P2;
|
||||
k[8] := AHailstone1.V1 - AHailstone2.V1;
|
||||
k[9] := AHailstone2.V2 - AHailstone1.V2;
|
||||
k[10] := AHailstone0.V1 - AHailstone2.V1;
|
||||
k[11] := AHailstone0.V1 - AHailstone1.V1;
|
||||
k[12] := AHailstone0.V2 - AHailstone1.V2;
|
||||
k[13] := AHailstone0.V2 - AHailstone2.V2;
|
||||
|
||||
k[14] := k[11] * k[9] + k[8] * k[12];
|
||||
k[15] := k[4] * k[9] + k[8] * k[6];
|
||||
k[16] := k[11] * k[13] - k[10] * k[12];
|
||||
k[17] := k[11] * k[7] + k[4] * k[13] + k[8] * k[5] - k[2] * k[9] - k[10] * k[6] - k[3] * k[12];
|
||||
k[18] := k[4] * k[7] - k[3] * k[6];
|
||||
k[19] := k[10] * k[5] + k[2] * k[13];
|
||||
k[20] := k[3] * k[5] + k[2] * k[7];
|
||||
|
||||
// Additional substitutions.
|
||||
// a_1 * k_9 - V_20 * d_1
|
||||
// = (V_00 * t_0 + k_0) * k_9 - V_20 * (k_12 * t_0 + k_6)
|
||||
// = (V_00 * k_9 - V_20 * k_12) * t_0 + k_0 * k_9 - V_20 * k_6
|
||||
// = k_21 * t_0 + k_22
|
||||
// d_1 - k_9 * t_0
|
||||
// = k_12 * t_0 + k_6 - k_9 * t_0
|
||||
// = (k_12 - k_9) * t_0 + k_6
|
||||
// a_1 * e_0 - V_20 * d_0
|
||||
// = (V_00 * t_0 + k_0) * (k_13 * t_0 + k_7) - V_20 * k_5 * t_0
|
||||
// = V_00 * k_13 * t_0^2 + (V_00 * k_7 + k_0 * k_13 - V_20 * k_5) * t_0 + k_0 * k_7
|
||||
// = k_23 * t_0^2 + k_24 * t_0 + k_25
|
||||
// d_0 - e_0 * t_0
|
||||
// = k_5 * t_0 - k_13 * t_0^2 - k_7 * t_0
|
||||
// = - k_13 * t_0^2 + k_26 * t_0
|
||||
// f_1^2
|
||||
// = (k_16 * t_0^2 + k_17 * t_0 + k_18)^2
|
||||
// = k_16^2 * t_0^4 + k_17^2 * t_0^2 + k_18^2 + 2 * k_16 * t_0^2 * k_17 * t_0 + 2 * k_16 * t_0^2 * k_18 + 2 * k_17 * t_0 * k_18
|
||||
// = k_16^2 * t_0^4 + 2 * k_16 * k_17 * t_0^3 + (k_17^2 + 2 * k_16 * k_18) * t_0^2 + 2 * k_17 * k_18 * t_0 + k_18^2
|
||||
// = k_16^2 * t_0^4 + k_27 * t_0^3 + k_29 * t_0^2 + k_30 * t_0 + k_18^2
|
||||
// f_2^2
|
||||
// = (k_14 * t_0 + k_15)^2
|
||||
// = k_14^2 * t_0^2 + 2 * k_14 * k_15 * t_0 + k_15^2
|
||||
// = k_14^2 * t_0^2 + k_31 * t_0 + k_15^2
|
||||
// f_0 * f_2
|
||||
// = (k_19 * t_0^2 + k_20 * t_0) * (k_14 * t_0 + k_15)
|
||||
// = k_19 * k_14 * t_0^3 + (k_19 * k_15 + k_20 * k_14) * t_0^2 + k_20 * k_15 * t_0
|
||||
// = k_33 * t_0^3 + k_34 * t_0^2 + k_35 * t_0
|
||||
// f_1^2 + 4 * f_0 * f_2
|
||||
// = k_16^2 * t_0^4 + k_27 * t_0^3 + k_29 * t_0^2 + k_30 * t_0 + k_18^2 + 4 * (k_33 * t_0^3 + k_34 * t_0^2 + k_35 * t_0)
|
||||
// = k_37 * t_0^4 + k_75 * t_0^3 + k_76 * t_0^2 + k_77 * t_0 + k_59
|
||||
// f_1^2 + 2 * f_0 * f_2
|
||||
// = k_16^2 * t_0^4 + k_27 * t_0^3 + k_29 * t_0^2 + k_30 * t_0 + k_18^2 + 2 * (k_33 * t_0^3 + k_34 * t_0^2 + k_35 * t_0)
|
||||
// = k_37 * t_0^4 + k_38 * t_0^3 + k_39 * t_0^2 + k_40 * t_0 + k_59
|
||||
|
||||
k[21] := AHailstone0.V0 * k[9] - AHailstone2.V0 * k[12];
|
||||
k[22] := k[0] * k[9] - AHailstone2.V0 * k[6];
|
||||
k[23] := AHailstone0.V0 * k[13];
|
||||
k[24] := AHailstone0.V0 * k[7] + k[0] * k[13] - AHailstone2.V0 * k[5];
|
||||
k[25] := k[0] * k[7];
|
||||
k[26] := k[5] - k[7];
|
||||
k[27] := 2 * k[16] * k[17];
|
||||
k[28] := k[17] * k[17];
|
||||
k[29] := k[28] + 2 * k[16] * k[18];
|
||||
k[30] := 2 * k[17] * k[18];
|
||||
k[31] := 2 * k[14] * k[15];
|
||||
k[32] := k[14] * k[14];
|
||||
k[33] := k[19] * k[14];
|
||||
k[34] := k[19] * k[15] + k[20] * k[14];
|
||||
k[35] := k[20] * k[15];
|
||||
k[36] := k[15] * k[15];
|
||||
k[37] := k[16] * k[16];
|
||||
k[38] := k[27] + 2 * k[33];
|
||||
k[39] := k[29] + 2 * k[34];
|
||||
k[40] := k[30] + 2 * k[35];
|
||||
k[41] := k[21] + AHailstone1.V0 * (k[12] - k[9]);
|
||||
k[42] := k[22] + AHailstone1.V0 * k[6];
|
||||
k[43] := k[23] - k[21] - AHailstone1.V0 * k[13] - (k[12] - k[9]) * AHailstone0.V0;
|
||||
k[44] := k[24] - k[22] + AHailstone1.V0 * k[26] - (k[12] - k[9]) * k[1] - k[6] * AHailstone0.V0;
|
||||
k[45] := k[25] - k[6] * k[1];
|
||||
k[46] := k[43] * k[14] - k[41] * k[16];
|
||||
k[47] := k[43] * k[15] + k[44] * k[14] - k[42] * k[16] - k[41] * k[17];
|
||||
k[48] := k[44] * k[15] + k[45] * k[14] - k[42] * k[17] - k[41] * k[18];
|
||||
k[49] := k[45] * k[15] - k[42] * k[18];
|
||||
k[50] := k[43] * k[16];
|
||||
k[51] := k[41] * k[37] - k[50] * k[14];
|
||||
k[52] := k[43] * k[17] + k[44] * k[16];
|
||||
k[53] := k[41] * k[38] + k[42] * k[37] - k[52] * k[14] - k[50] * k[15];
|
||||
k[54] := k[43] * k[18] + k[44] * k[17] + k[45] * k[16];
|
||||
k[55] := k[41] * k[39] + k[42] * k[38] - k[54] * k[14] - k[52] * k[15];
|
||||
k[56] := k[44] * k[18] + k[45] * k[17];
|
||||
k[57] := k[41] * k[40] + k[42] * k[39] - k[56] * k[14] - k[54] * k[15];
|
||||
k[58] := k[45] * k[18];
|
||||
k[59] := k[18] * k[18];
|
||||
k[60] := k[41] * k[59] + k[42] * k[40] - k[58] * k[14] - k[56] * k[15];
|
||||
k[61] := k[42] * k[59] - k[58] * k[15];
|
||||
k[62] := k[13] * AHailstone0.V0 - k[23];
|
||||
k[63] := 2 * k[32] * k[62];
|
||||
k[64] := k[13] * k[1] - k[26] * AHailstone0.V0 - k[24];
|
||||
k[65] := 2 * (k[31] * k[62] + k[32] * k[64]);
|
||||
k[66] := - k[26] * k[1] - k[25];
|
||||
k[67] := 2 * (k[36] * k[62] + k[31] * k[64] + k[32] * k[66]);
|
||||
k[68] := 2 * (k[36] * k[64] + k[31] * k[66]);
|
||||
k[69] := 2 * k[36] * k[66];
|
||||
k[70] := k[51] + k[63];
|
||||
k[71] := k[53] + k[65];
|
||||
k[72] := k[55] + k[67];
|
||||
k[73] := k[57] + k[68];
|
||||
k[74] := k[60] + k[69];
|
||||
// Unused, they are part of the polynomial inside the square root.
|
||||
//k[75] := k[27] + 4 * k[33];
|
||||
//k[76] := k[29] + 4 * k[34];
|
||||
//k[77] := k[30] + 4 * k[35];
|
||||
|
||||
// Continuing calculations for equation 5.
|
||||
// 0 = (k_21 * t_0 + k_22 + V_10 * ((k_12 - k_9) * t_0 + k_6)) * (k_37 * t_0^4 + k_38 * t_0^3 + k_39 * t_0^2 + k_40 * t_0 + k_59 -+ f_1 * sqrt(f_1^2 + 4 * f_0 * f_2))
|
||||
// + (k_23 * t_0^2 + k_24 * t_0 + k_25 - t_0 * (k_21 * t_0 + k_22) - ((k_12 - k_9) * t_0 + k_6) * a_2 - V_10 * (k_13 * t_0^2 - k_26 * t_0)) * (- f_1 * f_2 +- f_2 * sqrt(f_1^2 + 4 * f_0 * f_2))
|
||||
// - 2 * t_0 * (k_23 * t_0^2 + k_24 * t_0 + k_25) * (k_14^2 * t_0^2 + k_31 * t_0 + k_15^2) + 2 * (k_13 * t_0^2 - k_26 * t_0) * a_2 * (k_14^2 * t_0^2 + k_31 * t_0 + k_15^2)
|
||||
// 0 = (k_41 * t_0 + k_42) * (k_37 * t_0^4 + k_38 * t_0^3 + k_39 * t_0^2 + k_40 * t_0 + k_59 -+ f_1 * sqrt(f_1^2 + 4 * f_0 * f_2))
|
||||
// + ((k_23 - k_21 - V_10 * k_13 - (k_12 - k_9) * V_00) * t_0^2 + (k_24 - k_22 + V_10 * k_26 - (k_12 - k_9) * k_1 - k_6 * V_00) * t_0 + k_25 - k_6 * k_1) * (- f_1 * f_2 +- f_2 * sqrt(f_1^2 + 4 * f_0 * f_2))
|
||||
// - 2 * t_0 * (k_23 * t_0^2 + k_24 * t_0 + k_25) * (k_14^2 * t_0^2 + k_31 * t_0 + k_15^2) + 2 * (k_13 * t_0^2 - k_26 * t_0) * a_2 * (k_14^2 * t_0^2 + k_31 * t_0 + k_15^2)
|
||||
// 0 = (k_41 * t_0 + k_42) * (k_37 * t_0^4 + k_38 * t_0^3 + k_39 * t_0^2 + k_40 * t_0 + k_59)
|
||||
// -+ (k_41 * t_0 + k_42) * f_1 * sqrt(f_1^2 + 4 * f_0 * f_2)
|
||||
// + (k_43 * t_0^2 + k_44 * t_0 + k_45) * (- f_1 * f_2 +- f_2 * sqrt(f_1^2 + 4 * f_0 * f_2))
|
||||
// - 2 * t_0 * (k_23 * t_0^2 + k_24 * t_0 + k_25) * (k_14^2 * t_0^2 + k_31 * t_0 + k_15^2) + 2 * (k_13 * t_0^2 - k_26 * t_0) * a_2 * (k_14^2 * t_0^2 + k_31 * t_0 + k_15^2)
|
||||
// 0 = (k_41 * t_0 + k_42) * (k_37 * t_0^4 + k_38 * t_0^3 + k_39 * t_0^2 + k_40 * t_0 + k_59)
|
||||
// -+ (k_41 * t_0 + k_42) * f_1 * sqrt(f_1^2 + 4 * f_0 * f_2)
|
||||
// - (k_43 * t_0^2 + k_44 * t_0 + k_45) * f_1 * f_2
|
||||
// +- (k_43 * t_0^2 + k_44 * t_0 + k_45) * f_2 * sqrt(f_1^2 + 4 * f_0 * f_2)
|
||||
// - 2 * t_0 * (k_23 * t_0^2 + k_24 * t_0 + k_25) * (k_14^2 * t_0^2 + k_31 * t_0 + k_15^2) + 2 * (k_13 * t_0^2 - k_26 * t_0) * a_2 * (k_14^2 * t_0^2 + k_31 * t_0 + k_15^2)
|
||||
// 0 = +- ((k_43 * t_0^2 + k_44 * t_0 + k_45) * f_2 - (k_41 * t_0 + k_42) * f_1) * sqrt(f_1^2 + 4 * f_0 * f_2)
|
||||
// + (k_41 * t_0 + k_42) * (k_37 * t_0^4 + k_38 * t_0^3 + k_39 * t_0^2 + k_40 * t_0 + k_59)
|
||||
// - (k_43 * t_0^2 + k_44 * t_0 + k_45) * f_1 * f_2
|
||||
// - 2 * t_0 * (k_23 * t_0^2 + k_24 * t_0 + k_25) * (k_14^2 * t_0^2 + k_31 * t_0 + k_15^2) + 2 * (k_13 * t_0^2 - k_26 * t_0) * a_2 * (k_14^2 * t_0^2 + k_31 * t_0 + k_15^2)
|
||||
// 0 = +- ((k_43 * t_0^2 + k_44 * t_0 + k_45) * (k_14 * t_0 + k_15) - (k_41 * t_0 + k_42) * (k_16 * t_0^2 + k_17 * t_0 + k_18)) * sqrt(f_1^2 + 4 * f_0 * f_2)
|
||||
// + (k_41 * t_0 + k_42) * (k_37 * t_0^4 + k_38 * t_0^3 + k_39 * t_0^2 + k_40 * t_0 + k_59)
|
||||
// - (k_43 * t_0^2 + k_44 * t_0 + k_45) * (k_16 * t_0^2 + k_17 * t_0 + k_18) * (k_14 * t_0 + k_15)
|
||||
// - 2 * t_0 * (k_23 * t_0^2 + k_24 * t_0 + k_25) * (k_14^2 * t_0^2 + k_31 * t_0 + k_15^2) + 2 * (k_13 * t_0^2 - k_26 * t_0) * (V_00 * t_0 + k_1) * (k_14^2 * t_0^2 + k_31 * t_0 + k_15^2)
|
||||
// 0 = +- (
|
||||
// (k_43 * k_14 - k_41 * k_16) * t_0^3
|
||||
// + (k_43 * k_15 + k_44 * k_14 - k_42 * k_16 - k_41 * k_17) * t_0^2
|
||||
// + (k_44 * k_15 + k_45 * k_14 - k_42 * k_17 - k_41 * k_18) * t_0
|
||||
// + k_45 * k_15 - k_42 * k_18
|
||||
// ) * sqrt(f_1^2 + 4 * f_0 * f_2)
|
||||
// + (k_41 * k_37 - k_43 * k_16 * k_14) * t_0^5
|
||||
// + (k_41 * k_38 + k_42 * k_37 - k_43 * k_17 * k_14 - k_44 * k_16 * k_14 - k_43 * k_16 * k_15) * t_0^4
|
||||
// + (k_41 * k_39 + k_42 * k_38 - k_43 * k_18 * k_14 - k_44 * k_17 * k_14 - k_45 * k_16 * k_14 - k_43 * k_17 * k_15 - k_44 * k_16 * k_15) * t_0^3
|
||||
// + (k_41 * k_40 + k_42 * k_39 - k_44 * k_18 * k_14 - k_45 * k_17 * k_14 - k_43 * k_18 * k_15 - k_44 * k_17 * k_15 - k_45 * k_16 * k_15) * t_0^2
|
||||
// + (k_41 * k_59 + k_42 * k_40 - k_45 * k_18 * k_14 - k_44 * k_18 * k_15 - k_45 * k_17 * k_15) * t_0
|
||||
// + k_42 * k_59 - k_45 * k_18 * k_15
|
||||
// + 2 * (k_13 * V_00 * k_14^2 - k_23 * k_14^2) * t_0^5
|
||||
// + 2 * (k_13 * V_00 * k_31 + k_13 * k_1 * k_14^2 - k_26 * V_00 * k_14^2 - k_23 * k_31 - k_24 * k_14^2) * t_0^4
|
||||
// + 2 * (k_13 * V_00 * k_15^2 + k_13 * k_1 * k_31 - k_26 * V_00 * k_31 - k_26 * k_1 * k_14^2 - k_23 * k_15^2 - k_24 * k_31 - k_25 * k_14^2) * t_0^3
|
||||
// + 2 * (k_13 * k_1 * k_15^2 - k_26 * V_00 * k_15^2 - k_26 * k_1 * k_31 - k_24 * k_15^2 - k_25 * k_31) * t_0^2
|
||||
// + 2 * (- k_26 * k_1 * k_15^2 - k_25 * k_15^2) * t_0
|
||||
// 0 = +- (k_46 * t_0^3 + k_47 * t_0^2 + k_48 * t_0 + k_49) * sqrt(f_1^2 + 4 * f_0 * f_2)
|
||||
// + (k_51 + k_63) * t_0^5 + (k_53 + k_65) * t_0^4 + (k_55 + k_67) * t_0^3 + (k_57 + k_68) * t_0^2 + (k_60 + k_69) * t_0 + k_61
|
||||
// 0 = +- (k_46 * t_0^3 + k_47 * t_0^2 + k_48 * t_0 + k_49) * sqrt(k_37 * t_0^4 + k_75 * t_0^3 + k_76 * t_0^2 + k_77 * t_0 + k_59)
|
||||
// + k_70 * t_0^5 + k_71 * t_0^4 + k_72 * t_0^3 + k_73 * t_0^2 + k_74 * t_0 + k_61
|
||||
|
||||
OPolynomial0 := TBigIntPolynomial.Create([k[61], k[74], k[73], k[72], k[71], k[70]]);
|
||||
OPolynomial1 := TBigIntPolynomial.Create([k[49], k[48], k[47], k[46]]);
|
||||
|
||||
// Squaring that formula eliminates the square root, but may lead to a polynomial with all coefficients zero in some
|
||||
// cases. Therefore this part is merely included for the interested reader.
|
||||
// -+ (k_46 * t_0^3 + k_47 * t_0^2 + k_48 * t_0 + k_49) * sqrt(k_37 * t_0^4 + k_75 * t_0^3 + k_76 * t_0^2 + k_77 * t_0 + k_59) =
|
||||
// k_70 * t_0^5 + k_71 * t_0^4 + k_72 * t_0^3 + k_73 * t_0^2 + k_74 * t_0 + k_61
|
||||
// (k_46 * t_0^3 + k_47 * t_0^2 + k_48 * t_0 + k_49)^2 * (k_37 * t_0^4 + k_75 * t_0^3 + k_76 * t_0^2 + k_77 * t_0 + k_59) =
|
||||
// (k_70 * t_0^5 + k_71 * t_0^4 + k_72 * t_0^3 + k_73 * t_0^2 + k_74 * t_0 + k_61)^2
|
||||
// 0 =
|
||||
// (k_46^2 * t_0^6
|
||||
// + 2 * k_46 * k_47 * t_0^5
|
||||
// + k_47^2 * t_0^4 + 2 * k_46 * k_48 * t_0^4
|
||||
// + 2 * k_46 * k_49 * t_0^3 + 2 * k_47 * k_48 * t_0^3
|
||||
// + k_48^2 * t_0^2 + 2 * k_47 * k_49 * t_0^2
|
||||
// + 2 * k_48 * k_49 * t_0
|
||||
// + k_49^2
|
||||
// ) * (k_37 * t_0^4 + k_75 * t_0^3 + k_76 * t_0^2 + k_77 * t_0 + k_59)
|
||||
// - k_70^2 * t_0^10
|
||||
// - 2 * k_70 * k_71 * t_0^9
|
||||
// - (k_71^2 + 2 * k_70 * k_72) * t_0^8
|
||||
// - 2 * (k_70 * k_73 + k_71 * k_72) * t_0^7
|
||||
// - (k_72^2 + 2 * k_70 * k_74 + 2 * k_71 * k_73) * t_0^6
|
||||
// - 2 * (k_70 * k_61 + k_71 * k_74 + k_72 * k_73) * t_0^5
|
||||
// - (k_73^2 + 2 * k_71 * k_61 + 2 * k_72 * k_74) * t_0^4
|
||||
// - 2 * (k_72 * k_61 + k_73 * k_74) * t_0^3
|
||||
// - (k_74^2 + 2 * k_73 * k_61) * t_0^2
|
||||
// - 2 * k_74 * k_61 * t_0
|
||||
// - k_61^2
|
||||
// 0 = ak_10 * t_0^10 + ak_9 * t_0^9 + ak_8 * t_0^8 + ak_7 * t_0^7 + ak_6 * t_0^6 + ak_5 * t_0^5 + ak_4 * t_0^4 + ak_3 * t_0^3 + ak_2 * t_0^2 + ak_1 * t_0 + ak_0
|
||||
|
||||
//k[78] := k[46] * k[46];
|
||||
//k[79] := 2 * k[46] * k[47];
|
||||
//k[80] := k[47] * k[47] + 2 * k[46] * k[48];
|
||||
//k[81] := 2 * (k[46] * k[49] + k[47] * k[48]);
|
||||
//k[82] := k[48] * k[48] + 2 * k[47] * k[49];
|
||||
//k[83] := 2 * k[48] * k[49];
|
||||
//k[84] := k[49] * k[49];
|
||||
//ak[0] := k[59] * k[84] - k[61] * k[61];
|
||||
//ak[1] := k[77] * k[84] + k[59] * k[83] - 2 * k[74] * k[61];
|
||||
//ak[2] := k[76] * k[84] + k[77] * k[83] + k[59] * k[82] - k[74] * k[74] - 2 * k[73] * k[61];
|
||||
//ak[3] := k[76] * k[83] + k[77] * k[82] + k[59] * k[81] + k[75] * k[84]
|
||||
// - 2 * (k[72] * k[61] + k[73] * k[74]);
|
||||
//ak[4] := k[37] * k[84] + k[76] * k[82] + k[77] * k[81] + k[59] * k[80] + k[75] * k[83] - k[73] * k[73]
|
||||
// - 2 * (k[71] * k[61] + k[72] * k[74]);
|
||||
//ak[5] := k[37] * k[83] + k[76] * k[81] + k[77] * k[80] + k[59] * k[79] + k[75] * k[82]
|
||||
// - 2 * (k[70] * k[61] + k[71] * k[74] + k[72] * k[73]);
|
||||
//ak[6] := k[37] * k[82] + k[76] * k[80] + k[77] * k[79] + k[59] * k[78] + k[75] * k[81] - k[72] * k[72]
|
||||
// - 2 * (k[70] * k[74] + k[71] * k[73]);
|
||||
//ak[7] := k[37] * k[81] + k[76] * k[79] + k[77] * k[78] + k[75] * k[80] - 2 * (k[70] * k[73] + k[71] * k[72]);
|
||||
//ak[8] := k[37] * k[80] + k[75] * k[79] + k[76] * k[78] - k[71] * k[71] - 2 * k[70] * k[72];
|
||||
//ak[9] := k[37] * k[79] + k[75] * k[78] - 2 * k[70] * k[71];
|
||||
//ak[10] := k[37] * k[78] - k[70] * k[70];
|
||||
end;
|
||||
|
||||
function TNeverTellMeTheOdds.CalcRockThrowCollisionOptions(constref AHailstone0, AHailstone1, AHailstone2: THailstone):
|
||||
TInt64Array;
|
||||
var
|
||||
a0, a1: TBigIntPolynomial;
|
||||
a0Roots, a1Roots: TBigIntArray;
|
||||
options: specialize TList<Int64>;
|
||||
i, j: TBigInt;
|
||||
val: Int64;
|
||||
begin
|
||||
CalcCollisionPolynomials(AHailstone0, AHailstone1, AHailstone2, a0, a1);
|
||||
a0Roots := TPolynomialRoots.BisectInteger(a0, 64);
|
||||
a1Roots := TPolynomialRoots.BisectInteger(a1, 64);
|
||||
|
||||
options := specialize TList<Int64>.Create;
|
||||
for i in a0Roots do
|
||||
for j in a1Roots do
|
||||
if (i = j) and i.TryToInt64(val) then
|
||||
options.Add(val);
|
||||
Result := options.ToArray;
|
||||
options.Free;
|
||||
end;
|
||||
|
||||
function TNeverTellMeTheOdds.ValidateRockThrow(constref AHailstone0, AHailstone1, AHailstone2: THailstone; const AT0,
|
||||
AT1: Int64): Int64;
|
||||
var
|
||||
divisor, t: Int64;
|
||||
rock: THailstone;
|
||||
begin
|
||||
// V_x = (V_0 * t_0 - V_1 * t_1 + P_0 - P_1) / (t_0 - t_1)
|
||||
divisor := AT0 - AT1;
|
||||
rock := THailstone.Create;
|
||||
rock.V0 := (AHailstone0.V0 * AT0 - AHailstone1.V0 * AT1 + AHailstone0.P0 - AHailstone1.P0) div divisor;
|
||||
rock.V1 := (AHailstone0.V1 * AT0 - AHailstone1.V1 * AT1 + AHailstone0.P1 - AHailstone1.P1) div divisor;
|
||||
rock.V2 := (AHailstone0.V2 * AT0 - AHailstone1.V2 * AT1 + AHailstone0.P2 - AHailstone1.P2) div divisor;
|
||||
|
||||
// P_x = (V_0 - V_x) * t_0 + P_0
|
||||
rock.P0 := (AHailstone0.V0 - rock.V0) * AT0 + AHailstone0.P0;
|
||||
rock.P1 := (AHailstone0.V1 - rock.V1) * AT0 + AHailstone0.P1;
|
||||
rock.P2 := (AHailstone0.V2 - rock.V2) * AT0 + AHailstone0.P2;
|
||||
|
||||
Result := rock.P0 + rock.P1 + rock.P2;
|
||||
|
||||
// Checks collision with the third hailstone.
|
||||
if ((AHailstone2.V0 = rock.V0) and (AHailstone2.P0 <> rock.P0))
|
||||
or ((AHailstone2.V1 = rock.V1) and (AHailstone2.P1 <> rock.P1))
|
||||
or ((AHailstone2.V2 = rock.V2) and (AHailstone2.P2 <> rock.P2)) then
|
||||
Result := 0
|
||||
else begin
|
||||
t := (AHailstone2.P0 - rock.P0) div (rock.V0 - AHailstone2.V0);
|
||||
if (t <> (AHailstone2.P1 - rock.P1) div (rock.V1 - AHailstone2.V1))
|
||||
or (t <> (AHailstone2.P2 - rock.P2) div (rock.V2 - AHailstone2.V2)) then
|
||||
Result := 0;
|
||||
end;
|
||||
|
||||
rock.Free;
|
||||
end;
|
||||
|
||||
constructor TNeverTellMeTheOdds.Create(const AMin: Int64; const AMax: Int64);
|
||||
|
@ -617,19 +508,15 @@ end;
|
|||
|
||||
procedure TNeverTellMeTheOdds.Finish;
|
||||
var
|
||||
i, j, k: Integer;
|
||||
i, j: Integer;
|
||||
begin
|
||||
for i := 0 to FHailstones.Count - 2 do
|
||||
for j := i + 1 to FHailstones.Count - 1 do
|
||||
if AreIntersecting(FHailstones[i], FHailstones[j]) then
|
||||
Inc(FPart1);
|
||||
|
||||
for i := 0 to FHailstones.Count - 1 do
|
||||
for j := 0 to FHailstones.Count - 1 do
|
||||
for k := 0 to FHailstones.Count - 1 do
|
||||
if (i <> j) and (i <> k) and (j <> k) then
|
||||
FindRockThrow(i, j, k);
|
||||
//FindRockThrow(0, 1, 2);
|
||||
if FHailstones.Count >= 3 then
|
||||
FPart2 := FindRockThrow(0, 1, 2);
|
||||
end;
|
||||
|
||||
function TNeverTellMeTheOdds.GetDataFileName: string;
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
Solutions to the Advent Of Code.
|
||||
Copyright (C) 2023 Stefan Müller
|
||||
Copyright (C) 2023-2024 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
|
||||
|
@ -22,7 +22,7 @@ unit UPipeMaze;
|
|||
interface
|
||||
|
||||
uses
|
||||
Classes, SysUtils, Generics.Collections, USolver;
|
||||
Classes, SysUtils, Generics.Collections, USolver, UCommon;
|
||||
|
||||
const
|
||||
CStartChar = 'S';
|
||||
|
@ -115,24 +115,18 @@ procedure TPipeMaze.InitStepMappings;
|
|||
var
|
||||
i: Integer;
|
||||
begin
|
||||
FStepMappings.Add(TStepMapping.Create(Point(0, 1), Point(0, 1), '|',
|
||||
TPointArray.Create(Point(1, 0)),
|
||||
TPointArray.Create(Point(-1, 0))));
|
||||
FStepMappings.Add(TStepMapping.Create(Point(1, 0), Point(1, 0), '-',
|
||||
TPointArray.Create(Point(0, -1)),
|
||||
TPointArray.Create(Point(0, 1))));
|
||||
FStepMappings.Add(TStepMapping.Create(Point(-1, 0), Point(0, -1), 'L',
|
||||
TPointArray.Create(Point(0, 1), Point(-1, 1), Point(-1, 0)),
|
||||
[]));
|
||||
FStepMappings.Add(TStepMapping.Create(Point(0, 1), Point(-1, 0), 'J',
|
||||
TPointArray.Create(Point(1, 0), Point(1, 1), Point(0, 1)),
|
||||
[]));
|
||||
FStepMappings.Add(TStepMapping.Create(Point(1, 0), Point(0, 1), '7',
|
||||
TPointArray.Create(Point(0, -1), Point(1, -1), Point(1, 0)),
|
||||
[]));
|
||||
FStepMappings.Add(TStepMapping.Create(Point(0, -1), Point(1, 0), 'F',
|
||||
TPointArray.Create(Point(-1, 0), Point(-1, -1), Point(0, -1)),
|
||||
[]));
|
||||
FStepMappings.Add(TStepMapping.Create(CDirectionDown, CDirectionDown, '|',
|
||||
TPointArray.Create(CDirectionRight), TPointArray.Create(CDirectionLeft)));
|
||||
FStepMappings.Add(TStepMapping.Create(CDirectionRight, CDirectionRight, '-',
|
||||
TPointArray.Create(CDirectionUp), TPointArray.Create(CDirectionDown)));
|
||||
FStepMappings.Add(TStepMapping.Create(CDirectionLeft, CDirectionUp, 'L',
|
||||
TPointArray.Create(CDirectionDown, CDirectionLeftDown, CDirectionLeft), []));
|
||||
FStepMappings.Add(TStepMapping.Create(CDirectionDown, CDirectionLeft, 'J',
|
||||
TPointArray.Create(CDirectionRight, CDirectionRightDown, CDirectionDown), []));
|
||||
FStepMappings.Add(TStepMapping.Create(CDirectionRight, CDirectionDown, '7',
|
||||
TPointArray.Create(CDirectionUp, CDirectionRightUp, CDirectionRight), []));
|
||||
FStepMappings.Add(TStepMapping.Create(CDirectionUp, CDirectionRight, 'F',
|
||||
TPointArray.Create(CDirectionLeft, CDirectionLeftUp, CDirectionUp), []));
|
||||
|
||||
// Adds reverse step mappings.
|
||||
for i := 0 to FStepMappings.Count - 1 do
|
||||
|
@ -231,13 +225,12 @@ end;
|
|||
|
||||
function TPipeMaze.TryCountEnclosureSide(const AChar: Char; out OCount: Int64): Boolean;
|
||||
var
|
||||
directions: TPointArray;
|
||||
stack: specialize TStack<TPoint>;
|
||||
i, j: Integer;
|
||||
position, direction, neighbor: TPoint;
|
||||
position, neighbor: TPoint;
|
||||
pdirection: PPoint;
|
||||
c: Char;
|
||||
begin
|
||||
directions := TPointArray.Create(Point(0, -1), Point(-1, 0), Point(0, 1), Point(1, 0));
|
||||
stack := specialize TStack<TPoint>.Create;
|
||||
|
||||
OCount := 0;
|
||||
|
@ -259,12 +252,12 @@ begin
|
|||
begin
|
||||
position := stack.Pop;
|
||||
|
||||
for direction in directions do
|
||||
for pdirection in CPCardinalDirections do
|
||||
begin
|
||||
if CheckMapBounds(position + direction) then
|
||||
if CheckMapBounds(position + pdirection^) then
|
||||
begin
|
||||
// Checks the neighboring position.
|
||||
neighbor := position + direction;
|
||||
neighbor := position + pdirection^;
|
||||
c := GetEnclosureMapChar(neighbor);
|
||||
if (c <> CFloodFillChar) and (c <> CPathChar) then
|
||||
begin
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
Solutions to the Advent Of Code.
|
||||
Copyright (C) 2023 Stefan Müller
|
||||
Copyright (C) 2023-2024 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
|
||||
|
@ -22,7 +22,7 @@ unit UPulsePropagation;
|
|||
interface
|
||||
|
||||
uses
|
||||
Classes, SysUtils, Generics.Collections, Math, USolver;
|
||||
Classes, SysUtils, Generics.Collections, USolver;
|
||||
|
||||
type
|
||||
TModule = class;
|
||||
|
@ -49,12 +49,12 @@ type
|
|||
public
|
||||
property Name: string read FName;
|
||||
property OutputNames: TStringList read FOutputNames;
|
||||
property Outputs: TModules read FOutputs;
|
||||
constructor Create(const AName: string);
|
||||
destructor Destroy; override;
|
||||
procedure AddInput(const AInput: TModule); virtual;
|
||||
procedure AddOutput(const AOutput: TModule); virtual;
|
||||
function ReceivePulse(const ASender: TModule; const AIsHigh: Boolean): TPulses; virtual; abstract;
|
||||
function IsOff: Boolean; virtual;
|
||||
end;
|
||||
|
||||
{ TBroadcasterModule }
|
||||
|
@ -71,31 +71,29 @@ type
|
|||
FState: Boolean;
|
||||
public
|
||||
function ReceivePulse(const ASender: TModule; const AIsHigh: Boolean): TPulses; override;
|
||||
function IsOff: Boolean; override;
|
||||
end;
|
||||
|
||||
{ TConjectionBuffer }
|
||||
{ TConjunctionInputBuffer }
|
||||
|
||||
TConjectionBuffer = record
|
||||
TConjunctionInputBuffer = record
|
||||
Input: TModule;
|
||||
LastState: Boolean;
|
||||
end;
|
||||
|
||||
TConjectionBuffers = specialize TList<TConjectionBuffer>;
|
||||
TConjunctionInputBuffers = specialize TList<TConjunctionInputBuffer>;
|
||||
|
||||
{ TConjunctionModule }
|
||||
|
||||
TConjunctionModule = class(TModule)
|
||||
private
|
||||
FInputBuffers: TConjectionBuffers;
|
||||
FInputBuffers: TConjunctionInputBuffers;
|
||||
procedure UpdateInputBuffer(constref AInput: TModule; const AState: Boolean);
|
||||
function AreAllBuffersSame(const AIsHigh: Boolean): Boolean;
|
||||
function AreAllBuffersHigh: Boolean;
|
||||
public
|
||||
constructor Create(const AName: string);
|
||||
destructor Destroy; override;
|
||||
procedure AddInput(const AInput: TModule); override;
|
||||
function ReceivePulse(const ASender: TModule; const AIsHigh: Boolean): TPulses; override;
|
||||
function IsOff: Boolean; override;
|
||||
end;
|
||||
|
||||
{ TEndpointModule }
|
||||
|
@ -111,8 +109,6 @@ type
|
|||
LowCount, HighCount: Integer;
|
||||
end;
|
||||
|
||||
TButtonResults = specialize TList<TButtonResult>;
|
||||
|
||||
{ TPulsePropagation }
|
||||
|
||||
TPulsePropagation = class(TSolver)
|
||||
|
@ -121,7 +117,7 @@ type
|
|||
FBroadcaster: TModule;
|
||||
procedure UpdateModuleConnections;
|
||||
function PushButton: TButtonResult;
|
||||
function AreAllModulesOff: Boolean;
|
||||
function CalcCounterTarget(const AFirstFlipFlop: TModule): Int64;
|
||||
public
|
||||
constructor Create;
|
||||
destructor Destroy; override;
|
||||
|
@ -180,11 +176,6 @@ begin
|
|||
FOutputs.Add(AOutput);
|
||||
end;
|
||||
|
||||
function TModule.IsOff: Boolean;
|
||||
begin
|
||||
Result := True;
|
||||
end;
|
||||
|
||||
{ TBroadcasterModule }
|
||||
|
||||
function TBroadcasterModule.ReceivePulse(const ASender: TModule; const AIsHigh: Boolean): TPulses;
|
||||
|
@ -204,17 +195,12 @@ begin
|
|||
end;
|
||||
end;
|
||||
|
||||
function TFlipFlopModule.IsOff: Boolean;
|
||||
begin
|
||||
Result := not FState;
|
||||
end;
|
||||
|
||||
{ TConjunctionModule }
|
||||
|
||||
procedure TConjunctionModule.UpdateInputBuffer(constref AInput: TModule; const AState: Boolean);
|
||||
var
|
||||
i: Integer;
|
||||
buffer: TConjectionBuffer;
|
||||
buffer: TConjunctionInputBuffer;
|
||||
begin
|
||||
for i := 0 to FInputBuffers.Count - 1 do
|
||||
if FInputBuffers[i].Input = AInput then
|
||||
|
@ -226,13 +212,13 @@ begin
|
|||
end;
|
||||
end;
|
||||
|
||||
function TConjunctionModule.AreAllBuffersSame(const AIsHigh: Boolean): Boolean;
|
||||
function TConjunctionModule.AreAllBuffersHigh: Boolean;
|
||||
var
|
||||
buffer: TConjectionBuffer;
|
||||
buffer: TConjunctionInputBuffer;
|
||||
begin
|
||||
Result := True;
|
||||
for buffer in FInputBuffers do
|
||||
if buffer.LastState <> AIsHigh then
|
||||
if not buffer.LastState then
|
||||
begin
|
||||
Result := False;
|
||||
Exit;
|
||||
|
@ -242,7 +228,7 @@ end;
|
|||
constructor TConjunctionModule.Create(const AName: string);
|
||||
begin
|
||||
inherited Create(AName);
|
||||
FInputBuffers := TConjectionBuffers.Create;
|
||||
FInputBuffers := TConjunctionInputBuffers.Create;
|
||||
end;
|
||||
|
||||
destructor TConjunctionModule.Destroy;
|
||||
|
@ -253,7 +239,7 @@ end;
|
|||
|
||||
procedure TConjunctionModule.AddInput(const AInput: TModule);
|
||||
var
|
||||
buffer: TConjectionBuffer;
|
||||
buffer: TConjunctionInputBuffer;
|
||||
begin
|
||||
buffer.Input := AInput;
|
||||
buffer.LastState := False;
|
||||
|
@ -263,12 +249,7 @@ end;
|
|||
function TConjunctionModule.ReceivePulse(const ASender: TModule; const AIsHigh: Boolean): TPulses;
|
||||
begin
|
||||
UpdateInputBuffer(ASender, AIsHigh);
|
||||
Result := CreatePulsesToOutputs(not AreAllBuffersSame(True));
|
||||
end;
|
||||
|
||||
function TConjunctionModule.IsOff: Boolean;
|
||||
begin
|
||||
Result := AreAllBuffersSame(False);
|
||||
Result := CreatePulsesToOutputs(not AreAllBuffersHigh);
|
||||
end;
|
||||
|
||||
{ TEndpointModule }
|
||||
|
@ -342,17 +323,39 @@ begin
|
|||
queue.Free;
|
||||
end;
|
||||
|
||||
function TPulsePropagation.AreAllModulesOff: Boolean;
|
||||
function TPulsePropagation.CalcCounterTarget(const AFirstFlipFlop: TModule): Int64;
|
||||
var
|
||||
module: TModule;
|
||||
binDigit: Int64;
|
||||
current, next: TModule;
|
||||
i: Integer;
|
||||
begin
|
||||
Result := True;
|
||||
for module in FModules do
|
||||
if not module.IsOff then
|
||||
Result := 0;
|
||||
binDigit := 1;
|
||||
current := AFirstFlipFlop;
|
||||
while True do
|
||||
begin
|
||||
if current.Outputs.Count = 1 then
|
||||
begin
|
||||
Result := False;
|
||||
Exit;
|
||||
current := current.Outputs.First;
|
||||
if current is TConjunctionModule then
|
||||
begin
|
||||
Result := Result + binDigit;
|
||||
Break;
|
||||
end;
|
||||
end
|
||||
else begin
|
||||
Result := Result + binDigit;
|
||||
i := 0;
|
||||
repeat
|
||||
if i = current.Outputs.Count then
|
||||
Exit;
|
||||
next := current.Outputs[i];
|
||||
Inc(i);
|
||||
until next is TFlipFlopModule;
|
||||
current := next;
|
||||
end;
|
||||
binDigit := binDigit << 1;
|
||||
end;
|
||||
end;
|
||||
|
||||
constructor TPulsePropagation.Create;
|
||||
|
@ -392,42 +395,26 @@ end;
|
|||
|
||||
procedure TPulsePropagation.Finish;
|
||||
var
|
||||
results: TButtonResults;
|
||||
finalResult: TButtonResult;
|
||||
cycles, remainder, i, j, max: Integer;
|
||||
result, accumulated: TButtonResult;
|
||||
i: Integer;
|
||||
module: TModule;
|
||||
begin
|
||||
UpdateModuleConnections;
|
||||
|
||||
// The pulse counts for the full puzzle input repeat themselves in a very specific way, but the system state does not.
|
||||
// This indicates there is a better solution for this problem.
|
||||
// TODO: See if there is a better solution based on the repeating patterns in the pulse counts.
|
||||
results := TButtonResults.Create;
|
||||
repeat
|
||||
results.Add(PushButton);
|
||||
until AreAllModulesOff or (results.Count >= CButtonPushes);
|
||||
|
||||
DivMod(CButtonPushes, results.Count, cycles, remainder);
|
||||
finalResult.LowCount := 0;
|
||||
finalResult.HighCount := 0;
|
||||
max := results.Count - 1;
|
||||
for j := 0 to 1 do
|
||||
accumulated.LowCount := 0;
|
||||
accumulated.HighCount := 0;
|
||||
for i := 1 to CButtonPushes do
|
||||
begin
|
||||
for i := 0 to max do
|
||||
begin
|
||||
Inc(finalResult.LowCount, results[i].LowCount);
|
||||
Inc(finalResult.HighCount, results[i].HighCount);
|
||||
end;
|
||||
if j = 0 then
|
||||
begin
|
||||
finalResult.LowCount := finalResult.LowCount * cycles;
|
||||
finalResult.HighCount := finalResult.HighCount * cycles;
|
||||
max := remainder - 1;
|
||||
end;
|
||||
result := PushButton;
|
||||
Inc(accumulated.LowCount, result.LowCount);
|
||||
Inc(accumulated.HighCount, result.HighCount);
|
||||
end;
|
||||
|
||||
results.Free;
|
||||
FPart1 := accumulated.LowCount * accumulated.HighCount;
|
||||
|
||||
FPart1 := finalResult.LowCount * finalResult.HighCount;
|
||||
FPart2 := 1;
|
||||
for module in FBroadcaster.Outputs do
|
||||
FPart2 := FPart2 * CalcCounterTarget(module);
|
||||
end;
|
||||
|
||||
function TPulsePropagation.GetDataFileName: string;
|
||||
|
|
|
@ -230,13 +230,11 @@ begin
|
|||
end;
|
||||
|
||||
// Updates disintegration flag.
|
||||
if ABrick.SupportBricks.Count = 1 then
|
||||
if (ABrick.SupportBricks.Count = 1)
|
||||
and ABrick.SupportBricks[0].IsDisintegratable then
|
||||
begin
|
||||
if ABrick.SupportBricks[0].IsDisintegratable then
|
||||
begin
|
||||
ABrick.SupportBricks[0].IsDisintegratable := False;
|
||||
Dec(FPart1);
|
||||
end;
|
||||
ABrick.SupportBricks[0].IsDisintegratable := False;
|
||||
Dec(FPart1);
|
||||
end;
|
||||
for i := 0 to ABrick.SupportBricks.Count - 1 do
|
||||
ABrick.SupportBricks[i].AddTopBrick(ABrick);
|
||||
|
|
|
@ -0,0 +1,308 @@
|
|||
{
|
||||
Solutions to the Advent Of Code.
|
||||
Copyright (C) 2024 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/>.
|
||||
}
|
||||
|
||||
unit USnowverload;
|
||||
|
||||
{$mode ObjFPC}{$H+}
|
||||
|
||||
interface
|
||||
|
||||
uses
|
||||
Classes, SysUtils, Generics.Collections, USolver;
|
||||
|
||||
type
|
||||
TSnowComponent = class;
|
||||
TSnowComponents = specialize TObjectList<TSnowComponent>;
|
||||
|
||||
{ TSnowComponent }
|
||||
|
||||
TSnowComponent = class
|
||||
private
|
||||
FName: string;
|
||||
FMergedComponents: TSnowComponents;
|
||||
FMergeParent: TSnowComponent;
|
||||
public
|
||||
property Name: string read FName;
|
||||
procedure AddMerged(constref AMerge: TSnowComponent);
|
||||
function GetRootComponent: TSnowComponent;
|
||||
function GetMergeCount: Integer;
|
||||
procedure Reset;
|
||||
constructor Create(const AName: string);
|
||||
destructor Destroy; override;
|
||||
end;
|
||||
|
||||
{ TWire }
|
||||
|
||||
TWire = class
|
||||
private
|
||||
FComponent1, FComponent2: TSnowComponent;
|
||||
function GetComponent1: TSnowComponent;
|
||||
function GetComponent2: TSnowComponent;
|
||||
public
|
||||
property Component1: TSnowComponent read GetComponent1;
|
||||
property Component2: TSnowComponent read GetComponent2;
|
||||
function IsLoop: Boolean;
|
||||
constructor Create(constref AComponent1, AComponent2: TSnowComponent);
|
||||
end;
|
||||
|
||||
TWires = specialize TObjectList<TWire>;
|
||||
|
||||
{ TNetwork }
|
||||
|
||||
TNetwork = class
|
||||
private
|
||||
FComponents: TSnowComponents;
|
||||
FWires, FContracted: TWires;
|
||||
public
|
||||
function FindOrAddComponent(const AName: string): TSnowComponent;
|
||||
procedure AddWire(constref AComponent1, AComponent2: TSnowComponent);
|
||||
function RunMinCutContractionAlgorithm: Integer;
|
||||
procedure Reset;
|
||||
function GetResult: Integer;
|
||||
constructor Create;
|
||||
destructor Destroy; override;
|
||||
end;
|
||||
|
||||
{ TSnowverload }
|
||||
|
||||
TSnowverload = class(TSolver)
|
||||
private
|
||||
FNetwork: TNetwork;
|
||||
public
|
||||
constructor Create;
|
||||
destructor Destroy; override;
|
||||
procedure ProcessDataLine(const ALine: string); override;
|
||||
procedure Finish; override;
|
||||
function GetDataFileName: string; override;
|
||||
function GetPuzzleName: string; override;
|
||||
end;
|
||||
|
||||
implementation
|
||||
|
||||
{ TSnowComponent }
|
||||
|
||||
procedure TSnowComponent.AddMerged(constref AMerge: TSnowComponent);
|
||||
begin
|
||||
FMergedComponents.Add(AMerge);
|
||||
AMerge.FMergeParent := Self;
|
||||
end;
|
||||
|
||||
function TSnowComponent.GetRootComponent: TSnowComponent;
|
||||
begin
|
||||
Result := Self;
|
||||
while Result.FMergeParent <> nil do
|
||||
Result := Result.FMergeParent;
|
||||
end;
|
||||
|
||||
function TSnowComponent.GetMergeCount: Integer;
|
||||
var
|
||||
c: TSnowComponent;
|
||||
begin
|
||||
Result := 1;
|
||||
for c in FMergedComponents do
|
||||
Inc(Result, c.GetMergeCount);
|
||||
end;
|
||||
|
||||
procedure TSnowComponent.Reset;
|
||||
begin
|
||||
FMergedComponents.Clear;
|
||||
FMergeParent := nil;
|
||||
end;
|
||||
|
||||
constructor TSnowComponent.Create(const AName: string);
|
||||
begin
|
||||
FName := AName;
|
||||
FMergedComponents := TSnowComponents.Create(False);
|
||||
FMergeParent := nil;
|
||||
end;
|
||||
|
||||
destructor TSnowComponent.Destroy;
|
||||
begin
|
||||
FMergedComponents.Free;
|
||||
inherited Destroy;
|
||||
end;
|
||||
|
||||
{ TWire }
|
||||
|
||||
function TWire.GetComponent1: TSnowComponent;
|
||||
begin
|
||||
Result := FComponent1.GetRootComponent;
|
||||
end;
|
||||
|
||||
function TWire.GetComponent2: TSnowComponent;
|
||||
begin
|
||||
Result := FComponent2.GetRootComponent;
|
||||
end;
|
||||
|
||||
function TWire.IsLoop: Boolean;
|
||||
begin
|
||||
Result := Component1 = Component2;
|
||||
end;
|
||||
|
||||
constructor TWire.Create(constref AComponent1, AComponent2: TSnowComponent);
|
||||
begin
|
||||
FComponent1 := AComponent1;
|
||||
FComponent2 := AComponent2;
|
||||
end;
|
||||
|
||||
{ TNetwork }
|
||||
|
||||
function TNetwork.FindOrAddComponent(const AName: string): TSnowComponent;
|
||||
var
|
||||
found: Boolean;
|
||||
begin
|
||||
found := False;
|
||||
for Result in FComponents do
|
||||
if Result.Name = AName then
|
||||
begin
|
||||
found := True;
|
||||
Break;
|
||||
end;
|
||||
if not found then
|
||||
begin
|
||||
Result := TSnowComponent.Create(AName);
|
||||
FComponents.Add(Result);
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure TNetwork.AddWire(constref AComponent1, AComponent2: TSnowComponent);
|
||||
begin
|
||||
FWires.Add(TWire.Create(AComponent1, AComponent2));
|
||||
end;
|
||||
|
||||
function TNetwork.RunMinCutContractionAlgorithm: Integer;
|
||||
var
|
||||
r, count: Integer;
|
||||
w: TWire;
|
||||
begin
|
||||
count := FComponents.Count;
|
||||
while count > 2 do
|
||||
begin
|
||||
// Determines contraction wire.
|
||||
r := Random(FWires.Count - 1);
|
||||
w := FWires.ExtractIndex(r);
|
||||
FContracted.Add(w);
|
||||
|
||||
// Merges c2 into c1.
|
||||
if not w.IsLoop then
|
||||
begin
|
||||
w.Component1.AddMerged(w.Component2);
|
||||
Dec(count);
|
||||
end;
|
||||
end;
|
||||
|
||||
Result := 0;
|
||||
for w in FWires do
|
||||
if not w.IsLoop then
|
||||
Inc(Result);
|
||||
end;
|
||||
|
||||
procedure TNetwork.Reset;
|
||||
var
|
||||
c: TSnowComponent;
|
||||
i: Integer;
|
||||
w: TWire;
|
||||
begin
|
||||
for c in FComponents do
|
||||
c.Reset;
|
||||
|
||||
i := FContracted.Count - 1;
|
||||
while i >= 0 do
|
||||
begin
|
||||
w := FContracted.ExtractIndex(i);
|
||||
FWires.Add(w);
|
||||
Dec(i);
|
||||
end;
|
||||
end;
|
||||
|
||||
function TNetwork.GetResult: Integer;
|
||||
begin
|
||||
Result := FComponents[0].GetRootComponent.GetMergeCount;
|
||||
Result := Result * (FComponents.Count - Result);
|
||||
end;
|
||||
|
||||
constructor TNetwork.Create;
|
||||
begin
|
||||
FComponents := TSnowComponents.Create;
|
||||
FWires := TWires.Create;
|
||||
FContracted := TWires.Create;
|
||||
end;
|
||||
|
||||
destructor TNetwork.Destroy;
|
||||
begin
|
||||
FComponents.Free;
|
||||
FWires.Free;
|
||||
FContracted.Free;
|
||||
inherited Destroy;
|
||||
end;
|
||||
|
||||
{ TSnowverload }
|
||||
|
||||
constructor TSnowverload.Create;
|
||||
begin
|
||||
FNetwork := TNetwork.Create;
|
||||
end;
|
||||
|
||||
destructor TSnowverload.Destroy;
|
||||
begin
|
||||
FNetwork.Free;
|
||||
inherited Destroy;
|
||||
end;
|
||||
|
||||
procedure TSnowverload.ProcessDataLine(const ALine: string);
|
||||
var
|
||||
split: TStringArray;
|
||||
c1, c2: TSnowComponent;
|
||||
i: Integer;
|
||||
begin
|
||||
split := ALine.Split([':', ' ']);
|
||||
c1 := FNetwork.FindOrAddComponent(split[0]);
|
||||
for i := 2 to Length(split) - 1 do
|
||||
begin
|
||||
c2 := FNetwork.FindOrAddComponent(split[i]);
|
||||
FNetwork.AddWire(c1, c2);
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure TSnowverload.Finish;
|
||||
var
|
||||
cut: Integer;
|
||||
begin
|
||||
// Karger's algorithm with known minimum cut size.
|
||||
// See https://en.wikipedia.org/wiki/Karger%27s_algorithm
|
||||
Randomize;
|
||||
cut := FNetwork.RunMinCutContractionAlgorithm;
|
||||
while cut > 3 do
|
||||
begin
|
||||
FNetwork.Reset;
|
||||
cut := FNetwork.RunMinCutContractionAlgorithm;
|
||||
end;
|
||||
FPart1 := FNetwork.GetResult;
|
||||
end;
|
||||
|
||||
function TSnowverload.GetDataFileName: string;
|
||||
begin
|
||||
Result := 'snowverload.txt';
|
||||
end;
|
||||
|
||||
function TSnowverload.GetPuzzleName: string;
|
||||
begin
|
||||
Result := 'Day 25: Snowverload';
|
||||
end;
|
||||
|
||||
end.
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
Solutions to the Advent Of Code.
|
||||
Copyright (C) 2023 Stefan Müller
|
||||
Copyright (C) 2023-2024 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
|
||||
|
@ -22,23 +22,25 @@ unit UStepCounter;
|
|||
interface
|
||||
|
||||
uses
|
||||
Classes, SysUtils, Generics.Collections, USolver;
|
||||
Classes, SysUtils, USolver, UCommon;
|
||||
|
||||
type
|
||||
TPoints = specialize TList<TPoint>;
|
||||
|
||||
{ TStepCounter }
|
||||
|
||||
TStepCounter = class(TSolver)
|
||||
private
|
||||
FLines: TStringList;
|
||||
FWidth, FHeight, FMaxSteps: Integer;
|
||||
FWidth, FHeight, FMaxSteps1, FMaxSteps2: Integer;
|
||||
function FindStart: TPoint;
|
||||
function IsInBounds(constref APoint: TPoint): Boolean;
|
||||
function GetPosition(constref APoint: TPoint): Char;
|
||||
procedure SetPosition(constref APoint: TPoint; const AValue: Char);
|
||||
procedure PrepareMap;
|
||||
function DoSteps(const AMaxSteps: Integer): Int64;
|
||||
function CalcTargetPlotsOnInfiniteMap(const AMaxSteps: Integer): Int64;
|
||||
public
|
||||
constructor Create(const AMaxSteps: Integer = 64);
|
||||
constructor Create(const AMaxStepsPart1: Integer = 64; const AMaxStepsPart2: Integer = 26501365);
|
||||
destructor Destroy; override;
|
||||
procedure ProcessDataLine(const ALine: string); override;
|
||||
procedure Finish; override;
|
||||
|
@ -49,8 +51,8 @@ type
|
|||
const
|
||||
CStartChar = 'S';
|
||||
CPlotChar = '.';
|
||||
CRockChar = '#';
|
||||
CTraversedChar = '+';
|
||||
CDirections: array of TPoint = ((X: 1; Y: 0), (X: -1; Y: 0), (X: 0; Y: 1), (X: 0; Y: -1));
|
||||
|
||||
implementation
|
||||
|
||||
|
@ -89,9 +91,167 @@ begin
|
|||
FLines[APoint.Y] := s;
|
||||
end;
|
||||
|
||||
constructor TStepCounter.Create(const AMaxSteps: Integer);
|
||||
procedure TStepCounter.PrepareMap;
|
||||
var
|
||||
i, j: Integer;
|
||||
begin
|
||||
FMaxSteps := AMaxSteps;
|
||||
for i := 2 to FWidth - 1 do
|
||||
for j := 1 to FHeight - 2 do
|
||||
if (FLines[j][i] <> CRockChar) and (FLines[j - 1][i] = CRockChar) and (FLines[j + 1][i] = CRockChar)
|
||||
and (FLines[j][i - 1] = CRockChar) and (FLines[j][i + 1] = CRockChar) then
|
||||
SetPosition(Point(i, j), CRockChar);
|
||||
end;
|
||||
|
||||
function TStepCounter.DoSteps(const AMaxSteps: Integer): Int64;
|
||||
var
|
||||
mod2, currentStep: Integer;
|
||||
currentPlots, nextPlots, temp: TPoints;
|
||||
plot, next: TPoint;
|
||||
pdirection: PPoint;
|
||||
begin
|
||||
currentStep := 0;
|
||||
currentPlots := TPoints.Create;
|
||||
currentPlots.Add(FindStart);
|
||||
nextPlots := TPoints.Create;
|
||||
|
||||
// Counts the start if max steps is even.
|
||||
mod2 := AMaxSteps and 1;
|
||||
if mod2 = 0 then
|
||||
Result := 1
|
||||
else
|
||||
Result := 0;
|
||||
|
||||
while currentStep < AMaxSteps do
|
||||
begin
|
||||
for plot in currentPlots do
|
||||
for pdirection in CPCardinalDirections do
|
||||
begin
|
||||
next := plot + pdirection^;
|
||||
if IsInBounds(next) and (GetPosition(next) = CPlotChar) then
|
||||
begin
|
||||
SetPosition(next, CTraversedChar);
|
||||
nextPlots.Add(next);
|
||||
end;
|
||||
end;
|
||||
|
||||
currentPlots.Clear;
|
||||
temp := currentPlots;
|
||||
currentPlots := nextPlots;
|
||||
nextPlots := temp;
|
||||
Inc(currentStep);
|
||||
|
||||
// Positions where the number of steps are even or odd (for even or odd AMaxSteps, respectively) can be reached with
|
||||
// trivial backtracking, so they count.
|
||||
if currentStep and 1 = mod2 then
|
||||
Inc(Result, currentPlots.Count);
|
||||
end;
|
||||
|
||||
currentPlots.Free;
|
||||
nextPlots.Free;
|
||||
end;
|
||||
|
||||
function TStepCounter.CalcTargetPlotsOnInfiniteMap(const AMaxSteps: Integer): Int64;
|
||||
var
|
||||
half, k, i, j: Integer;
|
||||
factor1, factor1B, factor2, factor4A: Int64;
|
||||
begin
|
||||
Result := 0;
|
||||
|
||||
// Asserts square input map with odd size.
|
||||
if (FWidth <> FHeight) or (FWidth and 1 = 0) then
|
||||
Exit;
|
||||
// Asserts half map size is odd.
|
||||
half := FWidth shr 1;
|
||||
if half and 1 = 0 then
|
||||
Exit;
|
||||
// Asserts that there is an even k such that maximum number of steps is equal to k + 1/2 times the map size.
|
||||
// k is the number of visited repeated maps, not counting the start map, when taking all steps in a straight line in
|
||||
// any of the four directions.
|
||||
k := (AMaxSteps - half) div FWidth;
|
||||
if (k and 1 = 0) and (AMaxSteps <> k * FWidth + half) then
|
||||
Exit;
|
||||
|
||||
// Assuming that the rocks on the map are sparse enough, and the central vertical and horizontal lines are empty,
|
||||
// every free plot with odd (Manhattan) distance (not larger than AMaxSteps) to the start plot (because of trivial
|
||||
// backtracking) on the maps is reachable, essentially formning a 45-degree rotated square shape centered on the start
|
||||
// plot.
|
||||
|
||||
// Inside this "diamond" shape, 2k(k - 1) + 1 (k-th centered square number) copies of the map are traversed fully.
|
||||
// However, there are two different types of these. (k - 1)^2 are traversed like the start map, where all plots with
|
||||
// odd distance to the center are reachable (type 1), and k^2 are traversed such that all plots within odd distance to
|
||||
// the center are reachable (type 2).
|
||||
|
||||
// On each of the corners of this "diamond" shape, there is one map traversed fully except for two adjacent of its
|
||||
// corner triangles (type 3).
|
||||
|
||||
// On each of the edges of this "diamond" shape, there are k maps where only the corner triangle facing towards the
|
||||
// shapes center is traversed (type 4), and k - 1 maps that are fully traversed except for the corner triangle facing
|
||||
// away from the shapes center (type 5).
|
||||
|
||||
// The four different versions of type 4 do not overlap within a map, so they can be counted together (type 4A).
|
||||
|
||||
// Types 1, 3, and 5 share patterns, so they can also be counted together, but the parts of the patterns have
|
||||
// different counts. Each corner (type 1A) is traversed (k - 1)^2 times for type 1, 2 times for type 3, and 3(k - 1)
|
||||
// for type 5, that is (k - 1)^2 + 3k - 1 in total. The center (type 1B) is traversed (k - 1)^2 times for type 1, 4
|
||||
// times for type 3, and 4(k - 1) for type 5, that is (k - 1)^2 + 4k.
|
||||
// Equivalently, instead type 1 is traversed (k - 1)^2 + 3k - 1 times and type 1B is traversed k + 1 times.
|
||||
|
||||
// Types example for k = 2, half = 5:
|
||||
// 4 5 2 4A
|
||||
// ........... .....O.O.O. O.O.O.O.O.O O.O.O.O.O.O
|
||||
// ........... ....O.O.O.O .O.O.O.O.O. .O.O...O.O.
|
||||
// ........... ...O.O.O.O. O.O.O.O.O.O O.O.....O.O
|
||||
// ......#.... ..O.O.#.O.O .O.O.O#O.O. .O....#..O.
|
||||
// ...#....... .O.#.O.O.O. O.O#O.O.O.O O..#......O
|
||||
// ........... O.O.O.O.O.O .O.O.O.O.O. ...........
|
||||
// ....#..#..O .O.O#O.#.O. O.O.#.O.#.O O...#..#..O
|
||||
// .........O. O.O.O.O.O.O .O.O.O.O.O. .O.......O.
|
||||
// ........O.O .O.O.O.O.O. O.O.O.O.O.O O.O.....O.O
|
||||
// .......O.O. O.O.O.O.O.O .O.O.O.O.O. .O.O...O.O.
|
||||
// ......O.O.O .O.O.O.O.O. O.O.O.O.O.O O.O.O.O.O.O
|
||||
//
|
||||
// 3 2 1 1A 1B
|
||||
// .....O.O.O. O.O.O.O.O.O .O.O.O.O.O. .O.O...O.O. .....O.....
|
||||
// ....O.O.O.O .O.O.O.O.O. O.O.O.O.O.O O.O.....O.O ....O.O....
|
||||
// ...O.O.O.O. O.O.O.O.O.O .O.O.O.O.O. .O.......O. ...O.O.O...
|
||||
// ..O.O.#.O.O .O.O.O#O.O. O.O.O.#.O.O O.....#...O ..O.O.#.O..
|
||||
// .O.#.O.O.O. O.O#O.O.O.O .O.#.O.O.O. ...#....... .O.#.O.O.O.
|
||||
// O.O.O.O.O.O .O.O.O.O.O. O.O.OSO.O.O ........... O.O.O.O.O.O
|
||||
// .O.O#O.#.O. O.O.#.O.#.O .O.O#O.#.O. ....#..#... .O.O#O.#.O.
|
||||
// ..O.O.O.O.O .O.O.O.O.O. O.O.O.O.O.O O.........O ..O.O.O.O..
|
||||
// ...O.O.O.O. O.O.O.O.O.O .O.O.O.O.O. .O.......O. ...O.O.O...
|
||||
// ....O.O.O.O .O.O.O.O.O. O.O.O.O.O.O O.O.....O.O ....O.O....
|
||||
// .....O.O.O. O.O.O.O.O.O .O.O.O.O.O. .O.O...O.O. .....O.....
|
||||
|
||||
// Sets factors, aka number of occurrences, for each type.
|
||||
factor1 := (k - 1) * (k - 1) + 3 * k - 1;
|
||||
factor1B := k + 1;
|
||||
factor2 := k * k;
|
||||
factor4A := k;
|
||||
for i := 0 to FWidth - 1 do
|
||||
for j := 1 to FWidth do
|
||||
if FLines[i][j] <> CRockChar then
|
||||
if (i and 1) = (j and 1) then
|
||||
begin
|
||||
// Counts types 1.
|
||||
Result := Result + factor1;
|
||||
// Counts types 1B.
|
||||
if not ((i + j <= half) or (i + j > FWidth + half) or (i - j >= half) or (j - i > half + 1)) then
|
||||
Result := Result + factor1B;
|
||||
end
|
||||
else begin
|
||||
// Counts types 2.
|
||||
Result := Result + factor2;
|
||||
// Counts types 4A.
|
||||
if (i + j <= half) or (i + j > FWidth + half) or (i - j >= half) or (j - i > half + 1) then
|
||||
Result := Result + factor4A;
|
||||
end
|
||||
end;
|
||||
|
||||
constructor TStepCounter.Create(const AMaxStepsPart1: Integer; const AMaxStepsPart2: Integer);
|
||||
begin
|
||||
FMaxSteps1 := AMaxStepsPart1;
|
||||
FMaxSteps2 := AMaxStepsPart2;
|
||||
FLines := TStringList.Create;
|
||||
end;
|
||||
|
||||
|
@ -107,46 +267,13 @@ begin
|
|||
end;
|
||||
|
||||
procedure TStepCounter.Finish;
|
||||
var
|
||||
currentStep: Integer;
|
||||
currentPlots, nextPlots, temp: TPoints;
|
||||
plot, direction, next: TPoint;
|
||||
begin
|
||||
FWidth := Length(FLines[0]);
|
||||
FHeight := FLines.Count;
|
||||
PrepareMap;
|
||||
|
||||
currentStep := 0;
|
||||
currentPlots := TPoints.Create;
|
||||
currentPlots.Add(FindStart);
|
||||
Inc(FPart1);
|
||||
nextPlots := TPoints.Create;
|
||||
|
||||
while currentStep < FMaxSteps do
|
||||
begin
|
||||
for plot in currentPlots do
|
||||
for direction in CDirections do
|
||||
begin
|
||||
next := plot + direction;
|
||||
if IsInBounds(next) and (GetPosition(next) = CPlotChar) then
|
||||
begin
|
||||
SetPosition(next, CTraversedChar);
|
||||
nextPlots.Add(next);
|
||||
end;
|
||||
end;
|
||||
|
||||
currentPlots.Clear;
|
||||
temp := currentPlots;
|
||||
currentPlots := nextPlots;
|
||||
nextPlots := temp;
|
||||
Inc(currentStep);
|
||||
|
||||
// Positions where the number of steps are even can be reached with trivial backtracking, so they count.
|
||||
if currentStep mod 2 = 0 then
|
||||
Inc(FPart1, currentPlots.Count);
|
||||
end;
|
||||
|
||||
currentPlots.Free;
|
||||
nextPlots.Free;
|
||||
FPart2 := CalcTargetPlotsOnInfiniteMap(FMaxSteps2);
|
||||
FPart1 := DoSteps(FMaxSteps1);
|
||||
end;
|
||||
|
||||
function TStepCounter.GetDataFileName: string;
|
||||
|
|
|
@ -151,7 +151,7 @@ end;
|
|||
|
||||
function TTrebuchet.GetDataFileName: string;
|
||||
begin
|
||||
Result := 'trebuchet_calibration_document.txt';
|
||||
Result := 'trebuchet.txt';
|
||||
end;
|
||||
|
||||
function TTrebuchet.GetPuzzleName: string;
|
||||
|
|
|
@ -40,10 +40,6 @@
|
|||
<Filename Value="UGearRatiosTestCases.pas"/>
|
||||
<IsPartOfProject Value="True"/>
|
||||
</Unit>
|
||||
<Unit>
|
||||
<Filename Value="..\USolver.pas"/>
|
||||
<IsPartOfProject Value="True"/>
|
||||
</Unit>
|
||||
<Unit>
|
||||
<Filename Value="UBaseTestCases.pas"/>
|
||||
<IsPartOfProject Value="True"/>
|
||||
|
@ -144,10 +140,6 @@
|
|||
<Filename Value="UPolynomialTestCases.pas"/>
|
||||
<IsPartOfProject Value="True"/>
|
||||
</Unit>
|
||||
<Unit>
|
||||
<Filename Value="..\UPolynomialRoots.pas"/>
|
||||
<IsPartOfProject Value="True"/>
|
||||
</Unit>
|
||||
<Unit>
|
||||
<Filename Value="UPolynomialRootsTestCases.pas"/>
|
||||
<IsPartOfProject Value="True"/>
|
||||
|
@ -156,6 +148,10 @@
|
|||
<Filename Value="UBigIntTestCases.pas"/>
|
||||
<IsPartOfProject Value="True"/>
|
||||
</Unit>
|
||||
<Unit>
|
||||
<Filename Value="USnowverloadTestCases.pas"/>
|
||||
<IsPartOfProject Value="True"/>
|
||||
</Unit>
|
||||
</Units>
|
||||
</ProjectOptions>
|
||||
<CompilerOptions>
|
||||
|
|
|
@ -9,7 +9,8 @@ uses
|
|||
UHotSpringsTestCases, UPointOfIncidenceTestCases, UParabolicReflectorDishTestCases, ULensLibraryTestCases,
|
||||
UFloorWillBeLavaTestCases, UClumsyCrucibleTestCases, ULavaductLagoonTestCases, UAplentyTestCases,
|
||||
UPulsePropagationTestCases, UStepCounterTestCases, USandSlabsTestCases, ULongWalkTestCases,
|
||||
UNeverTellMeTheOddsTestCases, UBigIntTestCases;
|
||||
UNeverTellMeTheOddsTestCases, USnowverloadTestCases, UBigIntTestCases, UPolynomialTestCases,
|
||||
UPolynomialRootsTestCases;
|
||||
|
||||
{$R *.res}
|
||||
|
||||
|
|
|
@ -26,16 +26,6 @@ uses
|
|||
|
||||
type
|
||||
|
||||
{ TAplentyFullDataTestCase }
|
||||
|
||||
TAplentyFullDataTestCase = class(TEngineBaseTest)
|
||||
protected
|
||||
function CreateSolver: ISolver; override;
|
||||
published
|
||||
procedure TestPart1;
|
||||
procedure TestPart2;
|
||||
end;
|
||||
|
||||
{ TAplentyExampleTestCase }
|
||||
|
||||
TAplentyExampleTestCase = class(TExampleEngineBaseTest)
|
||||
|
@ -48,23 +38,6 @@ type
|
|||
|
||||
implementation
|
||||
|
||||
{ TAplentyFullDataTestCase }
|
||||
|
||||
function TAplentyFullDataTestCase.CreateSolver: ISolver;
|
||||
begin
|
||||
Result := TAplenty.Create;
|
||||
end;
|
||||
|
||||
procedure TAplentyFullDataTestCase.TestPart1;
|
||||
begin
|
||||
AssertEquals(331208, FSolver.GetResultPart1);
|
||||
end;
|
||||
|
||||
procedure TAplentyFullDataTestCase.TestPart2;
|
||||
begin
|
||||
AssertEquals(121464316215623, FSolver.GetResultPart2);
|
||||
end;
|
||||
|
||||
{ TAplentyExampleTestCase }
|
||||
|
||||
function TAplentyExampleTestCase.CreateSolver: ISolver;
|
||||
|
@ -84,7 +57,6 @@ end;
|
|||
|
||||
initialization
|
||||
|
||||
RegisterTest(TAplentyFullDataTestCase);
|
||||
RegisterTest(TAplentyExampleTestCase);
|
||||
RegisterTest('TAplenty', TAplentyExampleTestCase);
|
||||
end.
|
||||
|
||||
|
|
|
@ -43,14 +43,14 @@ type
|
|||
FEngine: TSolverEngine;
|
||||
procedure Setup; override;
|
||||
procedure TearDown; override;
|
||||
function GetDataPath: string; virtual;
|
||||
function GetDataPaths: TStringArray; virtual;
|
||||
end;
|
||||
|
||||
{ TExampleEngineBaseTest }
|
||||
|
||||
TExampleEngineBaseTest = class(TEngineBaseTest)
|
||||
protected
|
||||
function GetDataPath: string; override;
|
||||
function GetDataPaths: TStringArray; override;
|
||||
end;
|
||||
|
||||
implementation
|
||||
|
@ -74,7 +74,8 @@ end;
|
|||
procedure TEngineBaseTest.Setup;
|
||||
begin
|
||||
inherited Setup;
|
||||
FEngine := TSolverEngine.Create(GetDataPath);
|
||||
FEngine := TSolverEngine.Create(GetDataPaths);
|
||||
AssertTrue(FEngine.GetInvalidDataPathMessage(FSolver), FEngine.HasValidDataPath(FSolver));
|
||||
FEngine.ProcessData(FSolver);
|
||||
end;
|
||||
|
||||
|
@ -84,16 +85,24 @@ begin
|
|||
inherited TearDown;
|
||||
end;
|
||||
|
||||
function TEngineBaseTest.GetDataPath: string;
|
||||
function TEngineBaseTest.GetDataPaths: TStringArray;
|
||||
begin
|
||||
Result := ConcatPaths(['..', '..', 'bin', 'data']);
|
||||
Result := TStringArray.Create(
|
||||
ConcatPaths(['..', '..', 'bin', 'data']),
|
||||
ConcatPaths(['..', '..', '..', 'data']),
|
||||
ConcatPaths(['..', '..', 'data'])
|
||||
);
|
||||
end;
|
||||
|
||||
{ TExampleEngineBaseTest }
|
||||
|
||||
function TExampleEngineBaseTest.GetDataPath: string;
|
||||
function TExampleEngineBaseTest.GetDataPaths: TStringArray;
|
||||
begin
|
||||
Result := 'example_data';
|
||||
Result := TStringArray.Create(
|
||||
ConcatPaths(['..', '..', 'bin', 'data', 'example']),
|
||||
ConcatPaths(['..', '..', '..', 'data', 'example']),
|
||||
ConcatPaths(['..', '..', 'data', 'example'])
|
||||
);
|
||||
end;
|
||||
|
||||
end.
|
||||
|
|
|
@ -1016,18 +1016,18 @@ end;
|
|||
|
||||
initialization
|
||||
|
||||
RegisterTest(TBigIntSignTestCase);
|
||||
RegisterTest(TBigIntMostSignificantBitIndexTestCase);
|
||||
RegisterTest(TBigIntFromInt64TestCase);
|
||||
RegisterTest(TBigIntFromHexTestCase);
|
||||
RegisterTest(TBigIntFromBinTestCase);
|
||||
RegisterTest(TBigIntUnaryMinusTestCase);
|
||||
RegisterTest(TBigIntSumTestCase);
|
||||
RegisterTest(TBigIntDifferenceTestCase);
|
||||
RegisterTest(TBigIntProductTestCase);
|
||||
RegisterTest(TBigIntShiftLeftTestCase);
|
||||
RegisterTest(TBigIntShiftRightTestCase);
|
||||
RegisterTest(TBigIntEqualityTestCase);
|
||||
RegisterTest(TBigIntComparisonTestCase);
|
||||
RegisterTest('Helper.TBigInt', TBigIntSignTestCase);
|
||||
RegisterTest('Helper.TBigInt', TBigIntMostSignificantBitIndexTestCase);
|
||||
RegisterTest('Helper.TBigInt', TBigIntFromInt64TestCase);
|
||||
RegisterTest('Helper.TBigInt', TBigIntFromHexTestCase);
|
||||
RegisterTest('Helper.TBigInt', TBigIntFromBinTestCase);
|
||||
RegisterTest('Helper.TBigInt', TBigIntUnaryMinusTestCase);
|
||||
RegisterTest('Helper.TBigInt', TBigIntSumTestCase);
|
||||
RegisterTest('Helper.TBigInt', TBigIntDifferenceTestCase);
|
||||
RegisterTest('Helper.TBigInt', TBigIntProductTestCase);
|
||||
RegisterTest('Helper.TBigInt', TBigIntShiftLeftTestCase);
|
||||
RegisterTest('Helper.TBigInt', TBigIntShiftRightTestCase);
|
||||
RegisterTest('Helper.TBigInt', TBigIntEqualityTestCase);
|
||||
RegisterTest('Helper.TBigInt', TBigIntComparisonTestCase);
|
||||
end.
|
||||
|
||||
|
|
|
@ -26,16 +26,6 @@ uses
|
|||
|
||||
type
|
||||
|
||||
{ TCamelCardsFullDataTestCase }
|
||||
|
||||
TCamelCardsFullDataTestCase = class(TEngineBaseTest)
|
||||
protected
|
||||
function CreateSolver: ISolver; override;
|
||||
published
|
||||
procedure TestPart1;
|
||||
procedure TestPart2;
|
||||
end;
|
||||
|
||||
{ TCamelCardsExampleTestCase }
|
||||
|
||||
TCamelCardsExampleTestCase = class(TExampleEngineBaseTest)
|
||||
|
@ -48,23 +38,6 @@ type
|
|||
|
||||
implementation
|
||||
|
||||
{ TCamelCardsFullDataTestCase }
|
||||
|
||||
function TCamelCardsFullDataTestCase.CreateSolver: ISolver;
|
||||
begin
|
||||
Result := TCamelCards.Create;
|
||||
end;
|
||||
|
||||
procedure TCamelCardsFullDataTestCase.TestPart1;
|
||||
begin
|
||||
AssertEquals(254024898, FSolver.GetResultPart1);
|
||||
end;
|
||||
|
||||
procedure TCamelCardsFullDataTestCase.TestPart2;
|
||||
begin
|
||||
AssertEquals(254115617, FSolver.GetResultPart2);
|
||||
end;
|
||||
|
||||
{ TCamelCardsExampleTestCase }
|
||||
|
||||
function TCamelCardsExampleTestCase.CreateSolver: ISolver;
|
||||
|
@ -84,6 +57,5 @@ end;
|
|||
|
||||
initialization
|
||||
|
||||
RegisterTest(TCamelCardsFullDataTestCase);
|
||||
RegisterTest(TCamelCardsExampleTestCase);
|
||||
RegisterTest('TCamelCards', TCamelCardsExampleTestCase);
|
||||
end.
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
Solutions to the Advent Of Code.
|
||||
Copyright (C) 2023 Stefan Müller
|
||||
Copyright (C) 2023-2024 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
|
||||
|
@ -26,16 +26,6 @@ uses
|
|||
|
||||
type
|
||||
|
||||
{ TClumsyCrucibleFullDataTestCase }
|
||||
|
||||
TClumsyCrucibleFullDataTestCase = class(TEngineBaseTest)
|
||||
protected
|
||||
function CreateSolver: ISolver; override;
|
||||
published
|
||||
procedure TestPart1;
|
||||
procedure TestPart2;
|
||||
end;
|
||||
|
||||
{ TClumsyCrucibleExampleTestCase }
|
||||
|
||||
TClumsyCrucibleExampleTestCase = class(TExampleEngineBaseTest)
|
||||
|
@ -46,25 +36,23 @@ type
|
|||
procedure TestPart2;
|
||||
end;
|
||||
|
||||
{ TExample2ClumsyCrucible }
|
||||
|
||||
TExample2ClumsyCrucible = class(TClumsyCrucible)
|
||||
function GetDataFileName: string; override;
|
||||
end;
|
||||
|
||||
{ TClumsyCrucibleExample2TestCase }
|
||||
|
||||
TClumsyCrucibleExample2TestCase = class(TExampleEngineBaseTest)
|
||||
protected
|
||||
function CreateSolver: ISolver; override;
|
||||
published
|
||||
procedure TestPart2;
|
||||
end;
|
||||
|
||||
implementation
|
||||
|
||||
{ TClumsyCrucibleFullDataTestCase }
|
||||
|
||||
function TClumsyCrucibleFullDataTestCase.CreateSolver: ISolver;
|
||||
begin
|
||||
Result := TClumsyCrucible.Create;
|
||||
end;
|
||||
|
||||
procedure TClumsyCrucibleFullDataTestCase.TestPart1;
|
||||
begin
|
||||
AssertEquals(-1, FSolver.GetResultPart1);
|
||||
end;
|
||||
|
||||
procedure TClumsyCrucibleFullDataTestCase.TestPart2;
|
||||
begin
|
||||
AssertEquals(-1, FSolver.GetResultPart2);
|
||||
end;
|
||||
|
||||
{ TClumsyCrucibleExampleTestCase }
|
||||
|
||||
function TClumsyCrucibleExampleTestCase.CreateSolver: ISolver;
|
||||
|
@ -79,12 +67,31 @@ end;
|
|||
|
||||
procedure TClumsyCrucibleExampleTestCase.TestPart2;
|
||||
begin
|
||||
AssertEquals(-1, FSolver.GetResultPart2);
|
||||
AssertEquals(94, FSolver.GetResultPart2);
|
||||
end;
|
||||
|
||||
{ TExample2ClumsyCrucible }
|
||||
|
||||
function TExample2ClumsyCrucible.GetDataFileName: string;
|
||||
begin
|
||||
Result := 'clumsy_crucible2.txt';
|
||||
end;
|
||||
|
||||
{ TClumsyCrucibleExample2TestCase }
|
||||
|
||||
function TClumsyCrucibleExample2TestCase.CreateSolver: ISolver;
|
||||
begin
|
||||
Result := TExample2ClumsyCrucible.Create;
|
||||
end;
|
||||
|
||||
procedure TClumsyCrucibleExample2TestCase.TestPart2;
|
||||
begin
|
||||
AssertEquals(71, FSolver.GetResultPart2);
|
||||
end;
|
||||
|
||||
initialization
|
||||
|
||||
//RegisterTest(TClumsyCrucibleFullDataTestCase);
|
||||
//RegisterTest(TClumsyCrucibleExampleTestCase);
|
||||
RegisterTest('TClumsyCrucible', TClumsyCrucibleExampleTestCase);
|
||||
RegisterTest('TClumsyCrucible', TClumsyCrucibleExample2TestCase);
|
||||
end.
|
||||
|
||||
|
|
|
@ -26,16 +26,6 @@ uses
|
|||
|
||||
type
|
||||
|
||||
{ TCosmicExpansionFullDataTestCase }
|
||||
|
||||
TCosmicExpansionFullDataTestCase = class(TEngineBaseTest)
|
||||
protected
|
||||
function CreateSolver: ISolver; override;
|
||||
published
|
||||
procedure TestPart1;
|
||||
procedure TestPart2;
|
||||
end;
|
||||
|
||||
{ TCosmicExpansionExampleTestCase }
|
||||
|
||||
TCosmicExpansionExampleTestCase = class(TExampleEngineBaseTest)
|
||||
|
@ -65,23 +55,6 @@ type
|
|||
|
||||
implementation
|
||||
|
||||
{ TCosmicExpansionFullDataTestCase }
|
||||
|
||||
function TCosmicExpansionFullDataTestCase.CreateSolver: ISolver;
|
||||
begin
|
||||
Result := TCosmicExpansion.Create;
|
||||
end;
|
||||
|
||||
procedure TCosmicExpansionFullDataTestCase.TestPart1;
|
||||
begin
|
||||
AssertEquals(9686930, FSolver.GetResultPart1);
|
||||
end;
|
||||
|
||||
procedure TCosmicExpansionFullDataTestCase.TestPart2;
|
||||
begin
|
||||
AssertEquals(630728425490, FSolver.GetResultPart2);
|
||||
end;
|
||||
|
||||
{ TCosmicExpansionExampleTestCase }
|
||||
|
||||
function TCosmicExpansionExampleTestCase.CreateSolver: ISolver;
|
||||
|
@ -120,9 +93,8 @@ end;
|
|||
|
||||
initialization
|
||||
|
||||
RegisterTest(TCosmicExpansionFullDataTestCase);
|
||||
RegisterTest(TCosmicExpansionExampleTestCase);
|
||||
RegisterTest(TCosmicExpansionExampleFactor10TestCase);
|
||||
RegisterTest(TCosmicExpansionExampleFactor100TestCase);
|
||||
RegisterTest('TCosmicExpansion', TCosmicExpansionExampleTestCase);
|
||||
RegisterTest('TCosmicExpansion', TCosmicExpansionExampleFactor10TestCase);
|
||||
RegisterTest('TCosmicExpansion', TCosmicExpansionExampleFactor100TestCase);
|
||||
end.
|
||||
|
||||
|
|
|
@ -26,16 +26,6 @@ uses
|
|||
|
||||
type
|
||||
|
||||
{ TCubeConundrumFullDataTestCase }
|
||||
|
||||
TCubeConundrumFullDataTestCase = class(TEngineBaseTest)
|
||||
protected
|
||||
function CreateSolver: ISolver; override;
|
||||
published
|
||||
procedure TestPart1;
|
||||
procedure TestPart2;
|
||||
end;
|
||||
|
||||
{ TCubeConundrumExampleTestCase }
|
||||
|
||||
TCubeConundrumExampleTestCase = class(TExampleEngineBaseTest)
|
||||
|
@ -48,23 +38,6 @@ type
|
|||
|
||||
implementation
|
||||
|
||||
{ TCubeConundrumFullDataTestCase }
|
||||
|
||||
function TCubeConundrumFullDataTestCase.CreateSolver: ISolver;
|
||||
begin
|
||||
Result := TCubeConundrum.Create;
|
||||
end;
|
||||
|
||||
procedure TCubeConundrumFullDataTestCase.TestPart1;
|
||||
begin
|
||||
AssertEquals(2563, FSolver.GetResultPart1);
|
||||
end;
|
||||
|
||||
procedure TCubeConundrumFullDataTestCase.TestPart2;
|
||||
begin
|
||||
AssertEquals(70768, FSolver.GetResultPart2);
|
||||
end;
|
||||
|
||||
{ TCubeConundrumExampleTestCase }
|
||||
|
||||
function TCubeConundrumExampleTestCase.CreateSolver: ISolver;
|
||||
|
@ -84,6 +57,5 @@ end;
|
|||
|
||||
initialization
|
||||
|
||||
RegisterTest(TCubeConundrumFullDataTestCase);
|
||||
RegisterTest(TCubeConundrumExampleTestCase);
|
||||
RegisterTest('TCubeConundrum', TCubeConundrumExampleTestCase);
|
||||
end.
|
||||
|
|
|
@ -26,16 +26,6 @@ uses
|
|||
|
||||
type
|
||||
|
||||
{ TFloorWillBeLavaFullDataTestCase }
|
||||
|
||||
TFloorWillBeLavaFullDataTestCase = class(TEngineBaseTest)
|
||||
protected
|
||||
function CreateSolver: ISolver; override;
|
||||
published
|
||||
procedure TestPart1;
|
||||
procedure TestPart2;
|
||||
end;
|
||||
|
||||
{ TFloorWillBeLavaExampleTestCase }
|
||||
|
||||
TFloorWillBeLavaExampleTestCase = class(TExampleEngineBaseTest)
|
||||
|
@ -48,23 +38,6 @@ type
|
|||
|
||||
implementation
|
||||
|
||||
{ TFloorWillBeLavaFullDataTestCase }
|
||||
|
||||
function TFloorWillBeLavaFullDataTestCase.CreateSolver: ISolver;
|
||||
begin
|
||||
Result := TFloorWillBeLava.Create;
|
||||
end;
|
||||
|
||||
procedure TFloorWillBeLavaFullDataTestCase.TestPart1;
|
||||
begin
|
||||
AssertEquals(7392, FSolver.GetResultPart1);
|
||||
end;
|
||||
|
||||
procedure TFloorWillBeLavaFullDataTestCase.TestPart2;
|
||||
begin
|
||||
AssertEquals(7665, FSolver.GetResultPart2);
|
||||
end;
|
||||
|
||||
{ TFloorWillBeLavaExampleTestCase }
|
||||
|
||||
function TFloorWillBeLavaExampleTestCase.CreateSolver: ISolver;
|
||||
|
@ -84,7 +57,6 @@ end;
|
|||
|
||||
initialization
|
||||
|
||||
RegisterTest(TFloorWillBeLavaFullDataTestCase);
|
||||
RegisterTest(TFloorWillBeLavaExampleTestCase);
|
||||
RegisterTest('TFloorWillBeLava', TFloorWillBeLavaExampleTestCase);
|
||||
end.
|
||||
|
||||
|
|
|
@ -26,16 +26,6 @@ uses
|
|||
|
||||
type
|
||||
|
||||
{ TGearRatiosFullDataTestCase }
|
||||
|
||||
TGearRatiosFullDataTestCase = class(TEngineBaseTest)
|
||||
protected
|
||||
function CreateSolver: ISolver; override;
|
||||
published
|
||||
procedure TestPart1;
|
||||
procedure TestPart2;
|
||||
end;
|
||||
|
||||
{ TGearRatiosExampleTestCase }
|
||||
|
||||
TGearRatiosExampleTestCase = class(TExampleEngineBaseTest)
|
||||
|
@ -57,23 +47,6 @@ type
|
|||
|
||||
implementation
|
||||
|
||||
{ TGearRatiosFullDataTestCase }
|
||||
|
||||
function TGearRatiosFullDataTestCase.CreateSolver: ISolver;
|
||||
begin
|
||||
Result := TGearRatios.Create;
|
||||
end;
|
||||
|
||||
procedure TGearRatiosFullDataTestCase.TestPart1;
|
||||
begin
|
||||
AssertEquals(530495, FSolver.GetResultPart1);
|
||||
end;
|
||||
|
||||
procedure TGearRatiosFullDataTestCase.TestPart2;
|
||||
begin
|
||||
AssertEquals(80253814, FSolver.GetResultPart2);
|
||||
end;
|
||||
|
||||
{ TGearRatiosExampleTestCase }
|
||||
|
||||
function TGearRatiosExampleTestCase.CreateSolver: ISolver;
|
||||
|
@ -108,7 +81,6 @@ end;
|
|||
|
||||
initialization
|
||||
|
||||
RegisterTest(TGearRatiosFullDataTestCase);
|
||||
RegisterTest(TGearRatiosExampleTestCase);
|
||||
RegisterTest(TGearRatiosTestCase);
|
||||
RegisterTest('TGearRatios', TGearRatiosExampleTestCase);
|
||||
RegisterTest('TGearRatios', TGearRatiosTestCase);
|
||||
end.
|
||||
|
|
|
@ -26,16 +26,6 @@ uses
|
|||
|
||||
type
|
||||
|
||||
{ TGiveSeedFertilizerFullDataTestCase }
|
||||
|
||||
TGiveSeedFertilizerFullDataTestCase = class(TEngineBaseTest)
|
||||
protected
|
||||
function CreateSolver: ISolver; override;
|
||||
published
|
||||
procedure TestPart1;
|
||||
procedure TestPart2;
|
||||
end;
|
||||
|
||||
{ TGiveSeedFertilizerExampleTestCase }
|
||||
|
||||
TGiveSeedFertilizerExampleTestCase = class(TExampleEngineBaseTest)
|
||||
|
@ -48,23 +38,6 @@ type
|
|||
|
||||
implementation
|
||||
|
||||
{ TGiveSeedFertilizerFullDataTestCase }
|
||||
|
||||
function TGiveSeedFertilizerFullDataTestCase.CreateSolver: ISolver;
|
||||
begin
|
||||
Result := TGiveSeedFertilizer.Create;
|
||||
end;
|
||||
|
||||
procedure TGiveSeedFertilizerFullDataTestCase.TestPart1;
|
||||
begin
|
||||
AssertEquals(51580674, FSolver.GetResultPart1);
|
||||
end;
|
||||
|
||||
procedure TGiveSeedFertilizerFullDataTestCase.TestPart2;
|
||||
begin
|
||||
AssertEquals(99751240, FSolver.GetResultPart2);
|
||||
end;
|
||||
|
||||
{ TGiveSeedFertilizerExampleTestCase }
|
||||
|
||||
function TGiveSeedFertilizerExampleTestCase.CreateSolver: ISolver;
|
||||
|
@ -84,6 +57,5 @@ end;
|
|||
|
||||
initialization
|
||||
|
||||
RegisterTest(TGiveSeedFertilizerFullDataTestCase);
|
||||
RegisterTest(TGiveSeedFertilizerExampleTestCase);
|
||||
RegisterTest('TGiveSeedFertilizer', TGiveSeedFertilizerExampleTestCase);
|
||||
end.
|
||||
|
|
|
@ -26,16 +26,6 @@ uses
|
|||
|
||||
type
|
||||
|
||||
{ THauntedWastelandFullDataTestCase }
|
||||
|
||||
THauntedWastelandFullDataTestCase = class(TEngineBaseTest)
|
||||
protected
|
||||
function CreateSolver: ISolver; override;
|
||||
published
|
||||
procedure TestPart1;
|
||||
procedure TestPart2;
|
||||
end;
|
||||
|
||||
{ THauntedWastelandExampleTestCase }
|
||||
|
||||
THauntedWastelandExampleTestCase = class(TExampleEngineBaseTest)
|
||||
|
@ -77,23 +67,6 @@ type
|
|||
|
||||
implementation
|
||||
|
||||
{ THauntedWastelandFullDataTestCase }
|
||||
|
||||
function THauntedWastelandFullDataTestCase.CreateSolver: ISolver;
|
||||
begin
|
||||
Result := THauntedWasteland.Create;
|
||||
end;
|
||||
|
||||
procedure THauntedWastelandFullDataTestCase.TestPart1;
|
||||
begin
|
||||
AssertEquals(14257, FSolver.GetResultPart1);
|
||||
end;
|
||||
|
||||
procedure THauntedWastelandFullDataTestCase.TestPart2;
|
||||
begin
|
||||
AssertEquals(16187743689077, FSolver.GetResultPart2);
|
||||
end;
|
||||
|
||||
{ THauntedWastelandExampleTestCase }
|
||||
|
||||
function THauntedWastelandExampleTestCase.CreateSolver: ISolver;
|
||||
|
@ -146,8 +119,7 @@ end;
|
|||
|
||||
initialization
|
||||
|
||||
RegisterTest(THauntedWastelandFullDataTestCase);
|
||||
RegisterTest(THauntedWastelandExampleTestCase);
|
||||
RegisterTest(THauntedWastelandExample2TestCase);
|
||||
RegisterTest(THauntedWastelandExample3TestCase);
|
||||
RegisterTest('THauntedWasteland', THauntedWastelandExampleTestCase);
|
||||
RegisterTest('THauntedWasteland', THauntedWastelandExample2TestCase);
|
||||
RegisterTest('THauntedWasteland', THauntedWastelandExample3TestCase);
|
||||
end.
|
||||
|
|
|
@ -26,15 +26,6 @@ uses
|
|||
|
||||
type
|
||||
|
||||
{ THotSpringsFullDataTestCase }
|
||||
|
||||
THotSpringsFullDataTestCase = class(TEngineBaseTest)
|
||||
protected
|
||||
function CreateSolver: ISolver; override;
|
||||
published
|
||||
procedure TestPart1;
|
||||
end;
|
||||
|
||||
{ THotSpringsExampleTestCase }
|
||||
|
||||
THotSpringsExampleTestCase = class(TExampleEngineBaseTest)
|
||||
|
@ -61,18 +52,6 @@ type
|
|||
|
||||
implementation
|
||||
|
||||
{ THotSpringsFullDataTestCase }
|
||||
|
||||
function THotSpringsFullDataTestCase.CreateSolver: ISolver;
|
||||
begin
|
||||
Result := THotSprings.Create;
|
||||
end;
|
||||
|
||||
procedure THotSpringsFullDataTestCase.TestPart1;
|
||||
begin
|
||||
AssertEquals(7344, FSolver.GetResultPart1);
|
||||
end;
|
||||
|
||||
{ THotSpringsExampleTestCase }
|
||||
|
||||
function THotSpringsExampleTestCase.CreateSolver: ISolver;
|
||||
|
@ -132,8 +111,7 @@ end;
|
|||
|
||||
initialization
|
||||
|
||||
RegisterTest(THotSpringsFullDataTestCase);
|
||||
RegisterTest(THotSpringsExampleTestCase);
|
||||
RegisterTest(THotSpringsTestCase);
|
||||
RegisterTest('THotSprings', THotSpringsExampleTestCase);
|
||||
RegisterTest('THotSprings', THotSpringsTestCase);
|
||||
end.
|
||||
|
||||
|
|
|
@ -26,16 +26,6 @@ uses
|
|||
|
||||
type
|
||||
|
||||
{ TLavaductLagoonFullDataTestCase }
|
||||
|
||||
TLavaductLagoonFullDataTestCase = class(TEngineBaseTest)
|
||||
protected
|
||||
function CreateSolver: ISolver; override;
|
||||
published
|
||||
procedure TestPart1;
|
||||
procedure TestPart2;
|
||||
end;
|
||||
|
||||
{ TLavaductLagoonExampleTestCase }
|
||||
|
||||
TLavaductLagoonExampleTestCase = class(TExampleEngineBaseTest)
|
||||
|
@ -48,23 +38,6 @@ type
|
|||
|
||||
implementation
|
||||
|
||||
{ TLavaductLagoonFullDataTestCase }
|
||||
|
||||
function TLavaductLagoonFullDataTestCase.CreateSolver: ISolver;
|
||||
begin
|
||||
Result := TLavaductLagoon.Create;
|
||||
end;
|
||||
|
||||
procedure TLavaductLagoonFullDataTestCase.TestPart1;
|
||||
begin
|
||||
AssertEquals(-1, FSolver.GetResultPart1);
|
||||
end;
|
||||
|
||||
procedure TLavaductLagoonFullDataTestCase.TestPart2;
|
||||
begin
|
||||
AssertEquals(-1, FSolver.GetResultPart2);
|
||||
end;
|
||||
|
||||
{ TLavaductLagoonExampleTestCase }
|
||||
|
||||
function TLavaductLagoonExampleTestCase.CreateSolver: ISolver;
|
||||
|
@ -79,12 +52,11 @@ end;
|
|||
|
||||
procedure TLavaductLagoonExampleTestCase.TestPart2;
|
||||
begin
|
||||
AssertEquals(-1, FSolver.GetResultPart2);
|
||||
AssertEquals(952408144115, FSolver.GetResultPart2);
|
||||
end;
|
||||
|
||||
initialization
|
||||
|
||||
//RegisterTest(TLavaductLagoonFullDataTestCase);
|
||||
//RegisterTest(TLavaductLagoonExampleTestCase);
|
||||
RegisterTest('TLavaductLagoon', TLavaductLagoonExampleTestCase);
|
||||
end.
|
||||
|
||||
|
|
|
@ -26,16 +26,6 @@ uses
|
|||
|
||||
type
|
||||
|
||||
{ TLensLibraryFullDataTestCase }
|
||||
|
||||
TLensLibraryFullDataTestCase = class(TEngineBaseTest)
|
||||
protected
|
||||
function CreateSolver: ISolver; override;
|
||||
published
|
||||
procedure TestPart1;
|
||||
procedure TestPart2;
|
||||
end;
|
||||
|
||||
{ TLensLibraryExampleTestCase }
|
||||
|
||||
TLensLibraryExampleTestCase = class(TExampleEngineBaseTest)
|
||||
|
@ -48,23 +38,6 @@ type
|
|||
|
||||
implementation
|
||||
|
||||
{ TLensLibraryFullDataTestCase }
|
||||
|
||||
function TLensLibraryFullDataTestCase.CreateSolver: ISolver;
|
||||
begin
|
||||
Result := TLensLibrary.Create;
|
||||
end;
|
||||
|
||||
procedure TLensLibraryFullDataTestCase.TestPart1;
|
||||
begin
|
||||
AssertEquals(519041, FSolver.GetResultPart1);
|
||||
end;
|
||||
|
||||
procedure TLensLibraryFullDataTestCase.TestPart2;
|
||||
begin
|
||||
AssertEquals(260530, FSolver.GetResultPart2);
|
||||
end;
|
||||
|
||||
{ TLensLibraryExampleTestCase }
|
||||
|
||||
function TLensLibraryExampleTestCase.CreateSolver: ISolver;
|
||||
|
@ -84,7 +57,6 @@ end;
|
|||
|
||||
initialization
|
||||
|
||||
RegisterTest(TLensLibraryFullDataTestCase);
|
||||
RegisterTest(TLensLibraryExampleTestCase);
|
||||
RegisterTest('TLensLibrary', TLensLibraryExampleTestCase);
|
||||
end.
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
Solutions to the Advent Of Code.
|
||||
Copyright (C) 2023 Stefan Müller
|
||||
Copyright (C) 2023-2024 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
|
||||
|
@ -26,15 +26,6 @@ uses
|
|||
|
||||
type
|
||||
|
||||
{ TLongWalkFullDataTestCase }
|
||||
|
||||
TLongWalkFullDataTestCase = class(TEngineBaseTest)
|
||||
protected
|
||||
function CreateSolver: ISolver; override;
|
||||
published
|
||||
procedure TestPart1;
|
||||
end;
|
||||
|
||||
{ TLongWalkExampleTestCase }
|
||||
|
||||
TLongWalkExampleTestCase = class(TExampleEngineBaseTest)
|
||||
|
@ -42,22 +33,11 @@ type
|
|||
function CreateSolver: ISolver; override;
|
||||
published
|
||||
procedure TestPart1;
|
||||
procedure TestPart2;
|
||||
end;
|
||||
|
||||
implementation
|
||||
|
||||
{ TLongWalkFullDataTestCase }
|
||||
|
||||
function TLongWalkFullDataTestCase.CreateSolver: ISolver;
|
||||
begin
|
||||
Result := TLongWalk.Create;
|
||||
end;
|
||||
|
||||
procedure TLongWalkFullDataTestCase.TestPart1;
|
||||
begin
|
||||
AssertEquals(2218, FSolver.GetResultPart1);
|
||||
end;
|
||||
|
||||
{ TLongWalkExampleTestCase }
|
||||
|
||||
function TLongWalkExampleTestCase.CreateSolver: ISolver;
|
||||
|
@ -70,9 +50,13 @@ begin
|
|||
AssertEquals(94, FSolver.GetResultPart1);
|
||||
end;
|
||||
|
||||
procedure TLongWalkExampleTestCase.TestPart2;
|
||||
begin
|
||||
AssertEquals(154, FSolver.GetResultPart2);
|
||||
end;
|
||||
|
||||
initialization
|
||||
|
||||
RegisterTest(TLongWalkFullDataTestCase);
|
||||
RegisterTest(TLongWalkExampleTestCase);
|
||||
RegisterTest('TLongWalk', TLongWalkExampleTestCase);
|
||||
end.
|
||||
|
||||
|
|
|
@ -26,16 +26,6 @@ uses
|
|||
|
||||
type
|
||||
|
||||
{ TMirageMaintenanceFullDataTestCase }
|
||||
|
||||
TMirageMaintenanceFullDataTestCase = class(TEngineBaseTest)
|
||||
protected
|
||||
function CreateSolver: ISolver; override;
|
||||
published
|
||||
procedure TestPart1;
|
||||
procedure TestPart2;
|
||||
end;
|
||||
|
||||
{ TMirageMaintenanceExampleTestCase }
|
||||
|
||||
TMirageMaintenanceExampleTestCase = class(TExampleEngineBaseTest)
|
||||
|
@ -48,23 +38,6 @@ type
|
|||
|
||||
implementation
|
||||
|
||||
{ TMirageMaintenanceFullDataTestCase }
|
||||
|
||||
function TMirageMaintenanceFullDataTestCase.CreateSolver: ISolver;
|
||||
begin
|
||||
Result := TMirageMaintenance.Create;
|
||||
end;
|
||||
|
||||
procedure TMirageMaintenanceFullDataTestCase.TestPart1;
|
||||
begin
|
||||
AssertEquals(1877825184, FSolver.GetResultPart1);
|
||||
end;
|
||||
|
||||
procedure TMirageMaintenanceFullDataTestCase.TestPart2;
|
||||
begin
|
||||
AssertEquals(1108, FSolver.GetResultPart2);
|
||||
end;
|
||||
|
||||
{ TMirageMaintenanceExampleTestCase }
|
||||
|
||||
function TMirageMaintenanceExampleTestCase.CreateSolver: ISolver;
|
||||
|
@ -84,6 +57,5 @@ end;
|
|||
|
||||
initialization
|
||||
|
||||
RegisterTest(TMirageMaintenanceFullDataTestCase);
|
||||
RegisterTest(TMirageMaintenanceExampleTestCase);
|
||||
RegisterTest('TMirageMaintenance', TMirageMaintenanceExampleTestCase);
|
||||
end.
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
Solutions to the Advent Of Code.
|
||||
Copyright (C) 2023 Stefan Müller
|
||||
Copyright (C) 2023-2024 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
|
||||
|
@ -26,16 +26,6 @@ uses
|
|||
|
||||
type
|
||||
|
||||
{ TNeverTellMeTheOddsFullDataTestCase }
|
||||
|
||||
TNeverTellMeTheOddsFullDataTestCase = class(TEngineBaseTest)
|
||||
protected
|
||||
function CreateSolver: ISolver; override;
|
||||
published
|
||||
procedure TestPart1;
|
||||
procedure TestPart2;
|
||||
end;
|
||||
|
||||
{ TNeverTellMeTheOddsExampleTestCase }
|
||||
|
||||
TNeverTellMeTheOddsExampleTestCase = class(TExampleEngineBaseTest)
|
||||
|
@ -67,23 +57,6 @@ type
|
|||
|
||||
implementation
|
||||
|
||||
{ TNeverTellMeTheOddsFullDataTestCase }
|
||||
|
||||
function TNeverTellMeTheOddsFullDataTestCase.CreateSolver: ISolver;
|
||||
begin
|
||||
Result := TNeverTellMeTheOdds.Create;
|
||||
end;
|
||||
|
||||
procedure TNeverTellMeTheOddsFullDataTestCase.TestPart1;
|
||||
begin
|
||||
AssertEquals(15107, FSolver.GetResultPart1);
|
||||
end;
|
||||
|
||||
procedure TNeverTellMeTheOddsFullDataTestCase.TestPart2;
|
||||
begin
|
||||
AssertEquals(-1, FSolver.GetResultPart2);
|
||||
end;
|
||||
|
||||
{ TNeverTellMeTheOddsExampleTestCase }
|
||||
|
||||
function TNeverTellMeTheOddsExampleTestCase.CreateSolver: ISolver;
|
||||
|
@ -178,8 +151,7 @@ end;
|
|||
|
||||
initialization
|
||||
|
||||
RegisterTest(TNeverTellMeTheOddsFullDataTestCase);
|
||||
RegisterTest(TNeverTellMeTheOddsExampleTestCase);
|
||||
RegisterTest(TNeverTellMeTheOddsTestCase);
|
||||
RegisterTest('TNeverTellMeTheOdds', TNeverTellMeTheOddsExampleTestCase);
|
||||
RegisterTest('TNeverTellMeTheOdds', TNeverTellMeTheOddsTestCase);
|
||||
end.
|
||||
|
||||
|
|
|
@ -26,16 +26,6 @@ uses
|
|||
|
||||
type
|
||||
|
||||
{ TParabolicReflectorDishFullDataTestCase }
|
||||
|
||||
TParabolicReflectorDishFullDataTestCase = class(TEngineBaseTest)
|
||||
protected
|
||||
function CreateSolver: ISolver; override;
|
||||
published
|
||||
procedure TestPart1;
|
||||
procedure TestPart2;
|
||||
end;
|
||||
|
||||
{ TParabolicReflectorDishExampleTestCase }
|
||||
|
||||
TParabolicReflectorDishExampleTestCase = class(TExampleEngineBaseTest)
|
||||
|
@ -48,23 +38,6 @@ type
|
|||
|
||||
implementation
|
||||
|
||||
{ TParabolicReflectorDishFullDataTestCase }
|
||||
|
||||
function TParabolicReflectorDishFullDataTestCase.CreateSolver: ISolver;
|
||||
begin
|
||||
Result := TParabolicReflectorDish.Create;
|
||||
end;
|
||||
|
||||
procedure TParabolicReflectorDishFullDataTestCase.TestPart1;
|
||||
begin
|
||||
AssertEquals(103614, FSolver.GetResultPart1);
|
||||
end;
|
||||
|
||||
procedure TParabolicReflectorDishFullDataTestCase.TestPart2;
|
||||
begin
|
||||
AssertEquals(83790, FSolver.GetResultPart2);
|
||||
end;
|
||||
|
||||
{ TParabolicReflectorDishExampleTestCase }
|
||||
|
||||
function TParabolicReflectorDishExampleTestCase.CreateSolver: ISolver;
|
||||
|
@ -84,7 +57,6 @@ end;
|
|||
|
||||
initialization
|
||||
|
||||
RegisterTest(TParabolicReflectorDishFullDataTestCase);
|
||||
RegisterTest(TParabolicReflectorDishExampleTestCase);
|
||||
RegisterTest('TParabolicReflectorDish', TParabolicReflectorDishExampleTestCase);
|
||||
end.
|
||||
|
||||
|
|
|
@ -26,16 +26,6 @@ uses
|
|||
|
||||
type
|
||||
|
||||
{ TPipeMazeFullDataTestCase }
|
||||
|
||||
TPipeMazeFullDataTestCase = class(TEngineBaseTest)
|
||||
protected
|
||||
function CreateSolver: ISolver; override;
|
||||
published
|
||||
procedure TestPart1;
|
||||
procedure TestPart2;
|
||||
end;
|
||||
|
||||
{ TPipeMazeExampleTestCase }
|
||||
|
||||
TPipeMazeExampleTestCase = class(TExampleEngineBaseTest)
|
||||
|
@ -152,23 +142,6 @@ type
|
|||
|
||||
implementation
|
||||
|
||||
{ TPipeMazeFullDataTestCase }
|
||||
|
||||
function TPipeMazeFullDataTestCase.CreateSolver: ISolver;
|
||||
begin
|
||||
Result := TPipeMaze.Create;
|
||||
end;
|
||||
|
||||
procedure TPipeMazeFullDataTestCase.TestPart1;
|
||||
begin
|
||||
AssertEquals(7097, FSolver.GetResultPart1);
|
||||
end;
|
||||
|
||||
procedure TPipeMazeFullDataTestCase.TestPart2;
|
||||
begin
|
||||
AssertEquals(355, FSolver.GetResultPart2);
|
||||
end;
|
||||
|
||||
{ TPipeMazeExampleTestCase }
|
||||
|
||||
function TPipeMazeExampleTestCase.CreateSolver: ISolver;
|
||||
|
@ -316,13 +289,12 @@ end;
|
|||
|
||||
initialization
|
||||
|
||||
RegisterTest(TPipeMazeFullDataTestCase);
|
||||
RegisterTest(TPipeMazeExampleTestCase);
|
||||
RegisterTest(TPipeMazeExample2TestCase);
|
||||
RegisterTest(TPipeMazeExample3TestCase);
|
||||
RegisterTest(TPipeMazeExample4TestCase);
|
||||
RegisterTest(TPipeMazeExample5TestCase);
|
||||
RegisterTest(TPipeMazeExample6TestCase);
|
||||
RegisterTest(TPipeMazeExample7TestCase);
|
||||
RegisterTest(TPipeMazeExample8TestCase);
|
||||
RegisterTest('TPipeMaze', TPipeMazeExampleTestCase);
|
||||
RegisterTest('TPipeMaze', TPipeMazeExample2TestCase);
|
||||
RegisterTest('TPipeMaze', TPipeMazeExample3TestCase);
|
||||
RegisterTest('TPipeMaze', TPipeMazeExample4TestCase);
|
||||
RegisterTest('TPipeMaze', TPipeMazeExample5TestCase);
|
||||
RegisterTest('TPipeMaze', TPipeMazeExample6TestCase);
|
||||
RegisterTest('TPipeMaze', TPipeMazeExample7TestCase);
|
||||
RegisterTest('TPipeMaze', TPipeMazeExample8TestCase);
|
||||
end.
|
||||
|
|
|
@ -26,16 +26,6 @@ uses
|
|||
|
||||
type
|
||||
|
||||
{ TPointOfIncidenceFullDataTestCase }
|
||||
|
||||
TPointOfIncidenceFullDataTestCase = class(TEngineBaseTest)
|
||||
protected
|
||||
function CreateSolver: ISolver; override;
|
||||
published
|
||||
procedure TestPart1;
|
||||
procedure TestPart2;
|
||||
end;
|
||||
|
||||
{ TPointOfIncidenceExampleTestCase }
|
||||
|
||||
TPointOfIncidenceExampleTestCase = class(TExampleEngineBaseTest)
|
||||
|
@ -48,23 +38,6 @@ type
|
|||
|
||||
implementation
|
||||
|
||||
{ TPointOfIncidenceFullDataTestCase }
|
||||
|
||||
function TPointOfIncidenceFullDataTestCase.CreateSolver: ISolver;
|
||||
begin
|
||||
Result := TPointOfIncidence.Create;
|
||||
end;
|
||||
|
||||
procedure TPointOfIncidenceFullDataTestCase.TestPart1;
|
||||
begin
|
||||
AssertEquals(37718, FSolver.GetResultPart1);
|
||||
end;
|
||||
|
||||
procedure TPointOfIncidenceFullDataTestCase.TestPart2;
|
||||
begin
|
||||
AssertEquals(40995, FSolver.GetResultPart2);
|
||||
end;
|
||||
|
||||
{ TPointOfIncidenceExampleTestCase }
|
||||
|
||||
function TPointOfIncidenceExampleTestCase.CreateSolver: ISolver;
|
||||
|
@ -84,7 +57,6 @@ end;
|
|||
|
||||
initialization
|
||||
|
||||
RegisterTest(TPointOfIncidenceFullDataTestCase);
|
||||
RegisterTest(TPointOfIncidenceExampleTestCase);
|
||||
RegisterTest('TPointOfIncidence', TPointOfIncidenceExampleTestCase);
|
||||
end.
|
||||
|
||||
|
|
|
@ -0,0 +1,138 @@
|
|||
{
|
||||
Solutions to the Advent Of Code.
|
||||
Copyright (C) 2024 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/>.
|
||||
}
|
||||
|
||||
unit UPolynomialRootsTestCases;
|
||||
|
||||
{$mode ObjFPC}{$H+}
|
||||
|
||||
interface
|
||||
|
||||
uses
|
||||
Classes, SysUtils, fpcunit, testregistry, UPolynomial, UPolynomialRoots, UBigInt;
|
||||
|
||||
type
|
||||
|
||||
{ TPolynomialRootsTestCase }
|
||||
|
||||
TPolynomialRootsTestCase = class(TTestCase)
|
||||
private
|
||||
procedure AssertBisectIntervals(AIsolatingIntervals: TIsolatingIntervalArray; constref AExpectedRoots:
|
||||
array of Cardinal);
|
||||
procedure AssertBisectIntegers(ARoots: TBigIntArray; constref AExpectedRoots: array of Cardinal);
|
||||
published
|
||||
procedure TestBisectNoBound;
|
||||
procedure TestBisectWithBound;
|
||||
procedure TestBisectInteger;
|
||||
end;
|
||||
|
||||
implementation
|
||||
|
||||
{ TPolynomialRootsTestCase }
|
||||
|
||||
procedure TPolynomialRootsTestCase.AssertBisectIntervals(AIsolatingIntervals: TIsolatingIntervalArray;
|
||||
constref AExpectedRoots: array of Cardinal);
|
||||
var
|
||||
exp: Cardinal;
|
||||
found: Boolean;
|
||||
i, foundIndex: Integer;
|
||||
begin
|
||||
AssertEquals('Unexpected number of isolating intervals.', Length(AExpectedRoots), Length(AIsolatingIntervals));
|
||||
for exp in AExpectedRoots do
|
||||
begin
|
||||
found := False;
|
||||
for i := 0 to Length(AIsolatingIntervals) - 1 do
|
||||
if (AIsolatingIntervals[i].A <= exp) and (exp <= AIsolatingIntervals[i].B) then
|
||||
begin
|
||||
found := True;
|
||||
foundIndex := i;
|
||||
Break;
|
||||
end;
|
||||
AssertTrue('No isolating interval for expected root ' + IntToStr(exp) + ' found.', found);
|
||||
Delete(AIsolatingIntervals, foundIndex, 1);
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure TPolynomialRootsTestCase.AssertBisectIntegers(ARoots: TBigIntArray; constref AExpectedRoots:
|
||||
array of Cardinal);
|
||||
var
|
||||
exp: Cardinal;
|
||||
found: Boolean;
|
||||
i, foundIndex: Integer;
|
||||
begin
|
||||
AssertEquals('Unexpected number of integer roots.', Length(AExpectedRoots), Length(ARoots));
|
||||
for exp in AExpectedRoots do
|
||||
begin
|
||||
found := False;
|
||||
for i := 0 to Length(ARoots) - 1 do
|
||||
if ARoots[i] = exp then
|
||||
begin
|
||||
found := True;
|
||||
foundIndex := i;
|
||||
Break;
|
||||
end;
|
||||
AssertTrue('Expected root ' + IntToStr(exp) + ' not found.', found);
|
||||
Delete(ARoots, foundIndex, 1);
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure TPolynomialRootsTestCase.TestBisectNoBound;
|
||||
const
|
||||
expRoots: array of Cardinal = (34000, 23017, 5);
|
||||
var
|
||||
a: TBigIntPolynomial;
|
||||
r: TIsolatingIntervalArray;
|
||||
begin
|
||||
// y = 3 * (x - 34000) * (x - 23017) * (x - 5) * (x^2 - 19) * (x + 112)
|
||||
// = 3 * x^6 - 170730 * x^5 + 2329429920 * x^4 + 251300082690 * x^3 - 1270471872603 * x^2 + 4774763204640 * x - 24979889760000
|
||||
a := TBigIntPolynomial.Create([-24979889760000, 4774763204640, -1270471872603, 251300082690, 2329429920, -170730, 3]);
|
||||
r := TPolynomialRoots.BisectIsolation(a);
|
||||
AssertBisectIntervals(r, expRoots);
|
||||
end;
|
||||
|
||||
procedure TPolynomialRootsTestCase.TestBisectWithBound;
|
||||
const
|
||||
expRoots: array of Cardinal = (23017, 5);
|
||||
var
|
||||
a: TBigIntPolynomial;
|
||||
r: TIsolatingIntervalArray;
|
||||
begin
|
||||
// y = 3 * (x - 34000) * (x - 23017) * (x - 5) * (x^2 - 19) * (x + 112)
|
||||
// = 3 * x^6 - 170730 * x^5 + 2329429920 * x^4 + 251300082690 * x^3 - 1270471872603 * x^2 + 4774763204640 * x - 24979889760000
|
||||
a := TBigIntPolynomial.Create([-24979889760000, 4774763204640, -1270471872603, 251300082690, 2329429920, -170730, 3]);
|
||||
r := TPolynomialRoots.BisectIsolation(a, 15);
|
||||
AssertBisectIntervals(r, expRoots);
|
||||
end;
|
||||
|
||||
procedure TPolynomialRootsTestCase.TestBisectInteger;
|
||||
const
|
||||
expRoots: array of Cardinal = (23017, 5);
|
||||
var
|
||||
a: TBigIntPolynomial;
|
||||
r: TBigIntArray;
|
||||
begin
|
||||
// y = 3 * (x - 34000) * (x - 23017) * (x - 5) * (x^2 - 19) * (x + 112)
|
||||
// = 3 * x^6 - 170730 * x^5 + 2329429920 * x^4 + 251300082690 * x^3 - 1270471872603 * x^2 + 4774763204640 * x - 24979889760000
|
||||
a := TBigIntPolynomial.Create([-24979889760000, 4774763204640, -1270471872603, 251300082690, 2329429920, -170730, 3]);
|
||||
r := TPolynomialRoots.BisectInteger(a, 15);
|
||||
AssertBisectIntegers(r, expRoots);
|
||||
end;
|
||||
|
||||
initialization
|
||||
|
||||
RegisterTest('Helper', TPolynomialRootsTestCase);
|
||||
end.
|
||||
|
|
@ -0,0 +1,187 @@
|
|||
{
|
||||
Solutions to the Advent Of Code.
|
||||
Copyright (C) 2024 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/>.
|
||||
}
|
||||
|
||||
unit UPolynomialTestCases;
|
||||
|
||||
{$mode ObjFPC}{$H+}
|
||||
|
||||
interface
|
||||
|
||||
uses
|
||||
Classes, SysUtils, fpcunit, testregistry, UPolynomial, UBigInt;
|
||||
|
||||
type
|
||||
|
||||
{ TBigIntPolynomialTestCase }
|
||||
|
||||
TBigIntPolynomialTestCase = class(TTestCase)
|
||||
private
|
||||
procedure TestCreateWithDegree(const ACoefficients: array of TBigInt; const ADegree: Integer);
|
||||
published
|
||||
procedure TestCreate;
|
||||
procedure TestCreateDegreeZero;
|
||||
procedure TestEqual;
|
||||
procedure TestUnequalSameLength;
|
||||
procedure TestUnequalDifferentLength;
|
||||
procedure TestTrimLeadingZeros;
|
||||
procedure TestCalcValueAt;
|
||||
procedure TestSignVariations;
|
||||
procedure TestScaleByPowerOfTwo;
|
||||
procedure TestScaleVariable;
|
||||
procedure TestScaleVariableByPowerOfTwo;
|
||||
procedure TestTranslateVariableByOne;
|
||||
procedure TestRevertOrderOfCoefficients;
|
||||
procedure TestDivideByVariable;
|
||||
end;
|
||||
|
||||
implementation
|
||||
|
||||
{ TBigIntPolynomialTestCase }
|
||||
|
||||
procedure TBigIntPolynomialTestCase.TestCreateWithDegree(const ACoefficients: array of TBigInt; const ADegree: Integer);
|
||||
var
|
||||
a: TBigIntPolynomial;
|
||||
begin
|
||||
a := TBigIntPolynomial.Create(ACoefficients);
|
||||
AssertEquals('Degree of created polynomial incorrect.', ADegree, a.Degree);
|
||||
end;
|
||||
|
||||
procedure TBigIntPolynomialTestCase.TestCreate;
|
||||
begin
|
||||
TestCreateWithDegree([992123, 7, 20, 4550022], 3);
|
||||
end;
|
||||
|
||||
procedure TBigIntPolynomialTestCase.TestCreateDegreeZero;
|
||||
begin
|
||||
TestCreateWithDegree([4007], 0);
|
||||
TestCreateWithDegree([], 0);
|
||||
TestCreateWithDegree([0], 0);
|
||||
end;
|
||||
|
||||
procedure TBigIntPolynomialTestCase.TestEqual;
|
||||
var
|
||||
a, b: TBigIntPolynomial;
|
||||
begin
|
||||
a := TBigIntPolynomial.Create([10, 7, 5, 1034]);
|
||||
b := TBigIntPolynomial.Create([10, 7, 5, 1034]);
|
||||
AssertTrue('Polynomials are not equal.', a = b);
|
||||
end;
|
||||
|
||||
procedure TBigIntPolynomialTestCase.TestUnequalSameLength;
|
||||
var
|
||||
a, b: TBigIntPolynomial;
|
||||
begin
|
||||
a := TBigIntPolynomial.Create([103, 7, 5, 10]);
|
||||
b := TBigIntPolynomial.Create([1034, 7, 5, 10]);
|
||||
AssertTrue('Polynomials are equal.', a <> b);
|
||||
end;
|
||||
|
||||
procedure TBigIntPolynomialTestCase.TestUnequalDifferentLength;
|
||||
var
|
||||
a, b: TBigIntPolynomial;
|
||||
begin
|
||||
a := TBigIntPolynomial.Create([40000, 10, 7, 5, 1034]);
|
||||
b := TBigIntPolynomial.Create([10, 7, 5, 1034]);
|
||||
AssertTrue('Polynomials are equal.', a <> b);
|
||||
end;
|
||||
|
||||
procedure TBigIntPolynomialTestCase.TestTrimLeadingZeros;
|
||||
var
|
||||
a, b: TBigIntPolynomial;
|
||||
begin
|
||||
a := TBigIntPolynomial.Create([10, 7, 5, 1034, 0, 0]);
|
||||
b := TBigIntPolynomial.Create([10, 7, 5, 1034]);
|
||||
AssertTrue('Polynomials are not equal.', a = b);
|
||||
end;
|
||||
|
||||
procedure TBigIntPolynomialTestCase.TestCalcValueAt;
|
||||
var
|
||||
a: TBigIntPolynomial;
|
||||
exp: TBigInt;
|
||||
begin
|
||||
a := TBigIntPolynomial.Create([80, 892477222, 0, 921556, 7303]);
|
||||
exp:= TBigInt.FromInt64(16867124285);
|
||||
AssertTrue('Polynomial evaluation unexpected.', a.CalcValueAt(15) = exp);
|
||||
end;
|
||||
|
||||
procedure TBigIntPolynomialTestCase.TestSignVariations;
|
||||
var
|
||||
a: TBigIntPolynomial;
|
||||
begin
|
||||
a := TBigIntPolynomial.Create([-10, 15, 0, 10, -20, -15, 0, 0, 5, 10, -10]);
|
||||
AssertEquals(4, a.CalcSignVariations);
|
||||
end;
|
||||
|
||||
procedure TBigIntPolynomialTestCase.TestScaleByPowerOfTwo;
|
||||
var
|
||||
a, b: TBigIntPolynomial;
|
||||
begin
|
||||
a := TBigIntPolynomial.Create([10, 7, 5, 1034]).ScaleByPowerOfTwo(7);
|
||||
b := TBigIntPolynomial.Create([128 * 10, 128 * 7, 128 * 5, 128 * 1034]);
|
||||
AssertTrue('Polynomials are not equal.', a = b);
|
||||
end;
|
||||
|
||||
procedure TBigIntPolynomialTestCase.TestScaleVariable;
|
||||
var
|
||||
a, b: TBigIntPolynomial;
|
||||
begin
|
||||
a := TBigIntPolynomial.Create([10, 7, 5, 1034]).ScaleVariable(TBigInt.FromInt64(10));
|
||||
b := TBigIntPolynomial.Create([10, 70, 500, 1034000]);
|
||||
AssertTrue('Polynomials are not equal.', a = b);
|
||||
end;
|
||||
|
||||
procedure TBigIntPolynomialTestCase.TestScaleVariableByPowerOfTwo;
|
||||
var
|
||||
a, b: TBigIntPolynomial;
|
||||
begin
|
||||
a := TBigIntPolynomial.Create([10, 7, 5, 1034]).ScaleVariableByPowerOfTwo(5);
|
||||
b := TBigIntPolynomial.Create([10, 7 * 32, 5 * 32 * 32, 1034 * 32 * 32 * 32]);
|
||||
AssertTrue('Polynomials are not equal.', a = b);
|
||||
end;
|
||||
|
||||
procedure TBigIntPolynomialTestCase.TestTranslateVariableByOne;
|
||||
var
|
||||
a, b: TBigIntPolynomial;
|
||||
begin
|
||||
a := TBigIntPolynomial.Create([10, 7, 5, 1034]).TranslateVariableByOne;
|
||||
b := TBigIntPolynomial.Create([1056, 3119, 3107, 1034]);
|
||||
AssertTrue('Polynomials are not equal.', a = b);
|
||||
end;
|
||||
|
||||
procedure TBigIntPolynomialTestCase.TestRevertOrderOfCoefficients;
|
||||
var
|
||||
a, b: TBigIntPolynomial;
|
||||
begin
|
||||
a := TBigIntPolynomial.Create([0, 10, 7, 5, 1034]).RevertOrderOfCoefficients;
|
||||
b := TBigIntPolynomial.Create([1034, 5, 7, 10]);
|
||||
AssertTrue('Polynomials are not equal.', a = b);
|
||||
end;
|
||||
|
||||
procedure TBigIntPolynomialTestCase.TestDivideByVariable;
|
||||
var
|
||||
a, b: TBigIntPolynomial;
|
||||
begin
|
||||
a := TBigIntPolynomial.Create([0, 10, 7, 5, 1034]).DivideByVariable;
|
||||
b := TBigIntPolynomial.Create([10, 7, 5, 1034]);
|
||||
AssertTrue('Polynomials are not equal.', a = b);
|
||||
end;
|
||||
|
||||
initialization
|
||||
|
||||
RegisterTest('Helper', TBigIntPolynomialTestCase);
|
||||
end.
|
||||
|
|
@ -26,15 +26,6 @@ uses
|
|||
|
||||
type
|
||||
|
||||
{ TPulsePropagationFullDataTestCase }
|
||||
|
||||
TPulsePropagationFullDataTestCase = class(TEngineBaseTest)
|
||||
protected
|
||||
function CreateSolver: ISolver; override;
|
||||
published
|
||||
procedure TestPart1;
|
||||
end;
|
||||
|
||||
{ TPulsePropagationExampleTestCase }
|
||||
|
||||
TPulsePropagationExampleTestCase = class(TExampleEngineBaseTest)
|
||||
|
@ -61,18 +52,6 @@ type
|
|||
|
||||
implementation
|
||||
|
||||
{ TPulsePropagationFullDataTestCase }
|
||||
|
||||
function TPulsePropagationFullDataTestCase.CreateSolver: ISolver;
|
||||
begin
|
||||
Result := TPulsePropagation.Create;
|
||||
end;
|
||||
|
||||
procedure TPulsePropagationFullDataTestCase.TestPart1;
|
||||
begin
|
||||
AssertEquals(949764474, FSolver.GetResultPart1);
|
||||
end;
|
||||
|
||||
{ TPulsePropagationExampleTestCase }
|
||||
|
||||
function TPulsePropagationExampleTestCase.CreateSolver: ISolver;
|
||||
|
@ -106,8 +85,7 @@ end;
|
|||
|
||||
initialization
|
||||
|
||||
RegisterTest(TPulsePropagationFullDataTestCase);
|
||||
RegisterTest(TPulsePropagationExampleTestCase);
|
||||
RegisterTest(TPulsePropagationExample2TestCase);
|
||||
RegisterTest('TPulsePropagation', TPulsePropagationExampleTestCase);
|
||||
RegisterTest('TPulsePropagation', TPulsePropagationExample2TestCase);
|
||||
end.
|
||||
|
||||
|
|
|
@ -26,16 +26,6 @@ uses
|
|||
|
||||
type
|
||||
|
||||
{ TSandSlabsFullDataTestCase }
|
||||
|
||||
TSandSlabsFullDataTestCase = class(TEngineBaseTest)
|
||||
protected
|
||||
function CreateSolver: ISolver; override;
|
||||
published
|
||||
procedure TestPart1;
|
||||
procedure TestPart2;
|
||||
end;
|
||||
|
||||
{ TSandSlabsExampleTestCase }
|
||||
|
||||
TSandSlabsExampleTestCase = class(TExampleEngineBaseTest)
|
||||
|
@ -48,23 +38,6 @@ type
|
|||
|
||||
implementation
|
||||
|
||||
{ TSandSlabsFullDataTestCase }
|
||||
|
||||
function TSandSlabsFullDataTestCase.CreateSolver: ISolver;
|
||||
begin
|
||||
Result := TSandSlabs.Create;
|
||||
end;
|
||||
|
||||
procedure TSandSlabsFullDataTestCase.TestPart1;
|
||||
begin
|
||||
AssertEquals(389, FSolver.GetResultPart1);
|
||||
end;
|
||||
|
||||
procedure TSandSlabsFullDataTestCase.TestPart2;
|
||||
begin
|
||||
AssertEquals(70609, FSolver.GetResultPart2);
|
||||
end;
|
||||
|
||||
{ TSandSlabsExampleTestCase }
|
||||
|
||||
function TSandSlabsExampleTestCase.CreateSolver: ISolver;
|
||||
|
@ -84,7 +57,6 @@ end;
|
|||
|
||||
initialization
|
||||
|
||||
RegisterTest(TSandSlabsFullDataTestCase);
|
||||
RegisterTest(TSandSlabsExampleTestCase);
|
||||
RegisterTest('TSandSlabs', TSandSlabsExampleTestCase);
|
||||
end.
|
||||
|
||||
|
|
|
@ -26,16 +26,6 @@ uses
|
|||
|
||||
type
|
||||
|
||||
{ TScratchcardsFullDataTestCase }
|
||||
|
||||
TScratchcardsFullDataTestCase = class(TEngineBaseTest)
|
||||
protected
|
||||
function CreateSolver: ISolver; override;
|
||||
published
|
||||
procedure TestPart1;
|
||||
procedure TestPart2;
|
||||
end;
|
||||
|
||||
{ TScratchcardsExampleTestCase }
|
||||
|
||||
TScratchcardsExampleTestCase = class(TExampleEngineBaseTest)
|
||||
|
@ -48,23 +38,6 @@ type
|
|||
|
||||
implementation
|
||||
|
||||
{ TScratchcardsFullDataTestCase }
|
||||
|
||||
function TScratchcardsFullDataTestCase.CreateSolver: ISolver;
|
||||
begin
|
||||
Result := TScratchcards.Create;
|
||||
end;
|
||||
|
||||
procedure TScratchcardsFullDataTestCase.TestPart1;
|
||||
begin
|
||||
AssertEquals(21821, FSolver.GetResultPart1);
|
||||
end;
|
||||
|
||||
procedure TScratchcardsFullDataTestCase.TestPart2;
|
||||
begin
|
||||
AssertEquals(5539496, FSolver.GetResultPart2);
|
||||
end;
|
||||
|
||||
{ TScratchcardsExampleTestCase }
|
||||
|
||||
function TScratchcardsExampleTestCase.CreateSolver: ISolver;
|
||||
|
@ -84,6 +57,5 @@ end;
|
|||
|
||||
initialization
|
||||
|
||||
RegisterTest(TScratchcardsFullDataTestCase);
|
||||
RegisterTest(TScratchcardsExampleTestCase);
|
||||
RegisterTest('TScratchcards', TScratchcardsExampleTestCase);
|
||||
end.
|
||||
|
|
|
@ -0,0 +1,56 @@
|
|||
{
|
||||
Solutions to the Advent Of Code.
|
||||
Copyright (C) 2024 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/>.
|
||||
}
|
||||
|
||||
unit USnowverloadTestCases;
|
||||
|
||||
{$mode ObjFPC}{$H+}
|
||||
|
||||
interface
|
||||
|
||||
uses
|
||||
Classes, SysUtils, fpcunit, testregistry, USolver, UBaseTestCases, USnowverload;
|
||||
|
||||
type
|
||||
|
||||
{ TSnowverloadExampleTestCase }
|
||||
|
||||
TSnowverloadExampleTestCase = class(TExampleEngineBaseTest)
|
||||
protected
|
||||
function CreateSolver: ISolver; override;
|
||||
published
|
||||
procedure TestPart1;
|
||||
end;
|
||||
|
||||
implementation
|
||||
|
||||
{ TSnowverloadExampleTestCase }
|
||||
|
||||
function TSnowverloadExampleTestCase.CreateSolver: ISolver;
|
||||
begin
|
||||
Result := TSnowverload.Create;
|
||||
end;
|
||||
|
||||
procedure TSnowverloadExampleTestCase.TestPart1;
|
||||
begin
|
||||
AssertEquals(54, FSolver.GetResultPart1);
|
||||
end;
|
||||
|
||||
initialization
|
||||
|
||||
RegisterTest('TSnowverload', TSnowverloadExampleTestCase);
|
||||
end.
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
Solutions to the Advent Of Code.
|
||||
Copyright (C) 2023 Stefan Müller
|
||||
Copyright (C) 2023-2024 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
|
||||
|
@ -25,19 +25,22 @@ uses
|
|||
Classes, SysUtils, fpcunit, testregistry, USolver, UBaseTestCases, UStepCounter;
|
||||
|
||||
type
|
||||
// Note that the solver implementation does not work with the examples presented
|
||||
// in the puzzle description for part 2, therefore they are not represented here
|
||||
// as test cases.
|
||||
|
||||
{ TStepCounterFullDataTestCase }
|
||||
{ TStepCounterExampleSteps3TestCase }
|
||||
|
||||
TStepCounterFullDataTestCase = class(TEngineBaseTest)
|
||||
TStepCounterExampleSteps3TestCase = class(TExampleEngineBaseTest)
|
||||
protected
|
||||
function CreateSolver: ISolver; override;
|
||||
published
|
||||
procedure TestPart1;
|
||||
end;
|
||||
|
||||
{ TStepCounterMax6ExampleTestCase }
|
||||
{ TStepCounterExampleSteps6TestCase }
|
||||
|
||||
TStepCounterMax6ExampleTestCase = class(TExampleEngineBaseTest)
|
||||
TStepCounterExampleSteps6TestCase = class(TExampleEngineBaseTest)
|
||||
protected
|
||||
function CreateSolver: ISolver; override;
|
||||
published
|
||||
|
@ -46,33 +49,33 @@ type
|
|||
|
||||
implementation
|
||||
|
||||
{ TStepCounterFullDataTestCase }
|
||||
{ TStepCounterExampleSteps3TestCase }
|
||||
|
||||
function TStepCounterFullDataTestCase.CreateSolver: ISolver;
|
||||
function TStepCounterExampleSteps3TestCase.CreateSolver: ISolver;
|
||||
begin
|
||||
Result := TStepCounter.Create;
|
||||
Result := TStepCounter.Create(3, 3);
|
||||
end;
|
||||
|
||||
procedure TStepCounterFullDataTestCase.TestPart1;
|
||||
procedure TStepCounterExampleSteps3TestCase.TestPart1;
|
||||
begin
|
||||
AssertEquals(3809, FSolver.GetResultPart1);
|
||||
AssertEquals(6, FSolver.GetResultPart1);
|
||||
end;
|
||||
|
||||
{ TStepCounterMax6ExampleTestCase }
|
||||
{ TStepCounterExampleSteps6TestCase }
|
||||
|
||||
function TStepCounterMax6ExampleTestCase.CreateSolver: ISolver;
|
||||
function TStepCounterExampleSteps6TestCase.CreateSolver: ISolver;
|
||||
begin
|
||||
Result := TStepCounter.Create(6);
|
||||
Result := TStepCounter.Create(6, 6);
|
||||
end;
|
||||
|
||||
procedure TStepCounterMax6ExampleTestCase.TestPart1;
|
||||
procedure TStepCounterExampleSteps6TestCase.TestPart1;
|
||||
begin
|
||||
AssertEquals(16, FSolver.GetResultPart1);
|
||||
end;
|
||||
|
||||
initialization
|
||||
|
||||
RegisterTest(TStepCounterFullDataTestCase);
|
||||
RegisterTest(TStepCounterMax6ExampleTestCase);
|
||||
RegisterTest('TStepCounter', TStepCounterExampleSteps3TestCase);
|
||||
RegisterTest('TStepCounter', TStepCounterExampleSteps6TestCase);
|
||||
end.
|
||||
|
||||
|
|
|
@ -26,16 +26,6 @@ uses
|
|||
|
||||
type
|
||||
|
||||
{ TTrebuchetFullDataTestCase }
|
||||
|
||||
TTrebuchetFullDataTestCase = class(TEngineBaseTest)
|
||||
protected
|
||||
function CreateSolver: ISolver; override;
|
||||
published
|
||||
procedure TestPart1;
|
||||
procedure TestPart2;
|
||||
end;
|
||||
|
||||
{ TTrebuchetExampleTestCase }
|
||||
|
||||
TTrebuchetExampleTestCase = class(TExampleEngineBaseTest)
|
||||
|
@ -62,23 +52,6 @@ type
|
|||
|
||||
implementation
|
||||
|
||||
{ TTrebuchetFullDataTestCase }
|
||||
|
||||
function TTrebuchetFullDataTestCase.CreateSolver: ISolver;
|
||||
begin
|
||||
Result := TTrebuchet.Create;
|
||||
end;
|
||||
|
||||
procedure TTrebuchetFullDataTestCase.TestPart1;
|
||||
begin
|
||||
AssertEquals(56506, FSolver.GetResultPart1);
|
||||
end;
|
||||
|
||||
procedure TTrebuchetFullDataTestCase.TestPart2;
|
||||
begin
|
||||
AssertEquals(56017, FSolver.GetResultPart2);
|
||||
end;
|
||||
|
||||
{ TTrebuchetExampleTestCase }
|
||||
|
||||
function TTrebuchetExampleTestCase.CreateSolver: ISolver;
|
||||
|
@ -95,7 +68,7 @@ end;
|
|||
|
||||
function TExample2Trebuchet.GetDataFileName: string;
|
||||
begin
|
||||
Result := 'trebuchet_calibration_document2.txt';
|
||||
Result := 'trebuchet2.txt';
|
||||
end;
|
||||
|
||||
{ TTrebuchetExample2TestCase }
|
||||
|
@ -112,7 +85,6 @@ end;
|
|||
|
||||
initialization
|
||||
|
||||
RegisterTest(TTrebuchetFullDataTestCase);
|
||||
RegisterTest(TTrebuchetExampleTestCase);
|
||||
RegisterTest(TTrebuchetExample2TestCase);
|
||||
RegisterTest('TTrebuchet', TTrebuchetExampleTestCase);
|
||||
RegisterTest('TTrebuchet', TTrebuchetExample2TestCase);
|
||||
end.
|
||||
|
|
|
@ -26,16 +26,6 @@ uses
|
|||
|
||||
type
|
||||
|
||||
{ TWaitForItFullDataTestCase }
|
||||
|
||||
TWaitForItFullDataTestCase = class(TEngineBaseTest)
|
||||
protected
|
||||
function CreateSolver: ISolver; override;
|
||||
published
|
||||
procedure TestPart1;
|
||||
procedure TestPart2;
|
||||
end;
|
||||
|
||||
{ TWaitForItExampleTestCase }
|
||||
|
||||
TWaitForItExampleTestCase = class(TExampleEngineBaseTest)
|
||||
|
@ -48,23 +38,6 @@ type
|
|||
|
||||
implementation
|
||||
|
||||
{ TWaitForItFullDataTestCase }
|
||||
|
||||
function TWaitForItFullDataTestCase.CreateSolver: ISolver;
|
||||
begin
|
||||
Result := TWaitForIt.Create;
|
||||
end;
|
||||
|
||||
procedure TWaitForItFullDataTestCase.TestPart1;
|
||||
begin
|
||||
AssertEquals(4811940, FSolver.GetResultPart1);
|
||||
end;
|
||||
|
||||
procedure TWaitForItFullDataTestCase.TestPart2;
|
||||
begin
|
||||
AssertEquals(30077773, FSolver.GetResultPart2);
|
||||
end;
|
||||
|
||||
{ TWaitForItExampleTestCase }
|
||||
|
||||
function TWaitForItExampleTestCase.CreateSolver: ISolver;
|
||||
|
@ -84,6 +57,5 @@ end;
|
|||
|
||||
initialization
|
||||
|
||||
RegisterTest(TWaitForItFullDataTestCase);
|
||||
RegisterTest(TWaitForItExampleTestCase);
|
||||
RegisterTest('TWaitForIt', TWaitForItExampleTestCase);
|
||||
end.
|
||||
|
|
Loading…
Reference in New Issue