{
Solutions to the Advent Of Code.
Copyright (C) 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
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 .
}
unit USnowverload;
{$mode ObjFPC}{$H+}
interface
uses
Classes, SysUtils, Generics.Collections, USolver;
type
TSnowComponent = class;
TSnowComponents = specialize TObjectList;
{ TSnowComponent }
TSnowComponent = class
private
FName: string;
FMergedComponents: TSnowComponents;
public
property Name: string read FName;
procedure AddMerged(constref AMerge: TSnowComponent);
function GetMergeCount: Integer;
constructor Create(const AName: string);
destructor Destroy; override;
end;
{ TWire }
TWire = class
private
FComponent1, FComponent2, FOriginalComponent1, FOriginalComponent2: TSnowComponent;
public
property Component1: TSnowComponent read FComponent1;
property Component2: TSnowComponent read FComponent2;
procedure Reattach(constref AOldComponent, ANewComponent: TSnowComponent);
function IsLoop: Boolean;
procedure Reset;
constructor Create(constref AComponent1, AComponent2: TSnowComponent);
end;
TWires = specialize TObjectList;
{ TNetwork }
TNetwork = class
private
FComponents: TSnowComponents;
FDisconnectableWires, FContractedWires: TWires;
public
function FindOrCreateComponent(const AName: string): TSnowComponent;
procedure AddWire(constref AWire: TWire);
function RunMinCutContractionAlgorithm: Integer;
procedure Reset;
function GetResult: Integer;
constructor Create;
destructor Destroy; override;
end;
{ TSnowverload }
TSnowverload = class(TSolver)
private
FNetwork: TNetwork;
public
constructor Create;
destructor Destroy; override;
procedure ProcessDataLine(const ALine: string); override;
procedure Finish; override;
function GetDataFileName: string; override;
function GetPuzzleName: string; override;
end;
implementation
{ TSnowComponent }
procedure TSnowComponent.AddMerged(constref AMerge: TSnowComponent);
var
i: Integer;
c: TSnowComponent;
begin
FMergedComponents.Add(AMerge);
i := AMerge.FMergedComponents.Count - 1;
while i >= 0 do
begin
c := AMerge.FMergedComponents.ExtractIndex(i);
FMergedComponents.Add(c);
Dec(i);
end;
end;
function TSnowComponent.GetMergeCount: Integer;
begin
Result := FMergedComponents.Count + 1;
end;
constructor TSnowComponent.Create(const AName: string);
begin
FName := AName;
FMergedComponents := TSnowComponents.Create;
end;
destructor TSnowComponent.Destroy;
begin
FMergedComponents.Free;
inherited Destroy;
end;
{ TWire }
procedure TWire.Reattach(constref AOldComponent, ANewComponent: TSnowComponent);
begin
if FComponent1 = AOldComponent then
FComponent1 := ANewComponent;
if FComponent2 = AOldComponent then
FComponent2 := ANewComponent;
end;
function TWire.IsLoop: Boolean;
begin
Result := FComponent1 = FComponent2;
end;
procedure TWire.Reset;
begin
FComponent1 := FOriginalComponent1;
FComponent2 := FOriginalComponent2;
end;
constructor TWire.Create(constref AComponent1, AComponent2: TSnowComponent);
begin
FComponent1 := AComponent1;
FComponent2 := AComponent2;
FOriginalComponent1 := AComponent1;
FOriginalComponent2 := AComponent2;
end;
{ TNetwork }
function TNetwork.FindOrCreateComponent(const AName: string): TSnowComponent;
var
found: Boolean;
begin
found := False;
for Result in FComponents do
if Result.Name = AName then
begin
found := True;
Break;
end;
if not found then
begin
Result := TSnowComponent.Create(AName);
FComponents.Add(Result);
end;
end;
procedure TNetwork.AddWire(constref AWire: TWire);
begin
FDisconnectableWires.Add(AWire);
end;
function TNetwork.RunMinCutContractionAlgorithm: Integer;
var
r, i: Integer;
contraction, w: TWire;
begin
while FComponents.Count > 2 do
begin
// Determines contraction.
r := Random(FDisconnectableWires.Count - 1);
contraction := FDisconnectableWires.ExtractIndex(r);
FContractedWires.Add(contraction);
// Merges contraction.Component2 into contraction.Component1.
contraction.Component1.AddMerged(FComponents.Extract(contraction.Component2));
// Fix the wires connected to contraction.Component2.
i := FDisconnectableWires.Count - 1;
while i >= 0 do
begin
FDisconnectableWires[i].Reattach(contraction.Component2, contraction.Component1);
if FDisconnectableWires[i].IsLoop then
begin
w := FDisconnectableWires.ExtractIndex(i);
FContractedWires.Add(w);
end;
Dec(i);
end;
end;
Result := FDisconnectableWires.Count;
end;
procedure TNetwork.Reset;
var
i, j: Integer;
c: TSnowComponent;
w: TWire;
begin
// Resets the components.
i := 0;
while i < FComponents.Count do
begin
j := FComponents[i].FMergedComponents.Count - 1;
while j >= 0 do
begin
c := FComponents[i].FMergedComponents.ExtractIndex(j);
FComponents.Add(c);
Dec(j);
end;
Inc(i);
end;
// Resets the wires.
for w in FDisconnectableWires do
w.Reset;
i := FContractedWires.Count - 1;
while i >= 0 do
begin
w := FContractedWires.ExtractIndex(i);
w.Reset;
FDisconnectableWires.Add(w);
Dec(i);
end;
end;
function TNetwork.GetResult: Integer;
begin
Result := 0;
if FComponents.Count = 2 then
Result := FComponents[0].GetMergeCount * FComponents[1].GetMergeCount;
end;
constructor TNetwork.Create;
begin
FComponents := TSnowComponents.Create;
FDisconnectableWires := TWires.Create;
FContractedWires := TWires.Create;
end;
destructor TNetwork.Destroy;
begin
FComponents.Free;
FDisconnectableWires.Free;
FContractedWires.Free;
inherited Destroy;
end;
{ TSnowverload }
constructor TSnowverload.Create;
begin
FNetwork := TNetwork.Create;
end;
destructor TSnowverload.Destroy;
begin
FNetwork.Free;
inherited Destroy;
end;
procedure TSnowverload.ProcessDataLine(const ALine: string);
var
split: TStringArray;
c1, c2: TSnowComponent;
i: Integer;
begin
split := ALine.Split([':', ' ']);
c1 := FNetwork.FindOrCreateComponent(split[0]);
for i := 2 to Length(split) - 1 do
begin
c2 := FNetwork.FindOrCreateComponent(split[i]);
FNetwork.AddWire(TWire.Create(c1, c2));
end;
end;
procedure TSnowverload.Finish;
var
cut, count: Integer;
begin
// Karger's algorithm with known minimum cut size.
// See https://en.wikipedia.org/wiki/Karger%27s_algorithm
count := 1;
cut := FNetwork.RunMinCutContractionAlgorithm;
while cut > 3 do
begin
Inc(count);
FNetwork.Reset;
cut := FNetwork.RunMinCutContractionAlgorithm;
end;
FPart1 := FNetwork.GetResult;
end;
function TSnowverload.GetDataFileName: string;
begin
Result := 'snowverload.txt';
end;
function TSnowverload.GetPuzzleName: string;
begin
Result := 'Day 25: Snowverload';
end;
end.