2023-12-19 17:57:58 +01:00
|
|
|
{
|
|
|
|
Solutions to the Advent Of Code.
|
2024-06-29 01:03:16 +02:00
|
|
|
Copyright (C) 2024 Stefan Müller
|
2023-12-19 17:57:58 +01:00
|
|
|
|
|
|
|
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 ULavaductLagoon;
|
|
|
|
|
|
|
|
{$mode ObjFPC}{$H+}
|
|
|
|
|
|
|
|
interface
|
|
|
|
|
|
|
|
uses
|
2024-06-29 01:03:16 +02:00
|
|
|
Classes, SysUtils, Generics.Collections, USolver, UCommon;
|
2023-12-19 17:57:58 +01:00
|
|
|
|
|
|
|
type
|
|
|
|
|
2024-06-29 01:03:16 +02:00
|
|
|
{ TDig }
|
|
|
|
|
|
|
|
TDig = record
|
|
|
|
Direction: TPoint;
|
|
|
|
Length: Cardinal;
|
|
|
|
end;
|
|
|
|
|
|
|
|
TDigs = specialize TList<TDig>;
|
|
|
|
|
2023-12-19 17:57:58 +01:00
|
|
|
{ TLavaductLagoon }
|
|
|
|
|
|
|
|
TLavaductLagoon = class(TSolver)
|
2024-06-29 01:03:16 +02:00
|
|
|
FDigs: TDigs;
|
|
|
|
FCurrentPosiiton: TPoint;
|
|
|
|
FDigRect: TRect;
|
|
|
|
FDigSite: array of array of Boolean;
|
|
|
|
FHigh: TPoint;
|
|
|
|
function AddDig(const ALine: string): TDig;
|
|
|
|
procedure UpdateDigRect(constref ADig: TDig);
|
|
|
|
procedure CalculateDigSite;
|
|
|
|
function CalculateLagoonSize: Int64;
|
|
|
|
function CheckPositionUntouched(const AX, AY: Integer; var ACount: Integer): Boolean;
|
2023-12-19 17:57:58 +01:00
|
|
|
public
|
2024-06-29 01:03:16 +02:00
|
|
|
constructor Create;
|
|
|
|
destructor Destroy; override;
|
2023-12-19 17:57:58 +01:00
|
|
|
procedure ProcessDataLine(const ALine: string); override;
|
|
|
|
procedure Finish; override;
|
|
|
|
function GetDataFileName: string; override;
|
|
|
|
function GetPuzzleName: string; override;
|
|
|
|
end;
|
|
|
|
|
|
|
|
implementation
|
|
|
|
|
|
|
|
{ TLavaductLagoon }
|
|
|
|
|
2024-06-29 01:03:16 +02:00
|
|
|
function TLavaductLagoon.AddDig(const ALine: string): TDig;
|
|
|
|
var
|
|
|
|
split: TStringArray;
|
|
|
|
begin
|
|
|
|
split := ALine.Split([' ']);
|
|
|
|
case split[0] of
|
|
|
|
'R': Result.Direction := CDirectionRight;
|
|
|
|
'D': Result.Direction := CDirectionDown;
|
|
|
|
'L': Result.Direction := CDirectionLeft;
|
|
|
|
'U': Result.Direction := CDirectionUp;
|
|
|
|
end;
|
|
|
|
Result.Length := StrToUInt(split[1]);
|
|
|
|
FDigs.Add(Result);
|
|
|
|
end;
|
|
|
|
|
|
|
|
procedure TLavaductLagoon.UpdateDigRect(constref ADig: TDig);
|
2023-12-19 17:57:58 +01:00
|
|
|
begin
|
2024-06-29 01:03:16 +02:00
|
|
|
if ADig.Direction.Y = 0 then
|
|
|
|
begin
|
|
|
|
Inc(FCurrentPosiiton.X, ADig.Length * ADig.Direction.X);
|
|
|
|
if FCurrentPosiiton.X < FDigRect.Left then
|
|
|
|
FDigRect.Left := FCurrentPosiiton.X;
|
|
|
|
if FDigRect.Right < FCurrentPosiiton.X then
|
|
|
|
FDigRect.Right := FCurrentPosiiton.X;
|
|
|
|
end
|
|
|
|
else begin
|
|
|
|
Inc(FCurrentPosiiton.Y, ADig.Length * ADig.Direction.Y);
|
|
|
|
if FCurrentPosiiton.Y < FDigRect.Top then
|
|
|
|
FDigRect.Top := FCurrentPosiiton.Y;
|
|
|
|
if FDigRect.Bottom < FCurrentPosiiton.Y then
|
|
|
|
FDigRect.Bottom := FCurrentPosiiton.Y;
|
|
|
|
end;
|
|
|
|
end;
|
2023-12-19 17:57:58 +01:00
|
|
|
|
2024-06-29 01:03:16 +02:00
|
|
|
procedure TLavaductLagoon.CalculateDigSite;
|
|
|
|
var
|
|
|
|
i, j: Integer;
|
|
|
|
dig: TDig;
|
|
|
|
begin
|
|
|
|
// Initializes dig site array.
|
|
|
|
FHigh := FDigRect.BottomRight - FDigRect.TopLeft;
|
|
|
|
SetLength(FDigSite, FHigh.X + 1, FHigh.Y + 1);
|
|
|
|
for i := 0 to FHigh.X do
|
|
|
|
for j := 0 to FHigh.Y do
|
|
|
|
FDigSite[i, j] := False;
|
|
|
|
|
|
|
|
// Initializes start position.
|
|
|
|
FCurrentPosiiton := Point(-FDigRect.Left, -FDigRect.Top);
|
|
|
|
FDigSite[FCurrentPosiiton.X, FCurrentPosiiton.Y] := True;
|
|
|
|
|
|
|
|
// Applies digs to dig site array.
|
|
|
|
for dig in FDigs do
|
|
|
|
for i := 1 to dig.Length do
|
|
|
|
begin
|
|
|
|
FCurrentPosiiton := FCurrentPosiiton + dig.Direction;
|
|
|
|
FDigSite[FCurrentPosiiton.X, FCurrentPosiiton.Y] := True;
|
|
|
|
end;
|
2023-12-19 17:57:58 +01:00
|
|
|
end;
|
|
|
|
|
2024-06-29 01:03:16 +02:00
|
|
|
function TLavaductLagoon.CalculateLagoonSize: Int64;
|
|
|
|
var
|
|
|
|
stack: specialize TStack<TPoint>;
|
|
|
|
exteriors, i: Integer;
|
|
|
|
position, neighbor: TPoint;
|
|
|
|
direction: PPoint;
|
2023-12-19 17:57:58 +01:00
|
|
|
begin
|
2024-06-29 01:03:16 +02:00
|
|
|
// Counts exterior, untouched positions.
|
|
|
|
stack := specialize TStack<TPoint>.Create;
|
|
|
|
exteriors := 0;
|
|
|
|
|
|
|
|
// Counts untouched position on the edge of the dig site and their neighbors, and pushes those neighbors on the stack.
|
|
|
|
// With this we ensure that items on the stack are never on the edge, thus avoiding bounds checks later.
|
|
|
|
for i := 0 to FHigh.Y do
|
|
|
|
begin
|
|
|
|
if CheckPositionUntouched(0, i, exteriors)
|
|
|
|
and (1 < i) and (i < FHigh.Y)
|
|
|
|
and CheckPositionUntouched(1, i, exteriors) then
|
|
|
|
stack.Push(Point(1, i));
|
|
|
|
if CheckPositionUntouched(FHigh.X, i, exteriors)
|
|
|
|
and (0 < i) and (i < FHigh.Y - 1)
|
|
|
|
and CheckPositionUntouched(FHigh.X - 1, i, exteriors) then
|
|
|
|
stack.Push(Point(FHigh.X - 1, i));
|
|
|
|
end;
|
|
|
|
for i := 0 to FHigh.X do
|
|
|
|
begin
|
|
|
|
if CheckPositionUntouched(i, 0, exteriors)
|
|
|
|
and (0 < i) and (i < FHigh.X - 1)
|
|
|
|
and CheckPositionUntouched(i, 1, exteriors) then
|
|
|
|
stack.Push(Point(i, 1));
|
|
|
|
if CheckPositionUntouched(i, FHigh.Y, exteriors)
|
|
|
|
and (1 < i) and (i < FHigh.X)
|
|
|
|
and CheckPositionUntouched(i, FHigh.Y - 1, exteriors) then
|
|
|
|
stack.Push(Point(i, FHigh.Y - 1));
|
|
|
|
end;
|
|
|
|
|
|
|
|
// Counts the remaining exterior area while flood-filling the positions.
|
|
|
|
while stack.Count > 0 do
|
|
|
|
begin
|
|
|
|
position := stack.Pop;
|
|
|
|
for direction in CPCardinalDirections do
|
|
|
|
begin
|
|
|
|
neighbor := position + direction^;
|
|
|
|
if CheckPositionUntouched(neighbor.X, neighbor.Y, exteriors) then
|
|
|
|
stack.Push(neighbor);
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
stack.Free;
|
2023-12-19 17:57:58 +01:00
|
|
|
|
2024-06-29 01:03:16 +02:00
|
|
|
Result := (FHigh.X + 1) * (FHigh.Y + 1) - exteriors;
|
|
|
|
end;
|
|
|
|
|
|
|
|
function TLavaductLagoon.CheckPositionUntouched(const AX, AY: Integer; var ACount: Integer): Boolean;
|
|
|
|
begin
|
|
|
|
Result := not FDigSite[AX, AY];
|
|
|
|
if Result then
|
|
|
|
begin
|
|
|
|
FDigSite[AX, AY] := True;
|
|
|
|
Inc(ACount);
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
|
|
|
|
constructor TLavaductLagoon.Create;
|
|
|
|
begin
|
|
|
|
FDigs := TDigs.Create;
|
|
|
|
FCurrentPosiiton := Point(0, 0);
|
|
|
|
FDigRect := Rect(0, 0, 0, 0);
|
|
|
|
end;
|
|
|
|
|
|
|
|
destructor TLavaductLagoon.Destroy;
|
|
|
|
begin
|
|
|
|
FDigs.Free;
|
|
|
|
inherited Destroy;
|
|
|
|
end;
|
|
|
|
|
|
|
|
procedure TLavaductLagoon.ProcessDataLine(const ALine: string);
|
|
|
|
var
|
|
|
|
dig: TDig;
|
|
|
|
begin
|
|
|
|
dig := AddDig(ALine);
|
|
|
|
UpdateDigRect(dig);
|
|
|
|
end;
|
|
|
|
|
|
|
|
procedure TLavaductLagoon.Finish;
|
|
|
|
begin
|
|
|
|
CalculateDigSite;
|
|
|
|
FPart1 := CalculateLagoonSize;
|
2023-12-19 17:57:58 +01:00
|
|
|
end;
|
|
|
|
|
|
|
|
function TLavaductLagoon.GetDataFileName: string;
|
|
|
|
begin
|
|
|
|
Result := 'lavaduct_lagoon.txt';
|
|
|
|
end;
|
|
|
|
|
|
|
|
function TLavaductLagoon.GetPuzzleName: string;
|
|
|
|
begin
|
|
|
|
Result := 'Day 18: Lavaduct Lagoon';
|
|
|
|
end;
|
|
|
|
|
|
|
|
end.
|
|
|
|
|