Updated several of the solution infos in the readme

This commit is contained in:
Stefan Müller 2024-06-26 09:36:56 +02:00
parent 6d622d4c22
commit 5441700572
1 changed files with 14 additions and 12 deletions

View File

@ -32,7 +32,9 @@ That one seemed pretty straight forward. For each line, the solution immediately
: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 mutliple lines at once instead of one by one. Here, it also needs the lines directly before and after the one being processed.
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
@ -40,7 +42,7 @@ For this I modified the solver class to pass in three lines at once, shifting on
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
@ -54,13 +56,13 @@ For part 2, it is not necessary (and not feasible) to convert the input ranges i
: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
: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.
@ -86,7 +88,7 @@ A nice explanation of the Lagrange method can be found here <http://bueler.githu
: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.
@ -94,7 +96,7 @@ For part 2, I tracked tiles that are "left" and "right" of the path the algorith
: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*.
@ -102,11 +104,11 @@ This approach was trivial to adapt for part 2, since all that was needed was ano
: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 simultanously.
### Day 14: Parabolic Reflector Dish
@ -116,19 +118,19 @@ I spent too much time on this one. I had originally implemented a relatively nai
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
: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
: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 travelled 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.
@ -136,7 +138,7 @@ Once this was solved for one starting beam in part 1, I just iterated over all p
: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 minor shortcuts for this particular problem, since here we can assume to have a unique solution. However, this involved excessive manual pre-calculations, arbitrary length integer arithmetic, and a root finder for general polynomials, all implemented by myself without additional third-party libraries.
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