{ Solutions to the Advent Of Code. Copyright (C) 2023 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 UPointOfIncidence; {$mode ObjFPC}{$H+} interface uses Classes, SysUtils, Generics.Collections, Math, USolver; type TMirrorCompareResult = (mcSame, mcOneDifference, mcDifferent); { TMirrorCandidate } TMirrorCandidate = record Position: Integer; HasFix: Boolean; end; TMirrorCandidates = specialize TList; { TPointOfIncidence } TPointOfIncidence = class(TSolver) private FLines: TStringList; FHorizontalMirrorCandidates, FVerticalMirrorCandidates: specialize TList; FHorizontalFixedMirrorCandidates, FVerticalFixedMirrorCandidates: TMirrorCandidates; procedure ProcessPatternLine(const ALine: string); procedure VerifyHorizontalCandidates(const ANewLine: string); procedure VerifyHorizontalFixedCandidates(const ANewLine: string); procedure CheckForNewHorizontalCandidate(const ANewLine: string); function HaveSingleDifference(const ALine1, ALine2: string): Boolean; procedure VerifyVerticalCandidates(const ALine: string); procedure VerifyVerticalFixedCandidates(const ALine: string); procedure InitializeNewVerticalCandidates(const AFirstLine: string); function IsVerticalMirrorOfLine(const ALine: string; const AMirror: Integer): TMirrorCompareResult; procedure FinishPattern; function GetMirrorCandidate(const APosition: Integer; const AHasFix: Boolean): TMirrorCandidate; procedure SetMirrorCandidateFixed(const AMirrorCandidates: TMirrorCandidates; const AIndex: Integer); 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 { TPointOfIncidence } procedure TPointOfIncidence.ProcessPatternLine(const ALine: string); begin VerifyHorizontalCandidates(ALine); VerifyHorizontalFixedCandidates(ALine); CheckForNewHorizontalCandidate(ALine); if FLines.Count > 0 then begin VerifyVerticalCandidates(ALine); VerifyVerticalFixedCandidates(ALine); end else InitializeNewVerticalCandidates(ALine); FLines.Add(ALine); end; procedure TPointOfIncidence.VerifyHorizontalCandidates(const ANewLine: string); var i, mirroredIndex: Integer; begin // Checks if the current line fulfills the exisiting horizontal mirror candidates. i := FHorizontalMirrorCandidates.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 * FHorizontalMirrorCandidates[i] - FLines.Count - 1; if (mirroredIndex >= 0) and (ANewLine <> FLines[mirroredIndex]) then FHorizontalMirrorCandidates.Delete(i); Dec(i); 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); begin // Checks for new horizontal mirror candidate between ALine and the previous line. if FLines.Count > 0 then begin if ANewLine = FLines[FLines.Count - 1] then begin 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; procedure TPointOfIncidence.VerifyVerticalCandidates(const ALine: string); var i: Integer; begin // Checks if the current line fulfills the exisiting vertical mirror candidates. i := FVerticalMirrorCandidates.Count - 1; while i >= 0 do begin if IsVerticalMirrorOfLine(ALine, FVerticalMirrorCandidates[i]) <> mcSame then FVerticalMirrorCandidates.Delete(i); Dec(i); 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); end; end; procedure TPointOfIncidence.InitializeNewVerticalCandidates(const AFirstLine: string); var i: Integer; begin // Checks the line for vertical mirror candidates, i.e. mirrors that work for this line. for i := 1 to Length(AFirstLine) - 1 do case IsVerticalMirrorOfLine(AFirstLine, i) of mcSame: begin 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 Result := mcSame; for i := 0 to Min(AMirror - 1, Length(ALine) - AMirror - 1) do begin if ALine[AMirror - i] <> ALine[AMirror + i + 1] then begin if Result = mcSame then Result := mcOneDifference else begin Result := mcDifferent; Break; end; end; end; end; procedure TPointOfIncidence.FinishPattern; var i: Integer; begin for i := 0 to FHorizontalMirrorCandidates.Count - 1 do Inc(FPart1, FHorizontalMirrorCandidates[i] * 100); for i := 0 to FVerticalMirrorCandidates.Count - 1 do 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; FHorizontalMirrorCandidates.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; constructor TPointOfIncidence.Create; begin FLines := TStringList.Create; FHorizontalMirrorCandidates := specialize TList.Create; FVerticalMirrorCandidates := specialize TList.Create; FHorizontalFixedMirrorCandidates := TMirrorCandidates.Create; FVerticalFixedMirrorCandidates := TMirrorCandidates.Create; end; destructor TPointOfIncidence.Destroy; begin FLines.Free; FHorizontalMirrorCandidates.Free; FVerticalMirrorCandidates.Free; FHorizontalFixedMirrorCandidates.Free; FVerticalFixedMirrorCandidates.Free; inherited Destroy; end; procedure TPointOfIncidence.ProcessDataLine(const ALine: string); begin if ALine <> '' then ProcessPatternLine(ALine) else FinishPattern; end; procedure TPointOfIncidence.Finish; begin FinishPattern; end; function TPointOfIncidence.GetDataFileName: string; begin Result := 'point_of_incidence.txt'; end; function TPointOfIncidence.GetPuzzleName: string; begin Result := 'Day 13: Point of Incidence'; end; end.