Added solution for "Day 8: Haunted Wasteland", part 2

This commit is contained in:
Stefan Müller 2023-12-09 14:46:35 +01:00 committed by Stefan Müller
parent edf9cf3d72
commit f1b1439524
2 changed files with 97 additions and 38 deletions

View File

@ -22,7 +22,7 @@ unit UHauntedWasteland;
interface interface
uses uses
Classes, SysUtils, Generics.Collections, USolver; Classes, SysUtils, Generics.Collections, USolver, UNumberTheory;
type type
@ -36,7 +36,7 @@ type
constructor Create(const AName: string); constructor Create(const AName: string);
property Name: string read FName; property Name: string read FName;
procedure AddNeighbors(constref ALeft, ARight: TNode); procedure AddNeighbors(constref ALeft, ARight: TNode);
function TryStep(const ADirection: Char; out ONewLocation: TNode): Boolean; function Step(const ADirection: Char): TNode;
end; end;
TNodes = specialize TObjectList<TNode>; TNodes = specialize TObjectList<TNode>;
@ -47,10 +47,10 @@ type
private private
FDirections: string; FDirections: string;
FNextDirectionIndex: Integer; FNextDirectionIndex: Integer;
FNodes, FUnparsedNodes: TNodes; FNodes, FUnparsedNodes, FGhostCurrentNodes: TNodes;
FCurrentNode, FTargetNode: TNode;
procedure ProcessNode(const ALine: string); procedure ProcessNode(const ALine: string);
procedure DoSteps; procedure DoSteps;
procedure IncDirectionIndex;
public public
constructor Create; constructor Create;
destructor Destroy; override; destructor Destroy; override;
@ -77,23 +77,16 @@ begin
FRight := ARight; FRight := ARight;
end; end;
function TNode.TryStep(const ADirection: Char; out ONewLocation: TNode): Boolean; function TNode.Step(const ADirection: Char): TNode;
begin begin
ONewLocation := nil; Result := nil;
Result := False;
case ADirection of case ADirection of
'L': 'L':
if FLeft <> nil then if FLeft <> nil then
begin Result := FLeft;
ONewLocation := FLeft;
Result := True;
end;
'R': 'R':
if FRight <> nil then if FRight <> nil then
begin Result := FRight;
ONewLocation := FRight;
Result := True;
end;
end; end;
end; end;
@ -169,43 +162,71 @@ begin
parsingNode.AddNeighbors(leftNode, rightNode); parsingNode.AddNeighbors(leftNode, rightNode);
// Checks for start and end nodes. // Checks for start nodes.
if name = 'ZZZ' then if name[3] = 'A' then
FTargetNode := parsingNode FGhostCurrentNodes.Add(parsingNode);
else if name = 'AAA' then
FCurrentNode := parsingNode;
end; end;
procedure THauntedWasteland.DoSteps; procedure THauntedWasteland.DoSteps;
var var
new: TNode; counts: specialize TList<Cardinal>;
gcd, count: Cardinal;
node, current: TNode;
begin begin
if FCurrentNode <> nil then gcd := 0;
counts := specialize TList<Cardinal>.Create;
counts.Capacity := FGhostCurrentNodes.Count;
for node in FGhostCurrentNodes do
begin begin
while (FCurrentNode <> FTargetNode) and FCurrentNode.TryStep(FDirections[FNextDirectionIndex], new) do current := node;
begin FNextDirectionIndex := 1;
FCurrentNode := new; count := 0;
Inc(FNextDirectionIndex); repeat
if FNextDirectionIndex > Length(FDirections) then current := current.Step(FDirections[FNextDirectionIndex]);
FNextDirectionIndex := 1; IncDirectionIndex;
Inc(FPart1); Inc(count);
end; 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; 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; end;
constructor THauntedWasteland.Create; constructor THauntedWasteland.Create;
begin begin
FNextDirectionIndex := 1;
FNodes := TNodes.Create; FNodes := TNodes.Create;
FUnparsedNodes := TNodes.Create(False); FUnparsedNodes := TNodes.Create(False);
FCurrentNode := nil; FGhostCurrentNodes := TNodes.Create(False);
FTargetNode := nil;
end; end;
destructor THauntedWasteland.Destroy; destructor THauntedWasteland.Destroy;
begin begin
FNodes.Free; FNodes.Free;
FUnparsedNodes.Free; FUnparsedNodes.Free;
FGhostCurrentNodes.Free;
inherited Destroy; inherited Destroy;
end; end;
@ -213,16 +234,13 @@ procedure THauntedWasteland.ProcessDataLine(const ALine: string);
begin begin
if FDirections = '' then if FDirections = '' then
FDirections := ALine FDirections := ALine
else if (ALine <> '') and ((FTargetNode = nil) or (FCurrentNode <> FTargetNode)) then else if ALine <> '' then
begin
ProcessNode(ALine); ProcessNode(ALine);
DoSteps;
end;
end; end;
procedure THauntedWasteland.Finish; procedure THauntedWasteland.Finish;
begin begin
DoSteps;
end; end;
function THauntedWasteland.GetDataFileName: string; function THauntedWasteland.GetDataFileName: string;

View File

@ -33,6 +33,7 @@ type
function CreateSolver: ISolver; override; function CreateSolver: ISolver; override;
published published
procedure TestPart1; procedure TestPart1;
procedure TestPart2;
end; end;
{ THauntedWastelandExampleTestCase } { THauntedWastelandExampleTestCase }
@ -59,6 +60,21 @@ type
procedure TestPart1; procedure TestPart1;
end; end;
{ TExample3HauntedWasteland }
TExample3HauntedWasteland = class(THauntedWasteland)
function GetDataFileName: string; override;
end;
{ THauntedWastelandExample3TestCase }
THauntedWastelandExample3TestCase = class(TExampleEngineBaseTest)
protected
function CreateSolver: ISolver; override;
published
procedure TestPart2;
end;
implementation implementation
{ THauntedWastelandFullDataTestCase } { THauntedWastelandFullDataTestCase }
@ -73,6 +89,11 @@ begin
AssertEquals(14257, FSolver.GetResultPart1); AssertEquals(14257, FSolver.GetResultPart1);
end; end;
procedure THauntedWastelandFullDataTestCase.TestPart2;
begin
AssertEquals(16187743689077, FSolver.GetResultPart2);
end;
{ THauntedWastelandExampleTestCase } { THauntedWastelandExampleTestCase }
function THauntedWastelandExampleTestCase.CreateSolver: ISolver; function THauntedWastelandExampleTestCase.CreateSolver: ISolver;
@ -104,9 +125,29 @@ begin
AssertEquals(6, FSolver.GetResultPart1); AssertEquals(6, FSolver.GetResultPart1);
end; 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 initialization
RegisterTest(THauntedWastelandFullDataTestCase); RegisterTest(THauntedWastelandFullDataTestCase);
RegisterTest(THauntedWastelandExampleTestCase); RegisterTest(THauntedWastelandExampleTestCase);
RegisterTest(THauntedWastelandExample2TestCase); RegisterTest(THauntedWastelandExample2TestCase);
RegisterTest(THauntedWastelandExample3TestCase);
end. end.