Passed
Push — main ( 4a4379...b8ec9e )
by Jesse
10:31
created

Board::findOpenSpace()   A

Complexity

Conditions 4
Paths 4

Size

Total Lines 10
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 4
eloc 5
c 1
b 0
f 0
nc 4
nop 1
dl 0
loc 10
rs 10
1
<?php declare(strict_types=1);
2
3
namespace Stratadox\PuzzleSolver\Puzzle\SlidingPuzzle;
4
5
use RuntimeException;
6
use function array_fill;
7
use function array_map;
8
use function assert;
9
use function count;
10
use function implode;
11
use function range;
12
use function str_replace;
13
use const PHP_EOL;
14
15
final class Board
16
{
17
    /** @var int[][] */
18
    private $pieces;
19
    /** @var int */
20
    private $openRow;
21
    /** @var int */
22
    private $openCol;
23
    /** @var int */
24
    private $height;
25
    /** @var int */
26
    private $width;
27
28
    public function __construct(array ...$pieces)
29
    {
30
        $this->pieces = $pieces;
31
        [$this->openRow, $this->openCol] = $this->findOpenSpace(...$pieces);
32
        $this->height = count($pieces);
33
        assert($this->height > 0);
34
        $this->width = count($pieces[0]);
35
    }
36
37
    public function afterSliding(Slide ...$slides): self
38
    {
39
        $pieces = $this->pieces;
40
        foreach ($slides as $slide) {
41
            $pieces = $slide->applyTo($pieces);
42
        }
43
        return new self(...$pieces);
44
    }
45
46
    public function isSolved(): bool
47
    {
48
        return $this->goalState() === $this->currentState();
49
    }
50
51
    public function currentState(): string
52
    {
53
        return implode(array_map('implode', $this->pieces));
54
    }
55
56
    public function goalState(): string
57
    {
58
        return implode(range(1, $this->width * $this->height - 1)) . '0';
59
    }
60
61
    /** @return Slide[] */
62
    public function possibleSlides(): array
63
    {
64
        $slides = [];
65
        if ($this->openRow < $this->height - 1) {
66
            $slides[] = Slide::up($this->pieces, $this->openRow + 1, $this->openCol);
67
        }
68
        if ($this->openRow > 0) {
69
            $slides[] = Slide::down($this->pieces, $this->openRow - 1, $this->openCol);
70
        }
71
        if ($this->openCol < $this->width - 1) {
72
            $slides[] = Slide::left($this->pieces, $this->openRow, $this->openCol + 1);
73
        }
74
        if ($this->openCol > 0) {
75
            $slides[] = Slide::right($this->pieces, $this->openRow, $this->openCol - 1);
76
        }
77
        return $slides;
78
    }
79
80
    public function __toString(): string
81
    {
82
        return str_replace('0', ' ', implode(PHP_EOL, array_map(
83
            'implode',
84
            array_fill(0, $this->width, ', '),
85
            $this->pieces
86
        ))) . '  ';
87
    }
88
89
    /** @return int[] */
90
    private function findOpenSpace(array ...$pieces): array
91
    {
92
        foreach ($pieces as $r => $row) {
93
            foreach ($row as $c => $value) {
94
                if (0 === $value) {
95
                    return [$r, $c];
96
                }
97
            }
98
        }
99
        throw new RuntimeException('No open space!');
100
    }
101
}
102