Skip to content

Commit dab75e3

Browse files
mk-mxptomasnorre
authored andcommitted
Sync minesweeper (#701)
1 parent 09376cf commit dab75e3

4 files changed

Lines changed: 305 additions & 321 deletions

File tree

exercises/practice/minesweeper/.meta/config.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,8 @@
66
"arueckauer",
77
"kunicmarko20",
88
"kytrinyx",
9-
"neenjaw"
9+
"neenjaw",
10+
"mk-mxp"
1011
],
1112
"files": {
1213
"solution": [
Lines changed: 70 additions & 152 deletions
Original file line numberDiff line numberDiff line change
@@ -1,181 +1,99 @@
11
<?php
22

3-
/*
4-
* By adding type hints and enabling strict type checking, code can become
5-
* easier to read, self-documenting and reduce the number of potential bugs.
6-
* By default, type declarations are non-strict, which means they will attempt
7-
* to change the original type to match the type specified by the
8-
* type-declaration.
9-
*
10-
* In other words, if you pass a string to a function requiring a float,
11-
* it will attempt to convert the string value to a float.
12-
*
13-
* To enable strict mode, a single declare directive must be placed at the top
14-
* of the file.
15-
* This means that the strictness of typing is configured on a per-file basis.
16-
* This directive not only affects the type declarations of parameters, but also
17-
* a function's return type.
18-
*
19-
* For more info review the Concept on strict type checking in the PHP track
20-
* <link>.
21-
*
22-
* To disable strict typing, comment out the directive below.
23-
*/
24-
253
declare(strict_types=1);
264

27-
function solve($minesweeperBoard)
5+
class Minesweeper
286
{
29-
$minesweeperBoard = makeBoardFromString($minesweeperBoard);
30-
31-
validateBoardDimensions($minesweeperBoard);
32-
33-
validateBorders($minesweeperBoard);
34-
35-
$grid = removeBorders($minesweeperBoard);
7+
// In PHP < 8.1 `readonly` is unknown
8+
private array $minefield;
9+
private array $annotatedMinefield = [];
3610

37-
validateGridSize($grid);
38-
39-
validateContainsOnlyMines($grid);
11+
public function __construct(array $input)
12+
{
13+
$this->minefieldFrom($input);
14+
}
4015

41-
$gridWithResults = addMineCount($grid);
16+
public function annotate(): array
17+
{
18+
if (empty($this->annotatedMinefield)) {
19+
$this->annotateMinefield();
20+
}
4221

43-
return makeStringFromBoard(applyBorders($gridWithResults));
44-
}
22+
return $this->asAnnotatedMinefield();
23+
}
4524

46-
function makeBoardFromString($string)
47-
{
48-
return array_map('str_split', explode("\n", trim($string)));
49-
}
25+
private function minefieldFrom(array $input): void
26+
{
27+
$this->minefield = \array_map(
28+
// In PHP < 8.2, str_split returns [''] for empty strings.
29+
// In PHP >= 8.2 it returns the required [].
30+
fn ($row) => empty($row) ? [] : \str_split($row),
31+
$input,
32+
);
33+
}
5034

51-
function makeStringFromBoard($board)
52-
{
53-
return "\n" . implode("\n", $board) . "\n";
54-
}
35+
private function asAnnotatedMinefield(): array
36+
{
37+
return \array_map(
38+
fn ($row) => \implode('', $row),
39+
$this->annotatedMinefield,
40+
);
41+
}
5542

56-
function validateBoardDimensions($board)
57-
{
58-
$topRowWidth = count($board[0]);
59-
foreach ($board as $line) {
60-
if (count($line) !== $topRowWidth) {
61-
throw new InvalidArgumentException('Your rows are not of equal length');
43+
private function annotateMinefield(): void
44+
{
45+
$this->annotatedMinefield = $this->minefield;
46+
47+
foreach (\array_keys($this->minefield) as $row) {
48+
foreach (\array_keys($this->minefield[$row]) as $col) {
49+
if (!$this->isMine($row, $col)) {
50+
$mineCount = $this->countMinesAround($row, $col);
51+
$this->annotatedMinefield[$row][$col] =
52+
$mineCount > 0 ? $mineCount : ' ';
53+
}
54+
}
6255
}
6356
}
64-
}
65-
66-
function validateBorders($board)
67-
{
68-
$topBorder = current(array_slice($board, 0, 1));
69-
$middle = array_slice($board, 1, -1);
70-
$bottomBorder = current(array_slice($board, 0, 1));
71-
72-
validateArrayStartsAndEndsWith($topBorder, '+');
73-
validateArrayStartsAndEndsWith($bottomBorder, '+');
7457

75-
foreach (array_slice($topBorder, 1, -1) as $border) {
76-
if ($border !== '-') {
77-
throw new InvalidArgumentException('Top border is incomplete');
58+
private function countMinesAround(int $row, int $col): int
59+
{
60+
$mineCount = 0;
61+
if ($row > 0) {
62+
$mineCount += $this->countMinesOfRowAroundCol($row - 1, $col);
7863
}
79-
}
8064

81-
foreach (array_slice($bottomBorder, 1, -1) as $border) {
82-
if ($border !== '-') {
83-
throw new InvalidArgumentException('Bottom border is incomplete');
65+
$mineCount += $this->countMinesOfRowAroundCol($row, $col);
66+
67+
if ($row < \count($this->minefield) - 1) {
68+
$mineCount += $this->countMinesOfRowAroundCol($row + 1, $col);
8469
}
85-
}
8670

87-
foreach ($middle as $line) {
88-
validateArrayStartsAndEndsWith($line, '|');
71+
return $mineCount;
8972
}
90-
}
9173

92-
function validateArrayStartsAndEndsWith($arr, $char)
93-
{
94-
if (array_shift($arr) !== $char || array_pop($arr) !== $char) {
95-
throw new InvalidArgumentException('Invalid edge' . implode($arr) . ' ' . $char);
96-
}
97-
}
74+
private function countMinesOfRowAroundCol(int $row, int $col): int
75+
{
76+
$mineCount = 0;
77+
if ($col > 0) {
78+
$mineCount += $this->mineScore($row, $col - 1);
79+
}
9880

99-
function removeBorders($minesweeperBoard)
100-
{
101-
array_shift($minesweeperBoard);
102-
array_pop($minesweeperBoard);
81+
$mineCount += $this->mineScore($row, $col);
10382

104-
return array_map(function ($line) {
105-
return array_slice($line, 1, -1);
106-
}, $minesweeperBoard);
107-
}
83+
if ($col < \count($this->minefield[$row]) - 1) {
84+
$mineCount += $this->mineScore($row, $col + 1);
85+
}
10886

109-
function validateGridSize($grid)
110-
{
111-
if (count($grid[0]) < 2 && count($grid) < 2) {
112-
throw new InvalidArgumentException('Your grid is too small. Must be at least 2 squares');
87+
return $mineCount;
11388
}
114-
}
11589

116-
function validateContainsOnlyMines($board)
117-
{
118-
foreach ($board as $row) {
119-
foreach ($row as $cell) {
120-
if (!in_array($cell, [' ', '*'])) {
121-
throw new InvalidArgumentException('Your board contains illegal characters: ' . $cell);
122-
}
123-
}
90+
private function mineScore(int $row, int $col): int
91+
{
92+
return $this->isMine($row, $col) ? 1 : 0;
12493
}
125-
}
126-
127-
function applyBorders($grid)
128-
{
129-
$width = count($grid[0]);
130-
$mid = array_map(function ($line) {
131-
array_unshift($line, '|');
132-
$line[] = '|';
133-
return $line;
134-
}, $grid);
135-
$horizontalBorder = array_fill(0, $width, '-');
136-
array_unshift($horizontalBorder, '+');
137-
$horizontalBorder[] = '+';
138-
139-
array_unshift($mid, $horizontalBorder);
140-
$mid[] = $horizontalBorder;
141-
142-
return array_map('join', $mid);
143-
}
144-
145-
function numSurroundingMines($grid, $r, $c)
146-
{
147-
$positions = [
148-
[-1, -1],
149-
[-1, 0],
150-
[-1, 1],
151-
[0, -1],
152-
[0, 1],
153-
[1, -1],
154-
[1, 0],
155-
[1, 1],
156-
];
157-
158-
return array_reduce($positions, function ($mines, $offset) use ($grid, $r, $c) {
159-
$r = $r + $offset[0];
160-
$c = $c + $offset[1];
161-
if (isset($grid[$r][$c]) && $grid[$r][$c] == '*') {
162-
$mines += 1;
163-
}
164-
return $mines;
165-
}) ?: ' ';
166-
}
16794

168-
function addMineCount($grid)
169-
{
170-
foreach ($grid as $r => &$row) {
171-
foreach ($row as $c => &$cell) {
172-
if ($cell == '*') {
173-
continue;
174-
}
175-
if ($cell == ' ') {
176-
$cell = numSurroundingMines($grid, $r, $c);
177-
}
178-
}
95+
private function isMine(int $row, int $col): bool
96+
{
97+
return $this->minefield[$row][$col] === '*';
17998
}
180-
return $grid;
18199
}

exercises/practice/minesweeper/Minesweeper.php

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,14 @@
2424

2525
declare(strict_types=1);
2626

27-
function solve(string $minesweeperBoard): string
27+
class Minesweeper
2828
{
29-
throw new \BadFunctionCallException("Implement the solve function");
29+
public function __construct(array $minefield)
30+
{
31+
}
32+
33+
public function annotate(): array
34+
{
35+
throw new \BadFunctionCallException(sprintf('Implement the %s method', __FUNCTION__));
36+
}
3037
}

0 commit comments

Comments
 (0)