{ 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 UGiveSeedFertilizer; {$mode ObjFPC}{$H+} interface uses Classes, SysUtils, fgl, USolver; type { TAlmanacMapRange } TAlmanacMapRange = class private FDestinationStart, FSourceStart, FSourceEnd, FDifference1, FDifference2: Cardinal; public constructor Create(const ALine: string); constructor Create(const ADestinationStart, ASourceStart, ALength: Cardinal); function TryConvert(const AInput: Cardinal; out OOutput: Cardinal): Boolean; end; { TGiveSeedFertilizer } TGiveSeedFertilizer = class(TSolver) private FNew, FConverted, FProcessedIndices: specialize TFPGList; procedure ProcessSeeds(const ALine: string); procedure ProcessMapRange(const ALine: string); procedure FinishMap; 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 { TAlmanacMapRange } constructor TAlmanacMapRange.Create(const ALine: string); var split: TStringArray; begin split := ALine.Split(' '); Create(StrToDWord(split[0]), StrToDWord(split[1]), StrToDWord(split[2])); end; constructor TAlmanacMapRange.Create(const ADestinationStart, ASourceStart, ALength: Cardinal); begin FDestinationStart := ADestinationStart; FSourceStart := ASourceStart; // All input values are within Cardinal range, but the calculations might not be. if ASourceStart <= Cardinal.MaxValue - ALength then FSourceEnd := ASourceStart + ALength else FSourceEnd := Cardinal.MaxValue; if ADestinationStart >= ASourceStart then begin FDifference1 := ADestinationStart - ASourceStart; FDifference2 := 0; end else begin FDifference1 := 0; FDifference2 := ASourceStart - ADestinationStart; end; end; function TAlmanacMapRange.TryConvert(const AInput: Cardinal; out OOutput: Cardinal): Boolean; begin if (FSourceStart <= AInput) and (AInput < FSourceEnd) then begin if FDifference1 > 0 then OOutput := AInput + FDifference1 else OOutput := AInput - FDifference2; Result := True; end else begin OOutput := 0; Result := False; end; end; { TGiveSeedFertilizer } procedure TGiveSeedFertilizer.ProcessSeeds(const ALine: string); var split: TStringArray; i: Integer; begin split := Aline.Split(' '); for i := 1 to Length(split) - 1 do begin FNew.Add(StrToDWord(split[i])); end; end; procedure TGiveSeedFertilizer.ProcessMapRange(const ALine: string); var range: TAlmanacMapRange; i: Integer; converted: Cardinal; begin // Converts all "new" values that fall into this mapping range. range := TAlmanacMapRange.Create(ALine); for i := 0 to FNew.Count - 1 do begin if range.TryConvert(FNew[i], converted) then begin FConverted.Add(converted); FProcessedIndices.Add(i); end; end; range.Free; // Forgets the converted values. for i := FProcessedIndices.Count - 1 downto 0 do begin FNew.Delete(FProcessedIndices[i]); end; FProcessedIndices.Clear; end; procedure TGiveSeedFertilizer.FinishMap; var value: Cardinal; begin // Resets the lists for the next map, remaining values stay unconverted. for value in FConverted do begin FNew.Add(value); end; FConverted.Clear; end; constructor TGiveSeedFertilizer.Create; begin FNew := specialize TFPGList.Create; FConverted := specialize TFPGList.Create; FProcessedIndices := specialize TFPGList.Create; end; destructor TGiveSeedFertilizer.Destroy; begin FNew.Free; FConverted.Free; FProcessedIndices.Free; inherited Destroy; end; procedure TGiveSeedFertilizer.ProcessDataLine(const ALine: string); begin if LeftStr(ALine, 6) = 'seeds:' then ProcessSeeds(ALine) else if RightStr(ALine, 4) = 'map:' then FinishMap else if Aline <> '' then ProcessMapRange(ALine); end; procedure TGiveSeedFertilizer.Finish; var value: Cardinal; begin FinishMap; for value in FNew do begin if (FPart1 = 0) or (value < FPart1) then FPart1 := value; end; end; function TGiveSeedFertilizer.GetDataFileName: string; begin Result := 'give_seed_fertilizer.txt'; end; function TGiveSeedFertilizer.GetPuzzleName: string; begin Result := 'Day 5: If You Give A Seed A Fertilizer'; end; end.