Added solution for "Day 20: Pulse Propagation", part 2
This commit is contained in:
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user