From 307eb14b55fd798df706f45ccdbbe0a159d4c48d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefan=20M=C3=BCller?= Date: Thu, 14 Dec 2023 00:32:27 +0100 Subject: [PATCH] Added solution for "Day 13: Point of Incidence", part 2 --- solvers/UPointOfIncidence.pas | 178 +++++++++++++++++++++++---- tests/UPointOfIncidenceTestCases.pas | 12 ++ 2 files changed, 166 insertions(+), 24 deletions(-) diff --git a/solvers/UPointOfIncidence.pas b/solvers/UPointOfIncidence.pas index b4e9777..42d4c34 100644 --- a/solvers/UPointOfIncidence.pas +++ b/solvers/UPointOfIncidence.pas @@ -25,6 +25,16 @@ uses Classes, SysUtils, Generics.Collections, Math, USolver; type + TMirrorCompareResult = (mcSame, mcOneDifference, mcDifferent); + + { TMirrorCandidate } + + TMirrorCandidate = record + Position: Integer; + HasFix: Boolean; + end; + + TMirrorCandidates = specialize TList; { TPointOfIncidence } @@ -32,12 +42,19 @@ type 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; @@ -54,10 +71,14 @@ implementation procedure TPointOfIncidence.ProcessPatternLine(const ALine: string); begin VerifyHorizontalCandidates(ALine); + VerifyHorizontalFixedCandidates(ALine); CheckForNewHorizontalCandidate(ALine); if FLines.Count > 0 then - VerifyVerticalCandidates(ALine) + begin + VerifyVerticalCandidates(ALine); + VerifyVerticalFixedCandidates(ALine); + end else InitializeNewVerticalCandidates(ALine); @@ -80,30 +101,97 @@ begin 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) and (ANewLine = FLines[FLines.Count - 1]) then - FHorizontalMirrorCandidates.Add(FLines.Count); + 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, j, len, mirror: Integer; + i: 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; + 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; @@ -111,23 +199,36 @@ end; procedure TPointOfIncidence.InitializeNewVerticalCandidates(const AFirstLine: string); var - i, j, len: Integer; - isMirror: Boolean; + i: Integer; 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 + 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 AFirstLine[i - j] <> AFirstLine[i + j + 1] then - begin - isMirror := False; + if Result = mcSame then + Result := mcOneDifference + else begin + Result := mcDifferent; Break; end; end; - if isMirror then - FVerticalMirrorCandidates.Add(i); end; end; @@ -139,9 +240,34 @@ begin 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; @@ -149,6 +275,8 @@ begin FLines := TStringList.Create; FHorizontalMirrorCandidates := specialize TList.Create; FVerticalMirrorCandidates := specialize TList.Create; + FHorizontalFixedMirrorCandidates := TMirrorCandidates.Create; + FVerticalFixedMirrorCandidates := TMirrorCandidates.Create; end; destructor TPointOfIncidence.Destroy; @@ -156,6 +284,8 @@ begin FLines.Free; FHorizontalMirrorCandidates.Free; FVerticalMirrorCandidates.Free; + FHorizontalFixedMirrorCandidates.Free; + FVerticalFixedMirrorCandidates.Free; inherited Destroy; end; diff --git a/tests/UPointOfIncidenceTestCases.pas b/tests/UPointOfIncidenceTestCases.pas index afc44dd..e542ec7 100644 --- a/tests/UPointOfIncidenceTestCases.pas +++ b/tests/UPointOfIncidenceTestCases.pas @@ -33,6 +33,7 @@ type function CreateSolver: ISolver; override; published procedure TestPart1; + procedure TestPart2; end; { TPointOfIncidenceExampleTestCase } @@ -42,6 +43,7 @@ type function CreateSolver: ISolver; override; published procedure TestPart1; + procedure TestPart2; end; implementation @@ -58,6 +60,11 @@ begin AssertEquals(37718, FSolver.GetResultPart1); end; +procedure TPointOfIncidenceFullDataTestCase.TestPart2; +begin + AssertEquals(40995, FSolver.GetResultPart2); +end; + { TPointOfIncidenceExampleTestCase } function TPointOfIncidenceExampleTestCase.CreateSolver: ISolver; @@ -70,6 +77,11 @@ begin AssertEquals(405, FSolver.GetResultPart1); end; +procedure TPointOfIncidenceExampleTestCase.TestPart2; +begin + AssertEquals(400, FSolver.GetResultPart2); +end; + initialization RegisterTest(TPointOfIncidenceFullDataTestCase);