16
16
17
17
import os
18
18
import re
19
- import numpy as np
19
+
20
+ import six
20
21
21
22
from hbldhdoku .exceptions import SudokuException
23
+ from hbldhdoku import utils
22
24
23
25
24
26
class Sudoku (object ):
25
27
26
28
def __init__ (self , n = 3 ):
27
29
self .order = n
28
30
self .side = n ** 2
29
- self .matrix = np . zeros (( n ** 2 , n ** 2 ), 'int8' )
30
- self ._values = np . array ( range (0 , (n ** 2 ) + 1 ), 'int8' )
31
+ self .matrix = utils . get_list_of_lists ( self . side , self . side , fill_with = 0 )
32
+ self ._values = six . moves . range (0 , (n ** 2 ) + 1 )
31
33
self .solution_steps = []
32
34
33
35
self ._poss_rows = {}
@@ -39,9 +41,23 @@ def __str__(self):
39
41
40
42
def __eq__ (self , other ):
41
43
if isinstance (other , Sudoku ):
42
- return np .abs ((self .matrix - other .matrix )).sum ().sum () == 0
44
+ if self .order != other .order :
45
+ return False
46
+ for i in six .moves .range (self .side ):
47
+ for j in six .moves .range (self .side ):
48
+ if self .matrix [i ][j ] != other .matrix [i ][j ]:
49
+ return False
50
+ return True
43
51
return False
44
52
53
+ @property
54
+ def is_solved (self ):
55
+ for row in self .matrix :
56
+ for value in row :
57
+ if value == 0 :
58
+ return False
59
+ return True
60
+
45
61
@classmethod
46
62
def load_sudoku (cls , file_path , n = 3 ):
47
63
out = cls (n )
@@ -54,7 +70,7 @@ def _read_to_matrix(self, file_path):
54
70
55
71
# TODO: Parse metadata in textfile as well.
56
72
read_lines = read_lines .split ('\n ' )
57
- sudoku = np . zeros (( self .side , self .side ), 'int8' )
73
+ sudoku = utils . get_list_of_lists ( self .side , self .side , fill_with = 0 )
58
74
s_lines = []
59
75
pattern = re .compile (r"([1-9\*]{9,9})" )
60
76
for line in read_lines :
@@ -64,13 +80,14 @@ def _read_to_matrix(self, file_path):
64
80
# TODO: Make it work for larger Sudokus as well...
65
81
if len (s_lines ) != 9 :
66
82
raise SudokuException ("File did not contain a correctly defined sudoku." )
67
- for n , row in enumerate (s_lines ):
68
- sudoku [n , :] = eval ("," .join ([char for char in row .replace ('*' , '0' )]))
83
+ for i , row in enumerate (s_lines ):
84
+ for j , value in enumerate ([int (c ) for c in row .replace ('*' , '0' )]):
85
+ sudoku [i ][j ] = value
69
86
return sudoku
70
87
71
- def solve (self ):
88
+ def solve (self , verbose = False ):
72
89
n = 0
73
- while 0 in self .matrix :
90
+ while not self .is_solved :
74
91
n += 1
75
92
self ._update ()
76
93
# See if any position can be singled out.
@@ -81,28 +98,30 @@ def solve(self):
81
98
continue
82
99
else :
83
100
print (self .matrix )
84
- raise SudokuException ("This sudoku requires more advanced methods!" )
85
- print ("Sudoku solved in {0} iterations!\n {1}" .format (n ,self .matrix ))
86
- for step in self .solution_steps :
87
- print (step )
101
+ raise SudokuException ("This Sudoku requires more advanced methods!" )
102
+ if verbose :
103
+ print ("Sudoku solved in {0} iterations!\n {1}" .format (n , self .matrix ))
104
+ for step in self .solution_steps :
105
+ print (step )
88
106
89
107
def _update (self ):
90
108
# TODO: Use previously stored information.
91
- for i in xrange (self .side ):
109
+ for i in six . moves . range (self .side ):
92
110
self ._poss_rows [i ] = {}
93
111
mat_i = (i // 3 ) * 3
94
112
self ._poss_mats [mat_i ] = {}
95
- for j in xrange (self .side ):
113
+ for j in six . moves . range (self .side ):
96
114
if j not in self ._poss_cols :
97
115
self ._poss_cols [j ] = {}
98
116
mat_j = (j // 3 ) * 3
99
- if not self .matrix [i , j ]:
100
- possible_values = np .setdiff1d (
101
- self ._values , self .matrix [i , :])
102
- possible_values = np .setdiff1d (
103
- possible_values , self .matrix [:, j ])
104
- possible_values = np .setdiff1d (
105
- possible_values , self .matrix [mat_i :mat_i + 3 , mat_j : mat_j + 3 ])
117
+ if not self .matrix [i ][j ]:
118
+ possible_values = set (self ._values ).difference (self .matrix [i ])
119
+ possible_values = possible_values .difference (set ([row [j ] for row in self .matrix ]))
120
+ box = []
121
+ for k in six .moves .range (mat_i , mat_i + 3 ):
122
+ for kk in six .moves .range (mat_j , mat_j + 3 ):
123
+ box .append (self .matrix [k ][kk ])
124
+ possible_values = list (possible_values .difference (set (box )))
106
125
self ._poss_rows [i ][j ] = possible_values
107
126
self ._poss_cols [j ][i ] = possible_values
108
127
self ._poss_mats [mat_i ] = possible_values
@@ -113,7 +132,7 @@ def _fill_naked_singles(self):
113
132
for ind_j in self ._poss_rows [ind_i ]:
114
133
if len (self ._poss_rows [ind_i ][ind_j ]) == 1 :
115
134
# Only one possible value. Assign.
116
- self .matrix [ind_i , ind_j ] = self ._poss_rows [ind_i ][ind_j ][0 ]
135
+ self .matrix [ind_i ][ ind_j ] = self ._poss_rows [ind_i ][ind_j ][0 ]
117
136
self .solution_steps .append (self ._format_step ("NAKED" ,
118
137
(ind_i , ind_j ),
119
138
self ._poss_rows [ind_i ][ind_j ][0 ]))
@@ -131,10 +150,10 @@ def _fill_hidden_singles(self):
131
150
possibles = self ._poss_rows [ind_i ][ind_j ]
132
151
for ind_k in self ._poss_rows [ind_i ]:
133
152
if ind_k != ind_j :
134
- possibles = np . setdiff1d ( possibles , self ._poss_rows [ind_i ][ind_k ])
153
+ possibles = list ( set ( possibles ). difference ( self ._poss_rows [ind_i ][ind_k ]) )
135
154
if len (possibles ) == 1 :
136
155
# Found a hidden single in a row!
137
- self .matrix [ind_i , ind_j ] = possibles [0 ]
156
+ self .matrix [ind_i ][ ind_j ] = possibles [0 ]
138
157
self .solution_steps .append (self ._format_step ("HIDDEN-ROW" ,
139
158
(ind_i , ind_j ),
140
159
possibles [0 ]))
@@ -146,10 +165,10 @@ def _fill_hidden_singles(self):
146
165
possibles = self ._poss_cols [ind_j ][ind_i ]
147
166
for ind_k in self ._poss_cols [ind_j ]:
148
167
if ind_k != ind_i :
149
- possibles = np . setdiff1d ( possibles , self ._poss_cols [ind_j ][ind_k ])
168
+ possibles = list ( set ( possibles ). difference ( self ._poss_cols [ind_j ][ind_k ]) )
150
169
if len (possibles ) == 1 :
151
170
# Found a hidden single in a row!
152
- self .matrix [ind_i , ind_j ] = possibles [0 ]
171
+ self .matrix [ind_i ][ ind_j ] = possibles [0 ]
153
172
self .solution_steps .append (self ._format_step ("HIDDEN-COL" ,
154
173
(ind_i , ind_j ),
155
174
possibles [0 ]))
0 commit comments