Added solution for "Day 13: Point of Incidence", part 2

This commit is contained in:
Stefan Müller 2023-12-14 00:32:27 +01:00 committed by Stefan Müller
parent 6b888a3f68
commit 307eb14b55
2 changed files with 166 additions and 24 deletions

View File

@ -25,6 +25,16 @@ uses
Classes, SysUtils, Generics.Collections, Math, USolver; Classes, SysUtils, Generics.Collections, Math, USolver;
type type
TMirrorCompareResult = (mcSame, mcOneDifference, mcDifferent);
{ TMirrorCandidate }
TMirrorCandidate = record
Position: Integer;
HasFix: Boolean;
end;
TMirrorCandidates = specialize TList<TMirrorCandidate>;
{ TPointOfIncidence } { TPointOfIncidence }
@ -32,12 +42,19 @@ type
private private
FLines: TStringList; FLines: TStringList;
FHorizontalMirrorCandidates, FVerticalMirrorCandidates: specialize TList<Integer>; FHorizontalMirrorCandidates, FVerticalMirrorCandidates: specialize TList<Integer>;
FHorizontalFixedMirrorCandidates, FVerticalFixedMirrorCandidates: TMirrorCandidates;
procedure ProcessPatternLine(const ALine: string); procedure ProcessPatternLine(const ALine: string);
procedure VerifyHorizontalCandidates(const ANewLine: string); procedure VerifyHorizontalCandidates(const ANewLine: string);
procedure VerifyHorizontalFixedCandidates(const ANewLine: string);
procedure CheckForNewHorizontalCandidate(const ANewLine: string); procedure CheckForNewHorizontalCandidate(const ANewLine: string);
function HaveSingleDifference(const ALine1, ALine2: string): Boolean;
procedure VerifyVerticalCandidates(const ALine: string); procedure VerifyVerticalCandidates(const ALine: string);
procedure VerifyVerticalFixedCandidates(const ALine: string);
procedure InitializeNewVerticalCandidates(const AFirstLine: string); procedure InitializeNewVerticalCandidates(const AFirstLine: string);
function IsVerticalMirrorOfLine(const ALine: string; const AMirror: Integer): TMirrorCompareResult;
procedure FinishPattern; procedure FinishPattern;
function GetMirrorCandidate(const APosition: Integer; const AHasFix: Boolean): TMirrorCandidate;
procedure SetMirrorCandidateFixed(const AMirrorCandidates: TMirrorCandidates; const AIndex: Integer);
public public
constructor Create; constructor Create;
destructor Destroy; override; destructor Destroy; override;
@ -54,10 +71,14 @@ implementation
procedure TPointOfIncidence.ProcessPatternLine(const ALine: string); procedure TPointOfIncidence.ProcessPatternLine(const ALine: string);
begin begin
VerifyHorizontalCandidates(ALine); VerifyHorizontalCandidates(ALine);
VerifyHorizontalFixedCandidates(ALine);
CheckForNewHorizontalCandidate(ALine); CheckForNewHorizontalCandidate(ALine);
if FLines.Count > 0 then if FLines.Count > 0 then
VerifyVerticalCandidates(ALine) begin
VerifyVerticalCandidates(ALine);
VerifyVerticalFixedCandidates(ALine);
end
else else
InitializeNewVerticalCandidates(ALine); InitializeNewVerticalCandidates(ALine);
@ -80,54 +101,134 @@ begin
end; end;
end; end;
procedure TPointOfIncidence.VerifyHorizontalFixedCandidates(const ANewLine: string);
var
i, mirroredIndex: Integer;
begin
// Checks if the current line fulfills the exisiting horizontal mirror candidates.
i := FHorizontalFixedMirrorCandidates.Count - 1;
while i >= 0 do
begin
// Calculates the index of the line that the current line would be a duplicate of in regards to the i-th candidate.
mirroredIndex := 2 * FHorizontalFixedMirrorCandidates[i].Position - FLines.Count - 1;
if mirroredIndex >= 0 then
begin
if ANewLine <> FLines[mirroredIndex] then
begin
if not FHorizontalFixedMirrorCandidates[i].HasFix
and HaveSingleDifference(ANewLine, FLines[mirroredIndex]) then
SetMirrorCandidateFixed(FHorizontalFixedMirrorCandidates, i)
else
FHorizontalFixedMirrorCandidates.Delete(i);
end;
end;
Dec(i);
end;
end;
procedure TPointOfIncidence.CheckForNewHorizontalCandidate(const ANewLine: string); procedure TPointOfIncidence.CheckForNewHorizontalCandidate(const ANewLine: string);
begin begin
// Checks for new horizontal mirror candidate between ALine and the previous line. // Checks for new horizontal mirror candidate between ALine and the previous line.
if (FLines.Count > 0) and (ANewLine = FLines[FLines.Count - 1]) then if FLines.Count > 0 then
begin
if ANewLine = FLines[FLines.Count - 1] then
begin
FHorizontalMirrorCandidates.Add(FLines.Count); FHorizontalMirrorCandidates.Add(FLines.Count);
FHorizontalFixedMirrorCandidates.Add(GetMirrorCandidate(FLines.Count, False));
end
else if HaveSingleDifference(ANewLine, FLines[FLines.Count - 1]) then
FHorizontalFixedMirrorCandidates.Add(GetMirrorCandidate(FLines.Count, True));
end;
end;
function TPointOfIncidence.HaveSingleDifference(const ALine1, ALine2: string): Boolean;
var
i: Integer;
begin
Result := False;
for i := 1 to Length(ALine1) do
begin
if ALine1[i] <> ALine2[i] then
begin
if Result then
begin
// Found a second difference between the two lines, return False.
Result := False;
Exit;
end
else
// Found a difference between the two lines.
Result := True;
end;
end;
end; end;
procedure TPointOfIncidence.VerifyVerticalCandidates(const ALine: string); procedure TPointOfIncidence.VerifyVerticalCandidates(const ALine: string);
var var
i, j, len, mirror: Integer; i: Integer;
begin begin
// Checks if the current line fulfills the exisiting vertical mirror candidates. // Checks if the current line fulfills the exisiting vertical mirror candidates.
len := Length(ALine);
i := FVerticalMirrorCandidates.Count - 1; i := FVerticalMirrorCandidates.Count - 1;
while i >= 0 do while i >= 0 do
begin begin
mirror := FVerticalMirrorCandidates[i]; if IsVerticalMirrorOfLine(ALine, FVerticalMirrorCandidates[i]) <> mcSame then
for j := 0 to Min(mirror - 1, len - mirror - 1) do
begin
if ALine[mirror - j] <> ALine[mirror + j + 1] then
begin
FVerticalMirrorCandidates.Delete(i); FVerticalMirrorCandidates.Delete(i);
Break; Dec(i);
end; end;
end; end;
procedure TPointOfIncidence.VerifyVerticalFixedCandidates(const ALine: string);
var
i: Integer;
begin
i := FVerticalFixedMirrorCandidates.Count - 1;
while i >= 0 do
begin
case IsVerticalMirrorOfLine(ALine, FVerticalFixedMirrorCandidates[i].Position) of
mcOneDifference:
if FVerticalFixedMirrorCandidates[i].HasFix then
FVerticalFixedMirrorCandidates.Delete(i)
else
SetMirrorCandidateFixed(FVerticalFixedMirrorCandidates, i);
mcDifferent:
FVerticalFixedMirrorCandidates.Delete(i);
end;
Dec(i); Dec(i);
end; end;
end; end;
procedure TPointOfIncidence.InitializeNewVerticalCandidates(const AFirstLine: string); procedure TPointOfIncidence.InitializeNewVerticalCandidates(const AFirstLine: string);
var var
i, j, len: Integer; i: Integer;
isMirror: Boolean;
begin begin
// Checks the line for vertical mirror candidates, i.e. mirrors that work for this line. // Checks the line for vertical mirror candidates, i.e. mirrors that work for this line.
len := Length(AFirstLine); for i := 1 to Length(AFirstLine) - 1 do
for i := 1 to len - 1 do begin case IsVerticalMirrorOfLine(AFirstLine, i) of
isMirror := True; mcSame: begin
for j := 0 to Min(i - 1, len - i - 1) do FVerticalMirrorCandidates.Add(i);
FVerticalFixedMirrorCandidates.Add(GetMirrorCandidate(i, False));
end;
mcOneDifference:
FVerticalFixedMirrorCandidates.Add(GetMirrorCandidate(i, True));
end;
end;
function TPointOfIncidence.IsVerticalMirrorOfLine(const ALine: string; const AMirror: Integer): TMirrorCompareResult;
var
i : Integer;
begin begin
if AFirstLine[i - j] <> AFirstLine[i + j + 1] then Result := mcSame;
for i := 0 to Min(AMirror - 1, Length(ALine) - AMirror - 1) do
begin begin
isMirror := False; if ALine[AMirror - i] <> ALine[AMirror + i + 1] then
begin
if Result = mcSame then
Result := mcOneDifference
else begin
Result := mcDifferent;
Break; Break;
end; end;
end; end;
if isMirror then
FVerticalMirrorCandidates.Add(i);
end; end;
end; end;
@ -139,9 +240,34 @@ begin
Inc(FPart1, FHorizontalMirrorCandidates[i] * 100); Inc(FPart1, FHorizontalMirrorCandidates[i] * 100);
for i := 0 to FVerticalMirrorCandidates.Count - 1 do for i := 0 to FVerticalMirrorCandidates.Count - 1 do
Inc(FPart1, FVerticalMirrorCandidates[i]); Inc(FPart1, FVerticalMirrorCandidates[i]);
for i := 0 to FHorizontalFixedMirrorCandidates.Count - 1 do
if FHorizontalFixedMirrorCandidates[i].HasFix then
Inc(FPart2, FHorizontalFixedMirrorCandidates[i].Position * 100);
for i := 0 to FVerticalFixedMirrorCandidates.Count - 1 do
if FVerticalFixedMirrorCandidates[i].HasFix then
Inc(FPart2, FVerticalFixedMirrorCandidates[i].Position);
FLines.Clear; FLines.Clear;
FHorizontalMirrorCandidates.Clear; FHorizontalMirrorCandidates.Clear;
FVerticalMirrorCandidates.Clear; FVerticalMirrorCandidates.Clear;
FHorizontalFixedMirrorCandidates.Clear;
FVerticalFixedMirrorCandidates.Clear;
end;
function TPointOfIncidence.GetMirrorCandidate(const APosition: Integer; const AHasFix: Boolean): TMirrorCandidate;
begin
Result.Position := APosition;
Result.HasFix := AHasFix;
end;
procedure TPointOfIncidence.SetMirrorCandidateFixed(const AMirrorCandidates: TMirrorCandidates; const AIndex: Integer);
var
mirrorCandidate: TMirrorCandidate;
begin
mirrorCandidate := AMirrorCandidates[AIndex];
mirrorCandidate.HasFix := True;
AMirrorCandidates[AIndex] := mirrorCandidate;
end; end;
constructor TPointOfIncidence.Create; constructor TPointOfIncidence.Create;
@ -149,6 +275,8 @@ begin
FLines := TStringList.Create; FLines := TStringList.Create;
FHorizontalMirrorCandidates := specialize TList<Integer>.Create; FHorizontalMirrorCandidates := specialize TList<Integer>.Create;
FVerticalMirrorCandidates := specialize TList<Integer>.Create; FVerticalMirrorCandidates := specialize TList<Integer>.Create;
FHorizontalFixedMirrorCandidates := TMirrorCandidates.Create;
FVerticalFixedMirrorCandidates := TMirrorCandidates.Create;
end; end;
destructor TPointOfIncidence.Destroy; destructor TPointOfIncidence.Destroy;
@ -156,6 +284,8 @@ begin
FLines.Free; FLines.Free;
FHorizontalMirrorCandidates.Free; FHorizontalMirrorCandidates.Free;
FVerticalMirrorCandidates.Free; FVerticalMirrorCandidates.Free;
FHorizontalFixedMirrorCandidates.Free;
FVerticalFixedMirrorCandidates.Free;
inherited Destroy; inherited Destroy;
end; end;

View File

@ -33,6 +33,7 @@ type
function CreateSolver: ISolver; override; function CreateSolver: ISolver; override;
published published
procedure TestPart1; procedure TestPart1;
procedure TestPart2;
end; end;
{ TPointOfIncidenceExampleTestCase } { TPointOfIncidenceExampleTestCase }
@ -42,6 +43,7 @@ type
function CreateSolver: ISolver; override; function CreateSolver: ISolver; override;
published published
procedure TestPart1; procedure TestPart1;
procedure TestPart2;
end; end;
implementation implementation
@ -58,6 +60,11 @@ begin
AssertEquals(37718, FSolver.GetResultPart1); AssertEquals(37718, FSolver.GetResultPart1);
end; end;
procedure TPointOfIncidenceFullDataTestCase.TestPart2;
begin
AssertEquals(40995, FSolver.GetResultPart2);
end;
{ TPointOfIncidenceExampleTestCase } { TPointOfIncidenceExampleTestCase }
function TPointOfIncidenceExampleTestCase.CreateSolver: ISolver; function TPointOfIncidenceExampleTestCase.CreateSolver: ISolver;
@ -70,6 +77,11 @@ begin
AssertEquals(405, FSolver.GetResultPart1); AssertEquals(405, FSolver.GetResultPart1);
end; end;
procedure TPointOfIncidenceExampleTestCase.TestPart2;
begin
AssertEquals(400, FSolver.GetResultPart2);
end;
initialization initialization
RegisterTest(TPointOfIncidenceFullDataTestCase); RegisterTest(TPointOfIncidenceFullDataTestCase);