Skip to content

Commit f2855b7

Browse files
committed
Version 0.2.0
Refactored to eliminate numpy dependency.
1 parent 4fa45a0 commit f2855b7

File tree

5 files changed

+80
-29
lines changed

5 files changed

+80
-29
lines changed

.travis.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ sudo: false
33
python:
44
- 2.6
55
- 2.7
6+
- 3.3
7+
- 3.4
68
branches:
79
only:
810
- master

hbldhdoku/sudoku.py

Lines changed: 46 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -16,18 +16,20 @@
1616

1717
import os
1818
import re
19-
import numpy as np
19+
20+
import six
2021

2122
from hbldhdoku.exceptions import SudokuException
23+
from hbldhdoku import utils
2224

2325

2426
class Sudoku(object):
2527

2628
def __init__(self, n=3):
2729
self.order = n
2830
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)
3133
self.solution_steps = []
3234

3335
self._poss_rows = {}
@@ -39,9 +41,23 @@ def __str__(self):
3941

4042
def __eq__(self, other):
4143
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
4351
return False
4452

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+
4561
@classmethod
4662
def load_sudoku(cls, file_path, n=3):
4763
out = cls(n)
@@ -54,7 +70,7 @@ def _read_to_matrix(self, file_path):
5470

5571
# TODO: Parse metadata in textfile as well.
5672
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)
5874
s_lines = []
5975
pattern = re.compile(r"([1-9\*]{9,9})")
6076
for line in read_lines:
@@ -64,13 +80,14 @@ def _read_to_matrix(self, file_path):
6480
# TODO: Make it work for larger Sudokus as well...
6581
if len(s_lines) != 9:
6682
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
6986
return sudoku
7087

71-
def solve(self):
88+
def solve(self, verbose=False):
7289
n = 0
73-
while 0 in self.matrix:
90+
while not self.is_solved:
7491
n += 1
7592
self._update()
7693
# See if any position can be singled out.
@@ -81,28 +98,30 @@ def solve(self):
8198
continue
8299
else:
83100
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)
88106

89107
def _update(self):
90108
# TODO: Use previously stored information.
91-
for i in xrange(self.side):
109+
for i in six.moves.range(self.side):
92110
self._poss_rows[i] = {}
93111
mat_i = (i // 3) * 3
94112
self._poss_mats[mat_i] = {}
95-
for j in xrange(self.side):
113+
for j in six.moves.range(self.side):
96114
if j not in self._poss_cols:
97115
self._poss_cols[j] = {}
98116
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)))
106125
self._poss_rows[i][j] = possible_values
107126
self._poss_cols[j][i] = possible_values
108127
self._poss_mats[mat_i] = possible_values
@@ -113,7 +132,7 @@ def _fill_naked_singles(self):
113132
for ind_j in self._poss_rows[ind_i]:
114133
if len(self._poss_rows[ind_i][ind_j]) == 1:
115134
# 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]
117136
self.solution_steps.append(self._format_step("NAKED",
118137
(ind_i, ind_j),
119138
self._poss_rows[ind_i][ind_j][0]))
@@ -131,10 +150,10 @@ def _fill_hidden_singles(self):
131150
possibles = self._poss_rows[ind_i][ind_j]
132151
for ind_k in self._poss_rows[ind_i]:
133152
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]))
135154
if len(possibles) == 1:
136155
# 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]
138157
self.solution_steps.append(self._format_step("HIDDEN-ROW",
139158
(ind_i, ind_j),
140159
possibles[0]))
@@ -146,10 +165,10 @@ def _fill_hidden_singles(self):
146165
possibles = self._poss_cols[ind_j][ind_i]
147166
for ind_k in self._poss_cols[ind_j]:
148167
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]))
150169
if len(possibles) == 1:
151170
# 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]
153172
self.solution_steps.append(self._format_step("HIDDEN-COL",
154173
(ind_i, ind_j),
155174
possibles[0]))

hbldhdoku/utils.py

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
#!/usr/bin/env python
2+
# -*- coding: utf-8 -*-
3+
"""
4+
:mod:`utils`
5+
==================
6+
7+
.. module:: utils
8+
:platform: Unix, Windows
9+
:synopsis:
10+
11+
.. moduleauthor:: hbldh <[email protected]>
12+
13+
Created on 2015-10-07, 09:22
14+
15+
"""
16+
17+
from __future__ import division
18+
from __future__ import print_function
19+
from __future__ import unicode_literals
20+
from __future__ import absolute_import
21+
22+
import six
23+
24+
25+
def get_list(n_rows, fill_with=0.0):
26+
return [fill_with, ] * n_rows
27+
28+
29+
def get_list_of_lists(n_rows, n_cols, fill_with=0):
30+
return [get_list(n_rows, fill_with) for k in six.moves.range(n_cols)]

requirements.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
numpy>=1.6
1+
six>=1.9.0

setup.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121

2222
setup(
2323
name='hbldhdoku',
24-
version='0.1.0',
24+
version='0.2.0',
2525
author='Henrik Blidh',
2626
author_email='[email protected]',
2727
description='Sudoku Solver',

0 commit comments

Comments
 (0)