diff --git a/AdventOfCode.lpi b/AdventOfCode.lpi index 14849ab..f6ed2ae 100644 --- a/AdventOfCode.lpi +++ b/AdventOfCode.lpi @@ -121,6 +121,10 @@ + + + + diff --git a/AdventOfCode.lpr b/AdventOfCode.lpr index 483f69d..039cf72 100644 --- a/AdventOfCode.lpr +++ b/AdventOfCode.lpr @@ -27,7 +27,7 @@ uses UTrebuchet, UCubeConundrum, UGearRatios, UScratchcards, UGiveSeedFertilizer, UWaitForIt, UCamelCards, UHauntedWasteland, UNumberTheory, UMirageMaintenance, UPipeMaze, UCosmicExpansion, UHotSprings, UPointOfIncidence, UParabolicReflectorDish, ULensLibrary, UFloorWillBeLava, UClumsyCrucible, ULavaductLagoon, UAplenty, - UPulsePropagation; + UPulsePropagation, UStepCounter; type @@ -89,6 +89,7 @@ begin 18: engine.RunAndFree(TLavaductLagoon.Create); 19: engine.RunAndFree(TAplenty.Create); 20: engine.RunAndFree(TPulsePropagation.Create); + 21: engine.RunAndFree(TStepCounter.Create); end; engine.Free; diff --git a/solvers/UStepCounter.pas b/solvers/UStepCounter.pas new file mode 100644 index 0000000..0a86095 --- /dev/null +++ b/solvers/UStepCounter.pas @@ -0,0 +1,173 @@ +{ + Solutions to the Advent Of Code. + Copyright (C) 2023 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 . +} + +unit UStepCounter; + +{$mode ObjFPC}{$H+} + +interface + +uses + Classes, SysUtils, Generics.Collections, USolver; + +type + TPoints = specialize TList; + + { TStepCounter } + + TStepCounter = class(TSolver) + private + FLines: TStringList; + FWidth, FHeight, FMaxSteps: Integer; + function FindStart: TPoint; + function IsInBounds(constref APoint: TPoint): Boolean; + function GetPosition(constref APoint: TPoint): Char; + procedure SetPosition(constref APoint: TPoint; const AValue: Char); + public + property MaxSteps: Integer read FMaxSteps write FMaxSteps; + constructor Create; + destructor Destroy; override; + procedure ProcessDataLine(const ALine: string); override; + procedure Finish; override; + function GetDataFileName: string; override; + function GetPuzzleName: string; override; + end; + +const + CStartChar = 'S'; + CPlotChar = '.'; + CTraversedChar = '+'; + CDirections: array of TPoint = ((X: 1; Y: 0), (X: -1; Y: 0), (X: 0; Y: 1), (X: 0; Y: -1)); + +implementation + +{ TStepCounter } + +function TStepCounter.FindStart: TPoint; +var + i, j: Integer; +begin + for i := 1 to FWidth do + for j := 0 to FHeight - 1 do + if FLines[j][i] = CStartChar then + begin + Result.X := i; + Result.Y := j; + Exit; + end; +end; + +function TStepCounter.IsInBounds(constref APoint: TPoint): Boolean; +begin + Result := (0 < APoint.X) and (APoint.X <= FWidth) and (0 <= APoint.Y) and (APoint.Y < FHeight); +end; + +function TStepCounter.GetPosition(constref APoint: TPoint): Char; +begin + Result := FLines[APoint.Y][APoint.X]; +end; + +procedure TStepCounter.SetPosition(constref APoint: TPoint; const AValue: Char); +var + s: string; +begin + s := FLines[APoint.Y]; + s[APoint.X] := AValue; + FLines[APoint.Y] := s; +end; + +constructor TStepCounter.Create; +begin + FMaxSteps := 64; + FLines := TStringList.Create; +end; + +destructor TStepCounter.Destroy; +begin + FLines.Free; + inherited Destroy; +end; + +procedure TStepCounter.ProcessDataLine(const ALine: string); +begin + FLines.Add(ALine); +end; + +procedure TStepCounter.Finish; +var + currentStep: Integer; + currentPlots, nextPlots, temp: TPoints; + plot, direction, next: TPoint; + //s: string; +begin + FWidth := Length(FLines[0]); + FHeight := FLines.Count; + + 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 + //begin + Inc(FPart1, currentPlots.Count); + // for plot in currentPlots do + // SetPosition(plot, 'O'); + //end; + + //for s in FLines do + // WriteLn(s); + //WriteLn; + end; + + currentPlots.Free; + nextPlots.Free; +end; + +function TStepCounter.GetDataFileName: string; +begin + Result := 'step_counter.txt'; +end; + +function TStepCounter.GetPuzzleName: string; +begin + Result := 'Day 21: Step Counter'; +end; + +end. + diff --git a/tests/AdventOfCodeFPCUnit.lpi b/tests/AdventOfCodeFPCUnit.lpi index 6a6220f..64daaed 100644 --- a/tests/AdventOfCodeFPCUnit.lpi +++ b/tests/AdventOfCodeFPCUnit.lpi @@ -124,6 +124,10 @@ + + + + diff --git a/tests/AdventOfCodeFPCUnit.lpr b/tests/AdventOfCodeFPCUnit.lpr index 58d65a8..f7c9310 100644 --- a/tests/AdventOfCodeFPCUnit.lpr +++ b/tests/AdventOfCodeFPCUnit.lpr @@ -8,7 +8,7 @@ uses UHauntedWastelandTestCases, UMirageMaintenanceTestCases, UPipeMazeTestCases, UCosmicExpansionTestCases, UHotSpringsTestCases, UPointOfIncidenceTestCases, UParabolicReflectorDishTestCases, ULensLibraryTestCases, UFloorWillBeLavaTestCases, UClumsyCrucibleTestCases, ULavaductLagoonTestCases, UAplentyTestCases, -UPulsePropagationTestCases; + UPulsePropagationTestCases, UStepCounterTestCases; {$R *.res} diff --git a/tests/UStepCounterTestCases.pas b/tests/UStepCounterTestCases.pas new file mode 100644 index 0000000..802b7ad --- /dev/null +++ b/tests/UStepCounterTestCases.pas @@ -0,0 +1,82 @@ +{ + Solutions to the Advent Of Code. + Copyright (C) 2023 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 . +} + +unit UStepCounterTestCases; + +{$mode ObjFPC}{$H+} + +interface + +uses + Classes, SysUtils, fpcunit, testregistry, USolver, UBaseTestCases, UStepCounter; + +type + + { TStepCounterFullDataTestCase } + + TStepCounterFullDataTestCase = class(TEngineBaseTest) + protected + function CreateSolver: ISolver; override; + published + procedure TestPart1; + end; + + { TStepCounterMax6ExampleTestCase } + + TStepCounterMax6ExampleTestCase = class(TExampleEngineBaseTest) + protected + function CreateSolver: ISolver; override; + published + procedure TestPart1; + end; + +implementation + +{ TStepCounterFullDataTestCase } + +function TStepCounterFullDataTestCase.CreateSolver: ISolver; +begin + Result := TStepCounter.Create; +end; + +procedure TStepCounterFullDataTestCase.TestPart1; +begin + AssertEquals(3809, FSolver.GetResultPart1); +end; + +{ TStepCounterMax6ExampleTestCase } + +function TStepCounterMax6ExampleTestCase.CreateSolver: ISolver; +var + solver: TStepCounter; +begin + solver := TStepCounter.Create; + solver.MaxSteps := 6; + Result := solver; +end; + +procedure TStepCounterMax6ExampleTestCase.TestPart1; +begin + AssertEquals(16, FSolver.GetResultPart1); +end; + +initialization + + RegisterTest(TStepCounterFullDataTestCase); + RegisterTest(TStepCounterMax6ExampleTestCase); +end. +