Completed
Push — master ( 76ebac...80d2a1 )
by Burak
02:09
created

Sudoku::next_random()   B

Complexity

Conditions 5
Paths 5

Size

Total Lines 18
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 5
eloc 9
c 1
b 0
f 0
nc 5
nop 1
dl 0
loc 18
rs 8.8571
1
<?php
2
namespace Sudoxu;
3
4
/**
5
 * Class Sudoxu
6
 *
7
 * @package Sudoxu
8
 * @author  Burak <[email protected]>
9
 */
10
11
class Sudoku
12
{
13
14
    public  $sudoku,
15
            $result,
16
            $number;
17
    private $limit,
18
            $sq,
19
            $chars,
20
            $stack = array('1','2','3','4','5','6','7','8','9','0','A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z');
21
22
    /**
23
     * Sudoku constructor
24
     *
25
     * @param int $limit
26
     */
27
    public function __construct($limit = 9)
28
    {
29
        ini_set('memory_limit', -1);
30
        set_time_limit(0);
31
32
        $this->number = 0;
33
        $this->limit  = $limit;
34
        $this->sq     = (int)sqrt($this->limit);
35
36
        $this->logic();
37
38
        return $this;
39
40
    }
41
    public static function world()
42
    {
43
        return 'Hello World, Composer!';
44
    }
45
46
    public function logic()
47
    {
48
        if(count($this->stack) < $this->limit)
49
        {
50
            echo 'error';
51
            exit;
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...
52
        }
53
    }
54
55
    public function set($number)
56
    {
57
        $this->limit = $number;
58
        $this->sq    = (int)sqrt($this->limit);
59
60
        return $this;
61
62
    }
63
64
    private function return_row($cell)
65
    {
66
        return floor($cell / $this->limit);
67
68
    }
69
70
    private function return_col($cell)
71
    {
72
        return $cell % $this->limit;
73
74
    }
75
76
    private function return_block($cell)
77
    {
78
        return floor($this->return_row($cell) / $this->sq) * $this->sq + floor($this->return_col($cell) / $this->sq);
79
80
    }
81
82
    private function is_possible_row($number, $row)
83
    {
84
        $possible = true;
85
        for ($x = 0; $x < $this->limit; $x++)
86
        {
87
            if (isset($this->sudoku[$row * $this->limit + $x]) && $this->sudoku[$row * $this->limit + $x] == $number)
88
            {
89
                $possible = false;
90
            }
91
        }
92
93
        return $possible;
94
    }
95
96
    private function is_possible_col($number, $col)
97
    {
98
        $possible = true;
99
        for ($x = 0; $x < $this->limit; $x++)
100
        {
101
            if (isset($this->sudoku[$col + $this->limit * $x]) && $this->sudoku[$col + $this->limit * $x] == $number)
102
            {
103
                $possible = false;
104
            }
105
        }
106
107
        return $possible;
108
    }
109
110
    private function is_possible_block($number, $block)
111
    {
112
        $possible = true;
113
        for ($x = 0; $x < $this->limit; $x++)
114
        {
115
            $index = floor($block / $this->sq) * $this->sq * $this->limit + $x % $this->sq + $this->limit * floor($x / $this->sq) + $this->sq * ($block % $this->sq);
116
            if (isset($this->sudoku[$index]) && $this->sudoku[$index] == $number)
117
            {
118
                $possible = false;
119
            }
120
        }
121
122
        return $possible;
123
    }
124
125
    private function is_possible_number($cell, $number)
126
    {
127
        $row   = $this->return_row($cell);
128
        $col   = $this->return_col($cell);
129
        $block = $this->return_block($cell);
130
131
        return ($this->is_possible_row($number, $row) && $this->is_possible_col($number, $col) && $this->is_possible_block($number, $block));
132
    }
133
134
    private function is_correct_row($row)
135
    {
136
        $row_temp = array();
137
        for ($x = 0; $x < $this->limit; $x++)
138
        {
139
            if(!isset($this->sudoku[$row * $this->limit + $x]))
140
            {
141
                $this->sudoku[$row * $this->limit + $x] = null;
142
            }
143
            $row_temp[$x] = $this->sudoku[$row * $this->limit + $x];
144
        }
145
146
        return count(array_diff($this->chars, $row_temp)) == 0;
147
    }
148
149
    private function is_correct_col($col)
150
    {
151
        $col_temp = array();
152
        for ($x = 0; $x < $this->limit; $x++)
153
        {
154
            $col_temp[$x] = $this->sudoku[$col + $x * $this->limit];
155
        }
156
        return count(array_diff($this->chars, $col_temp)) == 0;
157
    }
158
159
    private function is_correct_block($block)
160
    {
161
        $block_temp = array();
162
        for ($x = 0; $x < $this->limit; $x++)
163
        {
164
            $lookingfor = floor($block / $this->sq) * ($this->sq * $this->limit) + ($x % $this->sq) + $this->limit * floor($x / $this->sq) + $this->sq * ($block % $this->sq);
165
166
            if (!isset($this->sudoku[$lookingfor]))
167
            {
168
                $this->sudoku[$lookingfor] = null;
169
            }
170
171
            $block_temp[$x] = $this->sudoku[$lookingfor];
172
        }
173
        return count(array_diff($this->chars, $block_temp)) == 0;
174
    }
175
176
    private function is_solved_sudoku()
177
    {
178
        for ($x = 0; $x < $this->limit; $x++)
179
        {
180
            if (!$this->is_correct_block($x) or !$this->is_correct_row($x) or !$this->is_correct_col($x))
181
            {
182
                return false;
183
            }
184
        }
185
        return true;
186
    }
187
188
    private function determine_possible_values($cell)
189
    {
190
        $possible = array();
191
        for ($x = 0; $x < $this->limit; $x++)
192
        {
193
            if ($this->is_possible_number($cell, $this->chars[$x]))
194
            {
195
                array_unshift($possible, $this->chars[$x]);
196
            }
197
        }
198
199
        return $possible;
200
    }
201
202
    private function determine_random_possible_value($possible, $cell)
203
    {
204
        return $possible[$cell][rand(0, count($possible[$cell]) - 1)];
205
    }
206
207
    private function scan_sudoku_for_unique()
208
    {
209
        $possible = false;
210
        for ($x = 0; $x < $this->limit * $this->limit; $x++)
211
        {
212
            if (!isset($this->sudoku[$x]))
213
            {
214
                $possible[$x] = $this->determine_possible_values($x, $this->sudoku);
0 ignored issues
show
Unused Code introduced by
The call to Sudoxu\Sudoku::determine_possible_values() has too many arguments starting with $this->sudoku. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

214
                /** @scrutinizer ignore-call */ 
215
                $possible[$x] = $this->determine_possible_values($x, $this->sudoku);

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
215
                if (count($possible[$x]) == 0)
216
                {
217
                    return (false);
218
                }
219
            }
220
        }
221
222
        return $possible;
223
    }
224
225
    private function remove_attempt($attempt_array, $number)
226
    {
227
        $new_array = array();
228
        $count     = count($attempt_array);
229
        for ($x = 0; $x < $count; $x++)
230
        {
231
            if ($attempt_array[$x] != $number)
232
            {
233
                array_unshift($new_array, $attempt_array[$x]);
234
            }
235
        }
236
        return $new_array;
237
    }
238
239
240
    private function next_random($possible)
241
    {
242
        $min_choices = null;
243
        $max         = $this->limit;
244
        for ($x = 0; $x < $this->limit * $this->limit; $x++)
245
        {
246
            if (!isset($possible[$x]))
247
            {
248
                $possible[$x] = null;
249
            }
250
251
            if ((count($possible[$x]) <= $max) && (count($possible[$x]) > 0))
252
            {
253
                $max         = count($possible[$x]);
254
                $min_choices = $x;
255
            }
256
        }
257
        return $min_choices;
258
    }
259
260
261
    private function build()
262
    {
263
        $this->sudoku = array();
264
        $this->chars  = array();
265
266
        for ($i = 0; $i < $this->limit; $i++)
267
        {
268
            $this->chars[] = $this->stack[$i];
269
        }
270
    }
271
272
    public function microtime()
273
    {
274
        return microtime(true);
275
    }
276
277
    public function generate()
278
    {
279
        $start     = $this->microtime();
280
        $this->build();
281
282
        $x         = 0;
283
        $saved     = array();
284
        $saved_sud = array();
285
        while (!$this->is_solved_sudoku())
286
        {
287
            $x++;
288
            $next_move = $this->scan_sudoku_for_unique();
289
290
            if ($next_move === false)
291
            {
292
                $next_move    = array_pop($saved);
293
                $this->sudoku = array_pop($saved_sud);
294
            }
295
296
            $what_to_try = $this->next_random($next_move);
297
            $attempt     = $this->determine_random_possible_value($next_move, $what_to_try);
298
299
300
            if (count($next_move[$what_to_try]) > 1)
301
            {
302
                $next_move[$what_to_try] = $this->remove_attempt($next_move[$what_to_try], $attempt);
303
                array_push($saved, $next_move);
304
                array_push($saved_sud, $this->sudoku);
305
            }
306
            $this->sudoku[$what_to_try] = $attempt;
307
        }
308
309
        $timing       = $this->microtime() - $start;
310
        $this->result = array(
311
            'created_in' => round($timing,2),
312
            'difficulty' => 'N/A'
313
        );
314
315
        return $this;
316
    }
317
318
    private function array2export()
319
    {
320
        return array(
321
            'sudoku' => $this->sudoku,
322
            'limit'  => $this->limit,
323
            'result' => $this->result,
324
325
        );
326
    }
327
328
    public function to($to)
329
    {
330
        if($to == 'json')
331
        {
332
            return json_encode($this->array2export());
333
        }
334
        else if($to == 'serialize')
335
        {
336
            return serialize($this->array2export());
337
        }
338
        else if($to == 'html')
339
        {
340
            return serialize($this->array2export());
341
        }
342
    }
343
344
345
    public function draw($echo = true)
346
    {
347
348
349
        $string = "<table cellpadding='0' cellspacing='0' style='margin:0 auto;font:30px Arial;border:3px solid black'>";
350
351
        for ($u = 0; $u < $this->limit; $u++)
352
        {
353
354
355
            if (($u + 1) % $this->sq == '0')
356
            {
357
                $border = 3;
358
            } else {
359
                $border = 1;
360
            }
361
362
            $string .= '<tr>';
363
364
            for ($v = 0; $v < $this->limit; $v++)
365
            {
366
367
368
                if (($v + 1) % $this->sq == '0')
369
                {
370
                    $border2 = 3;
371
                } else {
372
                    $border2 = 1;
373
                }
374
375
                $string .= '<td
376
                            data-row="'.$u.'"
377
                            data-col="'.$v.'"
378
                            style="border: 1px solid black;border-bottom:' . $border . 'px solid black;border-right:' . $border2 . 'px solid black;line-height: 50px; width: 50px; text-align: center; vertical-align: middle;">';
379
                $string .=($this->sudoku[$v * $this->limit + $u]);
380
                $string .= '</td>';
381
382
            }
383
384
            $string .= '</tr>';
385
386
        }
387
388
        $string .= "</table>";
389
390
391
        if($echo === true)
392
        {
393
            echo $string;
394
        }
395
        else
396
        {
397
            return $string;
398
        }
399
400
    }
401
402
}