Added solution for "Day 3: Gear Ratios", part 2

This commit is contained in:
Stefan Müller 2023-12-04 16:34:26 +01:00 committed by Stefan Müller
parent 015b823376
commit 9c4fb8b63a
3 changed files with 187 additions and 18 deletions

View File

@ -22,21 +22,59 @@ unit UGearRatios;
interface
uses
Classes, SysUtils, USolver;
Classes, SysUtils, fgl, USolver;
const
CDigitChars = ['0'..'9'];
CNonSymbolChars = ['0'..'9', '.'];
CGearChar = '*';
type
{ TGearCandidate }
TGearCandidate = class
private
FColumn, FPartNumber1, FPartNumber2, FPartNumber3: Integer;
public
constructor Create(AColumn: Integer);
procedure AddPartNumber(APartNumber: Integer);
function GetGearRatio: Integer;
property Column: Integer read FColumn;
end;
{ TGearCandidates }
TGearCandidates = specialize TFPGObjectList<TGearCandidate>;
{ TGearCandidateTracker }
TGearCandidateTracker = class
private
FPreviousLine, FLine, FNextLine, FCurrentNumber: TGearCandidates;
public
constructor Create;
destructor Destroy; override;
function GetLine(AIndex: Integer): TGearCandidates;
function FinishLine: Integer;
property PreviousLine: TGearCandidates read FPreviousLine;
property Line: TGearCandidates read FLine;
property NextLine: TGearCandidates read FNextLine;
property CurrentNumber: TGearCandidates read FCurrentNumber;
end;
{ TGearRatios }
TGearRatios = class(TSolver)
private
FPreviousLine, FLine: string;
FGearTracker: TGearCandidateTracker;
procedure ProcessDataLineTriplet(const APreviousLine, ALine, ANextLine: string);
function HasSymbolCharInColumn(const AIndex: Integer; const ALines: array of string): Boolean;
procedure LogForCurrentGearCandidates(const APartNumber: Integer);
public
constructor Create;
destructor Destroy; override;
procedure ProcessDataLine(const ALine: string); override;
procedure Finish; override;
function GetDataFileName: string; override;
@ -45,6 +83,80 @@ type
implementation
{ TGearCandidate }
constructor TGearCandidate.Create(AColumn: Integer);
begin
FColumn := AColumn;
FPartNumber1 := 0;
FPartNumber2 := 0;
FPartNumber3 := 0;
end;
procedure TGearCandidate.AddPartNumber(APartNumber: Integer);
begin
if FPartNumber1 = 0 then
FPartNumber1 := APartNumber
else if FPartNumber2 = 0 then
FPartNumber2 := APartNumber
else
FPartNumber3 := APartNumber;
end;
function TGearCandidate.GetGearRatio: Integer;
begin
if FPartNumber3 = 0 then
Result := FPartNumber1 * FPartNumber2
else
Result := 0;
end;
{ TGearCandidateTracker }
constructor TGearCandidateTracker.Create;
begin
FPreviousLine := TGearCandidates.Create;
FLine := TGearCandidates.Create;
FNextLine := TGearCandidates.Create;
FCurrentNumber := TGearCandidates.Create(False);
end;
destructor TGearCandidateTracker.Destroy;
begin
FPreviousLine.Free;
FLine.Free;
FNextLine.Free;
FCurrentNumber.Free;
inherited Destroy;
end;
function TGearCandidateTracker.GetLine(AIndex: Integer): TGearCandidates;
begin
case AIndex of
0: Result := FPreviousLine;
1: Result := FLine;
2: Result := FNextLine;
else Result := nil;
end;
end;
function TGearCandidateTracker.FinishLine: Integer;
var
candidate: TGearCandidate;
temp: TGearCandidates;
begin
Result := 0;
for candidate in FPreviousLine do
begin
Inc(Result, candidate.GetGearRatio);
end;
FPreviousLine.Clear;
temp := FPreviousLine;
FPreviousLine := FLine;
FLine := FNextLine;
FNextLine := temp;
end;
{ TGearRatios }
procedure TGearRatios.ProcessDataLineTriplet(const APreviousLine, ALine, ANextLine: string);
@ -61,35 +173,21 @@ begin
begin
inNumber := True;
numberStart := i;
FGearTracker.CurrentNumber.Clear;
// Checks for a symbol in the column before the first digit.
isPartNumber := (i > 1) and
(not (APreviousLine[i - 1] in CNonSymbolChars)
or not (ALine[i - 1] in CNonSymbolChars)
or not (ANextLine[i - 1] in CNonSymbolChars));
isPartNumber := (i > 1) and HasSymbolCharInColumn(i - 1, [APreviousLine, ALine, ANextLine]);
end;
// Checks for a symbol in the column of the current digit.
if inNumber and not isPartNumber and
(not (APreviousLine[i] in CNonSymbolChars)
or not (ANextLine[i] in CNonSymbolChars)) then
begin
if inNumber and HasSymbolCharInColumn(i, [APreviousLine, ALine, ANextLine]) then
isPartNumber := True;
end;
// Checks if number ends.
if inNumber and (not (ALine[i] in CDigitChars) or (i = ALine.Length)) then
begin
inNumber := False;
// Checks for a symbol in the column after the last digit.
if not (APreviousLine[i] in CNonSymbolChars)
or not (ALine[i] in CNonSymbolChars)
or not (ANextLine[i] in CNonSymbolChars) then
begin
isPartNumber := True;
end;
// Counts if it is a part number.
if isPartNumber then
begin
@ -98,9 +196,71 @@ begin
Inc(numberLength);
partNumber := StrToInt(Copy(ALine, numberStart, numberLength));
Inc(FPart1, partNumber);
LogForCurrentGearCandidates(partNumber);
end;
end;
end;
Inc(FPart2, FGearTracker.FinishLine);
end;
function TGearRatios.HasSymbolCharInColumn(const AIndex: Integer; const ALines: array of string): Boolean;
var
i: Integer;
candidates: TGearCandidates;
candidate: TGearCandidate;
exists: Boolean;
begin
Result := False;
for i := 0 to High(ALines) do
if not (ALines[i][AIndex] in CNonSymbolChars) then
begin
Result := True;
// Could exit here for part 1, the following is only for part 2.
if ALines[i][AIndex] = CGearChar then
begin
candidates := FGearTracker.GetLine(i);
exists := False;
for candidate in candidates do
begin
if candidate.Column = AIndex then
begin
// The current gear candidate has already been found.
FGearTracker.CurrentNumber.Add(candidate);
exists := True;
Break;
end;
end;
if not exists then
begin
// A new gear candidate was found.
candidate := TGearCandidate.Create(AIndex);
candidates.Add(candidate);
FGearTracker.CurrentNumber.Add(candidate);
end;
end;
end;
end;
procedure TGearRatios.LogForCurrentGearCandidates(const APartNumber: Integer);
var
candidate: TGearCandidate;
begin
for candidate in FGearTracker.CurrentNumber do
begin
candidate.AddPartNumber(APartNumber);
end;
end;
constructor TGearRatios.Create;
begin
FGearTracker := TGearCandidateTracker.Create;
end;
destructor TGearRatios.Destroy;
begin
FGearTracker.Free;
inherited Destroy;
end;
procedure TGearRatios.ProcessDataLine(const ALine: string);

View File

@ -44,6 +44,7 @@ type
procedure TearDown; override;
published
procedure TestPart1;
procedure TestPart2;
end;
{ TGearRatiosTestCase }
@ -97,6 +98,12 @@ begin
AssertEquals(530495, FSolver.GetResultPart1);
end;
procedure TGearRatiosFullDataTestCase.TestPart2;
begin
AssertEquals(80253814, FSolver.GetResultPart2);
end;
{ TGearRatiosTestCase }
procedure TGearRatiosTestCase.TestEndOfLineNumber;

View File

@ -11,6 +11,8 @@ TGearRatiosFullDataTestCase.Checked=1
TGearRatiosFullDataTestCase.Expanded=1
TGearRatiosFullDataTestCase.TestPart1.Checked=1
TGearRatiosFullDataTestCase.TestPart1.Expanded=0
TGearRatiosFullDataTestCase.TestPart2.Checked=1
TGearRatiosFullDataTestCase.TestPart2.Expanded=0
TGearRatiosTestCase.Checked=1
TGearRatiosTestCase.Expanded=1
TGearRatiosTestCase.TestEndOfLineNumber.Checked=1