Sudoku   A
last analyzed

Complexity

Total Complexity 21

Size/Duplication

Total Lines 124
Duplicated Lines 0 %

Test Coverage

Coverage 100%

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 61
c 1
b 0
f 0
dl 0
loc 124
ccs 50
cts 50
cp 1
rs 10
wmc 21

6 Methods

Rating   Name   Duplication   Size   Complexity  
A getIndex() 0 11 5
A preGenerate() 0 17 4
A init() 0 11 2
A generate() 0 14 3
A __construct() 0 10 2
A compare() 0 15 5
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Compolomus\Sudoku;
6
7
use InvalidArgumentException;
8
9
class Sudoku
10
{
11
    private array $shift = [
12
        4 => 1,
13
        9 => 2,
14
        16 => 3,
15
        25 => 4,
16
        36 => 5
17
    ];
18
19
    private int $lines;
20
21
    private int $chunkSide;
22
23
    private array $allEqual;
24
25
    private array $linesArray = [];
26
27
    private array $columnsArray = [];
28
29
    private array $squaresArray = [];
30
31
    /**
32
     * @param int $line
33
     * @throws InvalidArgumentException
34
     */
35 2
    public function __construct(int $line = 9)
36
    {
37 2
        if (!array_key_exists($line, $this->shift)) {
38 1
            throw new InvalidArgumentException(
39 1
                'Value options for lines is [' . implode(', ', array_keys($this->shift)) . ']'
40
            );
41
        }
42
43 2
        $this->lines = 9; // $this->lines = $line; // > 9 is hard
44 2
        $this->init();
45
    }
46
47
    /**
48
     * Set start values and linked arrays
49
     * @return void
50
     */
51 2
    private function init(): void
52
    {
53 2
        $all = $this->lines ** 2;
54 2
        $this->chunkSide = (int) sqrt($this->lines);
55 2
        $this->allEqual = array_fill(1, $all, 0);
56
57 2
        foreach ($this->allEqual as $key => &$value) {
58 2
            $index = $this->getIndex($key);
59 2
            $this->linesArray[$index['lineNum']][$key] = &$value;
60 2
            $this->columnsArray[$index['colNum']][$key] = &$value;
61 2
            $this->squaresArray[$index['chunkNum']][$key] = &$value;
62
        }
63
    }
64
65 2
    private function getIndex(int $index): array
66
    {
67 2
        $lineNum = $index <= $this->lines ? 1 : (int) ($index / $this->lines) + ($index % $this->lines === 0 ? 0 : 1);
68 2
        $colNum = $index <= $this->lines ? $index : ($index % $this->lines) + ($index % $this->lines === 0 ? $this->lines : 0);
69 2
        $bigLine = $this->chunkSide * $this->lines;
70 2
        $chunkNum = (ceil($colNum / $this->chunkSide)) + (($this->shift[$this->lines] * abs(ceil(($index - $bigLine) / $bigLine))) + (int) (--$index / $bigLine));
71
72
        return [
73 2
            'lineNum' => $lineNum,
74
            'colNum' => $colNum,
75
            'chunkNum' => $chunkNum
76
        ];
77
    }
78
79 1
    private function compare(int $square, int $value): array
80
    {
81 1
        $return = [];
82
83 1
        foreach (array_keys($this->squaresArray[$square]) as $pos) {
84 1
            $stats = $this->getIndex($pos);
85 1
            if ($this->allEqual[$pos] === 0
86 1
                && !in_array($value, $this->linesArray[$stats['lineNum']], true)
87 1
                && !in_array($value, $this->columnsArray[$stats['colNum']], true)
88
            ) {
89 1
                $return[] = $pos;
90
            }
91
        }
92
93 1
        return $return;
94
    }
95
96 1
    private function preGenerate(): int
97
    {
98 1
        $notSet = 0;
99
100 1
        for ($setNumber = 1; $setNumber <= $this->lines; $setNumber++) {
101 1
            for ($currentSquare = 1; $currentSquare <= $this->lines; $currentSquare++) {
102 1
                $availableKeys = $this->compare($currentSquare, $setNumber);
103 1
                if (count($availableKeys)) {
104 1
                    $randomValueKey = $availableKeys[array_rand($availableKeys)];
105 1
                    $this->allEqual[$randomValueKey] = $setNumber;
106
                } else {
107 1
                    ++$notSet;
108
                }
109
            }
110
        }
111
112 1
        return $notSet;
113
    }
114
115
    /**
116
     * Recursive generator
117
     * @return array
118
     */
119 1
    public function generate(): array
120
    {
121 1
        $process = true;
122 1
        while ($process) {
123 1
            $result = $this->preGenerate();
124 1
            if ($result > 0) {
125 1
                $this->init();
126 1
                $this->generate();
127 1
                break;
128
            }
129 1
            $process = false;
130
        }
131
132 1
        return $this->allEqual;
133
    }
134
}
135