sudokuhandler 0.1.2 → 0.1.3
Sign up to get free protection for your applications and to get access to all the features.
- 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
|