diff --git a/AdventOfCode.lpi b/AdventOfCode.lpi index 646efc6..0870890 100644 --- a/AdventOfCode.lpi +++ b/AdventOfCode.lpi @@ -41,6 +41,10 @@ + + + + diff --git a/AdventOfCode.lpr b/AdventOfCode.lpr index c5c3607..83e2617 100644 --- a/AdventOfCode.lpr +++ b/AdventOfCode.lpr @@ -25,7 +25,7 @@ uses {$ENDIF} Classes, SysUtils, CustApp, USolver, - UTrebuchet, UCubeConundrum; + UTrebuchet, UCubeConundrum, UGearRatios; type @@ -52,6 +52,7 @@ begin TTrebuchet.Run; TCubeConundrum.Run; engine := TSolverEngine.Create; + engine.RunAndFree(TGearRatios.Create); engine.Free; end; diff --git a/USolver.pas b/USolver.pas index a5e0de9..43b165c 100644 --- a/USolver.pas +++ b/USolver.pas @@ -53,7 +53,6 @@ type procedure Init; virtual; procedure ProcessDataLine(const ALine: string); virtual; abstract; procedure Finish; virtual; abstract; - procedure Free; virtual; function GetDataFileName: string; virtual; abstract; function GetPuzzleName: string; virtual; abstract; function GetResultPart1: Integer; virtual; @@ -80,11 +79,6 @@ begin FPart2 := 0; end; -procedure TSolver.Free; -begin - Free; -end; - function TSolver.GetResultPart1: Integer; begin Result := FPart1; diff --git a/solvers/UGearRatios.pas b/solvers/UGearRatios.pas new file mode 100644 index 0000000..d93548f --- /dev/null +++ b/solvers/UGearRatios.pas @@ -0,0 +1,147 @@ +{ + 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 UGearRatios; + +{$mode ObjFPC}{$H+} + +interface + +uses + Classes, SysUtils, USolver; + +const + CDigitChars = ['0'..'9']; + CNonSymbolChars = ['0'..'9', '.']; + +type + + { TGearRatios } + + TGearRatios = class(TSolver) + private + FPreviousLine, FLine: string; + procedure ProcessDataLineTriplet(const APreviousLine, ALine, ANextLine: string); + public + procedure ProcessDataLine(const ALine: string); override; + procedure Finish; override; + function GetDataFileName: string; override; + function GetPuzzleName: string; override; + end; + +implementation + +{ TGearRatios } + +procedure TGearRatios.ProcessDataLineTriplet(const APreviousLine, ALine, ANextLine: string); +var + i, numberStart, numberLength, partNumber: Integer; + inNumber, isPartNumber: Boolean; +begin + inNumber := False; + + for i := 1 to ALine.Length do + begin + // Checks if number starts. + if not inNumber and (ALine[i] in CDigitChars) then + begin + inNumber := True; + numberStart := i; + + // Checks for a symbol in the column before the first digit. + isPartNumber := (i > 1) and + (not (APreviousLine[i - 1] in CNonSymbolChars) + or not (ALine[i - 1] in CNonSymbolChars) + or not (ANextLine[i - 1] in CNonSymbolChars)); + end; + + // Checks for a symbol in the column of the current digit. + if inNumber and not isPartNumber and + (not (APreviousLine[i] in CNonSymbolChars) + or not (ANextLine[i] in CNonSymbolChars)) then + begin + isPartNumber := True; + end; + + // Checks if number ends. + if inNumber and (not (ALine[i] in CDigitChars) or (i = ALine.Length)) then + begin + inNumber := False; + + // Checks for a symbol in the column after the last digit. + if not (APreviousLine[i] in CNonSymbolChars) + or not (ALine[i] in CNonSymbolChars) + or not (ANextLine[i] in CNonSymbolChars) then + begin + isPartNumber := True; + end; + + // Counts if it is a part number. + if isPartNumber then + begin + numberLength := i - numberStart; + if ALine[i] in CDigitChars then + Inc(numberLength); + partNumber := StrToInt(Copy(ALine, numberStart, numberLength)); + Inc(FPart1, partNumber); + end; + end; + end; +end; + +procedure TGearRatios.ProcessDataLine(const ALine: string); +begin + if not (FLine = '') then + begin + // Processes lines 2 to n - 1 in calls for lines 3 to n. + ProcessDataLineTriplet(FPreviousLine, FLine, ALine); + FPreviousLine := FLine; + FLine := ALine; + end + else if not (FPreviousLine = '') then + begin + // Processes line 1 in call for line 2. + FLine := ALine; + // Duplicates the first line for the algorithm because it does not influence the result. + ProcessDataLineTriplet(FPreviousLine, FPreviousLine, FLine); + end + else + // Processes nothing in call for line 1. + FPreviousLine := ALine; +end; + +procedure TGearRatios.Finish; +begin + // Processes line n. + // Duplicates the last line for the algorithm because it does not influence the result. + if FLine = '' then + FLine := FPreviousLine; + ProcessDataLineTriplet(FPreviousLine, FLine, FLine); +end; + +function TGearRatios.GetDataFileName: string; +begin + Result := 'gear_ratios.txt'; +end; + +function TGearRatios.GetPuzzleName: string; +begin + Result := 'Day 3: Gear Ratios'; +end; + +end. + diff --git a/tests/AdventOfCodeFPCUnit.fpcunit.ini b/tests/AdventOfCodeFPCUnit.fpcunit.ini new file mode 100644 index 0000000..b64eb5d --- /dev/null +++ b/tests/AdventOfCodeFPCUnit.fpcunit.ini @@ -0,0 +1,15 @@ +[WindowState] +Left=664 +Top=189 +Width=575 +Height=663 + +[Tests] +All Tests.Checked=1 +All Tests.Expanded=1 +TGearRatiosTestCase.Checked=1 +TGearRatiosTestCase.Expanded=1 +TGearRatiosTestCase.TestPuzzleExample.Checked=1 +TGearRatiosTestCase.TestPuzzleExample.Expanded=0 +TGearRatiosTestCase.TestEndOfLineNumber.Checked=1 +TGearRatiosTestCase.TestEndOfLineNumber.Expanded=0 diff --git a/tests/AdventOfCodeFPCUnit.ico b/tests/AdventOfCodeFPCUnit.ico new file mode 100644 index 0000000..10c5fc1 Binary files /dev/null and b/tests/AdventOfCodeFPCUnit.ico differ diff --git a/tests/AdventOfCodeFPCUnit.lpi b/tests/AdventOfCodeFPCUnit.lpi new file mode 100644 index 0000000..1d9db93 --- /dev/null +++ b/tests/AdventOfCodeFPCUnit.lpi @@ -0,0 +1,84 @@ + + + + + + + + + <ResourceType Value="res"/> + <UseXPManifest Value="True"/> + <Icon Value="0"/> + </General> + <BuildModes> + <Item Name="Default" Default="True"/> + </BuildModes> + <PublishOptions> + <Version Value="2"/> + <UseFileFilters Value="True"/> + </PublishOptions> + <RunParams> + <FormatVersion Value="2"/> + </RunParams> + <RequiredPackages> + <Item> + <PackageName Value="fpcunittestrunner"/> + </Item> + <Item> + <PackageName Value="LCL"/> + </Item> + <Item> + <PackageName Value="FCL"/> + </Item> + </RequiredPackages> + <Units> + <Unit> + <Filename Value="AdventOfCodeFPCUnit.lpr"/> + <IsPartOfProject Value="True"/> + </Unit> + <Unit> + <Filename Value="UGearRatiosTestCases.pas"/> + <IsPartOfProject Value="True"/> + </Unit> + <Unit> + <Filename Value="..\USolver.pas"/> + <IsPartOfProject Value="True"/> + </Unit> + </Units> + </ProjectOptions> + <CompilerOptions> + <Version Value="11"/> + <PathDelim Value="\"/> + <Target> + <Filename Value="AdventOfCodeFPCUnit"/> + </Target> + <SearchPaths> + <IncludeFiles Value="$(ProjOutDir)"/> + <OtherUnitFiles Value="..\solvers;.."/> + <UnitOutputDirectory Value="lib\$(TargetCPU)-$(TargetOS)"/> + </SearchPaths> + <Linking> + <Debugging> + <DebugInfoType Value="dsDwarf3"/> + </Debugging> + <Options> + <Win32> + <GraphicApplication Value="True"/> + </Win32> + </Options> + </Linking> + </CompilerOptions> + <Debugging> + <Exceptions> + <Item> + <Name Value="EAbort"/> + </Item> + <Item> + <Name Value="ECodetoolError"/> + </Item> + <Item> + <Name Value="EFOpenError"/> + </Item> + </Exceptions> + </Debugging> +</CONFIG> diff --git a/tests/AdventOfCodeFPCUnit.lpr b/tests/AdventOfCodeFPCUnit.lpr new file mode 100644 index 0000000..6ba23fb --- /dev/null +++ b/tests/AdventOfCodeFPCUnit.lpr @@ -0,0 +1,15 @@ +program AdventOfCodeFPCUnit; + +{$mode objfpc}{$H+} + +uses + Interfaces, Forms, GuiTestRunner, UGearRatiosTestCases, USolver; + +{$R *.res} + +begin + Application.Initialize; + Application.CreateForm(TGuiTestRunner, TestRunner); + Application.Run; +end. + diff --git a/tests/UGearRatiosTestCases.pas b/tests/UGearRatiosTestCases.pas new file mode 100644 index 0000000..54d1036 --- /dev/null +++ b/tests/UGearRatiosTestCases.pas @@ -0,0 +1,83 @@ +{ + 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 <http://www.gnu.org/licenses/>. +} + +unit UGearRatiosTestCases; + +{$mode objfpc}{$H+} + +interface + +uses + Classes, SysUtils, fpcunit, testregistry, UGearRatios; + +type + + { TGearRatiosTestCase } + + TGearRatiosTestCase = class(TTestCase) + protected + FSolver: TGearRatios; + procedure Setup; override; + procedure TearDown; override; + published + procedure TestPuzzleExample; + procedure TestEndOfLineNumber; + end; + +implementation + +{ TGearRatiosTestCase } + +procedure TGearRatiosTestCase.Setup; +begin + FSolver := TGearRatios.Create; +end; + +procedure TGearRatiosTestCase.TearDown; +begin + FSolver.Free; +end; + +procedure TGearRatiosTestCase.TestPuzzleExample; +begin + FSolver.Init; + FSolver.ProcessDataLine('467..114..'); + FSolver.ProcessDataLine('...*......'); + FSolver.ProcessDataLine('..35..633.'); + FSolver.ProcessDataLine('......#...'); + FSolver.ProcessDataLine('617*......'); + FSolver.ProcessDataLine('.....+.58.'); + FSolver.ProcessDataLine('..592.....'); + FSolver.ProcessDataLine('......755.'); + FSolver.ProcessDataLine('...$.*....'); + FSolver.ProcessDataLine('.664.598..'); + FSolver.Finish; + AssertEquals('Result of part 1 calculation incorrect.', 4361, FSolver.GetResultPart1); +end; + +procedure TGearRatiosTestCase.TestEndOfLineNumber; +begin + FSolver.Init; + FSolver.ProcessDataLine('...$541'); + FSolver.Finish; + AssertEquals('Result of part 1 calculation incorrect.', 541, FSolver.GetResultPart1); +end; + +initialization + + RegisterTest(TGearRatiosTestCase); +end.