Added solution for "Day 20: Pulse Propagation", part 2
This commit is contained in:
parent
c84d3e6a2d
commit
75aab50d42
|
@ -166,6 +166,14 @@ Since the workflows are at the beginning of the puzzle input, each machine part
|
|||
|
||||
For part two, a virtual "multi machine part" that represents all possible values of ratings, modelled as four integer intervals, is sent through the same workflow graph. Each time one of rules is applied to a multi machine part, it is split into up to three new multi machine parts that continue to go through the workflows on separate paths. This is similar to [my day 5 solution](#day-5-if-you-give-a-seed-a-fertilizer).
|
||||
|
||||
### Day 20: Pulse Propagation
|
||||
|
||||
:mag_right: Puzzle: <https://adventofcode.com/2023/day/20>, :white_check_mark: Solver: [`UPulsePropagation.pas`](solvers/UPulsePropagation.pas)
|
||||
|
||||
For part 1, it's quite straight forward to model and simulate the module pulses for the first 1000 button pushes.
|
||||
|
||||
Part 2 seemed pretty daunting at first (and probably is quite difficult in the general case), but investigating the graph of the module connection reveals pretty quickly that the modules form a set of four independent counters of button pushes modulo different reset values, such that `rx` receives one low pulse if and only if all four counters reset as a result of the same button push. Clearly, the first time this happens is when the button is pushed a number of times equal to the product of the four counters' reset values.
|
||||
|
||||
### Day 22: Sand Slabs
|
||||
|
||||
:mag_right: Puzzle: <https://adventofcode.com/2023/day/22>, :white_check_mark: Solver: [`USandSlabs.pas`](solvers/USandSlabs.pas)
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in New Issue