{ 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 ADestinationStart, ASourceStart, ALength: Cardinal); function TryConvert(const AInput: Cardinal; out OOutput: Cardinal): Boolean; end; { TAlmanacMap } TAlmanacMap = class private FRanges: specialize TFPGObjectList; public constructor Create; destructor Destroy; override; procedure AddMapRange(const ALine: string); function Convert(const AInput: Cardinal): Cardinal; end; { TGiveSeedFertilizer } TGiveSeedFertilizer = class(TSolver) private FSeeds: specialize TFPGList; FMaps: specialize TFPGObjectList; procedure ProcessSeeds(const ALine: string); 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 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; { TAlmanacMap } constructor TAlmanacMap.Create; begin FRanges := specialize TFPGObjectList.Create; end; destructor TAlmanacMap.Destroy; begin FRanges.Free; inherited Destroy; end; procedure TAlmanacMap.AddMapRange(const ALine: string); var split: TStringArray; begin split := ALine.Split(' '); FRanges.Add(TAlmanacMapRange.Create(StrToDWord(split[0]), StrToDWord(split[1]), StrToDWord(split[2]))); end; function TAlmanacMap.Convert(const AInput: Cardinal): Cardinal; var range: TAlmanacMapRange; begin for range in FRanges do begin if range.TryConvert(AInput, Result) then Exit; end; Result := AInput; 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 FSeeds.Add(StrToDWord(split[i])); end; end; constructor TGiveSeedFertilizer.Create; begin FSeeds := specialize TFPGList.Create; FMaps := specialize TFPGObjectList.Create; end; destructor TGiveSeedFertilizer.Destroy; begin FSeeds.Free; FMaps.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 FMaps.Add(TAlmanacMap.Create) else if Aline <> '' then FMaps.Last.AddMapRange(ALine); end; procedure TGiveSeedFertilizer.Finish; var seed, value: Cardinal; map: TAlmanacMap; begin for seed in FSeeds do begin value := seed; for map in FMaps do begin value := map.Convert(value); end; 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.