diff --git a/AdventOfCode.lpr b/AdventOfCode.lpr index 5d362eb..0bcdded 100644 --- a/AdventOfCode.lpr +++ b/AdventOfCode.lpr @@ -54,7 +54,10 @@ var n: Integer; begin WriteLn('### Advent of Code 2023 ###'); - engine := TSolverEngine.Create('data'); + engine := TSolverEngine.Create(TStringArray.Create( + 'data', + ConcatPaths(['..', '..', 'data']) + )); solvers := specialize TList.Create; if HasOption('p', 'puzzle') then diff --git a/USolver.pas b/USolver.pas index 266d280..79350c2 100644 --- a/USolver.pas +++ b/USolver.pas @@ -63,13 +63,15 @@ type TSolverEngine = class private - FRelativeDataPath: string; + FRelativeDataPaths: TStringArray; public - constructor Create(const ARelativeDataPath: string); + constructor Create(constref ARelativeDataPaths: TStringArray); procedure ProcessData(const ASolver: ISolver); procedure Run(const ASolver: ISolver); procedure RunAndFree(const ASolver: ISolver); - function GetDataFileName(const ASolver: ISolver): string; + function HasValidDataPath(const ASolver: ISolver): Boolean; + function TryGetFirstValidDataPath(const ASolver: ISolver; out ODataFilePath: string): Boolean; + function GetInvalidDataPathMessage(const ASolver: ISolver): string; end; implementation @@ -94,19 +96,22 @@ end; { TSolverEngine } -constructor TSolverEngine.Create(const ARelativeDataPath: string); +constructor TSolverEngine.Create(constref ARelativeDataPaths: TStringArray); begin - FRelativeDataPath := ARelativeDataPath; + if (ARelativeDataPaths = nil) or (Length(ARelativeDataPaths) = 0) then + raise EArgumentOutOfRangeException.Create('Must specify at least one data path.'); + FRelativeDataPaths := ARelativeDataPaths; end; procedure TSolverEngine.ProcessData(const ASolver: ISolver); var data: TextFile; - s: string; + dataFilePath, s: string; begin ASolver.Init; - AssignFile(data, GetDataFileName(ASolver)); + TryGetFirstValidDataPath(ASolver, dataFilePath); + AssignFile(data, dataFilePath); try reset(data); while (not EOF(data)) do @@ -122,22 +127,17 @@ begin end; procedure TSolverEngine.Run(const ASolver: ISolver); -var - fileName: string; begin WriteLn; WriteLn('--- ', ASolver.PuzzleName, ' ---'); - fileName := GetDataFileName(ASolver); - if FileExists(fileName) then + if HasValidDataPath(ASolver) then begin ProcessData(ASolver); WriteLn('Part 1: ', ASolver.ResultPart1); WriteLn('Part 2: ', ASolver.ResultPart2); end - else begin - WriteLn('Cannot find puzzle input file ''', ExpandFileName(fileName), '''.'); - WriteLn('Please download the file content from https://adventofcode.com/2023/'); - end; + else + WriteLn(GetInvalidDataPathMessage(ASolver)); end; procedure TSolverEngine.RunAndFree(const ASolver: ISolver); @@ -146,9 +146,41 @@ begin ASolver.Free; end; -function TSolverEngine.GetDataFileName(const ASolver: ISolver): string; +function TSolverEngine.HasValidDataPath(const ASolver: ISolver): Boolean; +var + s: string; begin - Result := ConcatPaths([FRelativeDataPath, ASolver.DataFileName]); + Result := TryGetFirstValidDataPath(ASolver, s); +end; + +function TSolverEngine.TryGetFirstValidDataPath(const ASolver: ISolver; out ODataFilePath: string): Boolean; +var + path: string; +begin + for path in FRelativeDataPaths do + begin + ODataFilePath := ConcatPaths([path, ASolver.DataFileName]); + if FileExists(ODataFilePath) then + begin + Result := True; + Exit; + end; + end; + + Result := False; + ODataFilePath := ''; +end; + +function TSolverEngine.GetInvalidDataPathMessage(const ASolver: ISolver): string; +var + i: Integer; +begin + Result := 'Cannot find puzzle input file ''' + + ExpandFileName(ConcatPaths([FRelativeDataPaths[0], ASolver.DataFileName])); + for i := 1 to Length(FRelativeDataPaths) - 1 do + Result := Result + ''', or ''' + + ExpandFileName(ConcatPaths([FRelativeDataPaths[i], ASolver.DataFileName])); + Result := Result + '''. Please download the file content from https://adventofcode.com/2023/'; end; end. diff --git a/tests/UBaseTestCases.pas b/tests/UBaseTestCases.pas index 5f9d64a..27a0304 100644 --- a/tests/UBaseTestCases.pas +++ b/tests/UBaseTestCases.pas @@ -43,14 +43,14 @@ type FEngine: TSolverEngine; procedure Setup; override; procedure TearDown; override; - function GetDataPath: string; virtual; + function GetDataPaths: TStringArray; virtual; end; { TExampleEngineBaseTest } TExampleEngineBaseTest = class(TEngineBaseTest) protected - function GetDataPath: string; override; + function GetDataPaths: TStringArray; override; end; implementation @@ -72,15 +72,10 @@ end; { TEngineBaseTest } procedure TEngineBaseTest.Setup; -var - fileName: string; begin inherited Setup; - FEngine := TSolverEngine.Create(GetDataPath); - fileName := FEngine.GetDataFileName(FSolver); - AssertTrue('Cannot find puzzle input file ''' + ExpandFileName(fileName) + '''. ' - + 'Please download the file from https://adventofcode.com/2023/', - FileExists(fileName)); + FEngine := TSolverEngine.Create(GetDataPaths); + AssertTrue(FEngine.GetInvalidDataPathMessage(FSolver), FEngine.HasValidDataPath(FSolver)); FEngine.ProcessData(FSolver); end; @@ -90,16 +85,22 @@ begin inherited TearDown; end; -function TEngineBaseTest.GetDataPath: string; +function TEngineBaseTest.GetDataPaths: TStringArray; begin - Result := ConcatPaths(['..', '..', 'bin', 'data']); + Result := TStringArray.Create( + ConcatPaths(['..', '..', 'bin', 'data']), + ConcatPaths(['..', '..', '..', 'data']) + ); end; { TExampleEngineBaseTest } -function TExampleEngineBaseTest.GetDataPath: string; +function TExampleEngineBaseTest.GetDataPaths: TStringArray; begin - Result := 'example_data'; + Result := TStringArray.Create( + ConcatPaths(['..', '..', 'bin', 'data', 'example']), + ConcatPaths(['..', '..', '..', 'data', 'example']) + ); end; end.