Grid::set()   A
last analyzed

Complexity

Conditions 2
Paths 2

Size

Total Lines 6
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 3
dl 0
loc 6
c 0
b 0
f 0
rs 10
cc 2
nc 2
nop 2
1
<?php
2
3
declare(strict_types=1);
4
5
namespace CoenMooij\Sudoku\Puzzle;
6
7
use CoenMooij\Sudoku\Exception\InvalidValueException;
8
9
class Grid
10
{
11
    public const NUMBER_OF_ROWS = 9;
12
    public const NUMBER_OF_COLUMNS = 9;
13
    public const NUMBER_OF_BLOCKS = 9;
14
    public const NUMBER_OF_LOCATIONS = 81;
15
    public const POSSIBLE_VALUES = [1, 2, 3, 4, 5, 6, 7, 8, 9];
16
    public const EMPTY_VALUE = 0;
17
18
    /**
19
     * @var int[][]
20
     */
21
    private $grid;
22
23
    public function __construct()
24
    {
25
        $this->initializeGrid();
26
    }
27
28
    public static function valueIsValid(int $value): bool
29
    {
30
        return in_array($value, self::POSSIBLE_VALUES, true);
31
    }
32
33
    public function get(Location $location): int
34
    {
35
        return $this->grid[$location->getRow()][$location->getColumn()];
36
    }
37
38
    public function set(Location $location, int $value): void
39
    {
40
        if (!self::valueIsValid($value)) {
41
            throw new InvalidValueException();
42
        }
43
        $this->grid[$location->getRow()][$location->getColumn()] = $value;
44
    }
45
46
    /**
47
     * @return int[][]
48
     */
49
    public function getRows(): array
50
    {
51
        $rows = [];
52
        for ($i = 0; $i < self::NUMBER_OF_ROWS; $i++) {
53
            $rows[] = $this->getRow($i);
54
        }
55
56
        return $rows;
57
    }
58
59
    /**
60
     * @return int[]
61
     */
62
    public function getRow(int $row): array
63
    {
64
        return $this->grid[$row];
65
    }
66
67
    /**
68
     * @return int[][]
69
     */
70
    public function getColumns(): array
71
    {
72
        $columns = [];
73
        for ($i = 0; $i < self::NUMBER_OF_COLUMNS; $i++) {
74
            $columns[] = $this->getColumn($i);
75
        }
76
77
        return $columns;
78
    }
79
80
    /**
81
     * @return int[]
82
     */
83
    public function getColumn(int $column): array
84
    {
85
        $values = [];
86
        for ($row = 0; $row < 9; $row++) {
87
            $values[] = $this->grid[$row][$column];
88
        }
89
90
        return $values;
91
    }
92
93
    /**
94
     * @return int[][]
95
     */
96
    public function getBlocks(): array
97
    {
98
        $blocks = [];
99
        for ($i = 0; $i < self::NUMBER_OF_BLOCKS; $i++) {
100
            $blocks[] = $this->getBlock($i);
101
        }
102
103
        return $blocks;
104
    }
105
106
    /**
107
     * @return int[]
108
     */
109
    public function getBlock(int $block): array
110
    {
111
        $row = $block - ($block % 3);
112
        $column = ($block % 3) * 3;
113
114
        return $this->getBlockAt(new Location($row, $column));
115
    }
116
117
    /**
118
     * @return int[]
119
     */
120
    public function getBlockAt(Location $location): array
121
    {
122
        $firstLocation = $this->getFirstLocationInBlock($location);
123
        $block = [];
124
        for ($row = 0; $row < 3; $row++) {
125
            for ($column = 0; $column < 3; $column++) {
126
                $block[] = $this->grid[$firstLocation->getRow() + $row][$firstLocation->getColumn() + $column];
127
            }
128
        }
129
130
        return $block;
131
    }
132
133
    private function getFirstLocationInBlock(Location $location): Location
134
    {
135
        $firstRowInBlock = $location->getRow() - $location->getRow() % 3;
136
        $firstColumnInBlock = $location->getColumn() - $location->getColumn() % 3;
137
138
        return new Location($firstRowInBlock, $firstColumnInBlock);
139
    }
140
141
    /**
142
     * @return int[]
143
     */
144
    public function getAllPossibilitiesFor(Location $location): array
145
    {
146
        if (!$this->isEmpty($location)) {
147
            return [];
148
        }
149
        $impossibleValues = array_unique(
150
            array_merge(
151
                $this->getRow($location->getRow()),
152
                $this->getColumn($location->getColumn()),
153
                $this->getBlockAt($location)
154
            )
155
        );
156
157
        return array_values(array_diff(self::POSSIBLE_VALUES, $impossibleValues));
158
    }
159
160
    public function isEmpty($location): bool
161
    {
162
        return $this->get($location) === self::EMPTY_VALUE;
163
    }
164
165
    public function empty(Location $location): void
166
    {
167
        $this->grid[$location->getRow()][$location->getColumn()] = self::EMPTY_VALUE;
168
    }
169
170
    public function numberOfEmptyValues(): int
171
    {
172
        $numberOfEmptyValues = 0;
173
        foreach ($this->getRows() as $row) {
174
            for ($i = 0; $i < self::NUMBER_OF_COLUMNS; $i++) {
175
                $numberOfEmptyValues += ($row[$i] === self::EMPTY_VALUE);
176
            }
177
        }
178
179
        return $numberOfEmptyValues;
180
    }
181
182
    private function initializeGrid(): void
183
    {
184
        for ($row = 0; $row < self::NUMBER_OF_ROWS; $row++) {
185
            for ($column = 0; $column < self::NUMBER_OF_COLUMNS; $column++) {
186
                $this->empty(new Location($row, $column));
187
            }
188
        }
189
    }
190
}
191