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 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+
+
+
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 .
+}
+
+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.