diff --git a/AdventOfCode.lpi b/AdventOfCode.lpi index 8cebdb5..f9cc1bf 100644 --- a/AdventOfCode.lpi +++ b/AdventOfCode.lpi @@ -81,6 +81,10 @@ + + + + diff --git a/AdventOfCode.lpr b/AdventOfCode.lpr index 9bda50e..5d88d23 100644 --- a/AdventOfCode.lpr +++ b/AdventOfCode.lpr @@ -26,7 +26,7 @@ uses Classes, SysUtils, CustApp, USolver, UTrebuchet, UCubeConundrum, UGearRatios, UScratchcards, UGiveSeedFertilizer, UWaitForIt, UCamelCards, - UHauntedWasteland, UNumberTheory, UMirageMaintenance, UPipeMaze, UCosmicExpansion; + UHauntedWasteland, UNumberTheory, UMirageMaintenance, UPipeMaze, UCosmicExpansion, UHotSprings; type @@ -62,6 +62,7 @@ begin engine.RunAndFree(TMirageMaintenance.Create); engine.RunAndFree(TPipeMaze.Create); engine.RunAndFree(TCosmicExpansion.Create); + engine.RunAndFree(THotSprings.Create); engine.Free; end; diff --git a/solvers/UHotSprings.pas b/solvers/UHotSprings.pas new file mode 100644 index 0000000..6b27105 --- /dev/null +++ b/solvers/UHotSprings.pas @@ -0,0 +1,170 @@ +{ + 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 UHotSprings; + +{$mode ObjFPC}{$H+} + +interface + +uses + Classes, SysUtils, Generics.Collections, USolver; + +const + COperationalChar = '.'; + CDamagedChar = '#'; + CWildcardChar = '?'; + COperationalPatternChars = [COperationalChar, CWildcardChar]; + CDamagedPatternChars = [CDamagedChar, CWildcardChar]; + +type + + { THotSprings } + + THotSprings = class(TSolver) + private + FValidation: specialize TList; + FSpringPattern: string; + procedure ExtendArrangement(const AArrangement: string; const ARemainingFreeOperationalCount, ACurrentValidationIndex: + Integer); + function TryAppendOperationalChar(var AArrangement: string): Boolean; + function TryAppendValidationBlock(var AArrangement: string; const ALength: Integer): Boolean; + 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 + +{ THotSprings } + +procedure THotSprings.ExtendArrangement(const AArrangement: string; const ARemainingFreeOperationalCount, + ACurrentValidationIndex: Integer); +var + i, len: Integer; + match: Boolean; + temp: string; +begin + len := Length(AArrangement); + if len = Length(FSpringPattern) then + Inc(FPart1) + else begin + temp := AArrangement; + // Tries to append a dot (operational) to the current arrangement. + if (ARemainingFreeOperationalCount > 0) and TryAppendOperationalChar(temp) then + begin + ExtendArrangement(temp, ARemainingFreeOperationalCount - 1, ACurrentValidationIndex); + end; + + // Tries to append the current validation block (damaged) to the current arrangement. + if ACurrentValidationIndex < FValidation.Count then + begin + temp := AArrangement; + match := TryAppendValidationBlock(temp, FValidation[ACurrentValidationIndex]); + + // ... and the mandatory dot after the block, if it is not the last block. + if match + and (ACurrentValidationIndex < FValidation.Count - 1) + and not TryAppendOperationalChar(temp) then + match := False; + + if match then + ExtendArrangement(temp, ARemainingFreeOperationalCount, ACurrentValidationIndex + 1); + end; + end; +end; + +function THotSprings.TryAppendOperationalChar(var AArrangement: string): Boolean; +begin + if FSpringPattern[Length(AArrangement) + 1] in COperationalPatternChars then + begin + AArrangement := AArrangement + COperationalChar; + Result := True; + end + else + Result := False; +end; + +function THotSprings.TryAppendValidationBlock(var AArrangement: string; const ALength: Integer): Boolean; +var + i, len: Integer; +begin + Result := True; + len := Length(AArrangement); + for i := 1 to ALength do + begin + if FSpringPattern[len + i] in CDamagedPatternChars then + AArrangement := AArrangement + CDamagedChar + else begin + Result := False; + Break; + end; + end; +end; + +constructor THotSprings.Create; +begin + FValidation := specialize TList.Create; +end; + +destructor THotSprings.Destroy; +begin + FValidation.Free; + inherited Destroy; +end; + +procedure THotSprings.ProcessDataLine(const ALine: string); +var + split: TStringArray; + i, val, maxFreeOperationalCount: Integer; +begin + FValidation.Clear; + split := ALine.Split([' ', ',']); + FSpringPattern := split[0]; + + maxFreeOperationalCount := Length(FSpringPattern) - Length(split) + 2; + for i := 1 to Length(split) - 1 do + begin + val := StrToInt(split[i]); + FValidation.Add(val); + Dec(maxFreeOperationalCount, val); + end; + + ExtendArrangement('', maxFreeOperationalCount, 0); +end; + +procedure THotSprings.Finish; +begin + +end; + +function THotSprings.GetDataFileName: string; +begin + Result := 'hot_springs.txt'; +end; + +function THotSprings.GetPuzzleName: string; +begin + Result := 'Day 12: Hot Springs'; +end; + +end. + diff --git a/tests/AdventOfCodeFPCUnit.lpi b/tests/AdventOfCodeFPCUnit.lpi index 3875c21..178fda6 100644 --- a/tests/AdventOfCodeFPCUnit.lpi +++ b/tests/AdventOfCodeFPCUnit.lpi @@ -88,6 +88,10 @@ + + + + diff --git a/tests/AdventOfCodeFPCUnit.lpr b/tests/AdventOfCodeFPCUnit.lpr index 3a607b4..896d2ac 100644 --- a/tests/AdventOfCodeFPCUnit.lpr +++ b/tests/AdventOfCodeFPCUnit.lpr @@ -5,7 +5,8 @@ program AdventOfCodeFPCUnit; uses Interfaces, Forms, GuiTestRunner, USolver, UBaseTestCases, UTrebuchetTestCases, UCubeConundrumTestCases, UGearRatiosTestCases, UScratchcardsTestCases, UGiveSeedFertilizerTestCases, UWaitForItTestCases, UCamelCardsTestCases, - UHauntedWastelandTestCases, UMirageMaintenanceTestCases, UPipeMazeTestCases, UCosmicExpansionTestCases; + UHauntedWastelandTestCases, UMirageMaintenanceTestCases, UPipeMazeTestCases, UCosmicExpansionTestCases, + UHotSpringsTestCases; {$R *.res} diff --git a/tests/UHotSpringsTestCases.pas b/tests/UHotSpringsTestCases.pas new file mode 100644 index 0000000..1822c8c --- /dev/null +++ b/tests/UHotSpringsTestCases.pas @@ -0,0 +1,139 @@ +{ + 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 UHotSpringsTestCases; + +{$mode ObjFPC}{$H+} + +interface + +uses + Classes, SysUtils, fpcunit, testregistry, USolver, UBaseTestCases, UHotSprings; + +type + + { THotSpringsFullDataTestCase } + + THotSpringsFullDataTestCase = class(TEngineBaseTest) + protected + function CreateSolver: ISolver; override; + published + procedure TestPart1; + end; + + { THotSpringsExampleTestCase } + + THotSpringsExampleTestCase = class(TExampleEngineBaseTest) + protected + function CreateSolver: ISolver; override; + published + procedure TestPart1; + end; + + { THotSpringsTestCase } + + THotSpringsTestCase = class(TSolverTestCase) + protected + function CreateSolver: ISolver; override; + procedure TestSingleLine(const ALine: string; const AValue: Integer); + published + procedure TestExampleLine1; + procedure TestExampleLine2; + procedure TestExampleLine3; + procedure TestExampleLine4; + procedure TestExampleLine5; + procedure TestExampleLine6; + end; + +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; +begin + Result := THotSprings.Create; +end; + +procedure THotSpringsExampleTestCase.TestPart1; +begin + AssertEquals(21, FSolver.GetResultPart1); +end; + +{ THotSpringsTestCase } + +function THotSpringsTestCase.CreateSolver: ISolver; +begin + Result := THotSprings.Create; +end; + +procedure THotSpringsTestCase.TestSingleLine(const ALine: string; const AValue: Integer); +begin + FSolver.Init; + FSolver.ProcessDataLine(ALine); + FSolver.Finish; + AssertEquals(AValue, FSolver.GetResultPart1); +end; + +procedure THotSpringsTestCase.TestExampleLine1; +begin + TestSingleLine('???.### 1,1,3', 1); +end; + +procedure THotSpringsTestCase.TestExampleLine2; +begin + TestSingleLine('.??..??...?##. 1,1,3', 4); +end; + +procedure THotSpringsTestCase.TestExampleLine3; +begin + TestSingleLine('?#?#?#?#?#?#?#? 1,3,1,6', 1); +end; + +procedure THotSpringsTestCase.TestExampleLine4; +begin + TestSingleLine('????.#...#... 4,1,1', 1); +end; + +procedure THotSpringsTestCase.TestExampleLine5; +begin + TestSingleLine('????.######..#####. 1,6,5', 4); +end; + +procedure THotSpringsTestCase.TestExampleLine6; +begin + TestSingleLine('?###???????? 3,2,1', 10); +end; + +initialization + + RegisterTest(THotSpringsFullDataTestCase); + RegisterTest(THotSpringsExampleTestCase); + RegisterTest(THotSpringsTestCase); +end. +