Added solution for "Day 20: Pulse Propagation", part 1
This commit is contained in:
parent
b2bfbf1993
commit
55f8f3d674
|
@ -117,6 +117,10 @@
|
|||
<Filename Value="UIntervals.pas"/>
|
||||
<IsPartOfProject Value="True"/>
|
||||
</Unit>
|
||||
<Unit>
|
||||
<Filename Value="solvers\UPulsePropagation.pas"/>
|
||||
<IsPartOfProject Value="True"/>
|
||||
</Unit>
|
||||
</Units>
|
||||
</ProjectOptions>
|
||||
<CompilerOptions>
|
||||
|
|
|
@ -26,7 +26,7 @@ uses
|
|||
Classes, SysUtils, CustApp, USolver, UTrebuchet, UCubeConundrum, UGearRatios, UScratchcards, UGiveSeedFertilizer,
|
||||
UWaitForIt, UCamelCards, UHauntedWasteland, UNumberTheory, UMirageMaintenance, UPipeMaze, UCosmicExpansion,
|
||||
UHotSprings, UPointOfIncidence, UParabolicReflectorDish, ULensLibrary, UFloorWillBeLava, UClumsyCrucible,
|
||||
ULavaductLagoon, UAplenty;
|
||||
ULavaductLagoon, UAplenty, UPulsePropagation;
|
||||
|
||||
type
|
||||
|
||||
|
@ -70,6 +70,7 @@ begin
|
|||
engine.RunAndFree(TClumsyCrucible.Create);
|
||||
engine.RunAndFree(TLavaductLagoon.Create);
|
||||
engine.RunAndFree(TAplenty.Create);
|
||||
engine.RunAndFree(TPulsePropagation.Create);
|
||||
engine.Free;
|
||||
end;
|
||||
|
||||
|
|
|
@ -0,0 +1,444 @@
|
|||
{
|
||||
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 <http://www.gnu.org/licenses/>.
|
||||
}
|
||||
|
||||
unit UPulsePropagation;
|
||||
|
||||
{$mode ObjFPC}{$H+}
|
||||
|
||||
interface
|
||||
|
||||
uses
|
||||
Classes, SysUtils, Generics.Collections, Math, USolver;
|
||||
|
||||
type
|
||||
TModule = class;
|
||||
TModules = specialize TObjectList<TModule>;
|
||||
|
||||
{ TPulse }
|
||||
|
||||
TPulse = record
|
||||
Sender, Destination: TModule;
|
||||
IsHigh: Boolean;
|
||||
end;
|
||||
|
||||
TPulses = specialize TList<TPulse>;
|
||||
TPulseQueue = specialize TQueue<TPulse>;
|
||||
|
||||
{ TModule }
|
||||
|
||||
TModule = class
|
||||
private
|
||||
FName: string;
|
||||
FOutputNames: TStringList;
|
||||
FOutputs: TModules;
|
||||
function CreatePulsesToOutputs(const AHighPulse: Boolean): TPulses;
|
||||
public
|
||||
property Name: string read FName;
|
||||
property OutputNames: TStringList read FOutputNames;
|
||||
constructor Create(const AName: string);
|
||||
destructor Destroy; override;
|
||||
procedure AddInput(const AInput: TModule); virtual;
|
||||
procedure AddOutput(const AOutput: TModule); virtual;
|
||||
function ReceivePulse(const ASender: TModule; const AIsHigh: Boolean): TPulses; virtual; abstract;
|
||||
function IsOff: Boolean; virtual;
|
||||
end;
|
||||
|
||||
{ TBroadcasterModule }
|
||||
|
||||
TBroadcasterModule = class(TModule)
|
||||
public
|
||||
function ReceivePulse(const ASender: TModule; const AIsHigh: Boolean): TPulses; override;
|
||||
end;
|
||||
|
||||
{ TFlipFlopModule }
|
||||
|
||||
TFlipFlopModule = class(TModule)
|
||||
private
|
||||
FState: Boolean;
|
||||
public
|
||||
function ReceivePulse(const ASender: TModule; const AIsHigh: Boolean): TPulses; override;
|
||||
function IsOff: Boolean; override;
|
||||
end;
|
||||
|
||||
{ TConjectionBuffer }
|
||||
|
||||
TConjectionBuffer = record
|
||||
Input: TModule;
|
||||
LastState: Boolean;
|
||||
end;
|
||||
|
||||
TConjectionBuffers = specialize TList<TConjectionBuffer>;
|
||||
|
||||
{ TConjunctionModule }
|
||||
|
||||
TConjunctionModule = class(TModule)
|
||||
private
|
||||
FInputBuffers: TConjectionBuffers;
|
||||
procedure UpdateInputBuffer(constref AInput: TModule; const AState: Boolean);
|
||||
function AreAllBuffersSame(const AIsHigh: Boolean): Boolean;
|
||||
public
|
||||
constructor Create(const AName: string);
|
||||
destructor Destroy; override;
|
||||
procedure AddInput(const AInput: TModule); override;
|
||||
function ReceivePulse(const ASender: TModule; const AIsHigh: Boolean): TPulses; override;
|
||||
function IsOff: Boolean; override;
|
||||
end;
|
||||
|
||||
{ TEndpointModule }
|
||||
|
||||
TEndpointModule = class(TModule)
|
||||
public
|
||||
function ReceivePulse(const ASender: TModule; const AIsHigh: Boolean): TPulses; override;
|
||||
end;
|
||||
|
||||
{ TButtonResult }
|
||||
|
||||
TButtonResult = record
|
||||
LowCount, HighCount: Integer;
|
||||
end;
|
||||
|
||||
TButtonResults = specialize TList<TButtonResult>;
|
||||
|
||||
{ TPulsePropagation }
|
||||
|
||||
TPulsePropagation = class(TSolver)
|
||||
private
|
||||
FModules: TModules;
|
||||
FBroadcaster: TModule;
|
||||
procedure UpdateModuleConnections;
|
||||
function PushButton: TButtonResult;
|
||||
function AreAllModulesOff: Boolean;
|
||||
public
|
||||
constructor Create;
|
||||
destructor Destroy; override;
|
||||
procedure ProcessDataLine(const ALine: string); override;
|
||||
procedure Finish; override;
|
||||
function GetDataFileName: string; override;
|
||||
function GetPuzzleName: string; override;
|
||||
end;
|
||||
|
||||
const
|
||||
CBroadcasterName = 'broadcaster';
|
||||
CFlipFlopPrefix = '%';
|
||||
CConjunctionPrefix = '&';
|
||||
CButtonPushes = 1000;
|
||||
|
||||
implementation
|
||||
|
||||
{ TModule }
|
||||
|
||||
function TModule.CreatePulsesToOutputs(const AHighPulse: Boolean): TPulses;
|
||||
var
|
||||
pulse: TPulse;
|
||||
output: TModule;
|
||||
begin
|
||||
Result := TPulses.Create;
|
||||
pulse.Sender := Self;
|
||||
pulse.IsHigh := AHighPulse;
|
||||
for output in FOutputs do
|
||||
begin
|
||||
pulse.Destination := output;
|
||||
Result.Add(pulse);
|
||||
end;
|
||||
end;
|
||||
|
||||
constructor TModule.Create(const AName: string);
|
||||
begin
|
||||
FName := AName;
|
||||
FOutputNames := TStringList.Create;
|
||||
FOutputs := TModules.Create(False);
|
||||
end;
|
||||
|
||||
destructor TModule.Destroy;
|
||||
begin
|
||||
FOutputNames.Free;
|
||||
FOutputs.Free;
|
||||
inherited Destroy;
|
||||
end;
|
||||
|
||||
procedure TModule.AddInput(const AInput: TModule);
|
||||
begin
|
||||
|
||||
end;
|
||||
|
||||
procedure TModule.AddOutput(const AOutput: TModule);
|
||||
begin
|
||||
FOutputs.Add(AOutput);
|
||||
end;
|
||||
|
||||
function TModule.IsOff: Boolean;
|
||||
begin
|
||||
Result := True;
|
||||
end;
|
||||
|
||||
{ TBroadcasterModule }
|
||||
|
||||
function TBroadcasterModule.ReceivePulse(const ASender: TModule; const AIsHigh: Boolean): TPulses;
|
||||
begin
|
||||
Result := CreatePulsesToOutputs(AIsHigh);
|
||||
end;
|
||||
|
||||
{ TFlipFlopModule }
|
||||
|
||||
function TFlipFlopModule.ReceivePulse(const ASender: TModule; const AIsHigh: Boolean): TPulses;
|
||||
begin
|
||||
if AIsHigh then
|
||||
Result := TPulses.Create
|
||||
else begin
|
||||
FState := not FState;
|
||||
Result := CreatePulsesToOutputs(FState);
|
||||
end;
|
||||
end;
|
||||
|
||||
function TFlipFlopModule.IsOff: Boolean;
|
||||
begin
|
||||
Result := not FState;
|
||||
end;
|
||||
|
||||
{ TConjunctionModule }
|
||||
|
||||
procedure TConjunctionModule.UpdateInputBuffer(constref AInput: TModule; const AState: Boolean);
|
||||
var
|
||||
i: Integer;
|
||||
buffer: TConjectionBuffer;
|
||||
begin
|
||||
for i := 0 to FInputBuffers.Count - 1 do
|
||||
if FInputBuffers[i].Input = AInput then
|
||||
begin
|
||||
buffer := FInputBuffers[i];
|
||||
buffer.LastState := AState;
|
||||
FInputBuffers[i] := buffer;
|
||||
Exit;
|
||||
end;
|
||||
end;
|
||||
|
||||
function TConjunctionModule.AreAllBuffersSame(const AIsHigh: Boolean): Boolean;
|
||||
var
|
||||
buffer: TConjectionBuffer;
|
||||
begin
|
||||
Result := True;
|
||||
for buffer in FInputBuffers do
|
||||
if buffer.LastState <> AIsHigh then
|
||||
begin
|
||||
Result := False;
|
||||
Exit;
|
||||
end;
|
||||
end;
|
||||
|
||||
constructor TConjunctionModule.Create(const AName: string);
|
||||
begin
|
||||
inherited Create(AName);
|
||||
FInputBuffers := TConjectionBuffers.Create;
|
||||
end;
|
||||
|
||||
destructor TConjunctionModule.Destroy;
|
||||
begin
|
||||
FInputBuffers.Free;
|
||||
inherited Destroy;
|
||||
end;
|
||||
|
||||
procedure TConjunctionModule.AddInput(const AInput: TModule);
|
||||
var
|
||||
buffer: TConjectionBuffer;
|
||||
begin
|
||||
buffer.Input := AInput;
|
||||
buffer.LastState := False;
|
||||
FInputBuffers.Add(buffer);
|
||||
end;
|
||||
|
||||
function TConjunctionModule.ReceivePulse(const ASender: TModule; const AIsHigh: Boolean): TPulses;
|
||||
begin
|
||||
UpdateInputBuffer(ASender, AIsHigh);
|
||||
Result := CreatePulsesToOutputs(not AreAllBuffersSame(True));
|
||||
end;
|
||||
|
||||
function TConjunctionModule.IsOff: Boolean;
|
||||
begin
|
||||
Result := AreAllBuffersSame(False);
|
||||
end;
|
||||
|
||||
{ TEndpointModule }
|
||||
|
||||
function TEndpointModule.ReceivePulse(const ASender: TModule; const AIsHigh: Boolean): TPulses;
|
||||
begin
|
||||
Result := TPulses.Create;
|
||||
end;
|
||||
|
||||
{ TPulsePropagation }
|
||||
|
||||
procedure TPulsePropagation.UpdateModuleConnections;
|
||||
var
|
||||
module, outModule: TModule;
|
||||
name: string;
|
||||
found: Boolean;
|
||||
begin
|
||||
for module in FModules do
|
||||
begin
|
||||
for name in module.OutputNames do
|
||||
begin
|
||||
found := False;
|
||||
for outModule in FModules do
|
||||
if name = outModule.Name then
|
||||
begin
|
||||
found := True;
|
||||
Break;
|
||||
end;
|
||||
|
||||
if not found then
|
||||
begin
|
||||
outModule := TEndpointModule.Create(name);
|
||||
FModules.Add(outModule);
|
||||
end;
|
||||
|
||||
module.AddOutput(outModule);
|
||||
outModule.AddInput(module);
|
||||
end;
|
||||
|
||||
module.OutputNames.Clear;
|
||||
end;
|
||||
end;
|
||||
|
||||
function TPulsePropagation.PushButton: TButtonResult;
|
||||
var
|
||||
queue: TPulseQueue;
|
||||
pulse: TPulse;
|
||||
pulses: TPulses;
|
||||
begin
|
||||
queue := TPulseQueue.Create;
|
||||
pulse.Sender := nil;
|
||||
pulse.IsHigh := False;
|
||||
pulse.Destination := FBroadcaster;
|
||||
queue.Enqueue(pulse);
|
||||
Result.LowCount := 0;
|
||||
Result.HighCount := 0;
|
||||
|
||||
while queue.Count > 0 do
|
||||
begin
|
||||
pulse := queue.Dequeue;
|
||||
if pulse.IsHigh then
|
||||
Inc(Result.HighCount)
|
||||
else
|
||||
Inc(Result.LowCount);
|
||||
pulses := pulse.Destination.ReceivePulse(pulse.Sender, pulse.IsHigh);
|
||||
for pulse in pulses do
|
||||
queue.Enqueue(pulse);
|
||||
pulses.Free;
|
||||
end;
|
||||
|
||||
queue.Free;
|
||||
end;
|
||||
|
||||
function TPulsePropagation.AreAllModulesOff: Boolean;
|
||||
var
|
||||
module: TModule;
|
||||
begin
|
||||
Result := True;
|
||||
for module in FModules do
|
||||
if not module.IsOff then
|
||||
begin
|
||||
Result := False;
|
||||
Exit;
|
||||
end;
|
||||
end;
|
||||
|
||||
constructor TPulsePropagation.Create;
|
||||
begin
|
||||
FModules := TModules.Create;
|
||||
end;
|
||||
|
||||
destructor TPulsePropagation.Destroy;
|
||||
begin
|
||||
FModules.Free;
|
||||
inherited Destroy;
|
||||
end;
|
||||
|
||||
procedure TPulsePropagation.ProcessDataLine(const ALine: string);
|
||||
var
|
||||
split: TStringArray;
|
||||
module: TModule;
|
||||
i: Integer;
|
||||
begin
|
||||
split := ALine.Split(' ');
|
||||
if split[0] = CBroadcasterName then
|
||||
begin
|
||||
FBroadcaster := TBroadcasterModule.Create(split[0]);
|
||||
module := FBroadcaster;
|
||||
end
|
||||
else if split[0][1] = CFlipFlopPrefix then
|
||||
module := TFlipFlopModule.Create(Copy(split[0], 2, Length(split[0]) - 1))
|
||||
else if split[0][1] = CConjunctionPrefix then
|
||||
module := TConjunctionModule.Create(Copy(split[0], 2, Length(split[0]) - 1));
|
||||
|
||||
for i := 2 to Length(split) - 2 do
|
||||
module.OutputNames.Add(Copy(split[i], 1, Length(split[i]) - 1));
|
||||
module.OutputNames.Add(split[Length(split) - 1]);
|
||||
|
||||
FModules.Add(module);
|
||||
end;
|
||||
|
||||
procedure TPulsePropagation.Finish;
|
||||
var
|
||||
results: TButtonResults;
|
||||
finalResult: TButtonResult;
|
||||
cycles, remainder, i, j, max: Integer;
|
||||
begin
|
||||
UpdateModuleConnections;
|
||||
|
||||
// The pulse counts for the full puzzle input repeat themselves in a very specific way, but the system state does not.
|
||||
// This indicates there is a better solution for this problem.
|
||||
// TODO: See if there is a better solution based on the repeating patterns in the pulse counts.
|
||||
results := TButtonResults.Create;
|
||||
repeat
|
||||
results.Add(PushButton);
|
||||
until AreAllModulesOff or (results.Count >= CButtonPushes);
|
||||
|
||||
DivMod(CButtonPushes, results.Count, cycles, remainder);
|
||||
finalResult.LowCount := 0;
|
||||
finalResult.HighCount := 0;
|
||||
max := results.Count - 1;
|
||||
for j := 0 to 1 do
|
||||
begin
|
||||
for i := 0 to max do
|
||||
begin
|
||||
Inc(finalResult.LowCount, results[i].LowCount);
|
||||
Inc(finalResult.HighCount, results[i].HighCount);
|
||||
end;
|
||||
if j = 0 then
|
||||
begin
|
||||
finalResult.LowCount := finalResult.LowCount * cycles;
|
||||
finalResult.HighCount := finalResult.HighCount * cycles;
|
||||
max := remainder - 1;
|
||||
end;
|
||||
end;
|
||||
|
||||
results.Free;
|
||||
|
||||
FPart1 := finalResult.LowCount * finalResult.HighCount;
|
||||
end;
|
||||
|
||||
function TPulsePropagation.GetDataFileName: string;
|
||||
begin
|
||||
Result := 'pulse_propagation.txt';
|
||||
end;
|
||||
|
||||
function TPulsePropagation.GetPuzzleName: string;
|
||||
begin
|
||||
Result := 'Day 20: Pulse Propagation';
|
||||
end;
|
||||
|
||||
end.
|
||||
|
|
@ -120,6 +120,10 @@
|
|||
<Filename Value="UAplentyTestCases.pas"/>
|
||||
<IsPartOfProject Value="True"/>
|
||||
</Unit>
|
||||
<Unit>
|
||||
<Filename Value="UPulsePropagationTestCases.pas"/>
|
||||
<IsPartOfProject Value="True"/>
|
||||
</Unit>
|
||||
</Units>
|
||||
</ProjectOptions>
|
||||
<CompilerOptions>
|
||||
|
|
|
@ -7,7 +7,8 @@ uses
|
|||
UGearRatiosTestCases, UScratchcardsTestCases, UGiveSeedFertilizerTestCases, UWaitForItTestCases, UCamelCardsTestCases,
|
||||
UHauntedWastelandTestCases, UMirageMaintenanceTestCases, UPipeMazeTestCases, UCosmicExpansionTestCases,
|
||||
UHotSpringsTestCases, UPointOfIncidenceTestCases, UParabolicReflectorDishTestCases, ULensLibraryTestCases,
|
||||
UFloorWillBeLavaTestCases, UClumsyCrucibleTestCases, ULavaductLagoonTestCases, UAplentyTestCases;
|
||||
UFloorWillBeLavaTestCases, UClumsyCrucibleTestCases, ULavaductLagoonTestCases, UAplentyTestCases,
|
||||
UPulsePropagationTestCases;
|
||||
|
||||
{$R *.res}
|
||||
|
||||
|
|
|
@ -0,0 +1,119 @@
|
|||
{
|
||||
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 <http://www.gnu.org/licenses/>.
|
||||
}
|
||||
|
||||
unit UPulsePropagationTestCases;
|
||||
|
||||
{$mode ObjFPC}{$H+}
|
||||
|
||||
interface
|
||||
|
||||
uses
|
||||
Classes, SysUtils, fpcunit, testregistry, USolver, UBaseTestCases, UPulsePropagation;
|
||||
|
||||
type
|
||||
|
||||
{ TPulsePropagationFullDataTestCase }
|
||||
|
||||
TPulsePropagationFullDataTestCase = class(TEngineBaseTest)
|
||||
protected
|
||||
function CreateSolver: ISolver; override;
|
||||
published
|
||||
procedure TestPart1;
|
||||
procedure TestPart2;
|
||||
end;
|
||||
|
||||
{ TPulsePropagationExampleTestCase }
|
||||
|
||||
TPulsePropagationExampleTestCase = class(TExampleEngineBaseTest)
|
||||
protected
|
||||
function CreateSolver: ISolver; override;
|
||||
published
|
||||
procedure TestPart1;
|
||||
end;
|
||||
|
||||
{ TExample2PulsePropagation }
|
||||
|
||||
TExample2PulsePropagation = class(TPulsePropagation)
|
||||
function GetDataFileName: string; override;
|
||||
end;
|
||||
|
||||
{ TPulsePropagationExample2TestCase }
|
||||
|
||||
TPulsePropagationExample2TestCase = class(TExampleEngineBaseTest)
|
||||
protected
|
||||
function CreateSolver: ISolver; override;
|
||||
published
|
||||
procedure TestPart1;
|
||||
end;
|
||||
|
||||
implementation
|
||||
|
||||
{ TPulsePropagationFullDataTestCase }
|
||||
|
||||
function TPulsePropagationFullDataTestCase.CreateSolver: ISolver;
|
||||
begin
|
||||
Result := TPulsePropagation.Create;
|
||||
end;
|
||||
|
||||
procedure TPulsePropagationFullDataTestCase.TestPart1;
|
||||
begin
|
||||
AssertEquals(949764474, FSolver.GetResultPart1);
|
||||
end;
|
||||
|
||||
procedure TPulsePropagationFullDataTestCase.TestPart2;
|
||||
begin
|
||||
AssertEquals(-1, FSolver.GetResultPart2);
|
||||
end;
|
||||
|
||||
{ TPulsePropagationExampleTestCase }
|
||||
|
||||
function TPulsePropagationExampleTestCase.CreateSolver: ISolver;
|
||||
begin
|
||||
Result := TPulsePropagation.Create;
|
||||
end;
|
||||
|
||||
procedure TPulsePropagationExampleTestCase.TestPart1;
|
||||
begin
|
||||
AssertEquals(32000000, FSolver.GetResultPart1);
|
||||
end;
|
||||
|
||||
{ TExample2PulsePropagation }
|
||||
|
||||
function TExample2PulsePropagation.GetDataFileName: string;
|
||||
begin
|
||||
Result := 'pulse_propagation2.txt';
|
||||
end;
|
||||
|
||||
{ TPulsePropagationExample2TestCase }
|
||||
|
||||
function TPulsePropagationExample2TestCase.CreateSolver: ISolver;
|
||||
begin
|
||||
Result := TExample2PulsePropagation.Create;
|
||||
end;
|
||||
|
||||
procedure TPulsePropagationExample2TestCase.TestPart1;
|
||||
begin
|
||||
AssertEquals(11687500, FSolver.GetResultPart1);
|
||||
end;
|
||||
|
||||
initialization
|
||||
|
||||
RegisterTest(TPulsePropagationFullDataTestCase);
|
||||
RegisterTest(TPulsePropagationExampleTestCase);
|
||||
RegisterTest(TPulsePropagationExample2TestCase);
|
||||
end.
|
||||
|
Loading…
Reference in New Issue