Added solution for "Day 17: Clumsy Crucible", part 2
This commit is contained in:
parent
ba1cefc371
commit
7b33e8b406
|
@ -22,7 +22,7 @@ unit UClumsyCrucible;
|
||||||
interface
|
interface
|
||||||
|
|
||||||
uses
|
uses
|
||||||
Classes, SysUtils, Generics.Collections, Math, USolver;
|
Classes, SysUtils, Generics.Collections, Math, USolver, UCommon;
|
||||||
|
|
||||||
type
|
type
|
||||||
|
|
||||||
|
@ -39,27 +39,58 @@ type
|
||||||
NeedsUpdate: Boolean;
|
NeedsUpdate: Boolean;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
TAxisId = (axHorizontal, axVertical);
|
||||||
|
|
||||||
|
const
|
||||||
|
CAxisDirections: array[TAxisId] of array[0..1] of PPoint
|
||||||
|
= ((@CDirectionRight, @CDirectionLeft), (@CDirectionDown, @CDirectionUp));
|
||||||
|
COtherAxes: array[TAxisId] of TAxisId = (axVertical, axHorizontal);
|
||||||
|
|
||||||
|
type
|
||||||
{ TNode }
|
{ TNode }
|
||||||
|
|
||||||
TNode = record
|
TNode = record
|
||||||
Horizontal, Vertical: TAxisData;
|
Axes: array[TAxisId] of TAxisData;
|
||||||
LocalHeatLoss: Byte;
|
LocalHeatLoss: Byte;
|
||||||
end;
|
end;
|
||||||
|
PNode = ^TNode;
|
||||||
|
|
||||||
TNodeArray = array of TNode;
|
TNodeArray = array of TNode;
|
||||||
TNodeArrays = specialize TList<TNodeArray>;
|
TNodeArrays = specialize TList<TNodeArray>;
|
||||||
|
|
||||||
TWorkQueue = specialize TQueue<TPoint>;
|
TWorkQueue = specialize TQueue<TPoint>;
|
||||||
|
|
||||||
|
{ TNodeMap }
|
||||||
|
|
||||||
|
TNodeMap = class
|
||||||
|
private
|
||||||
|
// Each item in FNodes is a horizontal row of nodes.
|
||||||
|
FNodes: TNodeArrays;
|
||||||
|
FWidth: Integer;
|
||||||
|
FMinStraight, FMaxStraight: Integer;
|
||||||
|
function GetHeight: Integer;
|
||||||
|
function GetNode(APosition: TPoint): TNode;
|
||||||
|
function GetPNode(APosition: TPoint): PNode;
|
||||||
|
function IsPositionInMap(constref APosition: TPoint): Boolean;
|
||||||
|
procedure ClampPositionToMap(var APosition: TPoint);
|
||||||
|
procedure InitWorkQueue(constref AWorkQueue: TWorkQueue);
|
||||||
|
procedure InvalidateNeighbors(constref AWorkQueue: TWorkQueue; const AAxis: TAxisId; constref APosition: TPoint);
|
||||||
|
function FindStepNodeMinimum(const AAxis: TAxisId; constref APosition: TPoint): Cardinal;
|
||||||
|
public
|
||||||
|
property Width: Integer read FWidth;
|
||||||
|
property Height: Integer read GetHeight;
|
||||||
|
constructor Create;
|
||||||
|
destructor Destroy; override;
|
||||||
|
procedure AddNodes(const ALine: string);
|
||||||
|
function FindMinimumPathLength(const AMinStraight, AMaxStraight: Integer): Cardinal;
|
||||||
|
procedure Reset;
|
||||||
|
end;
|
||||||
|
|
||||||
{ TClumsyCrucible }
|
{ TClumsyCrucible }
|
||||||
|
|
||||||
TClumsyCrucible = class(TSolver)
|
TClumsyCrucible = class(TSolver)
|
||||||
private
|
private
|
||||||
// Each item in FMap is a horizontal row of nodes.
|
FMap: TNodeMap;
|
||||||
FMap: TNodeArrays;
|
|
||||||
FWidth: Integer;
|
|
||||||
procedure InvalidateHorizontalNeighbors(constref APosition: TPoint; constref AWorkQueue: TWorkQueue);
|
|
||||||
procedure InvalidateVerticalNeighbors(constref APosition: TPoint; constref AWorkQueue: TWorkQueue);
|
|
||||||
public
|
public
|
||||||
constructor Create;
|
constructor Create;
|
||||||
destructor Destroy; override;
|
destructor Destroy; override;
|
||||||
|
@ -72,39 +103,227 @@ type
|
||||||
implementation
|
implementation
|
||||||
|
|
||||||
const
|
const
|
||||||
|
CMinStraight = 1;
|
||||||
CMaxStraight = 3;
|
CMaxStraight = 3;
|
||||||
|
CUltraMinStraight = 4;
|
||||||
|
CUltraMaxStraight = 10;
|
||||||
|
|
||||||
|
{ TNodeMap }
|
||||||
|
|
||||||
|
function TNodeMap.GetHeight: Integer;
|
||||||
|
begin
|
||||||
|
Result := FNodes.Count;
|
||||||
|
end;
|
||||||
|
|
||||||
|
function TNodeMap.GetNode(APosition: TPoint): TNode;
|
||||||
|
begin
|
||||||
|
Result := FNodes[APosition.Y][APosition.X];
|
||||||
|
end;
|
||||||
|
|
||||||
|
function TNodeMap.GetPNode(APosition: TPoint): PNode;
|
||||||
|
begin
|
||||||
|
Result := @FNodes[APosition.Y][APosition.X];
|
||||||
|
end;
|
||||||
|
|
||||||
|
function TNodeMap.IsPositionInMap(constref APosition: TPoint): Boolean;
|
||||||
|
begin
|
||||||
|
Result := (0 <= APosition.X) and (APosition.X < Width) and (0 <= APosition.Y) and (APosition.Y < Height);
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TNodeMap.ClampPositionToMap(var APosition: TPoint);
|
||||||
|
begin
|
||||||
|
if APosition.X < -1 then
|
||||||
|
APosition.X := -1
|
||||||
|
else if APosition.X > Width then
|
||||||
|
APosition.X := Width;
|
||||||
|
if APosition.Y < -1 then
|
||||||
|
APosition.Y := -1
|
||||||
|
else if APosition.Y > Height then
|
||||||
|
APosition.Y := Height;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TNodeMap.InitWorkQueue(constref AWorkQueue: TWorkQueue);
|
||||||
|
var
|
||||||
|
position: TPoint;
|
||||||
|
last: PNode;
|
||||||
|
axis: TAxisId;
|
||||||
|
begin
|
||||||
|
// Initializes the end node and the work queue with its neighbors.
|
||||||
|
position := Point(Width - 1, Height - 1);
|
||||||
|
last := GetPNode(position);
|
||||||
|
for axis in TAxisId do
|
||||||
|
begin
|
||||||
|
last^.Axes[axis].Minimum := 0;
|
||||||
|
last^.Axes[axis].IsTraversed := True;
|
||||||
|
end;
|
||||||
|
InvalidateNeighbors(AWorkQueue, axHorizontal, position);
|
||||||
|
InvalidateNeighbors(AWorkQueue, axVertical, position);
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TNodeMap.InvalidateNeighbors(constref AWorkQueue: TWorkQueue; const AAxis: TAxisId; constref APosition:
|
||||||
|
TPoint);
|
||||||
|
var
|
||||||
|
otherAxis: TAxisId;
|
||||||
|
nodeMinimum: Cardinal;
|
||||||
|
direction: PPoint;
|
||||||
|
neighborPos, stop: TPoint;
|
||||||
|
neighbor: PNode;
|
||||||
|
begin
|
||||||
|
otherAxis := COtherAxes[AAxis];
|
||||||
|
nodeMinimum := GetNode(APosition).Axes[otherAxis].Minimum;
|
||||||
|
|
||||||
|
for direction in CAxisDirections[AAxis] do
|
||||||
|
begin
|
||||||
|
neighborPos := Point(APosition.X + direction^.X * FMinStraight, APosition.Y + direction^.Y * FMinStraight);
|
||||||
|
if IsPositionInMap(neighborPos) then
|
||||||
|
begin
|
||||||
|
stop := Point(APosition.X + direction^.X * (FMaxStraight + 1), APosition.Y + direction^.Y * (FMaxStraight + 1));
|
||||||
|
ClampPositionToMap(stop);
|
||||||
|
while neighborPos <> stop do
|
||||||
|
begin
|
||||||
|
neighbor := GetPNode(neighborPos);
|
||||||
|
if not neighbor^.Axes[AAxis].NeedsUpdate
|
||||||
|
and (not neighbor^.Axes[AAxis].IsTraversed or (neighbor^.Axes[AAxis].Minimum > nodeMinimum)) then
|
||||||
|
begin
|
||||||
|
neighbor^.Axes[AAxis].NeedsUpdate := True;
|
||||||
|
if not neighbor^.Axes[otherAxis].NeedsUpdate then
|
||||||
|
AWorkQueue.Enqueue(neighborPos);
|
||||||
|
end;
|
||||||
|
neighborPos := neighborPos + direction^;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
function TNodeMap.FindStepNodeMinimum(const AAxis: TAxisId; constref APosition: TPoint): Cardinal;
|
||||||
|
var
|
||||||
|
otherAxis: TAxisId;
|
||||||
|
direction: PPoint;
|
||||||
|
acc: Cardinal;
|
||||||
|
neighborPos, start, stop: TPoint;
|
||||||
|
isStartReached: Boolean;
|
||||||
|
neighbor: TNode;
|
||||||
|
begin
|
||||||
|
otherAxis := COtherAxes[AAxis];
|
||||||
|
Result := Cardinal.MaxValue;
|
||||||
|
|
||||||
|
for direction in CAxisDirections[AAxis] do
|
||||||
|
begin
|
||||||
|
acc := 0;
|
||||||
|
isStartReached := False;
|
||||||
|
neighborPos := APosition + direction^;
|
||||||
|
start := Point(APosition.X + direction^.X * FMinStraight, APosition.Y + direction^.Y * FMinStraight);
|
||||||
|
if IsPositionInMap(start) then
|
||||||
|
begin
|
||||||
|
stop := Point(APosition.X + direction^.X * (FMaxStraight + 1), APosition.Y + direction^.Y * (FMaxStraight + 1));
|
||||||
|
ClampPositionToMap(stop);
|
||||||
|
while neighborPos <> stop do
|
||||||
|
begin
|
||||||
|
if neighborPos = start then
|
||||||
|
isStartReached := True;
|
||||||
|
neighbor := GetNode(neighborPos);
|
||||||
|
Inc(acc, neighbor.LocalHeatLoss);
|
||||||
|
if isStartReached and neighbor.Axes[otherAxis].IsTraversed then
|
||||||
|
Result := Min(Result, neighbor.Axes[otherAxis].Minimum + acc);
|
||||||
|
neighborPos := neighborPos + direction^;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
constructor TNodeMap.Create;
|
||||||
|
begin
|
||||||
|
FNodes := TNodeArrays.Create;
|
||||||
|
end;
|
||||||
|
|
||||||
|
destructor TNodeMap.Destroy;
|
||||||
|
begin
|
||||||
|
FNodes.Free;
|
||||||
|
inherited Destroy;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TNodeMap.AddNodes(const ALine: string);
|
||||||
|
var
|
||||||
|
i: Integer;
|
||||||
|
nodes: TNodeArray;
|
||||||
|
axis: TAxisId;
|
||||||
|
begin
|
||||||
|
FWidth := Length(ALine);
|
||||||
|
SetLength(nodes, FWidth);
|
||||||
|
for i := 0 to FWidth - 1 do
|
||||||
|
begin
|
||||||
|
nodes[i].LocalHeatLoss := StrToInt(ALine[i + 1]);
|
||||||
|
for axis in TAxisId do
|
||||||
|
begin
|
||||||
|
nodes[i].Axes[axis].IsTraversed := False;
|
||||||
|
nodes[i].Axes[axis].NeedsUpdate := False;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
FNodes.Add(nodes);
|
||||||
|
end;
|
||||||
|
|
||||||
|
function TNodeMap.FindMinimumPathLength(const AMinStraight, AMaxStraight: Integer): Cardinal;
|
||||||
|
var
|
||||||
|
queue: TWorkQueue;
|
||||||
|
position: TPoint;
|
||||||
|
node: PNode;
|
||||||
|
axis: TAxisId;
|
||||||
|
start: TNode;
|
||||||
|
newMinimum: Cardinal;
|
||||||
|
begin
|
||||||
|
FMinStraight := AMinStraight;
|
||||||
|
FMaxStraight := AMaxStraight;
|
||||||
|
|
||||||
|
queue := TWorkQueue.Create;
|
||||||
|
InitWorkQueue(queue);
|
||||||
|
|
||||||
|
// Processes work queue.
|
||||||
|
while queue.Count > 0 do
|
||||||
|
begin
|
||||||
|
position := queue.Dequeue;
|
||||||
|
node := GetPNode(position);
|
||||||
|
|
||||||
|
for axis in TAxisId do
|
||||||
|
if node^.Axes[axis].NeedsUpdate then
|
||||||
|
begin
|
||||||
|
node^.Axes[axis].NeedsUpdate := False;
|
||||||
|
// Finds minimum for one step from this node along this axis.
|
||||||
|
newMinimum := FindStepNodeMinimum(axis, position);
|
||||||
|
if not node^.Axes[axis].IsTraversed or (node^.Axes[axis].Minimum > newMinimum) then
|
||||||
|
begin
|
||||||
|
// Updates this axis minimum and queues update for neighbors on the other axis.
|
||||||
|
node^.Axes[axis].IsTraversed := True;
|
||||||
|
node^.Axes[axis].Minimum := newMinimum;
|
||||||
|
InvalidateNeighbors(queue, COtherAxes[axis], position);
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
queue.Free;
|
||||||
|
|
||||||
|
start := GetNode(Point(0, 0));
|
||||||
|
Result := Min(start.Axes[axHorizontal].Minimum, start.Axes[axVertical].Minimum);
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TNodeMap.Reset;
|
||||||
|
var
|
||||||
|
i, j: Integer;
|
||||||
|
axis: TAxisId;
|
||||||
|
begin
|
||||||
|
for i := 0 to Width - 1 do
|
||||||
|
for j := 0 to Height - 1 do
|
||||||
|
for axis in TAxisId do
|
||||||
|
begin
|
||||||
|
FNodes[j][i].Axes[axis].IsTraversed := False;
|
||||||
|
FNodes[j][i].Axes[axis].NeedsUpdate := False;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
{ TClumsyCrucible }
|
{ TClumsyCrucible }
|
||||||
|
|
||||||
procedure TClumsyCrucible.InvalidateHorizontalNeighbors(constref APosition: TPoint; constref AWorkQueue: TWorkQueue);
|
|
||||||
var
|
|
||||||
i: Integer;
|
|
||||||
begin
|
|
||||||
for i := Min(FWidth - 1, APosition.X + CMaxStraight) downto Max(0, APosition.X - CMaxStraight) do
|
|
||||||
if (i <> APosition.X) and not FMap[APosition.Y][i].Horizontal.NeedsUpdate then
|
|
||||||
begin
|
|
||||||
FMap[APosition.Y][i].Horizontal.NeedsUpdate := True;
|
|
||||||
if not FMap[APosition.Y][i].Vertical.NeedsUpdate then
|
|
||||||
AWorkQueue.Enqueue(Point(i, APosition.Y));
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
|
|
||||||
procedure TClumsyCrucible.InvalidateVerticalNeighbors(constref APosition: TPoint; constref AWorkQueue: TWorkQueue);
|
|
||||||
var
|
|
||||||
i: Integer;
|
|
||||||
begin
|
|
||||||
for i := Min(FMap.Count - 1, APosition.Y + CMaxStraight) downto Max(0, APosition.Y - CMaxStraight) do
|
|
||||||
if (i <> APosition.Y) and not FMap[i][APosition.X].Vertical.NeedsUpdate then
|
|
||||||
begin
|
|
||||||
FMap[i][APosition.X].Vertical.NeedsUpdate := True;
|
|
||||||
if not FMap[i][APosition.X].Horizontal.NeedsUpdate then
|
|
||||||
AWorkQueue.Enqueue(Point(APosition.X, i));
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
|
|
||||||
constructor TClumsyCrucible.Create;
|
constructor TClumsyCrucible.Create;
|
||||||
begin
|
begin
|
||||||
FMap := TNodeArrays.Create;
|
FMap := TNodeMap.Create;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
destructor TClumsyCrucible.Destroy;
|
destructor TClumsyCrucible.Destroy;
|
||||||
|
@ -114,116 +333,15 @@ begin
|
||||||
end;
|
end;
|
||||||
|
|
||||||
procedure TClumsyCrucible.ProcessDataLine(const ALine: string);
|
procedure TClumsyCrucible.ProcessDataLine(const ALine: string);
|
||||||
var
|
|
||||||
i: Integer;
|
|
||||||
nodes: TNodeArray;
|
|
||||||
begin
|
begin
|
||||||
FWidth := Length(ALine);
|
FMap.AddNodes(ALine);
|
||||||
SetLength(nodes, FWidth);
|
|
||||||
for i := 0 to FWidth - 1 do
|
|
||||||
begin
|
|
||||||
nodes[i].LocalHeatLoss := StrToInt(ALine[i + 1]);
|
|
||||||
nodes[i].Horizontal.IsTraversed := False;
|
|
||||||
nodes[i].Horizontal.NeedsUpdate := False;
|
|
||||||
nodes[i].Vertical.IsTraversed := False;
|
|
||||||
nodes[i].Vertical.NeedsUpdate := False;
|
|
||||||
end;
|
|
||||||
FMap.Add(nodes);
|
|
||||||
end;
|
end;
|
||||||
|
|
||||||
procedure TClumsyCrucible.Finish;
|
procedure TClumsyCrucible.Finish;
|
||||||
var
|
|
||||||
queue: TWorkQueue;
|
|
||||||
position: TPoint;
|
|
||||||
node: TNode;
|
|
||||||
newMinimum, acc: Cardinal;
|
|
||||||
i: Integer;
|
|
||||||
begin
|
begin
|
||||||
queue := TWorkQueue.Create;
|
FPart1 := FMap.FindMinimumPathLength(CMinStraight, CMaxStraight);
|
||||||
|
FMap.Reset;
|
||||||
// Initializes work queue with end node.
|
FPart2 := FMap.FindMinimumPathLength(CUltraMinStraight, CUltraMaxStraight);
|
||||||
FMap.Last[FWidth - 1].Horizontal.Minimum := 0;
|
|
||||||
FMap.Last[FWidth - 1].Horizontal.IsTraversed := True;
|
|
||||||
FMap.Last[FWidth - 1].Vertical := FMap.Last[FWidth - 1].Horizontal;
|
|
||||||
position := Point(FWidth - 1, FMap.Count - 1);
|
|
||||||
InvalidateHorizontalNeighbors(position, queue);
|
|
||||||
InvalidateVerticalNeighbors(position, queue);
|
|
||||||
|
|
||||||
// Processes work queue.
|
|
||||||
while queue.Count > 0 do
|
|
||||||
begin
|
|
||||||
position := queue.Dequeue;
|
|
||||||
node := FMap[position.Y][position.X];
|
|
||||||
|
|
||||||
// Updates horizontal data.
|
|
||||||
if node.Horizontal.NeedsUpdate then
|
|
||||||
begin
|
|
||||||
node.Horizontal.NeedsUpdate := False;
|
|
||||||
|
|
||||||
// Checks for better minimum in left direction.
|
|
||||||
newMinimum := Cardinal.MaxValue;
|
|
||||||
acc := 0;
|
|
||||||
for i := position.X - 1 downto Max(0, position.X - CMaxStraight) do
|
|
||||||
begin
|
|
||||||
Inc(acc, FMap[position.Y][i].LocalHeatLoss);
|
|
||||||
if FMap[position.Y][i].Vertical.IsTraversed then
|
|
||||||
newMinimum := Min(newMinimum, FMap[position.Y][i].Vertical.Minimum + acc);
|
|
||||||
end;
|
|
||||||
// Checks for better minimum in right direction.
|
|
||||||
acc := 0;
|
|
||||||
for i := position.X + 1 to Min(FWidth - 1, position.X + CMaxStraight) do
|
|
||||||
begin
|
|
||||||
Inc(acc, FMap[position.Y][i].LocalHeatLoss);
|
|
||||||
if FMap[position.Y][i].Vertical.IsTraversed then
|
|
||||||
newMinimum := Min(newMinimum, FMap[position.Y][i].Vertical.Minimum + acc);
|
|
||||||
end;
|
|
||||||
// Updates horizontal minimum and queues update for neighbors.
|
|
||||||
if not node.Horizontal.IsTraversed or (node.Horizontal.Minimum > newMinimum) then
|
|
||||||
begin
|
|
||||||
node.Horizontal.IsTraversed := True;
|
|
||||||
node.Horizontal.Minimum := newMinimum;
|
|
||||||
InvalidateVerticalNeighbors(position, queue);
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
|
|
||||||
// Updates vertical data.
|
|
||||||
if node.Vertical.NeedsUpdate then
|
|
||||||
begin
|
|
||||||
node.Vertical.NeedsUpdate := False;
|
|
||||||
|
|
||||||
// Checks for better minimum in up direction.
|
|
||||||
newMinimum := Cardinal.MaxValue;
|
|
||||||
acc := 0;
|
|
||||||
for i := position.Y - 1 downto Max(0, position.Y - CMaxStraight) do
|
|
||||||
begin
|
|
||||||
Inc(acc, FMap[i][position.X].LocalHeatLoss);
|
|
||||||
if FMap[i][position.X].Horizontal.IsTraversed then
|
|
||||||
newMinimum := Min(newMinimum, FMap[i][position.X].Horizontal.Minimum + acc);
|
|
||||||
end;
|
|
||||||
// Checks for better minimum in down direction.
|
|
||||||
acc := 0;
|
|
||||||
for i := position.Y + 1 to Min(FMap.Count - 1, position.Y + CMaxStraight) do
|
|
||||||
begin
|
|
||||||
Inc(acc, FMap[i][position.X].LocalHeatLoss);
|
|
||||||
if FMap[i][position.X].Horizontal.IsTraversed then
|
|
||||||
newMinimum := Min(newMinimum, FMap[i][position.X].Horizontal.Minimum + acc);
|
|
||||||
end;
|
|
||||||
// Updates vertical minimum and queues update for neighbors.
|
|
||||||
if not node.Vertical.IsTraversed or (node.Vertical.Minimum > newMinimum) then
|
|
||||||
begin
|
|
||||||
node.Vertical.IsTraversed := True;
|
|
||||||
node.Vertical.Minimum := newMinimum;
|
|
||||||
InvalidateHorizontalNeighbors(position, queue);
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
|
|
||||||
FMap[position.Y][position.X] := node;
|
|
||||||
end;
|
|
||||||
|
|
||||||
queue.Free;
|
|
||||||
|
|
||||||
node := FMap[0][0];
|
|
||||||
FPart1 := Min(node.Horizontal.Minimum, node.Vertical.Minimum);
|
|
||||||
end;
|
end;
|
||||||
|
|
||||||
function TClumsyCrucible.GetDataFileName: string;
|
function TClumsyCrucible.GetDataFileName: string;
|
||||||
|
|
|
@ -33,6 +33,22 @@ type
|
||||||
function CreateSolver: ISolver; override;
|
function CreateSolver: ISolver; override;
|
||||||
published
|
published
|
||||||
procedure TestPart1;
|
procedure TestPart1;
|
||||||
|
procedure TestPart2;
|
||||||
|
end;
|
||||||
|
|
||||||
|
{ TExample2ClumsyCrucible }
|
||||||
|
|
||||||
|
TExample2ClumsyCrucible = class(TClumsyCrucible)
|
||||||
|
function GetDataFileName: string; override;
|
||||||
|
end;
|
||||||
|
|
||||||
|
{ TClumsyCrucibleExample2TestCase }
|
||||||
|
|
||||||
|
TClumsyCrucibleExample2TestCase = class(TExampleEngineBaseTest)
|
||||||
|
protected
|
||||||
|
function CreateSolver: ISolver; override;
|
||||||
|
published
|
||||||
|
procedure TestPart2;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
implementation
|
implementation
|
||||||
|
@ -49,8 +65,33 @@ begin
|
||||||
AssertEquals(102, FSolver.GetResultPart1);
|
AssertEquals(102, FSolver.GetResultPart1);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
procedure TClumsyCrucibleExampleTestCase.TestPart2;
|
||||||
|
begin
|
||||||
|
AssertEquals(94, FSolver.GetResultPart2);
|
||||||
|
end;
|
||||||
|
|
||||||
|
{ TExample2ClumsyCrucible }
|
||||||
|
|
||||||
|
function TExample2ClumsyCrucible.GetDataFileName: string;
|
||||||
|
begin
|
||||||
|
Result := 'clumsy_crucible2.txt';
|
||||||
|
end;
|
||||||
|
|
||||||
|
{ TClumsyCrucibleExample2TestCase }
|
||||||
|
|
||||||
|
function TClumsyCrucibleExample2TestCase.CreateSolver: ISolver;
|
||||||
|
begin
|
||||||
|
Result := TExample2ClumsyCrucible.Create;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TClumsyCrucibleExample2TestCase.TestPart2;
|
||||||
|
begin
|
||||||
|
AssertEquals(71, FSolver.GetResultPart2);
|
||||||
|
end;
|
||||||
|
|
||||||
initialization
|
initialization
|
||||||
|
|
||||||
RegisterTest('TClumsyCrucible', TClumsyCrucibleExampleTestCase);
|
RegisterTest('TClumsyCrucible', TClumsyCrucibleExampleTestCase);
|
||||||
|
RegisterTest('TClumsyCrucible', TClumsyCrucibleExample2TestCase);
|
||||||
end.
|
end.
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue