Added solution for "Day 5: If You Give A Seed A Fertilizer", part 2
This commit is contained in:
parent
474cfa6cc3
commit
26561ba421
|
@ -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
|
||||||
|
inputLast := AInput.Start + AInput.Length - 1
|
||||||
|
else
|
||||||
|
inputLast := Cardinal.MaxValue;
|
||||||
|
|
||||||
|
splitValue.Part := AInput.Part;
|
||||||
|
|
||||||
|
// Converts the convertible part of the value range, if any.
|
||||||
|
if Min(FSourceLast, inputLast) >= Max(FSourceStart, AInput.Start) then
|
||||||
begin
|
begin
|
||||||
|
splitValue.Start := Max(FSourceStart, AInput.Start);
|
||||||
|
splitValue.Length := Min(FSourceLast, inputLast) - splitValue.Start + 1;
|
||||||
if FDifference1 > 0 then
|
if FDifference1 > 0 then
|
||||||
OOutput := AInput + FDifference1
|
Inc(splitValue.Start, FDifference1)
|
||||||
else
|
else
|
||||||
OOutput := AInput - FDifference2;
|
Dec(splitValue.Start, FDifference2);
|
||||||
Result := True;
|
AConverted.Add(splitValue);
|
||||||
end
|
end;
|
||||||
else begin
|
|
||||||
OOutput := 0;
|
// Splits off the part before the convertible part, if any.
|
||||||
Result := False;
|
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;
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
Loading…
Reference in New Issue