Added solution for "Day 5: If You Give A Seed A Fertilizer", part 2

This commit is contained in:
Stefan Müller 2023-12-06 01:12:26 +01:00 committed by Stefan Müller
parent 474cfa6cc3
commit 26561ba421
2 changed files with 103 additions and 44 deletions

View File

@ -18,30 +18,41 @@
unit UGiveSeedFertilizer; unit UGiveSeedFertilizer;
{$mode ObjFPC}{$H+} {$mode ObjFPC}{$H+}
{$modeswitch AdvancedRecords+}
interface interface
uses uses
Classes, SysUtils, fgl, USolver; Classes, SysUtils, fgl, Math, USolver;
type type
{ TValueRange }
TValueRange = record
Part: Integer;
Start, Length: Cardinal;
class operator = (Left, Right : TValueRange): Boolean;
end;
TValueRanges = specialize TFPGList<TValueRange>;
{ TAlmanacMapRange } { TAlmanacMapRange }
TAlmanacMapRange = class TAlmanacMapRange = class
private private
FDestinationStart, FSourceStart, FSourceEnd, FDifference1, FDifference2: Cardinal; FDestinationStart, FSourceStart, FSourceLast, FLength, FDifference1, FDifference2: Cardinal;
public public
constructor Create(const ALine: string); constructor Create(const ALine: string);
constructor Create(const ADestinationStart, ASourceStart, ALength: Cardinal); constructor Create(const ADestinationStart, ASourceStart, ALength: Cardinal);
function TryConvert(const AInput: Cardinal; out OOutput: Cardinal): Boolean; procedure TryConvert(const AInput: TValueRange; const AUnconverted, AConverted: TValueRanges);
end; end;
{ TGiveSeedFertilizer } { TGiveSeedFertilizer }
TGiveSeedFertilizer = class(TSolver) TGiveSeedFertilizer = class(TSolver)
private private
FNew, FConverted, FProcessedIndices: specialize TFPGList<Cardinal>; FNew, FUnconverted, FConverted: TValueRanges;
procedure ProcessSeeds(const ALine: string); procedure ProcessSeeds(const ALine: string);
procedure ProcessMapRange(const ALine: string); procedure ProcessMapRange(const ALine: string);
procedure FinishMap; procedure FinishMap;
@ -56,6 +67,13 @@ type
implementation implementation
{ TValueRange }
class operator TValueRange. = (Left, Right: TValueRange): Boolean;
begin
Result := (Left.Part = Right.Part) and (Left.Start = Right.Start) and (Left.Length = Right.Length);
end;
{ TAlmanacMapRange } { TAlmanacMapRange }
constructor TAlmanacMapRange.Create(const ALine: string); constructor TAlmanacMapRange.Create(const ALine: string);
@ -70,12 +88,13 @@ constructor TAlmanacMapRange.Create(const ADestinationStart, ASourceStart, ALeng
begin begin
FDestinationStart := ADestinationStart; FDestinationStart := ADestinationStart;
FSourceStart := ASourceStart; FSourceStart := ASourceStart;
FLength := ALength;
// All input values are within Cardinal range, but the calculations might not be. // All input values are within Cardinal range, but the calculation results might not be.
if ASourceStart <= Cardinal.MaxValue - ALength then if ASourceStart <= Cardinal.MaxValue - ALength + 1 then
FSourceEnd := ASourceStart + ALength FSourceLast := ASourceStart + ALength - 1
else else
FSourceEnd := Cardinal.MaxValue; FSourceLast := Cardinal.MaxValue;
if ADestinationStart >= ASourceStart then if ADestinationStart >= ASourceStart then
begin begin
@ -88,19 +107,44 @@ begin
end; end;
end; end;
function TAlmanacMapRange.TryConvert(const AInput: Cardinal; out OOutput: Cardinal): Boolean; procedure TAlmanacMapRange.TryConvert(const AInput: TValueRange; const AUnconverted, AConverted: TValueRanges);
var
inputLast: Cardinal;
splitValue: TValueRange;
begin begin
if (FSourceStart <= AInput) and (AInput < FSourceEnd) then if AInput.Start <= Cardinal.MaxValue - AInput.Length + 1 then
begin inputLast := AInput.Start + AInput.Length - 1
if FDifference1 > 0 then
OOutput := AInput + FDifference1
else else
OOutput := AInput - FDifference2; inputLast := Cardinal.MaxValue;
Result := True;
end splitValue.Part := AInput.Part;
else begin
OOutput := 0; // Converts the convertible part of the value range, if any.
Result := False; if Min(FSourceLast, inputLast) >= Max(FSourceStart, AInput.Start) then
begin
splitValue.Start := Max(FSourceStart, AInput.Start);
splitValue.Length := Min(FSourceLast, inputLast) - splitValue.Start + 1;
if FDifference1 > 0 then
Inc(splitValue.Start, FDifference1)
else
Dec(splitValue.Start, FDifference2);
AConverted.Add(splitValue);
end;
// Splits off the part before the convertible part, if any.
if FSourceStart > AInput.Start then
begin
splitValue.Start := AInput.Start;
splitValue.Length := Min(FSourceStart - AInput.Start, AInput.Length);
AUnconverted.Add(splitValue);
end;
// Splits off the part after the convertible part, if any.
if inputLast > FSourceLast then
begin
splitValue.Start := Max(FSourceLast + 1, AInput.Start);
splitValue.Length := Min(inputLast - FSourceLast, AInput.Length);
AUnconverted.Add(splitValue);
end; end;
end; end;
@ -110,43 +154,51 @@ procedure TGiveSeedFertilizer.ProcessSeeds(const ALine: string);
var var
split: TStringArray; split: TStringArray;
i: Integer; i: Integer;
value: TValueRange;
begin begin
split := Aline.Split(' '); split := Aline.Split(' ');
for i := 1 to Length(split) - 1 do for i := 1 to Length(split) div 2 do
begin begin
FNew.Add(StrToDWord(split[i])); // Adds values for part 1.
value.Part := 1;
value.Length := 1;
value.Start := StrToDWord(split[2 * i - 1]);
FNew.Add(value);
value.Start := StrToDWord(split[2 * i]);
FNew.Add(value);
// Adds values for part 2.
value.Part := 2;
value.Start := StrToDWord(split[2 * i - 1]);
value.Length := StrToDWord(split[2 * i]);
FNew.Add(value);
end; end;
end; end;
procedure TGiveSeedFertilizer.ProcessMapRange(const ALine: string); procedure TGiveSeedFertilizer.ProcessMapRange(const ALine: string);
var var
range: TAlmanacMapRange; range: TAlmanacMapRange;
i: Integer; value: TValueRange;
converted: Cardinal; temp: TValueRanges;
begin begin
// Converts all "new" values that fall into this mapping range. // Tries to convert all value ranges that have not yet been converted by the current map.
range := TAlmanacMapRange.Create(ALine); range := TAlmanacMapRange.Create(ALine);
for i := 0 to FNew.Count - 1 do for value in FNew do
begin begin
if range.TryConvert(FNew[i], converted) then range.TryConvert(value, FUnconverted, FConverted);
begin
FConverted.Add(converted);
FProcessedIndices.Add(i);
end;
end; end;
range.Free; range.Free;
// Forgets the converted values. // Discards all new and moves unconverted back as new.
for i := FProcessedIndices.Count - 1 downto 0 do FNew.Clear;
begin temp := FNew;
FNew.Delete(FProcessedIndices[i]); FNew := FUnconverted;
end; FUnconverted := temp;
FProcessedIndices.Clear;
end; end;
procedure TGiveSeedFertilizer.FinishMap; procedure TGiveSeedFertilizer.FinishMap;
var var
value: Cardinal; value: TValueRange;
begin begin
// Resets the lists for the next map, remaining values stay unconverted. // Resets the lists for the next map, remaining values stay unconverted.
for value in FConverted do for value in FConverted do
@ -158,16 +210,16 @@ end;
constructor TGiveSeedFertilizer.Create; constructor TGiveSeedFertilizer.Create;
begin begin
FNew := specialize TFPGList<Cardinal>.Create; FNew := TValueRanges.Create;
FConverted := specialize TFPGList<Cardinal>.Create; FUnconverted := TValueRanges.Create;
FProcessedIndices := specialize TFPGList<Cardinal>.Create; FConverted := TValueRanges.Create;
end; end;
destructor TGiveSeedFertilizer.Destroy; destructor TGiveSeedFertilizer.Destroy;
begin begin
FNew.Free; FNew.Free;
FUnconverted.Free;
FConverted.Free; FConverted.Free;
FProcessedIndices.Free;
inherited Destroy; inherited Destroy;
end; end;
@ -183,13 +235,20 @@ end;
procedure TGiveSeedFertilizer.Finish; procedure TGiveSeedFertilizer.Finish;
var var
value: Cardinal; value: TValueRange;
begin begin
FinishMap; FinishMap;
for value in FNew do for value in FNew do
begin begin
if (FPart1 = 0) or (value < FPart1) then if value.Part = 1 then
FPart1 := value; begin
if (FPart1 = 0) or (value.Start < FPart1) then
FPart1 := value.Start;
end
else begin
if (FPart2 = 0) or (value.Start < FPart2) then
FPart2 := value.Start;
end;
end; end;
end; end;

View File

@ -62,7 +62,7 @@ end;
procedure TGiveSeedFertilizerFullDataTestCase.TestPart2; procedure TGiveSeedFertilizerFullDataTestCase.TestPart2;
begin begin
AssertEquals(-1, FSolver.GetResultPart2); AssertEquals(99751240, FSolver.GetResultPart2);
end; end;
{ TGiveSeedFertilizerExampleTestCase } { TGiveSeedFertilizerExampleTestCase }
@ -79,7 +79,7 @@ end;
procedure TGiveSeedFertilizerExampleTestCase.TestPart2; procedure TGiveSeedFertilizerExampleTestCase.TestPart2;
begin begin
AssertEquals(-1, FSolver.GetResultPart2); AssertEquals(46, FSolver.GetResultPart2);
end; end;
initialization initialization