Merged solution for "Day 12: Hot Springs", part 2
This commit is contained in:
commit
f61abcb9c3
|
@ -157,6 +157,14 @@
|
||||||
<Filename Value="UCommon.pas"/>
|
<Filename Value="UCommon.pas"/>
|
||||||
<IsPartOfProject Value="True"/>
|
<IsPartOfProject Value="True"/>
|
||||||
</Unit>
|
</Unit>
|
||||||
|
<Unit>
|
||||||
|
<Filename Value="UMultiIndexEnumerator.pas"/>
|
||||||
|
<IsPartOfProject Value="True"/>
|
||||||
|
</Unit>
|
||||||
|
<Unit>
|
||||||
|
<Filename Value="UBinomialCoefficients.pas"/>
|
||||||
|
<IsPartOfProject Value="True"/>
|
||||||
|
</Unit>
|
||||||
</Units>
|
</Units>
|
||||||
</ProjectOptions>
|
</ProjectOptions>
|
||||||
<CompilerOptions>
|
<CompilerOptions>
|
||||||
|
|
|
@ -0,0 +1,105 @@
|
||||||
|
{
|
||||||
|
Solutions to the Advent Of Code.
|
||||||
|
Copyright (C) 2024 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 <http://www.gnu.org/licenses/>.
|
||||||
|
}
|
||||||
|
|
||||||
|
unit UBinomialCoefficients;
|
||||||
|
|
||||||
|
{$mode ObjFPC}{$H+}
|
||||||
|
|
||||||
|
interface
|
||||||
|
|
||||||
|
uses
|
||||||
|
Classes, SysUtils, Generics.Collections;
|
||||||
|
|
||||||
|
type
|
||||||
|
TCardinalArray = array of Cardinal;
|
||||||
|
TCardinalArrays = specialize TList<TCardinalArray>;
|
||||||
|
|
||||||
|
{ TBinomialCoefficientCache }
|
||||||
|
|
||||||
|
TBinomialCoefficientCache = class
|
||||||
|
private
|
||||||
|
FCache: TCardinalArrays;
|
||||||
|
procedure AddRow;
|
||||||
|
public
|
||||||
|
constructor Create;
|
||||||
|
destructor Destroy; override;
|
||||||
|
// Returns N choose K, with N >= K >= 0.
|
||||||
|
function Get(const AN, AK: Cardinal): Cardinal;
|
||||||
|
// Returns the number of cached rows C = N + 1, where N is the highest from previously queried "N choose K". The
|
||||||
|
// actual number of cached binomial coefficient values is C * (C + 1) / 2.
|
||||||
|
function GetCachedRowsCount: Cardinal;
|
||||||
|
end;
|
||||||
|
|
||||||
|
var
|
||||||
|
BinomialCoefficients: TBinomialCoefficientCache;
|
||||||
|
|
||||||
|
implementation
|
||||||
|
|
||||||
|
{ TBinomialCoefficientCache }
|
||||||
|
|
||||||
|
procedure TBinomialCoefficientCache.AddRow;
|
||||||
|
var
|
||||||
|
row: TCardinalArray;
|
||||||
|
i: Cardinal;
|
||||||
|
begin
|
||||||
|
SetLength(row, FCache.Count + 1);
|
||||||
|
row[0] := 1;
|
||||||
|
if FCache.Count > 0 then
|
||||||
|
begin
|
||||||
|
row[FCache.Count] := 1;
|
||||||
|
for i := 1 to FCache.Count - 1 do
|
||||||
|
row[i] := FCache.Last[i - 1] + FCache.Last[i];
|
||||||
|
end;
|
||||||
|
FCache.Add(row);
|
||||||
|
end;
|
||||||
|
|
||||||
|
constructor TBinomialCoefficientCache.Create;
|
||||||
|
begin
|
||||||
|
FCache := TCardinalArrays.Create;
|
||||||
|
end;
|
||||||
|
|
||||||
|
destructor TBinomialCoefficientCache.Destroy;
|
||||||
|
begin
|
||||||
|
FCache.Free;
|
||||||
|
inherited Destroy;
|
||||||
|
end;
|
||||||
|
|
||||||
|
function TBinomialCoefficientCache.Get(const AN, AK: Cardinal): Cardinal;
|
||||||
|
var
|
||||||
|
i: Cardinal;
|
||||||
|
begin
|
||||||
|
if AN < AK then
|
||||||
|
raise ERangeError.Create('Cannot calculate binomial coefficient "n choose k" with k larger than n.');
|
||||||
|
|
||||||
|
for i := FCache.Count to AN do
|
||||||
|
AddRow;
|
||||||
|
Result := FCache[AN][AK];
|
||||||
|
end;
|
||||||
|
|
||||||
|
function TBinomialCoefficientCache.GetCachedRowsCount: Cardinal;
|
||||||
|
begin
|
||||||
|
Result := FCache.Count;
|
||||||
|
end;
|
||||||
|
|
||||||
|
initialization
|
||||||
|
BinomialCoefficients := TBinomialCoefficientCache.Create;
|
||||||
|
|
||||||
|
finalization
|
||||||
|
BinomialCoefficients.Free;
|
||||||
|
|
||||||
|
end.
|
||||||
|
|
|
@ -40,6 +40,7 @@ const
|
||||||
CPCardinalDirections: array[0..3] of PPoint = (@CDirectionRight, @CDirectionDown, @CDirectionLeft, @CDirectionUp);
|
CPCardinalDirections: array[0..3] of PPoint = (@CDirectionRight, @CDirectionDown, @CDirectionLeft, @CDirectionUp);
|
||||||
|
|
||||||
type
|
type
|
||||||
|
TInt64Array = array of Int64;
|
||||||
TIntegerList = specialize TList<Integer>;
|
TIntegerList = specialize TList<Integer>;
|
||||||
TPoints = specialize TList<TPoint>;
|
TPoints = specialize TList<TPoint>;
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,161 @@
|
||||||
|
{
|
||||||
|
Solutions to the Advent Of Code.
|
||||||
|
Copyright (C) 2024 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 <http://www.gnu.org/licenses/>.
|
||||||
|
}
|
||||||
|
|
||||||
|
unit UMultiIndexEnumerator;
|
||||||
|
|
||||||
|
{$mode ObjFPC}{$H+}
|
||||||
|
|
||||||
|
interface
|
||||||
|
|
||||||
|
uses
|
||||||
|
Classes, SysUtils;
|
||||||
|
|
||||||
|
type
|
||||||
|
TIndexArray = array of Integer;
|
||||||
|
|
||||||
|
TIndexValidationResult = (ivrValid, ivrSkip, ivrBacktrack);
|
||||||
|
|
||||||
|
TEnumerableMultiIndexStrategy = class;
|
||||||
|
|
||||||
|
{ TMultiIndexEnumerator }
|
||||||
|
|
||||||
|
TMultiIndexEnumerator = class(TInterfacedObject, specialize IEnumerator<TIndexArray>)
|
||||||
|
private
|
||||||
|
FStrategy: TEnumerableMultiIndexStrategy;
|
||||||
|
FCurrent: TIndexArray;
|
||||||
|
FMustInit: Boolean;
|
||||||
|
function UpdateArray(const AInit: Boolean): Boolean;
|
||||||
|
public
|
||||||
|
constructor Create(const AStrategy: TEnumerableMultiIndexStrategy);
|
||||||
|
function GetCurrent: TIndexArray;
|
||||||
|
function MoveNext: Boolean;
|
||||||
|
procedure Reset;
|
||||||
|
property Current: TIndexArray read GetCurrent;
|
||||||
|
end;
|
||||||
|
|
||||||
|
{ TEnumerableMultiIndexStrategy }
|
||||||
|
|
||||||
|
TEnumerableMultiIndexStrategy = class(TInterfacedObject, specialize IEnumerable<TIndexArray>)
|
||||||
|
public
|
||||||
|
function GetEnumerator: specialize IEnumerator<TIndexArray>;
|
||||||
|
// Returns the number of indices to iterate over, must return positive (non-zero) value.
|
||||||
|
function GetCardinality: Integer; virtual; abstract;
|
||||||
|
function TryGetStartIndexValue(constref ACurrentIndexArray: TIndexArray; const ACurrentIndex: Integer;
|
||||||
|
out AStartIndexValue: Integer): Boolean; virtual; abstract;
|
||||||
|
function ValidateIndexValue(constref ACurrentIndexArray: TIndexArray; const ACurrentIndex: Integer):
|
||||||
|
TIndexValidationResult; virtual; abstract;
|
||||||
|
end;
|
||||||
|
|
||||||
|
implementation
|
||||||
|
|
||||||
|
{ TMultiIndexEnumerator }
|
||||||
|
|
||||||
|
function TMultiIndexEnumerator.UpdateArray(const AInit: Boolean): Boolean;
|
||||||
|
var
|
||||||
|
i, initialized: Integer;
|
||||||
|
r: TIndexValidationResult;
|
||||||
|
begin
|
||||||
|
if AInit then
|
||||||
|
begin
|
||||||
|
i := 0;
|
||||||
|
initialized := -1;
|
||||||
|
end
|
||||||
|
else begin
|
||||||
|
i := Length(FCurrent) - 1;
|
||||||
|
initialized := i;
|
||||||
|
end;
|
||||||
|
|
||||||
|
while i < Length(FCurrent) do
|
||||||
|
begin
|
||||||
|
if initialized < i then
|
||||||
|
begin
|
||||||
|
// Checks whether start index value can be set, and backtracks or aborts if not.
|
||||||
|
if not FStrategy.TryGetStartIndexValue(FCurrent, i, FCurrent[i]) then
|
||||||
|
if i > 0 then
|
||||||
|
begin
|
||||||
|
Dec(i);
|
||||||
|
Continue;
|
||||||
|
end
|
||||||
|
else begin
|
||||||
|
Result := False;
|
||||||
|
Exit;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
else
|
||||||
|
// Sets next candidate for current index value.
|
||||||
|
Inc(FCurrent[i]);
|
||||||
|
|
||||||
|
// Checks if current index value is valid, and increases it until it is, or backtracks or aborts if so indicated.
|
||||||
|
while True do
|
||||||
|
begin
|
||||||
|
r := FStrategy.ValidateIndexValue(FCurrent, i);
|
||||||
|
case r of
|
||||||
|
ivrValid: begin
|
||||||
|
initialized := i;
|
||||||
|
Inc(i);
|
||||||
|
Break;
|
||||||
|
end;
|
||||||
|
ivrSkip:
|
||||||
|
Inc(FCurrent[i]);
|
||||||
|
ivrBacktrack:
|
||||||
|
if i > 0 then
|
||||||
|
begin
|
||||||
|
Dec(i);
|
||||||
|
Break;
|
||||||
|
end
|
||||||
|
else begin
|
||||||
|
Result := False;
|
||||||
|
Exit;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
Result := True;
|
||||||
|
end;
|
||||||
|
|
||||||
|
constructor TMultiIndexEnumerator.Create(const AStrategy: TEnumerableMultiIndexStrategy);
|
||||||
|
begin
|
||||||
|
FStrategy := AStrategy;
|
||||||
|
SetLength(FCurrent, FStrategy.GetCardinality);
|
||||||
|
Reset;
|
||||||
|
end;
|
||||||
|
|
||||||
|
function TMultiIndexEnumerator.GetCurrent: TIndexArray;
|
||||||
|
begin
|
||||||
|
Result := FCurrent;
|
||||||
|
end;
|
||||||
|
|
||||||
|
function TMultiIndexEnumerator.MoveNext: Boolean;
|
||||||
|
begin
|
||||||
|
Result := UpdateArray(FMustInit);
|
||||||
|
FMustInit := False;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TMultiIndexEnumerator.Reset;
|
||||||
|
begin
|
||||||
|
FMustInit := True;
|
||||||
|
end;
|
||||||
|
|
||||||
|
{ TEnumerableMultiIndexStrategy }
|
||||||
|
|
||||||
|
function TEnumerableMultiIndexStrategy.GetEnumerator: specialize IEnumerator<TIndexArray>;
|
||||||
|
begin
|
||||||
|
Result := TMultiIndexEnumerator.Create(Self);
|
||||||
|
end;
|
||||||
|
|
||||||
|
end.
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
Solutions to the Advent Of Code.
|
Solutions to the Advent Of Code.
|
||||||
Copyright (C) 2023 Stefan Müller
|
Copyright (C) 2023-2024 Stefan Müller
|
||||||
|
|
||||||
This program is free software: you can redistribute it and/or modify it under
|
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
|
the terms of the GNU General Public License as published by the Free Software
|
||||||
|
@ -22,27 +22,176 @@ unit UHotSprings;
|
||||||
interface
|
interface
|
||||||
|
|
||||||
uses
|
uses
|
||||||
Classes, SysUtils, Generics.Collections, USolver;
|
Classes, SysUtils, Math, Generics.Collections, USolver, UCommon, UMultiIndexEnumerator, UBinomialCoefficients;
|
||||||
|
|
||||||
const
|
const
|
||||||
COperationalChar = '.';
|
COperationalChar = '.';
|
||||||
CDamagedChar = '#';
|
CDamagedChar = '#';
|
||||||
CWildcardChar = '?';
|
CWildcardChar = '?';
|
||||||
COperationalPatternChars = [COperationalChar, CWildcardChar];
|
CPart2Repetition = 5;
|
||||||
CDamagedPatternChars = [CDamagedChar, CWildcardChar];
|
|
||||||
|
|
||||||
type
|
type
|
||||||
|
TValidationLengths = array of array of Integer;
|
||||||
|
|
||||||
|
{ TDamage }
|
||||||
|
|
||||||
|
TDamage = record
|
||||||
|
Start, Length, CharsRemaining: Integer;
|
||||||
|
end;
|
||||||
|
TDamages = specialize TList<TDamage>;
|
||||||
|
|
||||||
|
TBlockCombinationsCache = specialize THashMap<Int64, Int64>;
|
||||||
|
TCombinationsCache = specialize TObjectHashMap<string, TBlockCombinationsCache>;
|
||||||
|
|
||||||
|
{ TBlock }
|
||||||
|
|
||||||
|
TBlock = class
|
||||||
|
private
|
||||||
|
FPattern: string;
|
||||||
|
FDamages: TDamages;
|
||||||
|
FCombinationsCache: TBlockCombinationsCache;
|
||||||
|
procedure ParseDamages;
|
||||||
|
public
|
||||||
|
constructor Create(const APattern: string; constref ACombinationsCache: TBlockCombinationsCache);
|
||||||
|
destructor Destroy; override;
|
||||||
|
property Pattern: string read FPattern;
|
||||||
|
// List of damages in this block, containing exactly one entry for each sequence of consecutive damage characters in
|
||||||
|
// the block's pattern, ordered such that a damage with lower index is further left.
|
||||||
|
// For example, if Pattern is '??##?#?', then Damages would have 2 entries.
|
||||||
|
property Damages: TDamages read FDamages;
|
||||||
|
property CombinationsCache: TBlockCombinationsCache read FCombinationsCache;
|
||||||
|
end;
|
||||||
|
TBlocks = specialize TObjectList<TBlock>;
|
||||||
|
|
||||||
|
{ TAccumulatedCombinationsMultiIndexStrategy }
|
||||||
|
|
||||||
|
// Adds accumulated combinations to the enumerable strategy to allow calculation of combinations on the fly, and
|
||||||
|
// therefore early rejection of invalid multi-index configurations.
|
||||||
|
TAccumulatedCombinationsMultiIndexStrategy = class(TEnumerableMultiIndexStrategy)
|
||||||
|
private
|
||||||
|
FAccumulatedCombinations: TInt64Array;
|
||||||
|
protected
|
||||||
|
function CalcCombinations(constref ACurrentIndexArray: TIndexArray; const ACurrentIndex: Integer): Int64; virtual;
|
||||||
|
abstract;
|
||||||
|
function UpdateCombinations(const AValidationResult: TIndexValidationResult; constref ACurrentIndexArray:
|
||||||
|
TIndexArray; const ACurrentIndex: Integer): TIndexValidationResult;
|
||||||
|
public
|
||||||
|
function GetCombinations: Int64;
|
||||||
|
end;
|
||||||
|
|
||||||
|
TConditionRecord = class;
|
||||||
|
|
||||||
|
{ TValidationsToBlockAssignments }
|
||||||
|
|
||||||
|
// Enumerable strategy that enumerates all valid assignments of ranges of validation numbers to individual blocks in
|
||||||
|
// the form of start and stop indices.
|
||||||
|
TValidationsToBlockAssignments = class(TAccumulatedCombinationsMultiIndexStrategy)
|
||||||
|
private
|
||||||
|
FConditionRecord: TConditionRecord;
|
||||||
|
protected
|
||||||
|
function CalcCombinations(constref ACurrentIndexArray: TIndexArray; const ACurrentIndex: Integer): Int64; override;
|
||||||
|
public
|
||||||
|
constructor Create(constref AConditionRecord: TConditionRecord);
|
||||||
|
function GetCardinality: Integer; override;
|
||||||
|
function TryGetStartIndexValue(constref ACurrentIndexArray: TIndexArray; const ACurrentIndex: Integer;
|
||||||
|
out AStartIndexValue: Integer): Boolean; override;
|
||||||
|
function ValidateIndexValue(constref ACurrentIndexArray: TIndexArray; const ACurrentIndex: Integer):
|
||||||
|
TIndexValidationResult; override;
|
||||||
|
end;
|
||||||
|
|
||||||
|
{ TDamageToValidationAssignments }
|
||||||
|
|
||||||
|
// Enumerable strategy that enumerates all valid assignments of each damage in the block to a specific validation
|
||||||
|
// number from the validation numbers that have been assigned to the block, as indicated by start and stop indices.
|
||||||
|
TDamageToValidationAssignments = class(TEnumerableMultiIndexStrategy)
|
||||||
|
private
|
||||||
|
FConditionRecord: TConditionRecord;
|
||||||
|
FBlock: TBlock;
|
||||||
|
FValidationStartIndex, FValidationStopIndex: Integer;
|
||||||
|
// Calculates "span", the length of all damages for one validation number combined.
|
||||||
|
function CalcValidationSpan(constref ACurrentIndexArray: TIndexArray; const ALastDamageIndex, AValidationNumber:
|
||||||
|
Integer): Integer;
|
||||||
|
public
|
||||||
|
constructor Create(constref AConditionRecord: TConditionRecord; constref ABlock: TBlock;
|
||||||
|
const AStartValidationIndex, AStopValidationIndex: Integer);
|
||||||
|
function GetCardinality: Integer; override;
|
||||||
|
function TryGetStartIndexValue(constref ACurrentIndexArray: TIndexArray; const ACurrentIndex: Integer;
|
||||||
|
out AStartIndexValue: Integer): Boolean; override;
|
||||||
|
function ValidateIndexValue(constref ACurrentIndexArray: TIndexArray; const ACurrentIndex: Integer):
|
||||||
|
TIndexValidationResult; override;
|
||||||
|
end;
|
||||||
|
|
||||||
|
{ TValidationPositionInfo }
|
||||||
|
|
||||||
|
TValidationPositionInfo = record
|
||||||
|
ValidationIndex, MinStart, MaxStart: Integer;
|
||||||
|
end;
|
||||||
|
|
||||||
|
TValidationPositionInfos = specialize TList<TValidationPositionInfo>;
|
||||||
|
|
||||||
|
{ TValidationPositionOffsets }
|
||||||
|
|
||||||
|
// Enumerable strategy that enumerates all valid assignments of start positions (positions mean character indices in
|
||||||
|
// the block patterns) of validation numbers that have been assigned to damages in the current block, as indicated by
|
||||||
|
// provided TValidationPositionInfos.
|
||||||
|
TValidationPositionOffsets = class(TAccumulatedCombinationsMultiIndexStrategy)
|
||||||
|
private
|
||||||
|
FConditionRecord: TConditionRecord;
|
||||||
|
FPositionInfos: TValidationPositionInfos;
|
||||||
|
FBlockLength, FValidationStartIndex, FValidationStopIndex: Integer;
|
||||||
|
protected
|
||||||
|
function CalcCombinations(constref ACurrentIndexArray: TIndexArray; const ACurrentIndex: Integer): Int64; override;
|
||||||
|
public
|
||||||
|
constructor Create(constref AConditionRecord: TConditionRecord; constref APositionInfos: TValidationPositionInfos;
|
||||||
|
const ABlockLength, AValidationStartIndex, AValidationStopIndex: Integer);
|
||||||
|
function GetCardinality: Integer; override;
|
||||||
|
function TryGetStartIndexValue(constref ACurrentIndexArray: TIndexArray; const ACurrentIndex: Integer;
|
||||||
|
out AStartIndexValue: Integer): Boolean; override;
|
||||||
|
function ValidateIndexValue(constref ACurrentIndexArray: TIndexArray; const ACurrentIndex: Integer):
|
||||||
|
TIndexValidationResult; override;
|
||||||
|
end;
|
||||||
|
|
||||||
|
{ TConditionRecord }
|
||||||
|
|
||||||
|
TConditionRecord = class
|
||||||
|
private
|
||||||
|
// List of validation numbers as stated in the problem input.
|
||||||
|
FValidation: TIntegerList;
|
||||||
|
// List of non-empty, maximum-length parts of the pattern without operational springs ("blocks").
|
||||||
|
FBlocks: TBlocks;
|
||||||
|
// Array 'a' of accumulated validation series lengths. 'a[i, j]' denotes the combined length of consecutive
|
||||||
|
// validation numbers from 'FValidation[i]' to 'FValidation[j - 1]' with a single space in between each pair of
|
||||||
|
// them.
|
||||||
|
FValidationLengths: TValidationLengths;
|
||||||
|
// Array 'a' of minimum indices 'a[i]', such that all remaining validation numbers starting at index 'a[i] - 1'
|
||||||
|
// cannot fit into the remaining blocks starting at 'FBlocks[i]'.
|
||||||
|
FMinIndices: TIndexArray;
|
||||||
|
FCombinationsCache: TCombinationsCache;
|
||||||
|
procedure InitValidationLengths;
|
||||||
|
procedure InitMinIndices;
|
||||||
|
function CalcCombinationsBlockSingleValidation(constref ABlock: TBlock; const AIndex: Integer): Int64;
|
||||||
|
function CalcCombinationsBlockMultiValidations(constref ABlock: TBlock; constref AIndices: TIndexArray;
|
||||||
|
const AStartIndex, AStopIndex: Integer): Int64;
|
||||||
|
function CalcValidationsId(const AStartIndex, AStopIndex: Integer): Int64;
|
||||||
|
public
|
||||||
|
constructor Create(constref ACombinationsCache: TCombinationsCache);
|
||||||
|
destructor Destroy; override;
|
||||||
|
// Adds all non-empty, maximum-length parts of the pattern without operational springs ("blocks").
|
||||||
|
procedure AddBlocks(const APattern: string);
|
||||||
|
function GenerateBlockAssignments: Int64;
|
||||||
|
function CalcCombinationsBlock(constref ABlock: TBlock; const AStartIndex, AStopIndex: Integer): Int64;
|
||||||
|
function CalcCombinationsWildcardSequence(const ASequenceLength, AStartIndex, AStopIndex: Integer): Int64;
|
||||||
|
property Validation: TIntegerList read FValidation;
|
||||||
|
property Blocks: TBlocks read FBlocks;
|
||||||
|
property ValidationLengths: TValidationLengths read FValidationLengths;
|
||||||
|
property MinIndices: TIndexArray read FMinIndices;
|
||||||
|
end;
|
||||||
|
|
||||||
{ THotSprings }
|
{ THotSprings }
|
||||||
|
|
||||||
THotSprings = class(TSolver)
|
THotSprings = class(TSolver)
|
||||||
private
|
private
|
||||||
FValidation: specialize TList<Integer>;
|
FCombinationsCache: TCombinationsCache;
|
||||||
FSpringPattern: string;
|
|
||||||
procedure ExtendArrangement(const AArrangement: string; const ARemainingFreeOperationalCount, ACurrentValidationIndex:
|
|
||||||
Integer);
|
|
||||||
function TryAppendOperationalChar(var AArrangement: string): Boolean;
|
|
||||||
function TryAppendValidationBlock(var AArrangement: string; const ALength: Integer): Boolean;
|
|
||||||
public
|
public
|
||||||
constructor Create;
|
constructor Create;
|
||||||
destructor Destroy; override;
|
destructor Destroy; override;
|
||||||
|
@ -54,99 +203,549 @@ type
|
||||||
|
|
||||||
implementation
|
implementation
|
||||||
|
|
||||||
{ THotSprings }
|
{ TBlock }
|
||||||
|
|
||||||
procedure THotSprings.ExtendArrangement(const AArrangement: string; const ARemainingFreeOperationalCount,
|
procedure TBlock.ParseDamages;
|
||||||
ACurrentValidationIndex: Integer);
|
|
||||||
var
|
|
||||||
match: Boolean;
|
|
||||||
temp: string;
|
|
||||||
begin
|
|
||||||
if Length(AArrangement) = Length(FSpringPattern) then
|
|
||||||
Inc(FPart1)
|
|
||||||
else begin
|
|
||||||
temp := AArrangement;
|
|
||||||
// Tries to append a dot (operational) to the current arrangement.
|
|
||||||
if (ARemainingFreeOperationalCount > 0) and TryAppendOperationalChar(temp) then
|
|
||||||
begin
|
|
||||||
ExtendArrangement(temp, ARemainingFreeOperationalCount - 1, ACurrentValidationIndex);
|
|
||||||
end;
|
|
||||||
|
|
||||||
// Tries to append the current validation block (damaged) to the current arrangement.
|
|
||||||
if ACurrentValidationIndex < FValidation.Count then
|
|
||||||
begin
|
|
||||||
temp := AArrangement;
|
|
||||||
match := TryAppendValidationBlock(temp, FValidation[ACurrentValidationIndex]);
|
|
||||||
|
|
||||||
// ... and the mandatory dot after the block, if it is not the last block.
|
|
||||||
if match
|
|
||||||
and (ACurrentValidationIndex < FValidation.Count - 1)
|
|
||||||
and not TryAppendOperationalChar(temp) then
|
|
||||||
match := False;
|
|
||||||
|
|
||||||
if match then
|
|
||||||
ExtendArrangement(temp, ARemainingFreeOperationalCount, ACurrentValidationIndex + 1);
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
|
|
||||||
function THotSprings.TryAppendOperationalChar(var AArrangement: string): Boolean;
|
|
||||||
begin
|
|
||||||
if FSpringPattern[Length(AArrangement) + 1] in COperationalPatternChars then
|
|
||||||
begin
|
|
||||||
AArrangement := AArrangement + COperationalChar;
|
|
||||||
Result := True;
|
|
||||||
end
|
|
||||||
else
|
|
||||||
Result := False;
|
|
||||||
end;
|
|
||||||
|
|
||||||
function THotSprings.TryAppendValidationBlock(var AArrangement: string; const ALength: Integer): Boolean;
|
|
||||||
var
|
var
|
||||||
i, len: Integer;
|
i, len: Integer;
|
||||||
|
damage: TDamage;
|
||||||
begin
|
begin
|
||||||
Result := True;
|
FDamages := TDamages.Create;
|
||||||
len := Length(AArrangement);
|
damage.Length := 0;
|
||||||
for i := 1 to ALength do
|
len := Length(FPattern);
|
||||||
|
for i := 1 to len do
|
||||||
|
// The pattern must only contain damage and wildcard characters here.
|
||||||
|
if FPattern[i] = CDamagedChar then
|
||||||
begin
|
begin
|
||||||
if FSpringPattern[len + i] in CDamagedPatternChars then
|
if damage.Length = 0 then
|
||||||
AArrangement := AArrangement + CDamagedChar
|
damage.Start := i;
|
||||||
|
Inc(damage.Length);
|
||||||
|
end
|
||||||
|
else if damage.Length > 0 then
|
||||||
|
begin
|
||||||
|
damage.CharsRemaining := len - damage.Start - damage.Length + 1;
|
||||||
|
FDamages.Add(damage);
|
||||||
|
damage.Length := 0;
|
||||||
|
end;
|
||||||
|
|
||||||
|
if damage.Length > 0 then
|
||||||
|
begin
|
||||||
|
damage.CharsRemaining := 0;
|
||||||
|
FDamages.Add(damage);
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
constructor TBlock.Create(const APattern: string; constref ACombinationsCache: TBlockCombinationsCache);
|
||||||
|
begin
|
||||||
|
FPattern := APattern;
|
||||||
|
FCombinationsCache := ACombinationsCache;
|
||||||
|
ParseDamages;
|
||||||
|
end;
|
||||||
|
|
||||||
|
destructor TBlock.Destroy;
|
||||||
|
begin
|
||||||
|
FDamages.Free;
|
||||||
|
inherited Destroy;
|
||||||
|
end;
|
||||||
|
|
||||||
|
{ TAccumulatedCombinationsMultiIndexStrategy }
|
||||||
|
|
||||||
|
function TAccumulatedCombinationsMultiIndexStrategy.UpdateCombinations(const AValidationResult: TIndexValidationResult;
|
||||||
|
constref ACurrentIndexArray: TIndexArray; const ACurrentIndex: Integer): TIndexValidationResult;
|
||||||
|
var
|
||||||
|
combinations: Int64;
|
||||||
|
begin
|
||||||
|
Result := AValidationResult;
|
||||||
|
if Result = ivrValid then
|
||||||
|
begin
|
||||||
|
combinations := CalcCombinations(ACurrentIndexArray, ACurrentIndex);
|
||||||
|
if combinations = 0 then
|
||||||
|
Result := ivrSkip
|
||||||
|
else if ACurrentIndex > 0 then
|
||||||
|
FAccumulatedCombinations[ACurrentIndex] := combinations * FAccumulatedCombinations[ACurrentIndex - 1]
|
||||||
else begin
|
else begin
|
||||||
Result := False;
|
SetLength(FAccumulatedCombinations, GetCardinality);
|
||||||
Break;
|
FAccumulatedCombinations[ACurrentIndex] := combinations;
|
||||||
end;
|
end;
|
||||||
end;
|
end;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
function TAccumulatedCombinationsMultiIndexStrategy.GetCombinations: Int64;
|
||||||
|
begin
|
||||||
|
if FAccumulatedCombinations <> nil then
|
||||||
|
Result := FAccumulatedCombinations[GetCardinality - 1]
|
||||||
|
else
|
||||||
|
Result := 0;
|
||||||
|
end;
|
||||||
|
|
||||||
|
{ TValidationsToBlockAssignments }
|
||||||
|
|
||||||
|
function TValidationsToBlockAssignments.CalcCombinations(constref ACurrentIndexArray: TIndexArray; const ACurrentIndex:
|
||||||
|
Integer): Int64;
|
||||||
|
var
|
||||||
|
block: TBlock;
|
||||||
|
start, stop: Integer;
|
||||||
|
begin
|
||||||
|
// 'ACurrentIndexArray[i] - 1' denotes the index of the last validation number assigned to 'Block[i]', and the index
|
||||||
|
// of the first validation number in 'Validation' assigned to 'Block[i + 1]'. If two consecutive values in
|
||||||
|
// 'ACurrentIndexArray' are the same, then the block in between has no numbers assigned to it.
|
||||||
|
block := FConditionRecord.Blocks[ACurrentIndex];
|
||||||
|
if ACurrentIndex > 0 then
|
||||||
|
start := ACurrentIndexArray[ACurrentIndex - 1]
|
||||||
|
else
|
||||||
|
start := 0;
|
||||||
|
stop := ACurrentIndexArray[ACurrentIndex] - 1;
|
||||||
|
if block.Damages.Count > 0 then
|
||||||
|
Result := FConditionRecord.CalcCombinationsBlock(block, start, stop)
|
||||||
|
else
|
||||||
|
Result := FConditionRecord.CalcCombinationsWildcardSequence(Length(block.Pattern), start, stop);
|
||||||
|
end;
|
||||||
|
|
||||||
|
constructor TValidationsToBlockAssignments.Create(constref AConditionRecord: TConditionRecord);
|
||||||
|
begin
|
||||||
|
FConditionRecord := AConditionRecord;
|
||||||
|
end;
|
||||||
|
|
||||||
|
function TValidationsToBlockAssignments.GetCardinality: Integer;
|
||||||
|
begin
|
||||||
|
Result := FConditionRecord.Blocks.Count;
|
||||||
|
end;
|
||||||
|
|
||||||
|
function TValidationsToBlockAssignments.TryGetStartIndexValue(constref ACurrentIndexArray: TIndexArray;
|
||||||
|
const ACurrentIndex: Integer; out AStartIndexValue: Integer): Boolean;
|
||||||
|
begin
|
||||||
|
Result := True;
|
||||||
|
if ACurrentIndex + 1 = GetCardinality then
|
||||||
|
AStartIndexValue := FConditionRecord.Validation.Count
|
||||||
|
else if ACurrentIndex > 0 then
|
||||||
|
AStartIndexValue := Max(ACurrentIndexArray[ACurrentIndex - 1], FConditionRecord.MinIndices[ACurrentIndex])
|
||||||
|
else
|
||||||
|
AStartIndexValue := FConditionRecord.MinIndices[ACurrentIndex];
|
||||||
|
end;
|
||||||
|
|
||||||
|
function TValidationsToBlockAssignments.ValidateIndexValue(constref ACurrentIndexArray: TIndexArray;
|
||||||
|
const ACurrentIndex: Integer): TIndexValidationResult;
|
||||||
|
var
|
||||||
|
start: Integer;
|
||||||
|
begin
|
||||||
|
if ACurrentIndexArray[ACurrentIndex] > FConditionRecord.Validation.Count then
|
||||||
|
Result := ivrBacktrack
|
||||||
|
else begin
|
||||||
|
if ACurrentIndex > 0 then
|
||||||
|
start := ACurrentIndexArray[ACurrentIndex - 1]
|
||||||
|
else
|
||||||
|
start := 0;
|
||||||
|
|
||||||
|
if FConditionRecord.ValidationLengths[start, ACurrentIndexArray[ACurrentIndex]]
|
||||||
|
<= Length(FConditionRecord.Blocks[ACurrentIndex].Pattern) then
|
||||||
|
Result := ivrValid
|
||||||
|
else
|
||||||
|
Result := ivrBacktrack;
|
||||||
|
end;
|
||||||
|
|
||||||
|
Result := UpdateCombinations(Result, ACurrentIndexArray, ACurrentIndex);
|
||||||
|
end;
|
||||||
|
|
||||||
|
{ TDamageToValidationAssignments }
|
||||||
|
|
||||||
|
function TDamageToValidationAssignments.CalcValidationSpan(constref ACurrentIndexArray: TIndexArray;
|
||||||
|
const ALastDamageIndex, AValidationNumber: Integer): Integer;
|
||||||
|
var
|
||||||
|
spanStart: Integer;
|
||||||
|
begin
|
||||||
|
spanStart := ALastDamageIndex;
|
||||||
|
while (spanStart > 0) and (ACurrentIndexArray[spanStart - 1] = AValidationNumber) do
|
||||||
|
Dec(spanStart);
|
||||||
|
Result := FBlock.Damages[ALastDamageIndex].Length;
|
||||||
|
if spanStart < ALastDamageIndex then
|
||||||
|
Inc(Result, FBlock.Damages[ALastDamageIndex].Start - FBlock.Damages[spanStart].Start);
|
||||||
|
end;
|
||||||
|
|
||||||
|
constructor TDamageToValidationAssignments.Create(constref AConditionRecord: TConditionRecord; constref ABlock: TBlock;
|
||||||
|
const AStartValidationIndex, AStopValidationIndex: Integer);
|
||||||
|
begin
|
||||||
|
FConditionRecord := AConditionRecord;
|
||||||
|
FBlock := ABlock;
|
||||||
|
FValidationStartIndex := AStartValidationIndex;
|
||||||
|
FValidationStopIndex := AStopValidationIndex;
|
||||||
|
end;
|
||||||
|
|
||||||
|
function TDamageToValidationAssignments.GetCardinality: Integer;
|
||||||
|
begin
|
||||||
|
Result := FBlock.Damages.Count;
|
||||||
|
end;
|
||||||
|
|
||||||
|
function TDamageToValidationAssignments.TryGetStartIndexValue(constref ACurrentIndexArray: TIndexArray;
|
||||||
|
const ACurrentIndex: Integer; out AStartIndexValue: Integer): Boolean;
|
||||||
|
begin
|
||||||
|
Result := True;
|
||||||
|
if ACurrentIndex > 0 then
|
||||||
|
AStartIndexValue := ACurrentIndexArray[ACurrentIndex - 1]
|
||||||
|
else
|
||||||
|
AStartIndexValue := FValidationStartIndex;
|
||||||
|
end;
|
||||||
|
|
||||||
|
function TDamageToValidationAssignments.ValidateIndexValue(constref ACurrentIndexArray: TIndexArray;
|
||||||
|
const ACurrentIndex: Integer): TIndexValidationResult;
|
||||||
|
var
|
||||||
|
i, prev: Integer;
|
||||||
|
begin
|
||||||
|
i := ACurrentIndexArray[ACurrentIndex];
|
||||||
|
prev := ACurrentIndex - 1;
|
||||||
|
// Checks maximum index value.
|
||||||
|
if i > FValidationStopIndex then
|
||||||
|
Result := ivrBacktrack
|
||||||
|
// Checks if there is enough space after this damage for remaining validation numbers.
|
||||||
|
else if (i < FValidationStopIndex)
|
||||||
|
and (FConditionRecord.ValidationLengths[i + 1, FValidationStopIndex + 1] + 1 > FBlock.Damages[ACurrentIndex].CharsRemaining) then
|
||||||
|
Result := ivrSkip
|
||||||
|
// Checks if there is enough space before this damage for previous validation numbers.
|
||||||
|
else if (FValidationStartIndex < i)
|
||||||
|
and (FConditionRecord.ValidationLengths[FValidationStartIndex, i] + 1 >= FBlock.Damages[ACurrentIndex].Start) then
|
||||||
|
Result := ivrBacktrack
|
||||||
|
// Checks if there is enough space between previous and this damage for skipped validation numbers.
|
||||||
|
else if (ACurrentIndex > 0)
|
||||||
|
and (ACurrentIndexArray[prev] + 1 < i)
|
||||||
|
and (FConditionRecord.ValidationLengths[ACurrentIndexArray[prev] + 1, i] + 2
|
||||||
|
> FBlock.Damages[ACurrentIndex].Start - FBlock.Damages[prev].Start - FBlock.Damages[prev].Length) then
|
||||||
|
Result := ivrBacktrack
|
||||||
|
// Checks if span is small enough to fit within this validation number.
|
||||||
|
else if FConditionRecord.Validation[i] < CalcValidationSpan(ACurrentIndexArray, ACurrentIndex, i) then
|
||||||
|
Result := ivrSkip
|
||||||
|
else
|
||||||
|
Result := ivrValid;
|
||||||
|
end;
|
||||||
|
|
||||||
|
{ TValidationPositionOffsets }
|
||||||
|
|
||||||
|
function TValidationPositionOffsets.CalcCombinations(constref ACurrentIndexArray: TIndexArray; const ACurrentIndex:
|
||||||
|
Integer): Int64;
|
||||||
|
var
|
||||||
|
space, start, stop: Integer;
|
||||||
|
begin
|
||||||
|
stop := FPositionInfos[ACurrentIndex].ValidationIndex - 1;
|
||||||
|
if ACurrentIndex > 0 then
|
||||||
|
begin
|
||||||
|
space := ACurrentIndexArray[ACurrentIndex] - ACurrentIndexArray[ACurrentIndex - 1]
|
||||||
|
- FConditionRecord.Validation[FPositionInfos[ACurrentIndex - 1].ValidationIndex] - 2;
|
||||||
|
start := FPositionInfos[ACurrentIndex - 1].ValidationIndex + 1;
|
||||||
|
Result := FConditionRecord.CalcCombinationsWildcardSequence(space, start, stop);
|
||||||
|
end
|
||||||
|
else begin
|
||||||
|
// Handles first calculated offset.
|
||||||
|
space := ACurrentIndexArray[0] - 2;
|
||||||
|
Result := FConditionRecord.CalcCombinationsWildcardSequence(space, FValidationStartIndex, stop);
|
||||||
|
end;
|
||||||
|
|
||||||
|
if (Result > 0) and (ACurrentIndex + 1 = GetCardinality) then
|
||||||
|
begin
|
||||||
|
// Handles last calculated offset.
|
||||||
|
space := FBlockLength - ACurrentIndexArray[ACurrentIndex] - FConditionRecord.Validation[FPositionInfos.Last.ValidationIndex];
|
||||||
|
start := FPositionInfos.Last.ValidationIndex + 1;
|
||||||
|
Result := Result * FConditionRecord.CalcCombinationsWildcardSequence(space, start, FValidationStopIndex);
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
constructor TValidationPositionOffsets.Create(constref AConditionRecord: TConditionRecord; constref APositionInfos:
|
||||||
|
TValidationPositionInfos; const ABlockLength, AValidationStartIndex, AValidationStopIndex: Integer);
|
||||||
|
begin
|
||||||
|
FConditionRecord := AConditionRecord;
|
||||||
|
FPositionInfos := APositionInfos;
|
||||||
|
FBlockLength := ABlockLength;
|
||||||
|
FValidationStartIndex := AValidationStartIndex;
|
||||||
|
FValidationStopIndex := AValidationStopIndex;
|
||||||
|
inherited Create;
|
||||||
|
end;
|
||||||
|
|
||||||
|
function TValidationPositionOffsets.GetCardinality: Integer;
|
||||||
|
begin
|
||||||
|
Result := FPositionInfos.Count;
|
||||||
|
end;
|
||||||
|
|
||||||
|
function TValidationPositionOffsets.TryGetStartIndexValue(constref ACurrentIndexArray: TIndexArray;
|
||||||
|
const ACurrentIndex: Integer; out AStartIndexValue: Integer): Boolean;
|
||||||
|
var
|
||||||
|
info: TValidationPositionInfo;
|
||||||
|
begin
|
||||||
|
info := FPositionInfos[ACurrentIndex];
|
||||||
|
AStartIndexValue := info.MinStart;
|
||||||
|
// Adjusts start value to avoid overlap of this validation number with the previous one (the one from previous
|
||||||
|
// position info).
|
||||||
|
if ACurrentIndex > 0 then
|
||||||
|
AStartIndexValue := Max(AStartIndexValue,
|
||||||
|
ACurrentIndexArray[ACurrentIndex - 1] + FConditionRecord.Validation[FPositionInfos[ACurrentIndex - 1].ValidationIndex] + 1);
|
||||||
|
Result := True;
|
||||||
|
end;
|
||||||
|
|
||||||
|
function TValidationPositionOffsets.ValidateIndexValue(constref ACurrentIndexArray: TIndexArray; const ACurrentIndex:
|
||||||
|
Integer): TIndexValidationResult;
|
||||||
|
begin
|
||||||
|
if ACurrentIndexArray[ACurrentIndex] <= FPositionInfos[ACurrentIndex].MaxStart then
|
||||||
|
Result := ivrValid
|
||||||
|
else
|
||||||
|
Result := ivrBacktrack;
|
||||||
|
|
||||||
|
Result := UpdateCombinations(Result, ACurrentIndexArray, ACurrentIndex);
|
||||||
|
end;
|
||||||
|
|
||||||
|
{ TConditionRecord }
|
||||||
|
|
||||||
|
procedure TConditionRecord.InitValidationLengths;
|
||||||
|
var
|
||||||
|
i, j: Integer;
|
||||||
|
begin
|
||||||
|
SetLength(FValidationLengths, FValidation.Count + 1, FValidation.Count + 1);
|
||||||
|
for i := 0 to FValidation.Count do
|
||||||
|
begin
|
||||||
|
FValidationLengths[i, i] := 0;
|
||||||
|
for j := i + 1 to FValidation.Count do
|
||||||
|
if FValidationLengths[i, j - 1] <> 0 then
|
||||||
|
FValidationLengths[i, j] := FValidationLengths[i, j - 1] + FValidation[j - 1] + 1
|
||||||
|
else
|
||||||
|
FValidationLengths[i, j] := FValidationLengths[i, j - 1] + FValidation[j - 1]
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TConditionRecord.InitMinIndices;
|
||||||
|
var
|
||||||
|
i, j, patternsLength: Integer;
|
||||||
|
begin
|
||||||
|
SetLength(FMinIndices, FBlocks.Count - 1);
|
||||||
|
patternsLength := Length(FBlocks[FBlocks.Count - 1].Pattern);
|
||||||
|
j := FValidation.Count;
|
||||||
|
for i := FBlocks.Count - 2 downto 0 do
|
||||||
|
begin
|
||||||
|
while (j >= 0) and (FValidationLengths[j, FValidation.Count] <= patternsLength) do
|
||||||
|
Dec(j);
|
||||||
|
FMinIndices[i] := j + 1;
|
||||||
|
patternsLength := patternsLength + 1 + Length(FBlocks[i].Pattern);
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
function TConditionRecord.CalcCombinationsBlockSingleValidation(constref ABlock: TBlock; const AIndex: Integer): Int64;
|
||||||
|
var
|
||||||
|
len, combinedDamagesLength: Integer;
|
||||||
|
begin
|
||||||
|
len := Length(ABlock.Pattern);
|
||||||
|
if len < FValidation[AIndex] then
|
||||||
|
Result := 0
|
||||||
|
else if ABlock.Damages.Count = 0 then
|
||||||
|
Result := len - FValidation[AIndex] + 1
|
||||||
|
else begin
|
||||||
|
combinedDamagesLength := ABlock.Damages.Last.Start + ABlock.Damages.Last.Length - ABlock.Damages.First.Start;
|
||||||
|
if FValidation[AIndex] < combinedDamagesLength then
|
||||||
|
Result := 0
|
||||||
|
else begin
|
||||||
|
Result := Min(Min(Min(
|
||||||
|
ABlock.Damages.First.Start,
|
||||||
|
FValidation[AIndex] - combinedDamagesLength + 1),
|
||||||
|
len - FValidation[AIndex] + 1),
|
||||||
|
ABlock.Damages.Last.CharsRemaining + 1);
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
function TConditionRecord.CalcCombinationsBlockMultiValidations(constref ABlock: TBlock; constref AIndices:
|
||||||
|
TIndexArray; const AStartIndex, AStopIndex: Integer): Int64;
|
||||||
|
var
|
||||||
|
i, high: Integer;
|
||||||
|
position: TValidationPositionInfo;
|
||||||
|
positions: TValidationPositionInfos;
|
||||||
|
validationPositionOffsets: TValidationPositionOffsets;
|
||||||
|
offsets: TIndexArray;
|
||||||
|
begin
|
||||||
|
positions := TValidationPositionInfos.Create;
|
||||||
|
high := Length(AIndices) - 1;
|
||||||
|
// Initializes first info record.
|
||||||
|
position.ValidationIndex := AIndices[0];
|
||||||
|
position.MaxStart := ABlock.Damages[0].Start;
|
||||||
|
position.MinStart := 1;
|
||||||
|
for i := 1 to high do
|
||||||
|
if AIndices[i] <> position.ValidationIndex then
|
||||||
|
begin
|
||||||
|
// Finalizes current info record.
|
||||||
|
position.MaxStart := Min(position.MaxStart, ABlock.Damages[i].Start - 1 - FValidation[position.ValidationIndex]);
|
||||||
|
position.MinStart := Max(position.MinStart,
|
||||||
|
ABlock.Damages[i - 1].Start + ABlock.Damages[i - 1].Length - FValidation[position.ValidationIndex]);
|
||||||
|
positions.Add(position);
|
||||||
|
// Initializes next info record.
|
||||||
|
position.ValidationIndex := AIndices[i];
|
||||||
|
position.MaxStart := ABlock.Damages[i].Start;
|
||||||
|
position.MinStart := position.MinStart + FValidationLengths[AIndices[i - 1], AIndices[i]] + 1;
|
||||||
|
end;
|
||||||
|
// Finalizes last info record.
|
||||||
|
position.MaxStart := Min(position.MaxStart, Length(ABlock.Pattern) + 1 - FValidation[position.ValidationIndex]);
|
||||||
|
position.MinStart := Max(position.MinStart,
|
||||||
|
ABlock.Damages[high].Start + ABlock.Damages[high].Length - FValidation[position.ValidationIndex]);
|
||||||
|
positions.Add(position);
|
||||||
|
|
||||||
|
Result := 0;
|
||||||
|
validationPositionOffsets := TValidationPositionOffsets.Create(Self, positions, Length(ABlock.Pattern),
|
||||||
|
AStartIndex, AStopIndex);
|
||||||
|
for offsets in validationPositionOffsets do
|
||||||
|
Result := Result + validationPositionOffsets.GetCombinations;
|
||||||
|
validationPositionOffsets.Free;
|
||||||
|
|
||||||
|
positions.Free;
|
||||||
|
end;
|
||||||
|
|
||||||
|
function TConditionRecord.CalcValidationsId(const AStartIndex, AStopIndex: Integer): Int64;
|
||||||
|
var
|
||||||
|
i: Integer;
|
||||||
|
begin
|
||||||
|
// Requires 'FValidations[i] < 32' for each 'i' and 'AStopIndex - AStartIndex < 12'.
|
||||||
|
Result := FValidation[AStartIndex];
|
||||||
|
for i := AStartIndex + 1 to AStopIndex do
|
||||||
|
Result := (Result shl 5) or FValidation[i];
|
||||||
|
end;
|
||||||
|
|
||||||
|
constructor TConditionRecord.Create(constref ACombinationsCache: TCombinationsCache);
|
||||||
|
begin
|
||||||
|
FBlocks := TBlocks.Create;
|
||||||
|
FValidation := TIntegerList.Create;
|
||||||
|
FCombinationsCache := ACombinationsCache;
|
||||||
|
end;
|
||||||
|
|
||||||
|
destructor TConditionRecord.Destroy;
|
||||||
|
begin
|
||||||
|
FBlocks.Free;
|
||||||
|
FValidation.Free;
|
||||||
|
inherited Destroy;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TConditionRecord.AddBlocks(const APattern: string);
|
||||||
|
var
|
||||||
|
split: TStringArray;
|
||||||
|
part: string;
|
||||||
|
blockCache: TBlockCombinationsCache;
|
||||||
|
begin
|
||||||
|
split := APattern.Split([COperationalChar]);
|
||||||
|
for part in split do
|
||||||
|
if Length(part) > 0 then
|
||||||
|
begin
|
||||||
|
if not FCombinationsCache.TryGetValue(part, blockCache) then
|
||||||
|
begin
|
||||||
|
blockCache := TBlockCombinationsCache.Create;
|
||||||
|
FCombinationsCache.Add(part, blockCache);
|
||||||
|
end;
|
||||||
|
FBlocks.Add(TBlock.Create(part, blockCache));
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
function TConditionRecord.GenerateBlockAssignments: Int64;
|
||||||
|
var
|
||||||
|
validationsToBlockAssignments: TValidationsToBlockAssignments;
|
||||||
|
indices: TIndexArray;
|
||||||
|
begin
|
||||||
|
InitValidationLengths;
|
||||||
|
InitMinIndices;
|
||||||
|
|
||||||
|
Result := 0;
|
||||||
|
validationsToBlockAssignments := TValidationsToBlockAssignments.Create(Self);
|
||||||
|
for indices in validationsToBlockAssignments do
|
||||||
|
Result := Result + validationsToBlockAssignments.GetCombinations;
|
||||||
|
validationsToBlockAssignments.Free;
|
||||||
|
end;
|
||||||
|
|
||||||
|
function TConditionRecord.CalcCombinationsBlock(constref ABlock: TBlock; const AStartIndex, AStopIndex: Integer): Int64;
|
||||||
|
var
|
||||||
|
validationsId: Int64;
|
||||||
|
indices: TIndexArray;
|
||||||
|
damageToValidationAssignments: TDamageToValidationAssignments;
|
||||||
|
begin
|
||||||
|
// No validation number assigned to this block.
|
||||||
|
if AStartIndex > AStopIndex then
|
||||||
|
begin
|
||||||
|
if ABlock.Damages.Count = 0 then
|
||||||
|
Result := 1
|
||||||
|
else
|
||||||
|
Result := 0;
|
||||||
|
end
|
||||||
|
// One validation number assigned to this block.
|
||||||
|
else if AStartIndex = AStopIndex then
|
||||||
|
Result := CalcCombinationsBlockSingleValidation(ABlock, AStartIndex)
|
||||||
|
// Multiple validation numbers assigned to this block. Checks cache first.
|
||||||
|
else begin
|
||||||
|
validationsId := CalcValidationsId(AStartIndex, AStopIndex);
|
||||||
|
if not ABlock.CombinationsCache.TryGetValue(validationsId, Result) then
|
||||||
|
begin
|
||||||
|
Result := 0;
|
||||||
|
|
||||||
|
// Assigns validation numbers to specific damages.
|
||||||
|
damageToValidationAssignments := TDamageToValidationAssignments.Create(Self, ABlock, AStartIndex, AStopIndex);
|
||||||
|
for indices in damageToValidationAssignments do
|
||||||
|
Result := Result + CalcCombinationsBlockMultiValidations(ABlock, indices, AStartIndex, AStopIndex);
|
||||||
|
damageToValidationAssignments.Free;
|
||||||
|
|
||||||
|
ABlock.CombinationsCache.Add(validationsId, Result);
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
function TConditionRecord.CalcCombinationsWildcardSequence(const ASequenceLength, AStartIndex, AStopIndex: Integer):
|
||||||
|
Int64;
|
||||||
|
var
|
||||||
|
count, freedoms: Integer;
|
||||||
|
begin
|
||||||
|
if AStartIndex < AStopIndex + 1 then
|
||||||
|
begin
|
||||||
|
count := AStopIndex + 1 - AStartIndex;
|
||||||
|
freedoms := ASequenceLength - FValidationLengths[AStartIndex, AStopIndex + 1];
|
||||||
|
if freedoms >= 0 then
|
||||||
|
Result := BinomialCoefficients.Get(count + freedoms, freedoms)
|
||||||
|
else
|
||||||
|
Result := 0;
|
||||||
|
end
|
||||||
|
else
|
||||||
|
Result := 1;
|
||||||
|
end;
|
||||||
|
|
||||||
|
{ THotSprings }
|
||||||
|
|
||||||
constructor THotSprings.Create;
|
constructor THotSprings.Create;
|
||||||
begin
|
begin
|
||||||
FValidation := specialize TList<Integer>.Create;
|
FCombinationsCache := TCombinationsCache.Create([doOwnsValues]);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
destructor THotSprings.Destroy;
|
destructor THotSprings.Destroy;
|
||||||
begin
|
begin
|
||||||
FValidation.Free;
|
FCombinationsCache.Free;
|
||||||
inherited Destroy;
|
inherited Destroy;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
procedure THotSprings.ProcessDataLine(const ALine: string);
|
procedure THotSprings.ProcessDataLine(const ALine: string);
|
||||||
var
|
var
|
||||||
split: TStringArray;
|
conditionRecord1, conditionRecord2: TConditionRecord;
|
||||||
i, val, maxFreeOperationalCount: Integer;
|
mainSplit, split: TStringArray;
|
||||||
|
part, unfolded: string;
|
||||||
|
i: Integer;
|
||||||
begin
|
begin
|
||||||
FValidation.Clear;
|
conditionRecord1 := TConditionRecord.Create(FCombinationsCache);
|
||||||
split := ALine.Split([' ', ',']);
|
conditionRecord2 := TConditionRecord.Create(FCombinationsCache);
|
||||||
FSpringPattern := split[0];
|
|
||||||
|
|
||||||
maxFreeOperationalCount := Length(FSpringPattern) - Length(split) + 2;
|
mainSplit := ALine.Split([' ']);
|
||||||
for i := 1 to Length(split) - 1 do
|
|
||||||
begin
|
|
||||||
val := StrToInt(split[i]);
|
|
||||||
FValidation.Add(val);
|
|
||||||
Dec(maxFreeOperationalCount, val);
|
|
||||||
end;
|
|
||||||
|
|
||||||
ExtendArrangement('', maxFreeOperationalCount, 0);
|
// Adds blocks for part 1.
|
||||||
|
conditionRecord1.AddBlocks(mainSplit[0]);
|
||||||
|
|
||||||
|
// Adds blocks for part 2.
|
||||||
|
unfolded := mainSplit[0];
|
||||||
|
for i := 2 to CPart2Repetition do
|
||||||
|
unfolded := unfolded + CWildcardChar + mainSplit[0];
|
||||||
|
conditionRecord2.AddBlocks(unfolded);
|
||||||
|
|
||||||
|
// Adds validation numbers.
|
||||||
|
split := mainSplit[1].Split([',']);
|
||||||
|
for part in split do
|
||||||
|
conditionRecord1.Validation.Add(StrToInt(part));
|
||||||
|
for i := 1 to CPart2Repetition do
|
||||||
|
conditionRecord2.Validation.AddRange(conditionRecord1.Validation);
|
||||||
|
|
||||||
|
FPart1 := FPart1 + conditionRecord1.GenerateBlockAssignments;
|
||||||
|
FPart2 := FPart2 + conditionRecord2.GenerateBlockAssignments;
|
||||||
|
|
||||||
|
conditionRecord1.Free;
|
||||||
|
conditionRecord2.Free;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
procedure THotSprings.Finish;
|
procedure THotSprings.Finish;
|
||||||
|
|
|
@ -152,6 +152,10 @@
|
||||||
<Filename Value="USnowverloadTestCases.pas"/>
|
<Filename Value="USnowverloadTestCases.pas"/>
|
||||||
<IsPartOfProject Value="True"/>
|
<IsPartOfProject Value="True"/>
|
||||||
</Unit>
|
</Unit>
|
||||||
|
<Unit>
|
||||||
|
<Filename Value="UBinomialCoefficientsTestCases.pas"/>
|
||||||
|
<IsPartOfProject Value="True"/>
|
||||||
|
</Unit>
|
||||||
</Units>
|
</Units>
|
||||||
</ProjectOptions>
|
</ProjectOptions>
|
||||||
<CompilerOptions>
|
<CompilerOptions>
|
||||||
|
|
|
@ -10,7 +10,7 @@ uses
|
||||||
UFloorWillBeLavaTestCases, UClumsyCrucibleTestCases, ULavaductLagoonTestCases, UAplentyTestCases,
|
UFloorWillBeLavaTestCases, UClumsyCrucibleTestCases, ULavaductLagoonTestCases, UAplentyTestCases,
|
||||||
UPulsePropagationTestCases, UStepCounterTestCases, USandSlabsTestCases, ULongWalkTestCases,
|
UPulsePropagationTestCases, UStepCounterTestCases, USandSlabsTestCases, ULongWalkTestCases,
|
||||||
UNeverTellMeTheOddsTestCases, USnowverloadTestCases, UBigIntTestCases, UPolynomialTestCases,
|
UNeverTellMeTheOddsTestCases, USnowverloadTestCases, UBigIntTestCases, UPolynomialTestCases,
|
||||||
UPolynomialRootsTestCases;
|
UPolynomialRootsTestCases, UBinomialCoefficientsTestCases;
|
||||||
|
|
||||||
{$R *.res}
|
{$R *.res}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,138 @@
|
||||||
|
{
|
||||||
|
Solutions to the Advent Of Code.
|
||||||
|
Copyright (C) 2024 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 <http://www.gnu.org/licenses/>.
|
||||||
|
}
|
||||||
|
|
||||||
|
unit UBinomialCoefficientsTestCases;
|
||||||
|
|
||||||
|
{$mode ObjFPC}{$H+}
|
||||||
|
|
||||||
|
interface
|
||||||
|
|
||||||
|
uses
|
||||||
|
Classes, SysUtils, fpcunit, testregistry, UBinomialCoefficients;
|
||||||
|
|
||||||
|
type
|
||||||
|
|
||||||
|
{ TBinomialCoefficientsTestCase }
|
||||||
|
|
||||||
|
TBinomialCoefficientsTestCase = class(TTestCase)
|
||||||
|
private
|
||||||
|
FBinomialCoefficientCache: TBinomialCoefficientCache;
|
||||||
|
procedure RunRangeError;
|
||||||
|
procedure AssertEqualsCalculation(const AN, AK, AExpected: Cardinal);
|
||||||
|
procedure AssertEqualsCachedRowsCount(const AExpected: Cardinal);
|
||||||
|
protected
|
||||||
|
procedure SetUp; override;
|
||||||
|
procedure TearDown; override;
|
||||||
|
published
|
||||||
|
procedure TestZeroChooseZero;
|
||||||
|
procedure TestNChooseZero;
|
||||||
|
procedure TestNChooseN;
|
||||||
|
procedure TestNChooseK;
|
||||||
|
procedure TestCombined;
|
||||||
|
procedure TestFullRow;
|
||||||
|
procedure TestRangeError;
|
||||||
|
end;
|
||||||
|
|
||||||
|
implementation
|
||||||
|
|
||||||
|
{ TBinomialCoefficientsTestCase }
|
||||||
|
|
||||||
|
procedure TBinomialCoefficientsTestCase.RunRangeError;
|
||||||
|
begin
|
||||||
|
FBinomialCoefficientCache.Get(1, 5);
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TBinomialCoefficientsTestCase.AssertEqualsCalculation(const AN, AK, AExpected: Cardinal);
|
||||||
|
begin
|
||||||
|
AssertEquals('Unexpected calculation result', AExpected, FBinomialCoefficientCache.Get(AN, AK));
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TBinomialCoefficientsTestCase.AssertEqualsCachedRowsCount(const AExpected: Cardinal);
|
||||||
|
begin
|
||||||
|
AssertEquals('Unexpected cached rows count', AExpected, FBinomialCoefficientCache.GetCachedRowsCount);
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TBinomialCoefficientsTestCase.SetUp;
|
||||||
|
begin
|
||||||
|
FBinomialCoefficientCache := TBinomialCoefficientCache.Create;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TBinomialCoefficientsTestCase.TearDown;
|
||||||
|
begin
|
||||||
|
FBinomialCoefficientCache.Free;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TBinomialCoefficientsTestCase.TestZeroChooseZero;
|
||||||
|
begin
|
||||||
|
AssertEqualsCalculation(0, 0, 1);
|
||||||
|
AssertEqualsCachedRowsCount(1);
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TBinomialCoefficientsTestCase.TestNChooseZero;
|
||||||
|
begin
|
||||||
|
AssertEqualsCalculation(15, 0, 1);
|
||||||
|
AssertEqualsCachedRowsCount(16);
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TBinomialCoefficientsTestCase.TestNChooseN;
|
||||||
|
begin
|
||||||
|
AssertEqualsCalculation(11, 11, 1);
|
||||||
|
AssertEqualsCachedRowsCount(12);
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TBinomialCoefficientsTestCase.TestNChooseK;
|
||||||
|
begin
|
||||||
|
AssertEqualsCalculation(8, 3, 56);
|
||||||
|
AssertEqualsCachedRowsCount(9);
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TBinomialCoefficientsTestCase.TestCombined;
|
||||||
|
begin
|
||||||
|
AssertEqualsCalculation(5, 1, 5);
|
||||||
|
AssertEqualsCachedRowsCount(6);
|
||||||
|
AssertEqualsCalculation(8, 4, 70);
|
||||||
|
AssertEqualsCachedRowsCount(9);
|
||||||
|
AssertEqualsCalculation(3, 1, 3);
|
||||||
|
AssertEqualsCachedRowsCount(9);
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TBinomialCoefficientsTestCase.TestFullRow;
|
||||||
|
begin
|
||||||
|
AssertEqualsCalculation(5, 0, 1);
|
||||||
|
AssertEqualsCachedRowsCount(6);
|
||||||
|
AssertEqualsCalculation(5, 1, 5);
|
||||||
|
AssertEqualsCachedRowsCount(6);
|
||||||
|
AssertEqualsCalculation(5, 2, 10);
|
||||||
|
AssertEqualsCachedRowsCount(6);
|
||||||
|
AssertEqualsCalculation(5, 3, 10);
|
||||||
|
AssertEqualsCachedRowsCount(6);
|
||||||
|
AssertEqualsCalculation(5, 4, 5);
|
||||||
|
AssertEqualsCachedRowsCount(6);
|
||||||
|
AssertEqualsCalculation(5, 5, 1);
|
||||||
|
AssertEqualsCachedRowsCount(6);
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TBinomialCoefficientsTestCase.TestRangeError;
|
||||||
|
begin
|
||||||
|
AssertException(ERangeError, @RunRangeError);
|
||||||
|
end;
|
||||||
|
|
||||||
|
initialization
|
||||||
|
|
||||||
|
RegisterTest('Helper', TBinomialCoefficientsTestCase);
|
||||||
|
end.
|
||||||
|
|
|
@ -33,6 +33,7 @@ type
|
||||||
function CreateSolver: ISolver; override;
|
function CreateSolver: ISolver; override;
|
||||||
published
|
published
|
||||||
procedure TestPart1;
|
procedure TestPart1;
|
||||||
|
procedure TestPart2;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
{ THotSpringsTestCase }
|
{ THotSpringsTestCase }
|
||||||
|
@ -40,14 +41,20 @@ type
|
||||||
THotSpringsTestCase = class(TSolverTestCase)
|
THotSpringsTestCase = class(TSolverTestCase)
|
||||||
protected
|
protected
|
||||||
function CreateSolver: ISolver; override;
|
function CreateSolver: ISolver; override;
|
||||||
procedure TestSingleLine(const ALine: string; const AValue: Integer);
|
procedure TestSingleLine(const ALine: string);
|
||||||
published
|
published
|
||||||
procedure TestExampleLine1;
|
procedure TestExampleLine1Part1;
|
||||||
procedure TestExampleLine2;
|
procedure TestExampleLine2Part1;
|
||||||
procedure TestExampleLine3;
|
procedure TestExampleLine3Part1;
|
||||||
procedure TestExampleLine4;
|
procedure TestExampleLine4Part1;
|
||||||
procedure TestExampleLine5;
|
procedure TestExampleLine5Part1;
|
||||||
procedure TestExampleLine6;
|
procedure TestExampleLine6Part1;
|
||||||
|
procedure TestExampleLine1Part2;
|
||||||
|
procedure TestExampleLine2Part2;
|
||||||
|
procedure TestExampleLine3Part2;
|
||||||
|
procedure TestExampleLine4Part2;
|
||||||
|
procedure TestExampleLine5Part2;
|
||||||
|
procedure TestExampleLine6Part2;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
implementation
|
implementation
|
||||||
|
@ -64,6 +71,11 @@ begin
|
||||||
AssertEquals(21, FSolver.GetResultPart1);
|
AssertEquals(21, FSolver.GetResultPart1);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
procedure THotSpringsExampleTestCase.TestPart2;
|
||||||
|
begin
|
||||||
|
AssertEquals(525152, FSolver.GetResultPart2);
|
||||||
|
end;
|
||||||
|
|
||||||
{ THotSpringsTestCase }
|
{ THotSpringsTestCase }
|
||||||
|
|
||||||
function THotSpringsTestCase.CreateSolver: ISolver;
|
function THotSpringsTestCase.CreateSolver: ISolver;
|
||||||
|
@ -71,42 +83,83 @@ begin
|
||||||
Result := THotSprings.Create;
|
Result := THotSprings.Create;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
procedure THotSpringsTestCase.TestSingleLine(const ALine: string; const AValue: Integer);
|
procedure THotSpringsTestCase.TestSingleLine(const ALine: string);
|
||||||
begin
|
begin
|
||||||
FSolver.Init;
|
FSolver.Init;
|
||||||
FSolver.ProcessDataLine(ALine);
|
FSolver.ProcessDataLine(ALine);
|
||||||
FSolver.Finish;
|
FSolver.Finish;
|
||||||
AssertEquals(AValue, FSolver.GetResultPart1);
|
|
||||||
end;
|
end;
|
||||||
|
|
||||||
procedure THotSpringsTestCase.TestExampleLine1;
|
procedure THotSpringsTestCase.TestExampleLine1Part1;
|
||||||
begin
|
begin
|
||||||
TestSingleLine('???.### 1,1,3', 1);
|
TestSingleLine('???.### 1,1,3');
|
||||||
|
AssertEquals(1, FSolver.GetResultPart1);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
procedure THotSpringsTestCase.TestExampleLine2;
|
procedure THotSpringsTestCase.TestExampleLine2Part1;
|
||||||
begin
|
begin
|
||||||
TestSingleLine('.??..??...?##. 1,1,3', 4);
|
TestSingleLine('.??..??...?##. 1,1,3');
|
||||||
|
AssertEquals(4, FSolver.GetResultPart1);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
procedure THotSpringsTestCase.TestExampleLine3;
|
procedure THotSpringsTestCase.TestExampleLine3Part1;
|
||||||
begin
|
begin
|
||||||
TestSingleLine('?#?#?#?#?#?#?#? 1,3,1,6', 1);
|
TestSingleLine('?#?#?#?#?#?#?#? 1,3,1,6');
|
||||||
|
AssertEquals(1, FSolver.GetResultPart1);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
procedure THotSpringsTestCase.TestExampleLine4;
|
procedure THotSpringsTestCase.TestExampleLine4Part1;
|
||||||
begin
|
begin
|
||||||
TestSingleLine('????.#...#... 4,1,1', 1);
|
TestSingleLine('????.#...#... 4,1,1');
|
||||||
|
AssertEquals(1, FSolver.GetResultPart1);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
procedure THotSpringsTestCase.TestExampleLine5;
|
procedure THotSpringsTestCase.TestExampleLine5Part1;
|
||||||
begin
|
begin
|
||||||
TestSingleLine('????.######..#####. 1,6,5', 4);
|
TestSingleLine('????.######..#####. 1,6,5');
|
||||||
|
AssertEquals(4, FSolver.GetResultPart1);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
procedure THotSpringsTestCase.TestExampleLine6;
|
procedure THotSpringsTestCase.TestExampleLine6Part1;
|
||||||
begin
|
begin
|
||||||
TestSingleLine('?###???????? 3,2,1', 10);
|
TestSingleLine('?###???????? 3,2,1');
|
||||||
|
AssertEquals(10, FSolver.GetResultPart1);
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure THotSpringsTestCase.TestExampleLine1Part2;
|
||||||
|
begin
|
||||||
|
TestSingleLine('???.### 1,1,3');
|
||||||
|
AssertEquals(1, FSolver.GetResultPart2);
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure THotSpringsTestCase.TestExampleLine2Part2;
|
||||||
|
begin
|
||||||
|
TestSingleLine('.??..??...?##. 1,1,3');
|
||||||
|
AssertEquals(16384, FSolver.GetResultPart2);
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure THotSpringsTestCase.TestExampleLine3Part2;
|
||||||
|
begin
|
||||||
|
TestSingleLine('?#?#?#?#?#?#?#? 1,3,1,6');
|
||||||
|
AssertEquals(1, FSolver.GetResultPart2);
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure THotSpringsTestCase.TestExampleLine4Part2;
|
||||||
|
begin
|
||||||
|
TestSingleLine('????.#...#... 4,1,1');
|
||||||
|
AssertEquals(16, FSolver.GetResultPart2);
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure THotSpringsTestCase.TestExampleLine5Part2;
|
||||||
|
begin
|
||||||
|
TestSingleLine('????.######..#####. 1,6,5');
|
||||||
|
AssertEquals(2500, FSolver.GetResultPart2);
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure THotSpringsTestCase.TestExampleLine6Part2;
|
||||||
|
begin
|
||||||
|
TestSingleLine('?###???????? 3,2,1');
|
||||||
|
AssertEquals(506250, FSolver.GetResultPart2);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
initialization
|
initialization
|
||||||
|
|
Loading…
Reference in New Issue