{ 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 { TPointOfIncidence } TPointOfIncidence = class(TSolver) private FLines: TStringList; FHorizontalMirrorCandidates, FVerticalMirrorCandidates: specialize TList; procedure ProcessPatternLine(const ALine: string); procedure VerifyHorizontalCandidates(const ANewLine: string); procedure CheckForNewHorizontalCandidate(const ANewLine: string); procedure VerifyVerticalCandidates(const ALine: string); procedure InitializeNewVerticalCandidates(const AFirstLine: string); procedure FinishPattern; 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); CheckForNewHorizontalCandidate(ALine); if FLines.Count > 0 then VerifyVerticalCandidates(ALine) 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.CheckForNewHorizontalCandidate(const ANewLine: string); begin // Checks for new horizontal mirror candidate between ALine and the previous line. if (FLines.Count > 0) and (ANewLine = FLines[FLines.Count - 1]) then FHorizontalMirrorCandidates.Add(FLines.Count); end; procedure TPointOfIncidence.VerifyVerticalCandidates(const ALine: string); var i, j, len, mirror: Integer; begin // Checks if the current line fulfills the exisiting vertical mirror candidates. len := Length(ALine); i := FVerticalMirrorCandidates.Count - 1; while i >= 0 do begin mirror := FVerticalMirrorCandidates[i]; 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); Break; end; end; Dec(i); end; end; procedure TPointOfIncidence.InitializeNewVerticalCandidates(const AFirstLine: string); var i, j, len: Integer; isMirror: Boolean; begin // Checks the line for vertical mirror candidates, i.e. mirrors that work for this line. len := Length(AFirstLine); for i := 1 to len - 1 do begin isMirror := True; for j := 0 to Min(i - 1, len - i - 1) do begin if AFirstLine[i - j] <> AFirstLine[i + j + 1] then begin isMirror := False; Break; end; end; if isMirror then FVerticalMirrorCandidates.Add(i); 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]); FLines.Clear; FHorizontalMirrorCandidates.Clear; FVerticalMirrorCandidates.Clear; end; constructor TPointOfIncidence.Create; begin FLines := TStringList.Create; FHorizontalMirrorCandidates := specialize TList.Create; FVerticalMirrorCandidates := specialize TList.Create; end; destructor TPointOfIncidence.Destroy; begin FLines.Free; FHorizontalMirrorCandidates.Free; FVerticalMirrorCandidates.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.