Passed
Push — master ( e9d049...9c1611 )
by Burak
02:00
created

class.sudoxu.php (8 issues)

1
<?php
2
3
class sudoxu
4
{
5
6
    public  $sudoku,
7
            $result,
8
            $number;
9
    private $limit,
10
            $sq,
11
            $chars,
12
            $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');
13
14
    public function __construct($limit = 9)
15
    {
16
        ini_set('memory_limit', -1);
17
        set_time_limit(0);
18
19
        ini_set('display_errors',1);
20
        error_reporting(E_ALL);
21
22
        $this->number = 0;
23
        $this->limit  = $limit;
24
        $this->sq     = (int)sqrt($this->limit);
25
26
        $this->logic();
27
28
        return $this;
29
30
    }
31
32
    public function logic()
33
    {
34
        if(count($this->stack) < $this->limit)
35
        {
36
            echo 'error';
37
            exit;
38
        }
39
    }
40
41
    public function set($number)
42
    {
43
        $this->limit = $number;
44
        $this->sq    = (int)sqrt($this->limit);
45
46
        return $this;
47
48
    }
49
50
    private function return_row($cell)
51
    {
52
        return floor($cell / $this->limit);
53
54
    }
55
56
    private function return_col($cell)
57
    {
58
        return $cell % $this->limit;
59
60
    }
61
62
    private function return_block($cell)
63
    {
64
        return floor($this->return_row($cell) / $this->sq) * $this->sq + floor($this->return_col($cell) / $this->sq);
65
66
    }
67
68 View Code Duplication
    private function is_possible_row($number, $row)
0 ignored issues
show
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
69
    {
70
        $possible = true;
71
        for ($x = 0; $x < $this->limit; $x++)
72
        {
73
            if (isset($this->sudoku[$row * $this->limit + $x]) && $this->sudoku[$row * $this->limit + $x] == $number)
74
            {
75
                $possible = false;
76
            }
77
        }
78
79
        return $possible;
80
    }
81
82 View Code Duplication
    private function is_possible_col($number, $col)
0 ignored issues
show
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
83
    {
84
        $possible = true;
85
        for ($x = 0; $x < $this->limit; $x++)
86
        {
87
            if (isset($this->sudoku[$col + $this->limit * $x]) && $this->sudoku[$col + $this->limit * $x] == $number)
88
            {
89
                $possible = false;
90
            }
91
        }
92
93
        return $possible;
94
    }
95
96
    private function is_possible_block($number, $block)
97
    {
98
        $possible = true;
99
        for ($x = 0; $x < $this->limit; $x++)
100
        {
101
            $index = floor($block / $this->sq) * $this->sq * $this->limit + $x % $this->sq + $this->limit * floor($x / $this->sq) + $this->sq * ($block % $this->sq);
102
            if (isset($this->sudoku[$index]) && $this->sudoku[$index] == $number)
103
            {
104
                $possible = false;
105
            }
106
        }
107
108
        return $possible;
109
    }
110
111
    private function is_possible_number($cell, $number)
112
    {
113
        $row   = $this->return_row($cell);
114
        $col   = $this->return_col($cell);
115
        $block = $this->return_block($cell);
116
117
        return ($this->is_possible_row($number, $row) && $this->is_possible_col($number, $col) && $this->is_possible_block($number, $block));
118
    }
119
120
    private function is_correct_row($row)
121
    {
122
        $row_temp = array();
123
        for ($x = 0; $x < $this->limit; $x++)
124
        {
125
            if(!isset($this->sudoku[$row * $this->limit + $x]))
126
            {
127
                $this->sudoku[$row * $this->limit + $x] = null;
128
            }
129
            $row_temp[$x] = $this->sudoku[$row * $this->limit + $x];
130
        }
131
132
        return count(array_diff($this->chars, $row_temp)) == 0;
133
    }
134
135
    private function is_correct_col($col)
136
    {
137
        $col_temp = array();
138
        for ($x = 0; $x < $this->limit; $x++)
139
        {
140
            $col_temp[$x] = $this->sudoku[$col + $x * $this->limit];
141
        }
142
        return count(array_diff($this->chars, $col_temp)) == 0;
143
    }
144
145
    private function is_correct_block($block)
146
    {
147
        $block_temp = array();
148
        for ($x = 0; $x < $this->limit; $x++)
149
        {
150
            $lookingfor = floor($block / $this->sq) * ($this->sq * $this->limit) + ($x % $this->sq) + $this->limit * floor($x / $this->sq) + $this->sq * ($block % $this->sq);
151
152
            if (!isset($this->sudoku[$lookingfor]))
153
            {
154
                $this->sudoku[$lookingfor] = null;
155
            }
156
157
            $block_temp[$x] = $this->sudoku[$lookingfor];
158
        }
159
        return count(array_diff($this->chars, $block_temp)) == 0;
160
    }
161
162
    private function is_solved_sudoku()
163
    {
164
        for ($x = 0; $x < $this->limit; $x++)
165
        {
166
            if (!$this->is_correct_block($x) or !$this->is_correct_row($x) or !$this->is_correct_col($x))
167
            {
168
                return false;
169
                break;
0 ignored issues
show
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
170
            }
171
        }
172
        return true;
173
    }
174
175
    private function determine_possible_values($cell)
176
    {
177
        $possible = array();
178
        for ($x = 0; $x < $this->limit; $x++)
179
        {
180
            if ($this->is_possible_number($cell, $this->chars[$x]))
181
            {
182
                array_unshift($possible, $this->chars[$x]);
183
            }
184
        }
185
186
        return $possible;
187
    }
188
189
    private function determine_random_possible_value($possible, $cell)
190
    {
191
        return $possible[$cell][rand(0, count($possible[$cell]) - 1)];
192
    }
193
194
    private function scan_sudoku_for_unique()
195
    {
196
        $possible = false;
197
        for ($x = 0; $x < $this->limit * $this->limit; $x++)
198
        {
199
            if (!isset($this->sudoku[$x]))
200
            {
201
                $possible[$x] = $this->determine_possible_values($x, $this->sudoku);
0 ignored issues
show
The call to sudoxu::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

201
                $possible[$x] = /** @scrutinizer ignore-call */ $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...
202
                if (count($possible[$x]) == 0)
203
                {
204
                    return (false);
205
                    break;
0 ignored issues
show
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
206
                }
207
            }
208
        }
209
210
        return $possible;
211
    }
212
213
    private function remove_attempt($attempt_array, $number)
214
    {
215
        $new_array = array();
216
        for ($x = 0; $x < count($attempt_array); $x++)
0 ignored issues
show
Performance Best Practice introduced by
It seems like you are calling the size function count() as part of the test condition. You might want to compute the size beforehand, and not on each iteration.

If the size of the collection does not change during the iteration, it is generally a good practice to compute it beforehand, and not on each iteration:

for ($i=0; $i<count($array); $i++) { // calls count() on each iteration
}

// Better
for ($i=0, $c=count($array); $i<$c; $i++) { // calls count() just once
}
Loading history...
217
        {
218
            if ($attempt_array[$x] != $number)
219
            {
220
                array_unshift($new_array, $attempt_array[$x]);
221
            }
222
        }
223
        return $new_array;
224
    }
225
226
227
    private function next_random($possible)
228
    {
229
        $max = $this->limit;
230
        for ($x = 0; $x < $this->limit * $this->limit; $x++)
231
        {
232
            if (!isset($possible[$x]))
233
            {
234
                $possible[$x] = null;
235
            }
236
237
            if ((count($possible[$x]) <= $max) && (count($possible[$x]) > 0))
238
            {
239
                $max         = count($possible[$x]);
240
                $min_choices = $x;
241
            }
242
        }
243
        return $min_choices;
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $min_choices does not seem to be defined for all execution paths leading up to this point.
Loading history...
244
    }
245
246
247
    private function build()
248
    {
249
        $this->sudoku = array();
250
        $this->chars  = array();
251
252
        for ($i = 0; $i < $this->limit; $i++)
253
        {
254
            $this->chars[] = $this->stack[$i];
255
        }
256
    }
257
258
    public function microtime()
259
    {
260
        return microtime(true);
261
    }
262
263
    public function generate()
264
    {
265
        $start     = $this->microtime();
266
        $this->build();
267
268
        $x         = 0;
269
        $saved     = array();
270
        $saved_sud = array();
271
        while (!$this->is_solved_sudoku())
272
        {
273
            $x++;
274
            $next_move = $this->scan_sudoku_for_unique();
275
276
            if ($next_move == false)
0 ignored issues
show
Coding Style Best Practice introduced by
It seems like you are loosely comparing two booleans. Considering using the strict comparison === instead.

When comparing two booleans, it is generally considered safer to use the strict comparison operator.

Loading history...
277
            {
278
                $next_move    = array_pop($saved);
279
                $this->sudoku = array_pop($saved_sud);
280
            }
281
282
            $what_to_try = $this->next_random($next_move);
283
            $attempt     = $this->determine_random_possible_value($next_move, $what_to_try);
284
285
286
            if (count($next_move[$what_to_try]) > 1)
287
            {
288
                $next_move[$what_to_try] = $this->remove_attempt($next_move[$what_to_try], $attempt);
289
                array_push($saved, $next_move);
290
                array_push($saved_sud, $this->sudoku);
291
            }
292
            $this->sudoku[$what_to_try] = $attempt;
293
        }
294
295
        $timing       = $this->microtime() - $start;
296
        $this->result = array(
297
            'created_in' => round($timing,2),
298
            'difficulty' => 'N/A'
299
        );
300
301
        return $this;
302
    }
303
304
    private function array2export()
305
    {
306
        return array(
307
            'sudoku' => $this->sudoku,
308
            'limit'  => $this->limit,
309
            'result' => $this->result,
310
311
        );
312
    }
313
314
    public function to($to)
315
    {
316
        if($to == 'json')
317
        {
318
            return json_encode($this->array2export());
319
        }
320
        else if($to == 'serialize')
321
        {
322
            return serialize($this->array2export());
323
        }
324
        else if($to == 'html')
325
        {
326
            return serialize($this->array2export());
327
        }
328
    }
329
330
331
    public function draw($echo = true)
332
    {
333
334
335
        $string = "<table cellpadding='0' cellspacing='0' style='margin:0 auto;font:30px Arial;border:3px solid black'>";
336
337
        for ($u = 0; $u < $this->limit; $u++)
338
        {
339
340
341
            if (($u + 1) % $this->sq == '0')
342
            {
343
                $border = 3;
344
            } else {
345
                $border = 1;
346
            }
347
348
            $string .= '<tr>';
349
350
            for ($v = 0; $v < $this->limit; $v++)
351
            {
352
353
354
                if (($v + 1) % $this->sq == '0')
355
                {
356
                    $border2 = 3;
357
                } else {
358
                    $border2 = 1;
359
                }
360
361
                $string .= '<td
362
                            data-row="'.$u.'"
363
                            data-col="'.$v.'"
364
                            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;">';
365
                $string .=($this->sudoku[$v * $this->limit + $u]);
366
                $string .= '</td>';
367
368
            }
369
370
            $string .= '</tr>';
371
372
        }
373
374
        $string .= "</table>";
375
376
377
        if($echo === true)
378
        {
379
            echo $string;
380
        }
381
        else
382
        {
383
            return $string;
384
        }
385
386
    }
387
388
}