From f1b143952426dd0e3efcecf541751ec7ffc04f6a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefan=20M=C3=BCller?= Date: Sat, 9 Dec 2023 14:46:35 +0100 Subject: [PATCH] Added solution for "Day 8: Haunted Wasteland", part 2 --- solvers/UHauntedWasteland.pas | 94 +++++++++++++++++----------- tests/UHauntedWastelandTestCases.pas | 41 ++++++++++++ 2 files changed, 97 insertions(+), 38 deletions(-) diff --git a/solvers/UHauntedWasteland.pas b/solvers/UHauntedWasteland.pas index 97d611c..fca5ab1 100644 --- a/solvers/UHauntedWasteland.pas +++ b/solvers/UHauntedWasteland.pas @@ -22,7 +22,7 @@ unit UHauntedWasteland; interface uses - Classes, SysUtils, Generics.Collections, USolver; + Classes, SysUtils, Generics.Collections, USolver, UNumberTheory; type @@ -36,7 +36,7 @@ type constructor Create(const AName: string); property Name: string read FName; procedure AddNeighbors(constref ALeft, ARight: TNode); - function TryStep(const ADirection: Char; out ONewLocation: TNode): Boolean; + function Step(const ADirection: Char): TNode; end; TNodes = specialize TObjectList; @@ -47,10 +47,10 @@ type private FDirections: string; FNextDirectionIndex: Integer; - FNodes, FUnparsedNodes: TNodes; - FCurrentNode, FTargetNode: TNode; + FNodes, FUnparsedNodes, FGhostCurrentNodes: TNodes; procedure ProcessNode(const ALine: string); procedure DoSteps; + procedure IncDirectionIndex; public constructor Create; destructor Destroy; override; @@ -77,23 +77,16 @@ begin FRight := ARight; end; -function TNode.TryStep(const ADirection: Char; out ONewLocation: TNode): Boolean; +function TNode.Step(const ADirection: Char): TNode; begin - ONewLocation := nil; - Result := False; + Result := nil; case ADirection of 'L': if FLeft <> nil then - begin - ONewLocation := FLeft; - Result := True; - end; + Result := FLeft; 'R': if FRight <> nil then - begin - ONewLocation := FRight; - Result := True; - end; + Result := FRight; end; end; @@ -169,43 +162,71 @@ begin parsingNode.AddNeighbors(leftNode, rightNode); - // Checks for start and end nodes. - if name = 'ZZZ' then - FTargetNode := parsingNode - else if name = 'AAA' then - FCurrentNode := parsingNode; + // Checks for start nodes. + if name[3] = 'A' then + FGhostCurrentNodes.Add(parsingNode); end; procedure THauntedWasteland.DoSteps; var - new: TNode; + counts: specialize TList; + gcd, count: Cardinal; + node, current: TNode; begin - if FCurrentNode <> nil then + gcd := 0; + counts := specialize TList.Create; + counts.Capacity := FGhostCurrentNodes.Count; + + for node in FGhostCurrentNodes do begin - while (FCurrentNode <> FTargetNode) and FCurrentNode.TryStep(FDirections[FNextDirectionIndex], new) do - begin - FCurrentNode := new; - Inc(FNextDirectionIndex); - if FNextDirectionIndex > Length(FDirections) then - FNextDirectionIndex := 1; - Inc(FPart1); - end; + current := node; + FNextDirectionIndex := 1; + count := 0; + repeat + current := current.Step(FDirections[FNextDirectionIndex]); + IncDirectionIndex; + Inc(count); + until current.Name[3] = 'Z'; + + if node.Name = 'AAA' then + FPart1 := count; + + counts.Add(count); + if gcd = 0 then + gcd := count; + gcd := TNumberTheory.GreatestCommonDivisor(gcd, count); end; + + for count in counts do + begin + if FPart2 = 0 then + FPart2 := count + else + FPart2 := count div gcd * FPart2; + end; + + counts.Free; +end; + +procedure THauntedWasteland.IncDirectionIndex; +begin + Inc(FNextDirectionIndex); + if FNextDirectionIndex > Length(FDirections) then + FNextDirectionIndex := 1; end; constructor THauntedWasteland.Create; begin - FNextDirectionIndex := 1; FNodes := TNodes.Create; FUnparsedNodes := TNodes.Create(False); - FCurrentNode := nil; - FTargetNode := nil; + FGhostCurrentNodes := TNodes.Create(False); end; destructor THauntedWasteland.Destroy; begin FNodes.Free; FUnparsedNodes.Free; + FGhostCurrentNodes.Free; inherited Destroy; end; @@ -213,16 +234,13 @@ procedure THauntedWasteland.ProcessDataLine(const ALine: string); begin if FDirections = '' then FDirections := ALine - else if (ALine <> '') and ((FTargetNode = nil) or (FCurrentNode <> FTargetNode)) then - begin + else if ALine <> '' then ProcessNode(ALine); - DoSteps; - end; end; procedure THauntedWasteland.Finish; begin - + DoSteps; end; function THauntedWasteland.GetDataFileName: string; diff --git a/tests/UHauntedWastelandTestCases.pas b/tests/UHauntedWastelandTestCases.pas index 01fa5ba..1712650 100644 --- a/tests/UHauntedWastelandTestCases.pas +++ b/tests/UHauntedWastelandTestCases.pas @@ -33,6 +33,7 @@ type function CreateSolver: ISolver; override; published procedure TestPart1; + procedure TestPart2; end; { THauntedWastelandExampleTestCase } @@ -59,6 +60,21 @@ type procedure TestPart1; end; + { TExample3HauntedWasteland } + + TExample3HauntedWasteland = class(THauntedWasteland) + function GetDataFileName: string; override; + end; + + { THauntedWastelandExample3TestCase } + + THauntedWastelandExample3TestCase = class(TExampleEngineBaseTest) + protected + function CreateSolver: ISolver; override; + published + procedure TestPart2; + end; + implementation { THauntedWastelandFullDataTestCase } @@ -73,6 +89,11 @@ begin AssertEquals(14257, FSolver.GetResultPart1); end; +procedure THauntedWastelandFullDataTestCase.TestPart2; +begin + AssertEquals(16187743689077, FSolver.GetResultPart2); +end; + { THauntedWastelandExampleTestCase } function THauntedWastelandExampleTestCase.CreateSolver: ISolver; @@ -104,9 +125,29 @@ begin AssertEquals(6, FSolver.GetResultPart1); end; +{ TExample3HauntedWasteland } + +function TExample3HauntedWasteland.GetDataFileName: string; +begin + Result := 'haunted_wasteland3.txt'; +end; + +{ THauntedWastelandExample3TestCase } + +function THauntedWastelandExample3TestCase.CreateSolver: ISolver; +begin + Result := TExample3HauntedWasteland.Create; +end; + +procedure THauntedWastelandExample3TestCase.TestPart2; +begin + AssertEquals(6, FSolver.GetResultPart2); +end; + initialization RegisterTest(THauntedWastelandFullDataTestCase); RegisterTest(THauntedWastelandExampleTestCase); RegisterTest(THauntedWastelandExample2TestCase); + RegisterTest(THauntedWastelandExample3TestCase); end.