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
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<TNode>;
@ -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<Cardinal>;
gcd, count: Cardinal;
node, current: TNode;
begin
if FCurrentNode <> nil then
gcd := 0;
counts := specialize TList<Cardinal>.Create;
counts.Capacity := FGhostCurrentNodes.Count;
for node in FGhostCurrentNodes do
begin
while (FCurrentNode <> FTargetNode) and FCurrentNode.TryStep(FDirections[FNextDirectionIndex], new) do
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
FCurrentNode := new;
Inc(FNextDirectionIndex);
if FNextDirectionIndex > Length(FDirections) then
FNextDirectionIndex := 1;
Inc(FPart1);
end;
end;
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;

View File

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