sudokuhandler 0.1.2 → 0.1.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/README.md +104 -113
- data/Rakefile +34 -14
- data/ext/sudoku.c +85 -9
- data/lib/sudoku.rb +33 -197
- data/lib/sudoku/generator.rb +40 -0
- data/lib/sudoku/grid.rb +14 -0
- data/lib/sudoku/logic.rb +242 -0
- data/lib/sudoku/solver.rb +176 -0
- data/lib/sudoku/version.rb +1 -1
- data/tests/test_sudoku.rb +144 -41
- metadata +35 -5
data/lib/sudoku.rb
CHANGED
@@ -4,192 +4,14 @@
|
|
4
4
|
|
5
5
|
require "sudokucore"
|
6
6
|
require "sudoku/version"
|
7
|
+
require "sudoku/grid"
|
7
8
|
|
8
9
|
module Sudoku
|
9
10
|
#Exception lancée lors d'erreurs de lecture d'une chaine Sutxt
|
10
11
|
class MalformedSutxtError < Exception; end
|
11
12
|
|
12
|
-
#
|
13
|
-
|
14
|
-
#Remplit la diagonale descendante de 1 a self.size
|
15
|
-
def make_diagonal
|
16
|
-
each{|x,y,val| set x,x,x+1 if x==y}
|
17
|
-
self
|
18
|
-
end
|
19
|
-
|
20
|
-
#Remplit tout le sudoku de maniere a ce qu'il soit valide
|
21
|
-
def make_valid
|
22
|
-
pattern = Array.new(size){|i| i+1}
|
23
|
-
size.times do |y|
|
24
|
-
size.times do |x|
|
25
|
-
set x, y, pattern[x]
|
26
|
-
end
|
27
|
-
base.times{|i| pattern.push pattern.shift}
|
28
|
-
pattern.push pattern.shift if base - (y%base) == 1
|
29
|
-
end
|
30
|
-
self
|
31
|
-
end
|
32
|
-
end
|
33
|
-
|
34
|
-
#Taches communes a tous les Sudokus
|
35
|
-
module Grid
|
36
|
-
#Renvoie le contenu de la ligne x
|
37
|
-
def col x
|
38
|
-
res = []
|
39
|
-
size.times do |y|
|
40
|
-
val = get x, y
|
41
|
-
res << val if val != 0
|
42
|
-
end
|
43
|
-
res
|
44
|
-
end
|
45
|
-
|
46
|
-
#Renvoie le contenu de la ligne y
|
47
|
-
def row y
|
48
|
-
res = []
|
49
|
-
size.times do |x|
|
50
|
-
val = get x, y
|
51
|
-
res << val if val != 0
|
52
|
-
end
|
53
|
-
res
|
54
|
-
end
|
55
|
-
|
56
|
-
#Renvoie le contenu du carré contenant la case x,y
|
57
|
-
def square x, y
|
58
|
-
xmin = x - (x%base)
|
59
|
-
ymin = y - (y%base)
|
60
|
-
res = []
|
61
|
-
|
62
|
-
base.times do |xx|
|
63
|
-
base.times do |yy|
|
64
|
-
val = get xx+xmin, yy+ymin
|
65
|
-
res << val if val != 0
|
66
|
-
end
|
67
|
-
end
|
68
|
-
|
69
|
-
res
|
70
|
-
end
|
71
|
-
|
72
|
-
#Renvoie true si le sudoku ne contient aucune case vide
|
73
|
-
def complete?
|
74
|
-
each{|x,y,val| return false if val == 0}
|
75
|
-
true
|
76
|
-
end
|
77
|
-
|
78
|
-
#Renvoie true si chaque case a au moins 1 possibilité à ce stade
|
79
|
-
def completable?
|
80
|
-
completed = 0
|
81
|
-
each do |x, y, val|
|
82
|
-
return false if possibilities(x, y).empty?
|
83
|
-
end
|
84
|
-
return true
|
85
|
-
end
|
86
|
-
|
87
|
-
#Renvoie toutes les possibilités pour la case x,y
|
88
|
-
def possibilities x, y
|
89
|
-
res = Array.new(size){|i| i+1}
|
90
|
-
xmin = x-x%base
|
91
|
-
ymin = y-y%base
|
92
|
-
|
93
|
-
size.times do |i|
|
94
|
-
res.delete get(x,i) if i!=y
|
95
|
-
res.delete get(i,y) if i!=x
|
96
|
-
xx, yy = xmin+i%base, ymin+i/base
|
97
|
-
res.delete get(xx, yy) if xx!=x && yy!=y
|
98
|
-
end
|
99
|
-
|
100
|
-
res
|
101
|
-
end
|
102
|
-
|
103
|
-
#Renvoie true si tous les nombres de la grille sont valides
|
104
|
-
def valid_grid?
|
105
|
-
each do |x, y, val|
|
106
|
-
next if val.zero?
|
107
|
-
return false unless valid_cell? x, y, val
|
108
|
-
end
|
109
|
-
|
110
|
-
true
|
111
|
-
end
|
112
|
-
|
113
|
-
#Renvoie true si val est possible en x,y
|
114
|
-
def valid_cell? x, y, val
|
115
|
-
val = val.to_i
|
116
|
-
xmin = x-x%base
|
117
|
-
ymin = y-y%base
|
118
|
-
|
119
|
-
size.times do |i|
|
120
|
-
return false if i!=y && get(x,i)==val
|
121
|
-
return false if i!=x && get(i,y)==val
|
122
|
-
xx, yy = xmin+i%base, ymin+i/base
|
123
|
-
return false if xx!=x && yy!=y && get(xx, yy) == val
|
124
|
-
end
|
125
|
-
|
126
|
-
true
|
127
|
-
end
|
128
|
-
|
129
|
-
#Si aucun argument n'est passé => valid_grid?
|
130
|
-
#Si 3 arguments sont passés => valid_cell? x, y, val
|
131
|
-
def valid? *args
|
132
|
-
if args.empty?
|
133
|
-
valid_grid?
|
134
|
-
elsif args.length == 3
|
135
|
-
valid_cell? *args
|
136
|
-
else
|
137
|
-
raise ArgumentError, "wrong number of arguments(#{args.length} for 0 or 3)"
|
138
|
-
end
|
139
|
-
end
|
140
|
-
|
141
|
-
#Renvoie la base du sudoku
|
142
|
-
def base
|
143
|
-
if @base
|
144
|
-
@base
|
145
|
-
else
|
146
|
-
@base = (size**0.5).to_i
|
147
|
-
end
|
148
|
-
end
|
149
|
-
|
150
|
-
#Renvoie le nombre de cases dans le sudoku
|
151
|
-
def length
|
152
|
-
if @length
|
153
|
-
@length
|
154
|
-
else
|
155
|
-
@length = size*size
|
156
|
-
end
|
157
|
-
end
|
158
|
-
|
159
|
-
#Représentation texte humainement lisible
|
160
|
-
def to_s
|
161
|
-
res = ""
|
162
|
-
width = size.to_s.length
|
163
|
-
zero = ".".center width+1
|
164
|
-
|
165
|
-
size.times do |y|
|
166
|
-
res += "\n" if y>0 && y%base == 0
|
167
|
-
size.times do |x|
|
168
|
-
res += " " if x>0 && x%base == 0
|
169
|
-
val = get x, y
|
170
|
-
res += val.zero? ? zero : "#{val.to_s.center width} "
|
171
|
-
end
|
172
|
-
res += "\n"
|
173
|
-
end
|
174
|
-
res
|
175
|
-
end
|
176
|
-
|
177
|
-
#Représentation courte (utile dans irb)
|
178
|
-
def inspect
|
179
|
-
"#<#{self.class} #{size}x#{size} [#{get 0, 0}, #{get 0, 1}, ... , #{get size-2, size-1}, #{get size-1, size-1}]>"
|
180
|
-
end
|
181
|
-
|
182
|
-
#Représentation pour l'enregistrement
|
183
|
-
def to_sutxt
|
184
|
-
res = "#{base}:"
|
185
|
-
size.times do |y|
|
186
|
-
size.times do |x|
|
187
|
-
res << " #{get x, y}"
|
188
|
-
end
|
189
|
-
end
|
190
|
-
res+';'
|
191
|
-
end
|
192
|
-
end
|
13
|
+
#Exception lancée lors d'operations sur deux sudokus incompatibles
|
14
|
+
class NotCompatibleError < Exception; end
|
193
15
|
|
194
16
|
#Sudoku 9x9 (base 3) très rapide
|
195
17
|
class S3
|
@@ -201,6 +23,8 @@ module Sudoku
|
|
201
23
|
__initialize nil
|
202
24
|
end
|
203
25
|
|
26
|
+
#Renvoie le coté du Sudoku
|
27
|
+
# @return (Fixnum)
|
204
28
|
def size
|
205
29
|
SIZE
|
206
30
|
end
|
@@ -211,6 +35,8 @@ module Sudoku
|
|
211
35
|
include Grid
|
212
36
|
private :size_internal
|
213
37
|
|
38
|
+
#Renvoie le coté du Sudoku
|
39
|
+
# @return (Fixnum)
|
214
40
|
def size
|
215
41
|
if @size
|
216
42
|
@size
|
@@ -234,6 +60,8 @@ module Sudoku
|
|
234
60
|
end
|
235
61
|
end
|
236
62
|
|
63
|
+
#Renvoie le coté du Sudoku
|
64
|
+
# @return (Fixnum)
|
237
65
|
def set x, y, val
|
238
66
|
if x<0 || x>=size || y<0 || y>=size || val<0 || val > size
|
239
67
|
raise ArgumentError, "#{x},#{y} => #{val} is impossible in a #{size}x#{size} sudoku"
|
@@ -241,6 +69,8 @@ module Sudoku
|
|
241
69
|
@grid[x][y] = val
|
242
70
|
end
|
243
71
|
|
72
|
+
#Renvoie la valeur en x,y
|
73
|
+
# @return (Fixnum)
|
244
74
|
def get x, y
|
245
75
|
if x<0 || x>=size || y<0 || y>=size
|
246
76
|
raise ArgumentError, "Is there a #{x},#{y} cell in a #{size}x#{size} sudoku ?"
|
@@ -248,15 +78,20 @@ module Sudoku
|
|
248
78
|
@grid[x][y]
|
249
79
|
end
|
250
80
|
|
81
|
+
#Parcourt tout le sudoku
|
82
|
+
# @yield [x, y, val] la position et la valeur courante
|
83
|
+
# @return (self)
|
251
84
|
def each
|
252
85
|
@size.times do |y|
|
253
86
|
@size.times do |x|
|
254
87
|
yield x, y, @grid[x][y]
|
255
88
|
end
|
256
89
|
end
|
90
|
+
self
|
257
91
|
end
|
258
92
|
end
|
259
93
|
|
94
|
+
#Adaptateurs de base
|
260
95
|
ADAPTERS = [
|
261
96
|
[3, S3],
|
262
97
|
[4..15, S4_15],
|
@@ -264,8 +99,11 @@ module Sudoku
|
|
264
99
|
]
|
265
100
|
|
266
101
|
class << self
|
267
|
-
#Renvoie la classe de la
|
268
|
-
|
102
|
+
#Renvoie la classe de la première implémentation dont la zone comprend n
|
103
|
+
# @param [Fixnum] n La base du sudoku
|
104
|
+
# @return [Class] La première classe dont la zone comprend n, ou la première classe
|
105
|
+
# donc la zone est 0 (zone par défaut)
|
106
|
+
def best_class_for n
|
269
107
|
n = n.to_i
|
270
108
|
ADAPTERS.each do |ad|
|
271
109
|
zone = ad[0]
|
@@ -279,37 +117,35 @@ module Sudoku
|
|
279
117
|
end
|
280
118
|
end
|
281
119
|
end
|
120
|
+
alias :[] :best_class_for
|
282
121
|
|
283
|
-
#Ajoute un
|
284
|
-
|
122
|
+
#Ajoute un adapteur pour la zone definie.
|
123
|
+
# @param [Range, Fixnum] zone La zone de validité de l'adapteur
|
124
|
+
# @param [Class] adapter L'adaptateur à ajouter
|
125
|
+
def define_class_for zone, adapter
|
285
126
|
ADAPTERS.unshift [zone, adapter]
|
286
127
|
end
|
128
|
+
alias :[]= :define_class_for
|
287
129
|
|
288
130
|
#Renvoie une instance de la meilleure implémentation pour un sudoku de base n
|
131
|
+
# @param [Fixnum] n La base du sudoku
|
132
|
+
# @return [Grid]
|
289
133
|
def best_grid_for n=3
|
290
134
|
n = n.to_i
|
291
|
-
|
135
|
+
best_class_for(n).new n
|
292
136
|
end
|
293
137
|
alias :new :best_grid_for
|
294
138
|
|
295
139
|
#Renvoie un nouveau Sudoku a partir de la chaine donnee
|
140
|
+
# @param [String] str Une chaine Sutxt
|
141
|
+
# @return [Grid] Un sudoku rempli avec les données Sutxt
|
296
142
|
def parse str
|
297
143
|
unless str =~ /(\d+):(.+);/
|
298
144
|
raise MalformedSutxtError, "It doesn't seem to be a sutxt line..."
|
299
145
|
end
|
300
146
|
|
301
147
|
base = $1.to_i
|
302
|
-
|
303
|
-
unless data.length == base**4
|
304
|
-
raise MalformedSutxtError, "Expecting #{base**4} numbers, #{data.length} given"
|
305
|
-
end
|
306
|
-
|
307
|
-
res = self.best_grid_for base
|
308
|
-
res.each do |x, y, val|
|
309
|
-
res.set x, y, data[x+y*res.size]
|
310
|
-
end
|
311
|
-
|
312
|
-
return res
|
148
|
+
return best_grid_for(base).load(str)
|
313
149
|
end
|
314
150
|
end
|
315
151
|
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
module Sudoku
|
2
|
+
#Permet de generer rapidement un sudoku
|
3
|
+
module Generator
|
4
|
+
#Remplit la diagonale descendante de 1 a self.size
|
5
|
+
# @return [self]
|
6
|
+
def make_diagonal
|
7
|
+
each{|x,y,val| set x,x,x+1 if x==y}
|
8
|
+
self
|
9
|
+
end
|
10
|
+
|
11
|
+
#Remplit tout le sudoku de maniere a ce qu'il soit valide
|
12
|
+
# @return [self]
|
13
|
+
def make_valid
|
14
|
+
pattern = Array.new(size){|i| i+1}.shuffle
|
15
|
+
size.times do |y|
|
16
|
+
size.times do |x|
|
17
|
+
set x, y, pattern[x]
|
18
|
+
end
|
19
|
+
base.times{|i| pattern.push pattern.shift}
|
20
|
+
pattern.push pattern.shift if base - (y%base) == 1
|
21
|
+
end
|
22
|
+
self
|
23
|
+
end
|
24
|
+
|
25
|
+
#Cree un sudoku valide et laisse des cases vides au hasard
|
26
|
+
# @param [Fixnum] seed Le parametre a utiliser pour rand()
|
27
|
+
# @return [self]
|
28
|
+
def make_valid_incomplete seed=2
|
29
|
+
pattern = Array.new(size){|i| i+1}.shuffle
|
30
|
+
size.times do |y|
|
31
|
+
size.times do |x|
|
32
|
+
set x, y, pattern[x] if rand(seed) == seed-1
|
33
|
+
end
|
34
|
+
base.times{|i| pattern.push pattern.shift}
|
35
|
+
pattern.push pattern.shift if base - (y%base) == 1
|
36
|
+
end
|
37
|
+
self
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
data/lib/sudoku/grid.rb
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
SUDOKU_LIBDIR = File.dirname File.expand_path(__FILE__)
|
2
|
+
|
3
|
+
require "#{SUDOKU_LIBDIR}/logic"
|
4
|
+
require "#{SUDOKU_LIBDIR}/solver"
|
5
|
+
require "#{SUDOKU_LIBDIR}/generator"
|
6
|
+
|
7
|
+
module Sudoku
|
8
|
+
#Interface commune des sudokus
|
9
|
+
module Grid
|
10
|
+
include Logic
|
11
|
+
include Generator
|
12
|
+
include Solver
|
13
|
+
end
|
14
|
+
end
|
data/lib/sudoku/logic.rb
ADDED
@@ -0,0 +1,242 @@
|
|
1
|
+
module Sudoku
|
2
|
+
#Logique de base du Sudoku
|
3
|
+
module Logic
|
4
|
+
#Renvoie le contenu de la ligne x
|
5
|
+
# @return (Array)
|
6
|
+
def col x
|
7
|
+
res = []
|
8
|
+
size.times do |y|
|
9
|
+
val = get x, y
|
10
|
+
res << val if val != 0
|
11
|
+
end
|
12
|
+
res
|
13
|
+
end
|
14
|
+
|
15
|
+
#Renvoie le contenu de la ligne y
|
16
|
+
# @return (Array)
|
17
|
+
def row y
|
18
|
+
res = []
|
19
|
+
size.times do |x|
|
20
|
+
val = get x, y
|
21
|
+
res << val if val != 0
|
22
|
+
end
|
23
|
+
res
|
24
|
+
end
|
25
|
+
|
26
|
+
#Renvoie le contenu du carré contenant la case x,y
|
27
|
+
# @return (Array)
|
28
|
+
def square x, y
|
29
|
+
xmin = x - (x%base)
|
30
|
+
ymin = y - (y%base)
|
31
|
+
res = []
|
32
|
+
|
33
|
+
base.times do |xx|
|
34
|
+
base.times do |yy|
|
35
|
+
val = get xx+xmin, yy+ymin
|
36
|
+
res << val if val != 0
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
res
|
41
|
+
end
|
42
|
+
|
43
|
+
#Renvoie true si le sudoku ne contient aucune case vide
|
44
|
+
def complete?
|
45
|
+
each{|x,y,val| return false if val == 0}
|
46
|
+
true
|
47
|
+
end
|
48
|
+
|
49
|
+
#Renvoie true si chaque case a au moins 1 possibilité à ce stade
|
50
|
+
def completable?
|
51
|
+
completed = 0
|
52
|
+
each do |x, y, val|
|
53
|
+
return false if possibilities(x, y).empty?
|
54
|
+
end
|
55
|
+
return true
|
56
|
+
end
|
57
|
+
|
58
|
+
#Renvoie toutes les possibilités pour la case x,y
|
59
|
+
# @return (Array)
|
60
|
+
def possibilities x, y
|
61
|
+
res = Array.new(size){|i| i+1}
|
62
|
+
xmin = x-x%base
|
63
|
+
ymin = y-y%base
|
64
|
+
|
65
|
+
size.times do |i|
|
66
|
+
res.delete get(x,i) if i!=y
|
67
|
+
res.delete get(i,y) if i!=x
|
68
|
+
xx, yy = xmin+i%base, ymin+i/base
|
69
|
+
res.delete get(xx, yy) if xx!=x && yy!=y
|
70
|
+
end
|
71
|
+
|
72
|
+
res
|
73
|
+
end
|
74
|
+
|
75
|
+
#Renvoie true si tous les nombres de la grille sont valides
|
76
|
+
def valid_grid?
|
77
|
+
each do |x, y, val|
|
78
|
+
next if val.zero?
|
79
|
+
return false unless valid_cell? x, y, val
|
80
|
+
end
|
81
|
+
|
82
|
+
true
|
83
|
+
end
|
84
|
+
|
85
|
+
#Renvoie true si val est possible en x,y
|
86
|
+
def valid_cell? x, y, val
|
87
|
+
return true if val.zero?
|
88
|
+
val = val.to_i
|
89
|
+
xmin = x-x%base
|
90
|
+
ymin = y-y%base
|
91
|
+
|
92
|
+
size.times do |i|
|
93
|
+
return false if i!=y && get(x,i)==val
|
94
|
+
return false if i!=x && get(i,y)==val
|
95
|
+
xx, yy = xmin+i%base, ymin+i/base
|
96
|
+
return false if xx!=x && yy!=y && get(xx, yy) == val
|
97
|
+
end
|
98
|
+
|
99
|
+
true
|
100
|
+
end
|
101
|
+
|
102
|
+
# @overload valid?(x, y, val)
|
103
|
+
# Vérifie si val est valide en x,y
|
104
|
+
# @param [Fixnum] x La colonne de la valeur à vérifier
|
105
|
+
# @param [Fixnum] y La ligne de la valeur à vérifier
|
106
|
+
# @param [Fixnum] val La valeur à vérifier
|
107
|
+
# @return [Boolean] true si la valeur est valide, false sinon
|
108
|
+
# @overload valid?
|
109
|
+
# Vérifie que toutes les valeurs du Sudoku sont vlaides
|
110
|
+
# @return [Boolean] true si toutes les valeurs sont valides, false sinon
|
111
|
+
def valid? *args
|
112
|
+
if args.empty?
|
113
|
+
valid_grid?
|
114
|
+
elsif args.length == 3
|
115
|
+
valid_cell? *args
|
116
|
+
else
|
117
|
+
raise ArgumentError, "wrong number of arguments(#{args.length} for 0 or 3)"
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
#Renvoie la base du sudoku
|
122
|
+
# @return (Fixnum)
|
123
|
+
def base
|
124
|
+
if @base
|
125
|
+
@base
|
126
|
+
else
|
127
|
+
@base = (size**0.5).to_i
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
#Renvoie le nombre de cases dans le sudoku
|
132
|
+
# @return (Fixnum)
|
133
|
+
def length
|
134
|
+
if @length
|
135
|
+
@length
|
136
|
+
else
|
137
|
+
@length = size*size
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
# Compte le nombre d'occurences pour les valeurs
|
142
|
+
# @return [Hash] L'association valeur => occurences
|
143
|
+
# @overload count(*values)
|
144
|
+
# Compte les occurences pour les valeurs passées en paramètres
|
145
|
+
# @param [*Fixnum] values Les valeurs à compter
|
146
|
+
# @overload count
|
147
|
+
# Compte les occurences de chaque valeur
|
148
|
+
def count *values
|
149
|
+
values = Array.new(size){|i| i+1} if values.empty?
|
150
|
+
|
151
|
+
res = {}
|
152
|
+
values.each do |val|
|
153
|
+
if val<0 || val>size
|
154
|
+
raise ArgumentError, "Impossible value #{val} in a #{size}x#{size} sudoku"
|
155
|
+
end
|
156
|
+
res[val] = 0
|
157
|
+
end
|
158
|
+
|
159
|
+
each do |x, y, val|
|
160
|
+
res[val] += 1 if values.include? val
|
161
|
+
end
|
162
|
+
|
163
|
+
res
|
164
|
+
end
|
165
|
+
|
166
|
+
#Représentation texte humainement lisible
|
167
|
+
# @return (String)
|
168
|
+
def to_s
|
169
|
+
res = ""
|
170
|
+
width = size.to_s.length
|
171
|
+
zero = ".".center width+1
|
172
|
+
|
173
|
+
size.times do |y|
|
174
|
+
res += "\n" if y>0 && y%base == 0
|
175
|
+
size.times do |x|
|
176
|
+
res += " " if x>0 && x%base == 0
|
177
|
+
val = get x, y
|
178
|
+
res += val.zero? ? zero : "#{val.to_s.center width} "
|
179
|
+
end
|
180
|
+
res += "\n"
|
181
|
+
end
|
182
|
+
res
|
183
|
+
end
|
184
|
+
|
185
|
+
#Représentation courte (utile dans irb)
|
186
|
+
# @return [String]
|
187
|
+
def inspect
|
188
|
+
"#<#{self.class} #{size}x#{size} [#{get 0, 0}, #{get 0, 1}, ... , #{get size-2, size-1}, #{get size-1, size-1}]>"
|
189
|
+
end
|
190
|
+
|
191
|
+
#Représentation pour l'enregistrement
|
192
|
+
# @return (String)
|
193
|
+
def to_sutxt
|
194
|
+
res = "#{base}:"
|
195
|
+
size.times do |y|
|
196
|
+
size.times do |x|
|
197
|
+
res << " #{get x, y}"
|
198
|
+
end
|
199
|
+
end
|
200
|
+
res+';'
|
201
|
+
end
|
202
|
+
|
203
|
+
#Charge un Sudoku depuis une chaine Sutxt
|
204
|
+
# @return (self)
|
205
|
+
# @raise [MalformedSutxtError] Chaine Sutxt mal formatee
|
206
|
+
# @raise [NotCompatibleError] La chaine Sutxt correspond a un Sudoku de base differente
|
207
|
+
def load sutxt_str
|
208
|
+
unless sutxt_str =~ /(\d+):(.+);/
|
209
|
+
raise MalformedSutxtError, "It doesn't seem to be a sutxt line..."
|
210
|
+
end
|
211
|
+
|
212
|
+
sutxt_base = $1.to_i
|
213
|
+
unless sutxt_base == base
|
214
|
+
raise NotCompatibleError, "A #{base} sudoku cannot load a #{sutxt_base} Sutxt"
|
215
|
+
end
|
216
|
+
|
217
|
+
data = $2.split(/\s+/).delete_if(&:empty?).map(&:to_i)
|
218
|
+
unless data.length == length
|
219
|
+
raise MalformedSutxtError, "Expecting #{length} numbers, #{data.length} given"
|
220
|
+
end
|
221
|
+
|
222
|
+
size.times do |y|
|
223
|
+
size.times do |x|
|
224
|
+
set x, y, data.shift
|
225
|
+
end
|
226
|
+
end
|
227
|
+
self
|
228
|
+
end
|
229
|
+
|
230
|
+
#Charge un sudoku depuis un autre sudoku
|
231
|
+
# @param [Grid] l'autre Sudoku
|
232
|
+
# @return (self)
|
233
|
+
# @raise [NotCompatibleError] L'autre sudoku est de base differente
|
234
|
+
def import other
|
235
|
+
unless size == other.size
|
236
|
+
raise NotCompatibleError, "Cannot import a #{other.base} sudoku in a #{base} sudoku"
|
237
|
+
end
|
238
|
+
other.each{|x,y,v| set x,y,v}
|
239
|
+
self
|
240
|
+
end
|
241
|
+
end
|
242
|
+
end
|