From c9e9eca35a99a11f2aa590ab10757f9054b54928 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefan=20M=C3=BCller?= Date: Tue, 5 Dec 2023 14:44:32 +0100 Subject: [PATCH] Added solution for "Day 5: If You Give A Seed A Fertilizer", part 1 --- AdventOfCode.lpi | 4 + AdventOfCode.lpr | 3 +- UGiveSeedFertilizer.pas | 208 +++++++++++++++++++++++++ tests/AdventOfCodeFPCUnit.lpi | 4 + tests/AdventOfCodeFPCUnit.lpr | 3 +- tests/UGiveSeedFertilizerTestCases.pas | 89 +++++++++++ 6 files changed, 309 insertions(+), 2 deletions(-) create mode 100644 UGiveSeedFertilizer.pas create mode 100644 tests/UGiveSeedFertilizerTestCases.pas diff --git a/AdventOfCode.lpi b/AdventOfCode.lpi index 102137f..dea1b1c 100644 --- a/AdventOfCode.lpi +++ b/AdventOfCode.lpi @@ -49,6 +49,10 @@ + + + + diff --git a/AdventOfCode.lpr b/AdventOfCode.lpr index 43c82a3..51224b5 100644 --- a/AdventOfCode.lpr +++ b/AdventOfCode.lpr @@ -25,7 +25,7 @@ uses {$ENDIF} Classes, SysUtils, CustApp, USolver, - UTrebuchet, UCubeConundrum, UGearRatios, UScratchcards; + UTrebuchet, UCubeConundrum, UGearRatios, UScratchcards, UGiveSeedFertilizer; type @@ -54,6 +54,7 @@ begin engine := TSolverEngine.Create('data'); engine.RunAndFree(TGearRatios.Create); engine.RunAndFree(TScratchcards.Create); + engine.RunAndFree(TGiveSeedFertilizer.Create); engine.Free; end; diff --git a/UGiveSeedFertilizer.pas b/UGiveSeedFertilizer.pas new file mode 100644 index 0000000..dc44a6b --- /dev/null +++ b/UGiveSeedFertilizer.pas @@ -0,0 +1,208 @@ +{ + 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 UGiveSeedFertilizer; + +{$mode ObjFPC}{$H+} + +interface + +uses + Classes, SysUtils, fgl, USolver; + +type + + { TAlmanacMapRange } + + TAlmanacMapRange = class + private + FDestinationStart, FSourceStart, FSourceEnd, FDifference1, FDifference2: Cardinal; + public + constructor Create(const ADestinationStart, ASourceStart, ALength: Cardinal); + function TryConvert(const AInput: Cardinal; out OOutput: Cardinal): Boolean; + end; + + { TAlmanacMap } + + TAlmanacMap = class + private + FRanges: specialize TFPGObjectList; + public + constructor Create; + destructor Destroy; override; + procedure AddMapRange(const ALine: string); + function Convert(const AInput: Cardinal): Cardinal; + end; + + { TGiveSeedFertilizer } + + TGiveSeedFertilizer = class(TSolver) + private + FSeeds: specialize TFPGList; + FMaps: specialize TFPGObjectList; + procedure ProcessSeeds(const ALine: string); + 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 + +{ TAlmanacMapRange } + +constructor TAlmanacMapRange.Create(const ADestinationStart, ASourceStart, ALength: Cardinal); +begin + FDestinationStart := ADestinationStart; + FSourceStart := ASourceStart; + + // All input values are within Cardinal range, but the calculations might not be. + if ASourceStart <= Cardinal.MaxValue - ALength then + FSourceEnd := ASourceStart + ALength + else + FSourceEnd := Cardinal.MaxValue; + + if ADestinationStart >= ASourceStart then + begin + FDifference1 := ADestinationStart - ASourceStart; + FDifference2 := 0; + end + else begin + FDifference1 := 0; + FDifference2 := ASourceStart - ADestinationStart; + end; +end; + +function TAlmanacMapRange.TryConvert(const AInput: Cardinal; out OOutput: Cardinal): Boolean; +begin + if (FSourceStart <= AInput) and (AInput < FSourceEnd) then + begin + if FDifference1 > 0 then + OOutput := AInput + FDifference1 + else + OOutput := AInput - FDifference2; + Result := True; + end + else begin + OOutput := 0; + Result := False; + end; +end; + +{ TAlmanacMap } + +constructor TAlmanacMap.Create; +begin + FRanges := specialize TFPGObjectList.Create; +end; + +destructor TAlmanacMap.Destroy; +begin + FRanges.Free; + inherited Destroy; +end; + +procedure TAlmanacMap.AddMapRange(const ALine: string); +var + split: TStringArray; +begin + split := ALine.Split(' '); + FRanges.Add(TAlmanacMapRange.Create(StrToDWord(split[0]), StrToDWord(split[1]), StrToDWord(split[2]))); +end; + +function TAlmanacMap.Convert(const AInput: Cardinal): Cardinal; +var + range: TAlmanacMapRange; +begin + for range in FRanges do + begin + if range.TryConvert(AInput, Result) then + Exit; + end; + + Result := AInput; +end; + +{ TGiveSeedFertilizer } + +procedure TGiveSeedFertilizer.ProcessSeeds(const ALine: string); +var + split: TStringArray; + i: Integer; +begin + split := Aline.Split(' '); + for i := 1 to Length(split) - 1 do + begin + FSeeds.Add(StrToDWord(split[i])); + end; +end; + +constructor TGiveSeedFertilizer.Create; +begin + FSeeds := specialize TFPGList.Create; + FMaps := specialize TFPGObjectList.Create; +end; + +destructor TGiveSeedFertilizer.Destroy; +begin + FSeeds.Free; + FMaps.Free; + inherited Destroy; +end; + +procedure TGiveSeedFertilizer.ProcessDataLine(const ALine: string); +begin + if LeftStr(ALine, 6) = 'seeds:' then + ProcessSeeds(ALine) + else if RightStr(ALine, 4) = 'map:' then + FMaps.Add(TAlmanacMap.Create) + else if Aline <> '' then + FMaps.Last.AddMapRange(ALine); +end; + +procedure TGiveSeedFertilizer.Finish; +var + seed, value: Cardinal; + map: TAlmanacMap; +begin + for seed in FSeeds do + begin + value := seed; + for map in FMaps do + begin + value := map.Convert(value); + end; + if (FPart1 = 0) or (value < FPart1) then + FPart1 := value; + end; +end; + +function TGiveSeedFertilizer.GetDataFileName: string; +begin + Result := 'give_seed_fertilizer.txt'; +end; + +function TGiveSeedFertilizer.GetPuzzleName: string; +begin + Result := 'Day 5: If You Give A Seed A Fertilizer'; +end; + +end. + diff --git a/tests/AdventOfCodeFPCUnit.lpi b/tests/AdventOfCodeFPCUnit.lpi index 4f20461..6a82f6b 100644 --- a/tests/AdventOfCodeFPCUnit.lpi +++ b/tests/AdventOfCodeFPCUnit.lpi @@ -52,6 +52,10 @@ + + + + diff --git a/tests/AdventOfCodeFPCUnit.lpr b/tests/AdventOfCodeFPCUnit.lpr index 3aa8ff7..65174d4 100644 --- a/tests/AdventOfCodeFPCUnit.lpr +++ b/tests/AdventOfCodeFPCUnit.lpr @@ -3,7 +3,8 @@ program AdventOfCodeFPCUnit; {$mode objfpc}{$H+} uses - Interfaces, Forms, GuiTestRunner, USolver, UBaseTestCases, UGearRatiosTestCases, UScratchcardsTestCases; + Interfaces, Forms, GuiTestRunner, USolver, UBaseTestCases, UGearRatiosTestCases, UScratchcardsTestCases, +UGiveSeedFertilizerTestCases; {$R *.res} diff --git a/tests/UGiveSeedFertilizerTestCases.pas b/tests/UGiveSeedFertilizerTestCases.pas new file mode 100644 index 0000000..4e42cce --- /dev/null +++ b/tests/UGiveSeedFertilizerTestCases.pas @@ -0,0 +1,89 @@ +{ + 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 UGiveSeedFertilizerTestCases; + +{$mode ObjFPC}{$H+} + +interface + +uses + Classes, SysUtils, fpcunit, testregistry, USolver, UBaseTestCases, UGiveSeedFertilizer; + +type + + { TGiveSeedFertilizerFullDataTestCase } + + TGiveSeedFertilizerFullDataTestCase = class(TEngineBaseTest) + protected + function CreateSolver: ISolver; override; + published + procedure TestPart1; + procedure TestPart2; + end; + + { TGiveSeedFertilizerExampleTestCase } + + TGiveSeedFertilizerExampleTestCase = class(TExampleEngineBaseTest) + protected + function CreateSolver: ISolver; override; + published + procedure TestPart1; + procedure TestPart2; + end; + +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(-1, FSolver.GetResultPart2); +end; + +{ TGiveSeedFertilizerExampleTestCase } + +function TGiveSeedFertilizerExampleTestCase.CreateSolver: ISolver; +begin + Result := TGiveSeedFertilizer.Create; +end; + +procedure TGiveSeedFertilizerExampleTestCase.TestPart1; +begin + AssertEquals(35, FSolver.GetResultPart1); +end; + +procedure TGiveSeedFertilizerExampleTestCase.TestPart2; +begin + AssertEquals(-1, FSolver.GetResultPart2); +end; + +initialization + + RegisterTest(TGiveSeedFertilizerFullDataTestCase); + RegisterTest(TGiveSeedFertilizerExampleTestCase); +end.