diff --git a/AdventOfCode.lpi b/AdventOfCode.lpi
index 0ceead4..dbeeb5b 100644
--- a/AdventOfCode.lpi
+++ b/AdventOfCode.lpi
@@ -73,6 +73,10 @@
+
+
+
+
diff --git a/AdventOfCode.lpr b/AdventOfCode.lpr
index accce58..e80b665 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;
+ UHauntedWasteland, UNumberTheory, UMirageMaintenance, UPipeMaze;
type
@@ -60,6 +60,7 @@ begin
engine.RunAndFree(TCamelCards.Create);
engine.RunAndFree(THauntedWasteland.Create);
engine.RunAndFree(TMirageMaintenance.Create);
+ engine.RunAndFree(TPipeMaze.Create);
engine.Free;
end;
diff --git a/solvers/UPipeMaze.pas b/solvers/UPipeMaze.pas
new file mode 100644
index 0000000..9e73d2b
--- /dev/null
+++ b/solvers/UPipeMaze.pas
@@ -0,0 +1,240 @@
+{
+ 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 UPipeMaze;
+
+{$mode ObjFPC}{$H+}
+
+interface
+
+uses
+ Classes, SysUtils, USolver;
+
+const
+ CStartChar = 'S';
+
+type
+ TDirection = (drEast, drSouth, drWest, drNorth);
+
+ { TPipeMaze }
+
+ TPipeMaze = class(TSolver)
+ private
+ FWidth, FHeight: Integer;
+ FStrings: TStringList;
+ procedure ParseMaze;
+ function FindStart: TPoint;
+ procedure DoFirstStep(var APosition: TPoint; out OStartDirection: TDirection);
+ procedure DoStep(var APosition: TPoint; var ADirection: TDirection);
+ 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
+
+{ TPipeMaze }
+
+procedure TPipeMaze.ParseMaze;
+var
+ start, current: TPoint;
+ nextDirection: TDirection;
+begin
+ FHeight := FStrings.Count;
+ FWidth := Length(FStrings[0]);
+
+ start := FindStart;
+ current := start;
+
+ DoFirstStep(current, nextDirection);
+ DoStep(current, nextDirection);
+ Inc(FPart1);
+
+ repeat
+ DoStep(current, nextDirection);
+ DoStep(current, nextDirection);
+ Inc(FPart1);
+ until current = start;
+end;
+
+function TPipeMaze.FindStart: TPoint;
+var
+ i, j: Integer;
+ foundStart: Boolean;
+begin
+ foundStart := False;
+ i := 0;
+ while not foundStart and (i < FStrings.Count) do
+ begin
+ j := 1;
+ while not foundStart and (j <= Length(FStrings[i])) do
+ begin
+ if FStrings[i][j] = CStartChar then
+ begin
+ foundStart := True;
+ Result.X := j;
+ Result.Y := i;
+ end;
+ Inc(j);
+ end;
+ Inc(i);
+ end;
+end;
+
+procedure TPipeMaze.DoFirstStep(var APosition: TPoint; out OStartDirection: TDirection);
+begin
+ // Checks going East.
+ if APosition.X < FWidth then
+ begin
+ if FStrings[APosition.Y][APosition.X + 1] = '-' then
+ begin
+ Inc(APosition.X);
+ OStartDirection := drEast;
+ Exit;
+ end
+ else if FStrings[APosition.Y][APosition.X + 1] = 'J' then
+ begin
+ Inc(APosition.X);
+ OStartDirection := drNorth;
+ Exit;
+ end
+ else if FStrings[APosition.Y][APosition.X + 1] = '7' then
+ begin
+ Inc(APosition.X);
+ OStartDirection := drSouth;
+ Exit;
+ end;
+ end;
+
+ // Checks going South.
+ if APosition.Y < FHeight - 1 then
+ begin
+ if FStrings[APosition.Y + 1][APosition.X] = '|' then
+ begin
+ Inc(APosition.Y);
+ OStartDirection := drSouth;
+ Exit;
+ end
+ else if FStrings[APosition.Y + 1][APosition.X] = 'L' then
+ begin
+ Inc(APosition.Y);
+ OStartDirection := drEast;
+ Exit;
+ end
+ else if FStrings[APosition.Y + 1][APosition.X] = 'J' then
+ begin
+ Inc(APosition.Y);
+ OStartDirection := drWest;
+ Exit;
+ end;
+ end;
+
+ // Checks going West.
+ if APosition.X > 1 then
+ begin
+ if FStrings[APosition.Y][APosition.X - 1] = '-' then
+ begin
+ Dec(APosition.X);
+ OStartDirection := drWest;
+ Exit;
+ end
+ else if FStrings[APosition.Y][APosition.X - 1] = 'L' then
+ begin
+ Dec(APosition.X);
+ OStartDirection := drNorth;
+ Exit;
+ end
+ else if FStrings[APosition.Y][APosition.X - 1] = 'F' then
+ begin
+ Dec(APosition.X);
+ OStartDirection := drSouth;
+ Exit;
+ end;
+ end;
+end;
+
+procedure TPipeMaze.DoStep(var APosition: TPoint; var ADirection: TDirection);
+begin
+ case ADirection of
+ drEast: begin
+ Inc(APosition.X);
+ case FStrings[APosition.Y][APosition.X] of
+ 'J': ADirection := drNorth;
+ '7': ADirection := drSouth;
+ end;
+ end;
+ drSouth: begin
+ Inc(APosition.Y);
+ case FStrings[APosition.Y][APosition.X] of
+ 'L': ADirection := drEast;
+ 'J': ADirection := drWest;
+ end;
+ end;
+ drWest: begin
+ Dec(APosition.X);
+ case FStrings[APosition.Y][APosition.X] of
+ 'L': ADirection := drNorth;
+ 'F': ADirection := drSouth;
+ end;
+ end;
+ drNorth: begin
+ Dec(APosition.Y);
+ case FStrings[APosition.Y][APosition.X] of
+ '7': ADirection := drWest;
+ 'F': ADirection := drEast;
+ end;
+ end;
+ end;
+end;
+
+constructor TPipeMaze.Create;
+begin
+ FStrings := TStringList.Create;
+end;
+
+destructor TPipeMaze.Destroy;
+begin
+ FStrings.Free;
+ inherited Destroy;
+end;
+
+procedure TPipeMaze.ProcessDataLine(const ALine: string);
+begin
+ FStrings.Add(ALine);
+end;
+
+procedure TPipeMaze.Finish;
+begin
+ ParseMaze;
+end;
+
+function TPipeMaze.GetDataFileName: string;
+begin
+ Result := 'pipe_maze.txt';
+end;
+
+function TPipeMaze.GetPuzzleName: string;
+begin
+ Result := 'Day 10: Pipe Maze';
+end;
+
+end.
+
diff --git a/tests/AdventOfCodeFPCUnit.lpi b/tests/AdventOfCodeFPCUnit.lpi
index 17393bc..46f8cdf 100644
--- a/tests/AdventOfCodeFPCUnit.lpi
+++ b/tests/AdventOfCodeFPCUnit.lpi
@@ -80,6 +80,10 @@
+
+
+
+
diff --git a/tests/AdventOfCodeFPCUnit.lpr b/tests/AdventOfCodeFPCUnit.lpr
index 11cc80b..2697a07 100644
--- a/tests/AdventOfCodeFPCUnit.lpr
+++ b/tests/AdventOfCodeFPCUnit.lpr
@@ -5,7 +5,7 @@ program AdventOfCodeFPCUnit;
uses
Interfaces, Forms, GuiTestRunner, USolver, UBaseTestCases, UTrebuchetTestCases, UCubeConundrumTestCases,
UGearRatiosTestCases, UScratchcardsTestCases, UGiveSeedFertilizerTestCases, UWaitForItTestCases, UCamelCardsTestCases,
- UHauntedWastelandTestCases, UMirageMaintenanceTestCases;
+ UHauntedWastelandTestCases, UMirageMaintenanceTestCases, UPipeMazeTestCases;
{$R *.res}
diff --git a/tests/UPipeMazeTestCases.pas b/tests/UPipeMazeTestCases.pas
new file mode 100644
index 0000000..25c77ff
--- /dev/null
+++ b/tests/UPipeMazeTestCases.pas
@@ -0,0 +1,188 @@
+{
+ 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 UPipeMazeTestCases;
+
+{$mode ObjFPC}{$H+}
+
+interface
+
+uses
+ Classes, SysUtils, fpcunit, testregistry, USolver, UBaseTestCases, UPipeMaze;
+
+type
+
+ { TPipeMazeFullDataTestCase }
+
+ TPipeMazeFullDataTestCase = class(TEngineBaseTest)
+ protected
+ function CreateSolver: ISolver; override;
+ published
+ procedure TestPart1;
+ procedure TestPart2;
+ end;
+
+ { TPipeMazeExampleTestCase }
+
+ TPipeMazeExampleTestCase = class(TExampleEngineBaseTest)
+ protected
+ function CreateSolver: ISolver; override;
+ published
+ procedure TestPart1;
+ end;
+
+ { TExample2PipeMaze }
+
+ TExample2PipeMaze = class(TPipeMaze)
+ function GetDataFileName: string; override;
+ end;
+
+ { TPipeMazeExample2TestCase }
+
+ TPipeMazeExample2TestCase = class(TExampleEngineBaseTest)
+ protected
+ function CreateSolver: ISolver; override;
+ published
+ procedure TestPart1;
+ end;
+
+ { TExample3PipeMaze }
+
+ TExample3PipeMaze = class(TPipeMaze)
+ function GetDataFileName: string; override;
+ end;
+
+ { TPipeMazeExample3TestCase }
+
+ TPipeMazeExample3TestCase = class(TExampleEngineBaseTest)
+ protected
+ function CreateSolver: ISolver; override;
+ published
+ procedure TestPart1;
+ end;
+
+ { TExample4PipeMaze }
+
+ TExample4PipeMaze = class(TPipeMaze)
+ function GetDataFileName: string; override;
+ end;
+
+ { TPipeMazeExample4TestCase }
+
+ TPipeMazeExample4TestCase = class(TExampleEngineBaseTest)
+ protected
+ function CreateSolver: ISolver; override;
+ published
+ procedure TestPart1;
+ end;
+
+implementation
+
+{ TPipeMazeFullDataTestCase }
+
+function TPipeMazeFullDataTestCase.CreateSolver: ISolver;
+begin
+ Result := TPipeMaze.Create;
+end;
+
+procedure TPipeMazeFullDataTestCase.TestPart1;
+begin
+ AssertEquals(7097, FSolver.GetResultPart1);
+end;
+
+procedure TPipeMazeFullDataTestCase.TestPart2;
+begin
+ AssertEquals(-1, FSolver.GetResultPart2);
+end;
+
+{ TPipeMazeExampleTestCase }
+
+function TPipeMazeExampleTestCase.CreateSolver: ISolver;
+begin
+ Result := TPipeMaze.Create;
+end;
+
+procedure TPipeMazeExampleTestCase.TestPart1;
+begin
+ AssertEquals(4, FSolver.GetResultPart1);
+end;
+
+{ TExample2PipeMaze }
+
+function TExample2PipeMaze.GetDataFileName: string;
+begin
+ Result := 'pipe_maze2.txt';
+end;
+
+{ TPipeMazeExample2TestCase }
+
+function TPipeMazeExample2TestCase.CreateSolver: ISolver;
+begin
+ Result := TExample2PipeMaze.Create;
+end;
+
+procedure TPipeMazeExample2TestCase.TestPart1;
+begin
+ AssertEquals(4, FSolver.GetResultPart1);
+end;
+
+{ TExample3PipeMaze }
+
+function TExample3PipeMaze.GetDataFileName: string;
+begin
+ Result := 'pipe_maze3.txt';
+end;
+
+{ TPipeMazeExample3TestCase }
+
+function TPipeMazeExample3TestCase.CreateSolver: ISolver;
+begin
+ Result := TExample3PipeMaze.Create;
+end;
+
+procedure TPipeMazeExample3TestCase.TestPart1;
+begin
+ AssertEquals(8, FSolver.GetResultPart1);
+end;
+
+{ TExample4PipeMaze }
+
+function TExample4PipeMaze.GetDataFileName: string;
+begin
+ Result := 'pipe_maze4.txt';
+end;
+
+{ TPipeMazeExample4TestCase }
+
+function TPipeMazeExample4TestCase.CreateSolver: ISolver;
+begin
+ Result := TExample4PipeMaze.Create;
+end;
+
+procedure TPipeMazeExample4TestCase.TestPart1;
+begin
+ AssertEquals(8, FSolver.GetResultPart1);
+end;
+
+initialization
+
+ RegisterTest(TPipeMazeFullDataTestCase);
+ RegisterTest(TPipeMazeExampleTestCase);
+ RegisterTest(TPipeMazeExample2TestCase);
+ RegisterTest(TPipeMazeExample3TestCase);
+ RegisterTest(TPipeMazeExample4TestCase);
+end.