Added solution for "Day 20: Pulse Propagation", part 2

This commit is contained in:
2024-08-20 17:50:07 +02:00
parent c84d3e6a2d
commit 75aab50d42
2 changed files with 66 additions and 71 deletions

View File

@@ -1,6 +1,6 @@
{
Solutions to the Advent Of Code.
Copyright (C) 2023 Stefan Müller
Copyright (C) 2023-2024 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
@@ -22,7 +22,7 @@ unit UPulsePropagation;
interface
uses
Classes, SysUtils, Generics.Collections, Math, USolver;
Classes, SysUtils, Generics.Collections, USolver;
type
TModule = class;
@@ -49,12 +49,12 @@ type
public
property Name: string read FName;
property OutputNames: TStringList read FOutputNames;
property Outputs: TModules read FOutputs;
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 }
@@ -71,31 +71,29 @@ type
FState: Boolean;
public
function ReceivePulse(const ASender: TModule; const AIsHigh: Boolean): TPulses; override;
function IsOff: Boolean; override;
end;
{ TConjectionBuffer }
{ TConjunctionInputBuffer }
TConjectionBuffer = record
TConjunctionInputBuffer = record
Input: TModule;
LastState: Boolean;
end;
TConjectionBuffers = specialize TList<TConjectionBuffer>;
TConjunctionInputBuffers = specialize TList<TConjunctionInputBuffer>;
{ TConjunctionModule }
TConjunctionModule = class(TModule)
private
FInputBuffers: TConjectionBuffers;
FInputBuffers: TConjunctionInputBuffers;
procedure UpdateInputBuffer(constref AInput: TModule; const AState: Boolean);
function AreAllBuffersSame(const AIsHigh: Boolean): Boolean;
function AreAllBuffersHigh: 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 }
@@ -111,8 +109,6 @@ type
LowCount, HighCount: Integer;
end;
TButtonResults = specialize TList<TButtonResult>;
{ TPulsePropagation }
TPulsePropagation = class(TSolver)
@@ -121,7 +117,7 @@ type
FBroadcaster: TModule;
procedure UpdateModuleConnections;
function PushButton: TButtonResult;
function AreAllModulesOff: Boolean;
function CalcCounterTarget(const AFirstFlipFlop: TModule): Int64;
public
constructor Create;
destructor Destroy; override;
@@ -180,11 +176,6 @@ begin
FOutputs.Add(AOutput);
end;
function TModule.IsOff: Boolean;
begin
Result := True;
end;
{ TBroadcasterModule }
function TBroadcasterModule.ReceivePulse(const ASender: TModule; const AIsHigh: Boolean): TPulses;
@@ -204,17 +195,12 @@ begin
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;
buffer: TConjunctionInputBuffer;
begin
for i := 0 to FInputBuffers.Count - 1 do
if FInputBuffers[i].Input = AInput then
@@ -226,13 +212,13 @@ begin
end;
end;
function TConjunctionModule.AreAllBuffersSame(const AIsHigh: Boolean): Boolean;
function TConjunctionModule.AreAllBuffersHigh: Boolean;
var
buffer: TConjectionBuffer;
buffer: TConjunctionInputBuffer;
begin
Result := True;
for buffer in FInputBuffers do
if buffer.LastState <> AIsHigh then
if not buffer.LastState then
begin
Result := False;
Exit;
@@ -242,7 +228,7 @@ end;
constructor TConjunctionModule.Create(const AName: string);
begin
inherited Create(AName);
FInputBuffers := TConjectionBuffers.Create;
FInputBuffers := TConjunctionInputBuffers.Create;
end;
destructor TConjunctionModule.Destroy;
@@ -253,7 +239,7 @@ end;
procedure TConjunctionModule.AddInput(const AInput: TModule);
var
buffer: TConjectionBuffer;
buffer: TConjunctionInputBuffer;
begin
buffer.Input := AInput;
buffer.LastState := False;
@@ -263,12 +249,7 @@ 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);
Result := CreatePulsesToOutputs(not AreAllBuffersHigh);
end;
{ TEndpointModule }
@@ -342,17 +323,39 @@ begin
queue.Free;
end;
function TPulsePropagation.AreAllModulesOff: Boolean;
function TPulsePropagation.CalcCounterTarget(const AFirstFlipFlop: TModule): Int64;
var
module: TModule;
binDigit: Int64;
current, next: TModule;
i: Integer;
begin
Result := True;
for module in FModules do
if not module.IsOff then
Result := 0;
binDigit := 1;
current := AFirstFlipFlop;
while True do
begin
if current.Outputs.Count = 1 then
begin
Result := False;
Exit;
current := current.Outputs.First;
if current is TConjunctionModule then
begin
Result := Result + binDigit;
Break;
end;
end
else begin
Result := Result + binDigit;
i := 0;
repeat
if i = current.Outputs.Count then
Exit;
next := current.Outputs[i];
Inc(i);
until next is TFlipFlopModule;
current := next;
end;
binDigit := binDigit << 1;
end;
end;
constructor TPulsePropagation.Create;
@@ -392,42 +395,26 @@ end;
procedure TPulsePropagation.Finish;
var
results: TButtonResults;
finalResult: TButtonResult;
cycles, remainder, i, j, max: Integer;
result, accumulated: TButtonResult;
i: Integer;
module: TModule;
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
accumulated.LowCount := 0;
accumulated.HighCount := 0;
for i := 1 to CButtonPushes 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;
result := PushButton;
Inc(accumulated.LowCount, result.LowCount);
Inc(accumulated.HighCount, result.HighCount);
end;
results.Free;
FPart1 := accumulated.LowCount * accumulated.HighCount;
FPart1 := finalResult.LowCount * finalResult.HighCount;
FPart2 := 1;
for module in FBroadcaster.Outputs do
FPart2 := FPart2 * CalcCounterTarget(module);
end;
function TPulsePropagation.GetDataFileName: string;