{ 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 UGearRatios; {$mode ObjFPC}{$H+} interface uses Classes, SysUtils, USolver; const CDigitChars = ['0'..'9']; CNonSymbolChars = ['0'..'9', '.']; type { TGearRatios } TGearRatios = class(TSolver) private FPreviousLine, FLine: string; procedure ProcessDataLineTriplet(const APreviousLine, ALine, ANextLine: string); public procedure ProcessDataLine(const ALine: string); override; procedure Finish; override; function GetDataFileName: string; override; function GetPuzzleName: string; override; end; implementation { TGearRatios } procedure TGearRatios.ProcessDataLineTriplet(const APreviousLine, ALine, ANextLine: string); var i, numberStart, numberLength, partNumber: Integer; inNumber, isPartNumber: Boolean; begin inNumber := False; for i := 1 to ALine.Length do begin // Checks if number starts. if not inNumber and (ALine[i] in CDigitChars) then begin inNumber := True; numberStart := i; // Checks for a symbol in the column before the first digit. isPartNumber := (i > 1) and (not (APreviousLine[i - 1] in CNonSymbolChars) or not (ALine[i - 1] in CNonSymbolChars) or not (ANextLine[i - 1] in CNonSymbolChars)); end; // Checks for a symbol in the column of the current digit. if inNumber and not isPartNumber and (not (APreviousLine[i] in CNonSymbolChars) or not (ANextLine[i] in CNonSymbolChars)) then begin isPartNumber := True; end; // Checks if number ends. if inNumber and (not (ALine[i] in CDigitChars) or (i = ALine.Length)) then begin inNumber := False; // Checks for a symbol in the column after the last digit. if not (APreviousLine[i] in CNonSymbolChars) or not (ALine[i] in CNonSymbolChars) or not (ANextLine[i] in CNonSymbolChars) then begin isPartNumber := True; end; // Counts if it is a part number. if isPartNumber then begin numberLength := i - numberStart; if ALine[i] in CDigitChars then Inc(numberLength); partNumber := StrToInt(Copy(ALine, numberStart, numberLength)); Inc(FPart1, partNumber); end; end; end; end; procedure TGearRatios.ProcessDataLine(const ALine: string); begin if not (FLine = '') then begin // Processes lines 2 to n - 1 in calls for lines 3 to n. ProcessDataLineTriplet(FPreviousLine, FLine, ALine); FPreviousLine := FLine; FLine := ALine; end else if not (FPreviousLine = '') then begin // Processes line 1 in call for line 2. FLine := ALine; // Duplicates the first line for the algorithm because it does not influence the result. ProcessDataLineTriplet(FPreviousLine, FPreviousLine, FLine); end else // Processes nothing in call for line 1. FPreviousLine := ALine; end; procedure TGearRatios.Finish; begin // Processes line n. // Duplicates the last line for the algorithm because it does not influence the result. if FLine = '' then FLine := FPreviousLine; ProcessDataLineTriplet(FPreviousLine, FLine, FLine); end; function TGearRatios.GetDataFileName: string; begin Result := 'gear_ratios.txt'; end; function TGearRatios.GetPuzzleName: string; begin Result := 'Day 3: Gear Ratios'; end; end.