Passed
Push — main ( f4e9d2...4a4379 )
by Jesse
01:41
created

chooseSettings()   A

Complexity

Conditions 3
Paths 2

Size

Total Lines 7
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 3
eloc 4
c 0
b 0
f 0
nc 2
nop 0
dl 0
loc 7
rs 10
1
<?php declare(strict_types=1);
2
3
use Stratadox\PuzzleSolver\Find;
4
use Stratadox\PuzzleSolver\NoSolution;
5
use Stratadox\PuzzleSolver\PuzzleDescription;
6
use Stratadox\PuzzleSolver\PuzzleFactory;
7
use Stratadox\PuzzleSolver\PuzzleSolverFactory;
8
use Stratadox\PuzzleSolver\Renderer\MovesToFileRenderer;
9
use Stratadox\PuzzleSolver\Renderer\PuzzleStatesToFileRenderer;
10
use Stratadox\PuzzleSolver\SearchSettings;
11
use Stratadox\PuzzleSolver\SolutionRenderer;
12
use Stratadox\PuzzleSolver\Solutions;
13
14
require dirname(__DIR__) . '/vendor/autoload.php';
15
16
const OUT = 'php://stdout';
17
18
$directory = dirname(__DIR__) . str_replace(
19
    '/',
20
    DIRECTORY_SEPARATOR,
21
    '/sample/levels/%s/'
22
);
23
24
$json = file_get_contents(dirname(__DIR__) . '/sample/puzzles.json');
25
$puzzleData = json_decode($json, true);
26
$factories = [];
27
foreach ($puzzleData['factories'] as $type => $factory) {
28
    assert(is_callable($factory));
29
    $factories[$type] = $factory();
30
}
31
32
$sep = str_repeat(PHP_EOL, 40);
33
$time = 600000;
34
$puzzleInfo = [];
35
foreach ($puzzleData['puzzles'] as $puzzle) {
36
    $dir = sprintf($directory, $puzzle['type']);
37
    $description = new PuzzleDescription(
38
        Find::fromString($puzzle['find']),
39
        $puzzle['variable_cost'] ?? false,
40
        $puzzle['exhausting'] ?? false,
41
        isset($puzzle['heuristic']) ? new $puzzle['heuristic']() : null
42
    );
43
    $renderer = ($puzzle['display'] ?? 'states') === 'states' ?
44
        PuzzleStatesToFileRenderer::fromFilenameAndSeparator(OUT, $sep, $time) :
45
        MovesToFileRenderer::fromFilenameAndSeparator(OUT, $sep, $time);
46
47
    $puzzleInfo[$puzzle['name']] = [];
48
    foreach (scandir($dir) as $file) {
49
        if (substr($file, -4) !== '.txt') {
50
            continue;
51
        }
52
        $puzzleInfo[$puzzle['name']][$file[0]] = [
53
            'levelFile' => $dir . $file,
54
            'factory' => $factories[$puzzle['type']],
55
            'description' => $description,
56
            'renderer' => $renderer,
57
            'isDefault' => (($puzzle['default'] ?? '') . '.txt') ===  $file,
58
            'isTheOnlyOne' => (bool) ($puzzle['level'] ?? false),
59
        ];
60
    }
61
}
62
63
$puzzleName = choosePuzzle($puzzleInfo);
64
echo PHP_EOL . 'Selected puzzle: ' . $puzzleName . PHP_EOL . PHP_EOL;
65
$levelTag = chooseLevel($puzzleInfo, $puzzleName);
66
$puzzleData = $puzzleInfo[$puzzleName][$levelTag];
67
$level = file_get_contents($puzzleData['levelFile']);
68
if (!empty($level)) {
69
    echo 'The chosen level is: ' . PHP_EOL . PHP_EOL . $level . PHP_EOL . PHP_EOL;
70
}
71
$settings = chooseSettings();
72
$solutions = solvePuzzle(
73
    $puzzleData['factory'],
74
    $puzzleData['description'],
75
    $settings,
76
    $level
77
);
78
render($solutions, $puzzleData['renderer']);
79
80
function choosePuzzle(array $puzzleInfo): string
81
{
82
    $puzzleChoices = array_keys($puzzleInfo);
83
    $welcome = [
84
        'Welcome to the universal puzzle solver!',
85
        'The following puzzles are installed:'
86
    ];
87
    foreach ($puzzleChoices as $n => $name) {
88
        $welcome[] = sprintf('Type %d: %s', $n, $name);
89
    }
90
91
    echo implode(PHP_EOL, $welcome) . PHP_EOL . PHP_EOL;
92
93
    do {
94
        $puzzleChoice = readline('Which puzzle would you like to solve? ');
95
    } while (!ctype_digit($puzzleChoice));
96
97
    return $puzzleChoices[$puzzleChoice];
98
}
99
100
function chooseLevel(array $puzzleInfo, string $name): string
101
{
102
    $welcome = [
103
        'The following levels are available:'
104
    ];
105
    foreach ($puzzleInfo[$name] as $letter => $puzzleDate) {
106
        if ($puzzleDate['isTheOnlyOne']) {
107
            return $letter;
108
        }
109
        $n = str_replace(' ', '', substr(basename($puzzleDate['levelFile']), 2, -4));
110
        $welcome[] = sprintf('%s: %s', $letter, $n);
111
    }
112
113
    echo implode(PHP_EOL, $welcome) . PHP_EOL . PHP_EOL;
114
115
    do {
116
        $levelChoice = strtoupper(readline('Which level would you like to solve? '));
117
    } while (strlen($levelChoice) !== 1);
118
119
    return $levelChoice;
120
}
121
122
function chooseSettings()
123
{
124
    $visual = readline('Would you like to visualise the search? [Y]/n ');
125
    if ($visual !== '' && strtolower($visual) !== 'y') {
126
        return SearchSettings::defaults();
127
    }
128
    return new SearchSettings(OUT, str_repeat(PHP_EOL, 40), 80000);
129
}
130
131
function solvePuzzle(
132
    PuzzleFactory $puzzleFactory,
133
    PuzzleDescription $description,
134
    SearchSettings $settings,
135
    string $level
136
): Solutions {
137
    $puzzle = $puzzleFactory->fromString($level);
138
139
    $solver = PuzzleSolverFactory::make()
140
        ->forAPuzzleWith($description, $settings);
141
142
    try {
143
        return $solver->solve($puzzle);
144
    } catch (NoSolution $exception) {
145
        die($exception->getMessage());
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
Bug Best Practice introduced by
In this branch, the function will implicitly return null which is incompatible with the type-hinted return Stratadox\PuzzleSolver\Solutions. Consider adding a return statement or allowing null as return value.

For hinted functions/methods where all return statements with the correct type are only reachable via conditions, ?null? gets implicitly returned which may be incompatible with the hinted type. Let?s take a look at an example:

interface ReturnsInt {
    public function returnsIntHinted(): int;
}

class MyClass implements ReturnsInt {
    public function returnsIntHinted(): int
    {
        if (foo()) {
            return 123;
        }
        // here: null is implicitly returned
    }
}
Loading history...
146
    }
147
}
148
149
150
function render(Solutions $solutions, SolutionRenderer $renderer)
151
{
152
    $nl = str_repeat(PHP_EOL, 40);
153
    echo $nl . 'Solved!' . str_repeat(PHP_EOL, 5);
154
    sleep(1);
155
    if (count($solutions) === 1) {
156
        $renderer->render($solutions[0]);
157
        echo $nl . $solutions[0]->state()->representation();
158
    } else {
159
        foreach ($solutions as $i => $solution) {
160
            echo sprintf('%sSolution %d: %s', $nl, $i + 1, str_repeat(PHP_EOL, 5));
161
            sleep(1);
162
            echo $nl;
163
            $renderer->render($solution);
164
            echo $nl . $solution->state()->representation();
165
        }
166
    }
167
}
168