1
|
|
|
<?php declare(strict_types=1); |
2
|
|
|
|
3
|
|
|
namespace Stratadox\PuzzleSolver; |
4
|
|
|
|
5
|
|
|
use Stratadox\PuzzleSolver\SearchStrategy\BestFirstStrategyFactory; |
6
|
|
|
use Stratadox\PuzzleSolver\SearchStrategy\BreadthFirstStrategyFactory; |
7
|
|
|
use Stratadox\PuzzleSolver\SearchStrategy\DebugLoggerFactory; |
8
|
|
|
use Stratadox\PuzzleSolver\SearchStrategy\DepthFirstStrategyFactory; |
9
|
|
|
use Stratadox\PuzzleSolver\SearchStrategy\DuplicateNodeSkipperFactory; |
10
|
|
|
use Stratadox\PuzzleSolver\SearchStrategy\SearchStrategyFactory; |
11
|
|
|
use Stratadox\PuzzleSolver\SearchStrategy\VisitedNodeCostCheckerFactory; |
12
|
|
|
use Stratadox\PuzzleSolver\SearchStrategy\VisitedNodeSkipperFactory; |
13
|
|
|
use Stratadox\PuzzleSolver\SearchStrategy\WorseThanBestSolutionSkipperFactory; |
14
|
|
|
use Stratadox\PuzzleSolver\Solver\EagerSolver; |
15
|
|
|
use Stratadox\PuzzleSolver\Solver\LazySolver; |
16
|
|
|
|
17
|
|
|
/** |
18
|
|
|
* Factory for @see PuzzleSolver |
19
|
|
|
* |
20
|
|
|
* @author Stratadox |
21
|
|
|
*/ |
22
|
|
|
final class PuzzleSolverFactory implements SolverFactory |
23
|
|
|
{ |
24
|
|
|
public function forAPuzzleWith( |
25
|
|
|
PuzzleDescription $puzzle, |
26
|
|
|
SearchSettings $settings |
27
|
|
|
): PuzzleSolver { |
28
|
|
|
return $puzzle->singleSolution() ? |
29
|
|
|
EagerSolver::using($this->add($settings, $this->eagerStrategy($puzzle))) : |
30
|
|
|
LazySolver::using($this->add($settings, $this->lazyStrategy($puzzle))); |
31
|
|
|
} |
32
|
|
|
|
33
|
|
|
private function add( |
34
|
|
|
SearchSettings $settings, |
35
|
|
|
SearchStrategyFactory $strategyFactory |
36
|
|
|
): SearchStrategyFactory { |
37
|
|
|
if (null === $settings->loggingFile()) { |
38
|
|
|
return $strategyFactory; |
39
|
|
|
} |
40
|
|
|
return DebugLoggerFactory::make( |
41
|
|
|
$settings->iterationInterval(), |
42
|
|
|
$strategyFactory, |
43
|
|
|
$settings->logSeparator(), |
44
|
|
|
$settings->loggingFile() |
45
|
|
|
); |
46
|
|
|
} |
47
|
|
|
|
48
|
|
|
/** |
49
|
|
|
* Retrieves the eagerStrategy, based on the current description. |
50
|
|
|
* |
51
|
|
|
* @return SearchStrategyFactory |
52
|
|
|
* |
53
|
|
|
* @todo Add more test puzzles that require a single solution, to challenge |
54
|
|
|
* the assumptions currently made. |
55
|
|
|
*/ |
56
|
|
|
private function eagerStrategy(PuzzleDescription $puzzle): SearchStrategyFactory |
57
|
|
|
{ |
58
|
|
|
if (null !== $puzzle->heuristic()) { |
59
|
|
|
return VisitedNodeCostCheckerFactory::using( |
60
|
|
|
BestFirstStrategyFactory::withHeuristic($puzzle->heuristic()) |
61
|
|
|
); |
62
|
|
|
} |
63
|
|
|
if ($puzzle->isWeightedMoves()) { |
64
|
|
|
return VisitedNodeCostCheckerFactory::using( |
65
|
|
|
BestFirstStrategyFactory::noHeuristic() |
66
|
|
|
); |
67
|
|
|
} |
68
|
|
|
return VisitedNodeSkipperFactory::using( |
69
|
|
|
$puzzle->isExhausting() ? |
70
|
|
|
DepthFirstStrategyFactory::make() : |
71
|
|
|
BreadthFirstStrategyFactory::make() |
72
|
|
|
); |
73
|
|
|
} |
74
|
|
|
|
75
|
|
|
/** |
76
|
|
|
* Retrieves the lazyStrategy, based on the current description. |
77
|
|
|
* |
78
|
|
|
* @return SearchStrategyFactory |
79
|
|
|
* |
80
|
|
|
* @todo Add more test puzzles that require multiple solutions, to challenge |
81
|
|
|
* the assumptions currently made. |
82
|
|
|
*/ |
83
|
|
|
private function lazyStrategy(PuzzleDescription $puzzle): SearchStrategyFactory |
84
|
|
|
{ |
85
|
|
|
if ($puzzle->isExhausting()) { |
86
|
|
|
return VisitedNodeSkipperFactory::using( |
87
|
|
|
DepthFirstStrategyFactory::make() |
88
|
|
|
); |
89
|
|
|
} |
90
|
|
|
if ($puzzle->onlyBest()) { |
91
|
|
|
return WorseThanBestSolutionSkipperFactory::using( |
92
|
|
|
BreadthFirstStrategyFactory::make() |
93
|
|
|
); |
94
|
|
|
} |
95
|
|
|
return DuplicateNodeSkipperFactory::using( |
96
|
|
|
DepthFirstStrategyFactory::make() |
97
|
|
|
); |
98
|
|
|
} |
99
|
|
|
} |
100
|
|
|
|