| line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
|
1
|
|
|
|
|
|
|
=head1 NAME |
|
2
|
|
|
|
|
|
|
|
|
3
|
|
|
|
|
|
|
Games::Sudoku::General - Solve sudoku-like puzzles. |
|
4
|
|
|
|
|
|
|
|
|
5
|
|
|
|
|
|
|
=head1 SYNOPSIS |
|
6
|
|
|
|
|
|
|
|
|
7
|
|
|
|
|
|
|
$su = Games::Sudoku::General->new (); |
|
8
|
|
|
|
|
|
|
print $su->problem(<solution(); |
|
9
|
|
|
|
|
|
|
3 . . . . 8 . 2 . |
|
10
|
|
|
|
|
|
|
. . . . . 9 . . . |
|
11
|
|
|
|
|
|
|
. . 2 7 . 5 . . . |
|
12
|
|
|
|
|
|
|
2 4 . 5 . . 8 . . |
|
13
|
|
|
|
|
|
|
. 8 5 . 7 4 . . 6 |
|
14
|
|
|
|
|
|
|
. 3 . . . . 9 4 . |
|
15
|
|
|
|
|
|
|
1 . 4 . . . . 7 2 |
|
16
|
|
|
|
|
|
|
. . 6 9 . . . 5 . |
|
17
|
|
|
|
|
|
|
. 7 . 6 1 2 . . 9 |
|
18
|
|
|
|
|
|
|
eod |
|
19
|
|
|
|
|
|
|
|
|
20
|
|
|
|
|
|
|
=head1 DESCRIPTION |
|
21
|
|
|
|
|
|
|
|
|
22
|
|
|
|
|
|
|
This package solves puzzles that involve the allocation of symbols |
|
23
|
|
|
|
|
|
|
among a number of sets, such that no set contains more than one of |
|
24
|
|
|
|
|
|
|
any symbol. This class of problem includes the puzzles known as |
|
25
|
|
|
|
|
|
|
'Sudoku', 'Number Place', and 'Wasabi'. |
|
26
|
|
|
|
|
|
|
|
|
27
|
|
|
|
|
|
|
Each Sudoku puzzle is considered to be made up of a number of cells, |
|
28
|
|
|
|
|
|
|
each of which is a member of one or more sets, and each of which may |
|
29
|
|
|
|
|
|
|
contain exactly one symbol. The contents of some of the cells are |
|
30
|
|
|
|
|
|
|
given, and the problem is to deduce the contents of the rest of the |
|
31
|
|
|
|
|
|
|
cells. |
|
32
|
|
|
|
|
|
|
|
|
33
|
|
|
|
|
|
|
Although such puzzles as Sudoku are presented on a square grid, this |
|
34
|
|
|
|
|
|
|
package does not assume any particular geometry. Instead, the topology |
|
35
|
|
|
|
|
|
|
of the puzzle is defined by the user in terms of a list of the sets |
|
36
|
|
|
|
|
|
|
to which each cell belongs. Some topology generators are provided, but |
|
37
|
|
|
|
|
|
|
the user has the option of hand-specifying an arbitrary topology. |
|
38
|
|
|
|
|
|
|
|
|
39
|
|
|
|
|
|
|
Even on the standard 9 x 9 Sudoku topology there are variants in which |
|
40
|
|
|
|
|
|
|
unspecified cells are constrained in various ways (odd/even, high/low). |
|
41
|
|
|
|
|
|
|
Such variants are accommodated by defining named sets of allowed |
|
42
|
|
|
|
|
|
|
symbols, and then giving the set name for each unoccupied cell to which |
|
43
|
|
|
|
|
|
|
it applies. See the C attribute for more |
|
44
|
|
|
|
|
|
|
information and an example. |
|
45
|
|
|
|
|
|
|
|
|
46
|
|
|
|
|
|
|
This module is able not only to solve a variety of Sudoku-like |
|
47
|
|
|
|
|
|
|
puzzles, but to 'explain' how it arrived at its solution. The |
|
48
|
|
|
|
|
|
|
steps() method, called after a solution is generated, lists in order |
|
49
|
|
|
|
|
|
|
what solution constraints were applied, what cell each constraint |
|
50
|
|
|
|
|
|
|
is applied to, and what symbol the cell was constrained to. |
|
51
|
|
|
|
|
|
|
|
|
52
|
|
|
|
|
|
|
Test script t/sudoku.t demonstrates these features. ActivePerl users |
|
53
|
|
|
|
|
|
|
will have to download the kit from L or |
|
54
|
|
|
|
|
|
|
L to get this |
|
55
|
|
|
|
|
|
|
file. |
|
56
|
|
|
|
|
|
|
|
|
57
|
|
|
|
|
|
|
=head2 Exported symbols |
|
58
|
|
|
|
|
|
|
|
|
59
|
|
|
|
|
|
|
No symbols are exported by default, but the following things are |
|
60
|
|
|
|
|
|
|
available for export: |
|
61
|
|
|
|
|
|
|
|
|
62
|
|
|
|
|
|
|
Status values exported by the :status tag |
|
63
|
|
|
|
|
|
|
SUDOKU_SUCCESS |
|
64
|
|
|
|
|
|
|
This means what you think it does. |
|
65
|
|
|
|
|
|
|
SUDOKU_NO_SOLUTION |
|
66
|
|
|
|
|
|
|
This means the method exhausted all possible |
|
67
|
|
|
|
|
|
|
soltions without finding one |
|
68
|
|
|
|
|
|
|
SUDOKU_TOO_HARD |
|
69
|
|
|
|
|
|
|
This means the iteration_limit attribute was |
|
70
|
|
|
|
|
|
|
set to a positive number and the solution() |
|
71
|
|
|
|
|
|
|
method hit the limit without finding a solution. |
|
72
|
|
|
|
|
|
|
|
|
73
|
|
|
|
|
|
|
The :all tag is provided for convenience, but it exports the same |
|
74
|
|
|
|
|
|
|
symbols as :status. |
|
75
|
|
|
|
|
|
|
|
|
76
|
|
|
|
|
|
|
=head2 Attributes |
|
77
|
|
|
|
|
|
|
|
|
78
|
|
|
|
|
|
|
Games::Sudoku::General objects have the following attributes, which may |
|
79
|
|
|
|
|
|
|
normally be accessed by the get() method, and changed by the set() |
|
80
|
|
|
|
|
|
|
method. |
|
81
|
|
|
|
|
|
|
|
|
82
|
|
|
|
|
|
|
In parentheses after the name of the attribute is the word "boolean", |
|
83
|
|
|
|
|
|
|
"number" or "string", giving the data type of the attribute. Booleans |
|
84
|
|
|
|
|
|
|
are interpreted in the Perl sense: undef, 0, and '' are false, and |
|
85
|
|
|
|
|
|
|
anything else is true. The parentheses may also contain the words |
|
86
|
|
|
|
|
|
|
"read-only" to denote a read-only attribute or "write-only" to denote |
|
87
|
|
|
|
|
|
|
a write-only attribute. |
|
88
|
|
|
|
|
|
|
|
|
89
|
|
|
|
|
|
|
In general, the write-only attributes exist as a convenience to the |
|
90
|
|
|
|
|
|
|
user, and provide a shorthand way to set a cluster of attributes at |
|
91
|
|
|
|
|
|
|
the same time. At the moment all of them are concerned with generating |
|
92
|
|
|
|
|
|
|
problem topologies, which are a real pain to specify by hand. |
|
93
|
|
|
|
|
|
|
|
|
94
|
|
|
|
|
|
|
=over |
|
95
|
|
|
|
|
|
|
|
|
96
|
|
|
|
|
|
|
=item allowed_symbols (string) |
|
97
|
|
|
|
|
|
|
|
|
98
|
|
|
|
|
|
|
This attribute names and defines sets of allowed symbols which may |
|
99
|
|
|
|
|
|
|
appear in empty cells. The set definitions are whitespace-delimited |
|
100
|
|
|
|
|
|
|
and each consists of a string of the form 'name=symbol,symbol...' |
|
101
|
|
|
|
|
|
|
where the 'name' is the name of the set, and the symbols are a list |
|
102
|
|
|
|
|
|
|
of the symbols valid in a cell to which that set applies. |
|
103
|
|
|
|
|
|
|
|
|
104
|
|
|
|
|
|
|
For example, if you have an odd/even puzzle (i.e. you are given that |
|
105
|
|
|
|
|
|
|
at least some of the unoccupied cells are even or odd but not both), |
|
106
|
|
|
|
|
|
|
you might want to |
|
107
|
|
|
|
|
|
|
|
|
108
|
|
|
|
|
|
|
$su->set (allowed_symbols => <
|
|
109
|
|
|
|
|
|
|
o=1,3,5,7,9 |
|
110
|
|
|
|
|
|
|
e=2,4,6,8 |
|
111
|
|
|
|
|
|
|
eod |
|
112
|
|
|
|
|
|
|
|
|
113
|
|
|
|
|
|
|
and then define the problem like this: |
|
114
|
|
|
|
|
|
|
|
|
115
|
|
|
|
|
|
|
$su->problem (<
|
|
116
|
|
|
|
|
|
|
1 o e o e e o e 3 |
|
117
|
|
|
|
|
|
|
o o e o 6 e o o e |
|
118
|
|
|
|
|
|
|
e e 3 o o 1 o e e |
|
119
|
|
|
|
|
|
|
e 7 o 1 o e e o e |
|
120
|
|
|
|
|
|
|
o e 8 e e o 5 o o |
|
121
|
|
|
|
|
|
|
o e o o e 3 e 4 o |
|
122
|
|
|
|
|
|
|
e o o 8 o o 6 o e |
|
123
|
|
|
|
|
|
|
o o o e 1 e e e o |
|
124
|
|
|
|
|
|
|
6 e e e o o o o 7 |
|
125
|
|
|
|
|
|
|
eod |
|
126
|
|
|
|
|
|
|
|
|
127
|
|
|
|
|
|
|
To eliminate an individual allowed symbol set, set it to an empty |
|
128
|
|
|
|
|
|
|
string (e.g. $su->set (allowed_symbols => 'o=');). To eliminate all |
|
129
|
|
|
|
|
|
|
symbol sets, set the entire attribute to the empty string. |
|
130
|
|
|
|
|
|
|
|
|
131
|
|
|
|
|
|
|
Allowed symbol set names may not conflict with symbol names. If you set |
|
132
|
|
|
|
|
|
|
the symbol attribute, all allowed symbol sets are deleted, because |
|
133
|
|
|
|
|
|
|
that seemed to be the most expeditious way to enforce this restriction |
|
134
|
|
|
|
|
|
|
across a symbol set change. |
|
135
|
|
|
|
|
|
|
|
|
136
|
|
|
|
|
|
|
Because symbol set names must be parsed like symbol names when a |
|
137
|
|
|
|
|
|
|
problem is defined, they also affect the need for whitespace on |
|
138
|
|
|
|
|
|
|
problem input. See the L documentation for |
|
139
|
|
|
|
|
|
|
full details. |
|
140
|
|
|
|
|
|
|
|
|
141
|
|
|
|
|
|
|
=item autocopy (boolean) |
|
142
|
|
|
|
|
|
|
|
|
143
|
|
|
|
|
|
|
If true, this attribute causes the generate() method to implicitly call |
|
144
|
|
|
|
|
|
|
copy() to copy the generated problem to the clipboard. |
|
145
|
|
|
|
|
|
|
|
|
146
|
|
|
|
|
|
|
This attribute is false by default. |
|
147
|
|
|
|
|
|
|
|
|
148
|
|
|
|
|
|
|
=item brick (string, write-only) |
|
149
|
|
|
|
|
|
|
|
|
150
|
|
|
|
|
|
|
This "virtual" attribute is a convenience, which causes the object to be |
|
151
|
|
|
|
|
|
|
configured with a topology of rows, columns, and rectangles. The value |
|
152
|
|
|
|
|
|
|
set must be either a comma-separated list of two numbers (e.g. '3,2') |
|
153
|
|
|
|
|
|
|
or a reference to a list containing two numbers (e.g. [3, 2]). Either |
|
154
|
|
|
|
|
|
|
way, the numbers represent the horizontal dimension of the rectangle (in |
|
155
|
|
|
|
|
|
|
columns) and the vertical dimension of the rectangle (in rows). The |
|
156
|
|
|
|
|
|
|
overall size of the puzzle square is the product of these. For example, |
|
157
|
|
|
|
|
|
|
|
|
158
|
|
|
|
|
|
|
$su->set( brick => [ 3, 2 ] ) |
|
159
|
|
|
|
|
|
|
|
|
160
|
|
|
|
|
|
|
generates a topology that looks like this |
|
161
|
|
|
|
|
|
|
|
|
162
|
|
|
|
|
|
|
+-------+-------+ |
|
163
|
|
|
|
|
|
|
| x x x | x x x | |
|
164
|
|
|
|
|
|
|
| x x x | x x x | |
|
165
|
|
|
|
|
|
|
+-------+-------+ |
|
166
|
|
|
|
|
|
|
| x x x | x x x | |
|
167
|
|
|
|
|
|
|
| x x x | x x x | |
|
168
|
|
|
|
|
|
|
+-------+-------+ |
|
169
|
|
|
|
|
|
|
| x x x | x x x | |
|
170
|
|
|
|
|
|
|
| x x x | x x x | |
|
171
|
|
|
|
|
|
|
+-------+-------+ |
|
172
|
|
|
|
|
|
|
|
|
173
|
|
|
|
|
|
|
Originally there was a third argument giving the total size of the |
|
174
|
|
|
|
|
|
|
puzzle. Beginning with version 0.006 this was deprecated, since it |
|
175
|
|
|
|
|
|
|
appeared to me to be redundant. As of version 0.021, all uses of this |
|
176
|
|
|
|
|
|
|
argument resulted in a warning. As of version 0.022, use of the third |
|
177
|
|
|
|
|
|
|
argument will become fatal. |
|
178
|
|
|
|
|
|
|
|
|
179
|
|
|
|
|
|
|
Setting this attribute modifies the following "real" attributes: |
|
180
|
|
|
|
|
|
|
|
|
181
|
|
|
|
|
|
|
columns is set to the size of the big square; |
|
182
|
|
|
|
|
|
|
symbols is set to "." and the numbers "1", "2", |
|
183
|
|
|
|
|
|
|
and so on, up to the size of the big square; |
|
184
|
|
|
|
|
|
|
topology is set to represent the rows, columns, |
|
185
|
|
|
|
|
|
|
and small rectangles in the big square, with row |
|
186
|
|
|
|
|
|
|
sets named "r0", "r1", and so on, column sets |
|
187
|
|
|
|
|
|
|
named "c0", "c1", and so on, and small |
|
188
|
|
|
|
|
|
|
rectangle sets named "s0", "s1", and so on for |
|
189
|
|
|
|
|
|
|
historical reasons. |
|
190
|
|
|
|
|
|
|
|
|
191
|
|
|
|
|
|
|
=item columns (number) |
|
192
|
|
|
|
|
|
|
|
|
193
|
|
|
|
|
|
|
This attribute defines the number of columns of data to present in a |
|
194
|
|
|
|
|
|
|
line of output when formatting the topology attribute, or the solution |
|
195
|
|
|
|
|
|
|
to a puzzle. |
|
196
|
|
|
|
|
|
|
|
|
197
|
|
|
|
|
|
|
=item corresponding (number, write-only) |
|
198
|
|
|
|
|
|
|
|
|
199
|
|
|
|
|
|
|
This "virtual" attribute is a convenience, which causes the object |
|
200
|
|
|
|
|
|
|
to be configured for "corresponding-cell" Sudoku. The topology is |
|
201
|
|
|
|
|
|
|
the same as C ... )>, but in addition corresponding |
|
202
|
|
|
|
|
|
|
cells in the small squares must have different values. The extra set |
|
203
|
|
|
|
|
|
|
names are "u0", "u1", and so on. |
|
204
|
|
|
|
|
|
|
|
|
205
|
|
|
|
|
|
|
This kind of puzzle is also called "disjoint groups." |
|
206
|
|
|
|
|
|
|
|
|
207
|
|
|
|
|
|
|
=item cube (string, write-only) |
|
208
|
|
|
|
|
|
|
|
|
209
|
|
|
|
|
|
|
This "virtual" attribute is a convenience, which causes the object to |
|
210
|
|
|
|
|
|
|
be configured for cubical sudoku. The string is either a number, or |
|
211
|
|
|
|
|
|
|
'full', or 'half'. |
|
212
|
|
|
|
|
|
|
|
|
213
|
|
|
|
|
|
|
* a number sets the topology to a Dion cube of the given order. |
|
214
|
|
|
|
|
|
|
That is, |
|
215
|
|
|
|
|
|
|
|
|
216
|
|
|
|
|
|
|
sudokug> set cube 3 |
|
217
|
|
|
|
|
|
|
|
|
218
|
|
|
|
|
|
|
generates a 9 x 9 x 9 Dion cube, with the small squares being 3 x 3. |
|
219
|
|
|
|
|
|
|
The problem is entered in plane, row, and column order, as though you |
|
220
|
|
|
|
|
|
|
were entering the required number of normal Sudoku puzzles |
|
221
|
|
|
|
|
|
|
back-to-back. |
|
222
|
|
|
|
|
|
|
|
|
223
|
|
|
|
|
|
|
* 'full' generates a topology that includes all faces of the cube. The |
|
224
|
|
|
|
|
|
|
sets are the faces of the cube, and the rows, columns, and (for lack |
|
225
|
|
|
|
|
|
|
of a better word) planes of cells that circle the cube. |
|
226
|
|
|
|
|
|
|
|
|
227
|
|
|
|
|
|
|
To enter the problem, imagine the cube unfolded to make a Latin cross. |
|
228
|
|
|
|
|
|
|
Then, enter the problem in order by faces, rows, and columns, top to |
|
229
|
|
|
|
|
|
|
bottom and left to right. The order of entry is actually by cell |
|
230
|
|
|
|
|
|
|
number, as given below. |
|
231
|
|
|
|
|
|
|
|
|
232
|
|
|
|
|
|
|
+-------------+ |
|
233
|
|
|
|
|
|
|
| 0 1 2 3 | |
|
234
|
|
|
|
|
|
|
| 4 5 6 7 | |
|
235
|
|
|
|
|
|
|
| 8 9 10 11 | |
|
236
|
|
|
|
|
|
|
| 12 13 14 15 | |
|
237
|
|
|
|
|
|
|
+-------------+-------------+-------------+ |
|
238
|
|
|
|
|
|
|
| 16 17 18 19 | 32 33 34 35 | 48 49 50 51 | |
|
239
|
|
|
|
|
|
|
| 20 21 22 23 | 36 37 38 39 | 52 53 54 55 | |
|
240
|
|
|
|
|
|
|
| 24 25 26 27 | 40 41 42 43 | 56 57 58 59 | |
|
241
|
|
|
|
|
|
|
| 28 29 30 31 | 44 45 46 47 | 60 61 62 63 | |
|
242
|
|
|
|
|
|
|
+-------------+-------------+-------------+ |
|
243
|
|
|
|
|
|
|
| 64 65 66 67 | |
|
244
|
|
|
|
|
|
|
| 68 69 70 71 | |
|
245
|
|
|
|
|
|
|
| 72 73 74 75 | |
|
246
|
|
|
|
|
|
|
| 76 77 78 79 | |
|
247
|
|
|
|
|
|
|
+-------------+ |
|
248
|
|
|
|
|
|
|
| 80 81 82 83 | |
|
249
|
|
|
|
|
|
|
| 84 85 86 87 | |
|
250
|
|
|
|
|
|
|
| 88 89 90 91 | |
|
251
|
|
|
|
|
|
|
| 92 93 94 95 | |
|
252
|
|
|
|
|
|
|
+-------------+ |
|
253
|
|
|
|
|
|
|
|
|
254
|
|
|
|
|
|
|
The solution will be displayed in order by cell number, with line |
|
255
|
|
|
|
|
|
|
breaks controlled by the C attribute, just |
|
256
|
|
|
|
|
|
|
like any other solution presented by this package. |
|
257
|
|
|
|
|
|
|
|
|
258
|
|
|
|
|
|
|
I have seen such puzzles presented with the bottom square placed to the |
|
259
|
|
|
|
|
|
|
right and rotated counterclockwise 90 degrees. You will need to perform |
|
260
|
|
|
|
|
|
|
the opposite rotation when you enter the problem. |
|
261
|
|
|
|
|
|
|
|
|
262
|
|
|
|
|
|
|
* 'half' generates a topology that looks like an isometric view of a |
|
263
|
|
|
|
|
|
|
cube, with the puzzle on the visible faces. The faces are divided in |
|
264
|
|
|
|
|
|
|
half, since the set size here is 8, not 16. Imagine the isometric |
|
265
|
|
|
|
|
|
|
unfolded to make an L-shape. Then, enter the problem in order by faces, |
|
266
|
|
|
|
|
|
|
rows, and columns, top to bottom and left to right. The order of entry |
|
267
|
|
|
|
|
|
|
is actually in order by cell number, as given below. |
|
268
|
|
|
|
|
|
|
|
|
269
|
|
|
|
|
|
|
+-------------------+ |
|
270
|
|
|
|
|
|
|
| 0 1 2 3 | |
|
271
|
|
|
|
|
|
|
| | |
|
272
|
|
|
|
|
|
|
| 4 5 6 7 | |
|
273
|
|
|
|
|
|
|
+-------------------+ |
|
274
|
|
|
|
|
|
|
| 8 9 10 11 | |
|
275
|
|
|
|
|
|
|
| | |
|
276
|
|
|
|
|
|
|
| 12 13 14 15 | |
|
277
|
|
|
|
|
|
|
+---------+---------+-------------------+ |
|
278
|
|
|
|
|
|
|
| 16 17 | 18 19 | 32 33 34 35 | |
|
279
|
|
|
|
|
|
|
| | | | |
|
280
|
|
|
|
|
|
|
| 20 21 | 22 23 | 36 37 38 39 | |
|
281
|
|
|
|
|
|
|
| | +-------------------+ |
|
282
|
|
|
|
|
|
|
| 24 25 | 26 27 | 40 41 42 43 | |
|
283
|
|
|
|
|
|
|
| | | | |
|
284
|
|
|
|
|
|
|
| 28 29 | 30 31 | 44 45 46 47 | |
|
285
|
|
|
|
|
|
|
+---------+---------+-------------------+ |
|
286
|
|
|
|
|
|
|
|
|
287
|
|
|
|
|
|
|
The solution will be displayed in order by cell number, with line |
|
288
|
|
|
|
|
|
|
breaks controlled by the C attribute, just |
|
289
|
|
|
|
|
|
|
like any other solution presented by this package. |
|
290
|
|
|
|
|
|
|
|
|
291
|
|
|
|
|
|
|
For the 'full' and 'half' cube puzzles, the C attribute is |
|
292
|
|
|
|
|
|
|
set to 4, and the C attribute to the numbers 1 |
|
293
|
|
|
|
|
|
|
to the size of the largest set (16 for the full cube, 8 for the half |
|
294
|
|
|
|
|
|
|
or isometric cube). I have seen full cube puzzles done with hex digits |
|
295
|
|
|
|
|
|
|
0 to F; these are handled most easily by setting the |
|
296
|
|
|
|
|
|
|
C attribute appropriately: |
|
297
|
|
|
|
|
|
|
|
|
298
|
|
|
|
|
|
|
$su->set (cube => 'full', symbols => <
|
|
299
|
|
|
|
|
|
|
. 0 1 2 3 4 5 6 7 8 9 A B C D E F |
|
300
|
|
|
|
|
|
|
eod |
|
301
|
|
|
|
|
|
|
|
|
302
|
|
|
|
|
|
|
=item debug (number) |
|
303
|
|
|
|
|
|
|
|
|
304
|
|
|
|
|
|
|
This attribute, if not 0, causes debugging information to be displayed. |
|
305
|
|
|
|
|
|
|
Values other than 0 are not supported, in the sense that the author |
|
306
|
|
|
|
|
|
|
makes no commitment what will happen when a non-zero value is set, and |
|
307
|
|
|
|
|
|
|
further reserves the right to change this behavior without notice of |
|
308
|
|
|
|
|
|
|
any sort, and without documenting the changes. |
|
309
|
|
|
|
|
|
|
|
|
310
|
|
|
|
|
|
|
=item generation_limit (number) |
|
311
|
|
|
|
|
|
|
|
|
312
|
|
|
|
|
|
|
This attribute governs how hard the generate() method tries to generate |
|
313
|
|
|
|
|
|
|
a problem. If generate() cannot generate a problem after this number of |
|
314
|
|
|
|
|
|
|
tries, it gives up. |
|
315
|
|
|
|
|
|
|
|
|
316
|
|
|
|
|
|
|
The default is 30. |
|
317
|
|
|
|
|
|
|
|
|
318
|
|
|
|
|
|
|
=item iteration_limit (number) |
|
319
|
|
|
|
|
|
|
|
|
320
|
|
|
|
|
|
|
This attribute governs how hard the solution() method tries to solve |
|
321
|
|
|
|
|
|
|
a problem. An iteration is an attempt to use the backtrack constraint. |
|
322
|
|
|
|
|
|
|
Since what this really counts is the number of times we place a |
|
323
|
|
|
|
|
|
|
backtrack constraint on the stack, not the number of values generated |
|
324
|
|
|
|
|
|
|
from that constraint, I suspect 10 to 20 is reasonable for a "normal" |
|
325
|
|
|
|
|
|
|
sudoku problem. |
|
326
|
|
|
|
|
|
|
|
|
327
|
|
|
|
|
|
|
The default is 0, which imposes no limit. |
|
328
|
|
|
|
|
|
|
|
|
329
|
|
|
|
|
|
|
=item largest_set (number, read-only) |
|
330
|
|
|
|
|
|
|
|
|
331
|
|
|
|
|
|
|
This read-only attribute returns the size of the largest set defined by |
|
332
|
|
|
|
|
|
|
the current topology. |
|
333
|
|
|
|
|
|
|
|
|
334
|
|
|
|
|
|
|
=item latin (number, write-only) |
|
335
|
|
|
|
|
|
|
|
|
336
|
|
|
|
|
|
|
This "virtual" attribute is a convenience, which causes the object to |
|
337
|
|
|
|
|
|
|
be configured to handle a Latin square. The value gives the size of |
|
338
|
|
|
|
|
|
|
the square. Setting this modifies the following "real" attributes: |
|
339
|
|
|
|
|
|
|
|
|
340
|
|
|
|
|
|
|
columns is set to the size of the square; |
|
341
|
|
|
|
|
|
|
symbols is set to "." and the letters "A", "B", |
|
342
|
|
|
|
|
|
|
and so on, up to the size of the square; |
|
343
|
|
|
|
|
|
|
topology is set to represent the rows and columns |
|
344
|
|
|
|
|
|
|
of a square, with row sets named "r0", "r1", |
|
345
|
|
|
|
|
|
|
and so on, and the column sets named "c0", |
|
346
|
|
|
|
|
|
|
"c1", and so on. |
|
347
|
|
|
|
|
|
|
|
|
348
|
|
|
|
|
|
|
=item max_tuple (number) |
|
349
|
|
|
|
|
|
|
|
|
350
|
|
|
|
|
|
|
This attribute represents the maximum-sized tuple to consider for the |
|
351
|
|
|
|
|
|
|
tuple constraint. It is possible that one might want to modify this |
|
352
|
|
|
|
|
|
|
upward for large puzzles, or downward for small ones. |
|
353
|
|
|
|
|
|
|
|
|
354
|
|
|
|
|
|
|
The default is 4, meaning that the solution considers doubles, triples, |
|
355
|
|
|
|
|
|
|
and quads only. |
|
356
|
|
|
|
|
|
|
|
|
357
|
|
|
|
|
|
|
=item name (string) |
|
358
|
|
|
|
|
|
|
|
|
359
|
|
|
|
|
|
|
This attribute is for information, and is not used by the class. |
|
360
|
|
|
|
|
|
|
|
|
361
|
|
|
|
|
|
|
=item null (string, write-only) |
|
362
|
|
|
|
|
|
|
|
|
363
|
|
|
|
|
|
|
This "virtual" attribute is a convenience, which causes the object to |
|
364
|
|
|
|
|
|
|
be configured with the given number of cells, but no topology. The |
|
365
|
|
|
|
|
|
|
topology must be added later using the add_set method once for each |
|
366
|
|
|
|
|
|
|
set of cells to be created. |
|
367
|
|
|
|
|
|
|
|
|
368
|
|
|
|
|
|
|
The value must be either a comma-separated list of one to three numbers |
|
369
|
|
|
|
|
|
|
(e.g. '81,9,9') or a reference to a list containing one to three |
|
370
|
|
|
|
|
|
|
numbers (e.g. [81, 9, 9]). The first (and only required) number gives the |
|
371
|
|
|
|
|
|
|
number of cells. The second, if supplied, sets the 'columns' attribute, |
|
372
|
|
|
|
|
|
|
and the third, if supplied, sets the 'rows' attribute. For example, |
|
373
|
|
|
|
|
|
|
|
|
374
|
|
|
|
|
|
|
$su->set (null => [36, 6]); |
|
375
|
|
|
|
|
|
|
$su->add_set (r0 => 0, 1, 2, 3, 4, 5); |
|
376
|
|
|
|
|
|
|
$su->add_set (r1 => 6, 7, 8, 9, 10, 11); |
|
377
|
|
|
|
|
|
|
... |
|
378
|
|
|
|
|
|
|
$su->add_set (c0 => 0, 6, 12, 18, 24, 30); |
|
379
|
|
|
|
|
|
|
$su->add_set (c1 => 1, 7, 13, 19, 25, 31); |
|
380
|
|
|
|
|
|
|
... |
|
381
|
|
|
|
|
|
|
$su->add_set (s0 => 0, 1, 2, 6, 7, 8); |
|
382
|
|
|
|
|
|
|
$su->add_set (s1 => 3, 4, 5, 9, 10, 11); |
|
383
|
|
|
|
|
|
|
... |
|
384
|
|
|
|
|
|
|
|
|
385
|
|
|
|
|
|
|
Generates the topology equivalent to |
|
386
|
|
|
|
|
|
|
|
|
387
|
|
|
|
|
|
|
$su->set (brick => [3, 2]) |
|
388
|
|
|
|
|
|
|
|
|
389
|
|
|
|
|
|
|
=item output_delimiter (string) |
|
390
|
|
|
|
|
|
|
|
|
391
|
|
|
|
|
|
|
This attribute specifies the delimiter to be used between cell values |
|
392
|
|
|
|
|
|
|
on output. The default is a single space. |
|
393
|
|
|
|
|
|
|
|
|
394
|
|
|
|
|
|
|
=item quincunx (text, write-only) |
|
395
|
|
|
|
|
|
|
|
|
396
|
|
|
|
|
|
|
This "virtual" attribute is a convenience, which causes the object to be |
|
397
|
|
|
|
|
|
|
configured as a quincunx (a. k. a. 'Samurai Sudoku' at |
|
398
|
|
|
|
|
|
|
L). The value must be |
|
399
|
|
|
|
|
|
|
either a comma-separated list of one to two numbers (e.g. '3,1') or a |
|
400
|
|
|
|
|
|
|
reference to a list of one to two numbers (e.g. [3, 1]). In either case, |
|
401
|
|
|
|
|
|
|
the numbers are the order of the quincunx (3 corresponding to the usual |
|
402
|
|
|
|
|
|
|
'Samurai Sudoku' configuration), and the gap between the arms of the |
|
403
|
|
|
|
|
|
|
quincunx, in small squares. The gap must be strictly less than the |
|
404
|
|
|
|
|
|
|
order, and the same parity (odd or even) as the order. If the gap is not |
|
405
|
|
|
|
|
|
|
specified, it defaults to the smallest possible. |
|
406
|
|
|
|
|
|
|
|
|
407
|
|
|
|
|
|
|
To be specific, |
|
408
|
|
|
|
|
|
|
|
|
409
|
|
|
|
|
|
|
$su->set(quincunx => 3) |
|
410
|
|
|
|
|
|
|
|
|
411
|
|
|
|
|
|
|
is equivalent to |
|
412
|
|
|
|
|
|
|
|
|
413
|
|
|
|
|
|
|
$su->set(quincunx => [3, 1]) |
|
414
|
|
|
|
|
|
|
|
|
415
|
|
|
|
|
|
|
and both specify the 'Samurai Sudoku' configuration. |
|
416
|
|
|
|
|
|
|
|
|
417
|
|
|
|
|
|
|
The actual topology is set up as a square of (2 * order + gap) * order |
|
418
|
|
|
|
|
|
|
cells on a side, with the cells in the gap being unused. The sets used |
|
419
|
|
|
|
|
|
|
are the same as for sudoku of the same order, but with 'g0' through 'g4' |
|
420
|
|
|
|
|
|
|
prepended to their names, with g0 being the top left sudoku grid, g1 the |
|
421
|
|
|
|
|
|
|
top right, g2 the middle, g3 the bottom left, and g4 the bottom right. |
|
422
|
|
|
|
|
|
|
|
|
423
|
|
|
|
|
|
|
In the case of the 's' sets, this would result in duplicate sets being |
|
424
|
|
|
|
|
|
|
generated in the overlap area, so the 's' set from the higher-numbered |
|
425
|
|
|
|
|
|
|
grid is suppressed. For example, in the 'Samurai Sudoku' configuration, |
|
426
|
|
|
|
|
|
|
sets g0s8, g1s6, g2s6, and g2s8 contain exactly the same cells as g2s0, |
|
427
|
|
|
|
|
|
|
g2s2, g3s2, and g4s0 respectively, so the latter are suppressed, and |
|
428
|
|
|
|
|
|
|
only the former appear in the topology. |
|
429
|
|
|
|
|
|
|
|
|
430
|
|
|
|
|
|
|
Problems are specified left-to-right by rows. The cells in the gaps are |
|
431
|
|
|
|
|
|
|
unused, and are not specified. For example, the May 2, 2008 'Samurai |
|
432
|
|
|
|
|
|
|
Sudoku' problem could be specified as |
|
433
|
|
|
|
|
|
|
|
|
434
|
|
|
|
|
|
|
. . . . . 1 . . . . . . 4 . . . . . |
|
435
|
|
|
|
|
|
|
. . . . 3 . 6 . . . . 7 . 2 . . . . |
|
436
|
|
|
|
|
|
|
. . . 7 . . . 5 . . 4 . . . 5 . . . |
|
437
|
|
|
|
|
|
|
|
|
438
|
|
|
|
|
|
|
. . 6 9 . . . . 7 6 . . . . 9 1 . . |
|
439
|
|
|
|
|
|
|
. 5 . . 2 . . 4 . . 2 . . 5 . . 9 . |
|
440
|
|
|
|
|
|
|
4 . . . . 5 2 . . . . 8 1 . . . . 7 |
|
441
|
|
|
|
|
|
|
|
|
442
|
|
|
|
|
|
|
. 2 . . . 4 . . . . 8 . . . . 3 . . . 2 . |
|
443
|
|
|
|
|
|
|
. . 5 . 6 . . . . 4 . 5 . . . . 8 . 4 . . |
|
444
|
|
|
|
|
|
|
. . . 1 . . . . . . 7 . . . . . . 7 . . . |
|
445
|
|
|
|
|
|
|
|
|
446
|
|
|
|
|
|
|
. 4 . . 6 . . 2 . |
|
447
|
|
|
|
|
|
|
6 . 7 8 . 9 4 . 1 |
|
448
|
|
|
|
|
|
|
. 1 . . 4 . . 3 . |
|
449
|
|
|
|
|
|
|
|
|
450
|
|
|
|
|
|
|
. . . 7 . . . . . . 9 . . . . . . 6 . . . |
|
451
|
|
|
|
|
|
|
. . 8 . 2 . . . . 2 . 8 . . . . 8 . 5 . . |
|
452
|
|
|
|
|
|
|
. 4 . . . 3 . . . . 5 . . . . 3 . . . 2 . |
|
453
|
|
|
|
|
|
|
|
|
454
|
|
|
|
|
|
|
2 . . . . 7 8 . . . . 4 1 . . . . 6 |
|
455
|
|
|
|
|
|
|
. 3 . . 5 . . 4 . . 3 . . 2 . . 4 . |
|
456
|
|
|
|
|
|
|
. . 4 8 . . . . 7 2 . . . . 3 1 . . |
|
457
|
|
|
|
|
|
|
|
|
458
|
|
|
|
|
|
|
. . . 9 . . . 1 . . 5 . . . 8 . . . |
|
459
|
|
|
|
|
|
|
. . . . 6 . 9 . . . . 7 . 4 . . . . |
|
460
|
|
|
|
|
|
|
. . . . . 4 . . . . . . 2 . . . . . |
|
461
|
|
|
|
|
|
|
|
|
462
|
|
|
|
|
|
|
Setting this attribute causes the rows and columns attributes to be set |
|
463
|
|
|
|
|
|
|
to (2 * order + gap) * order. The symbols attribute is set to '.' and |
|
464
|
|
|
|
|
|
|
the numbers 1, 2, ... up to order * order. |
|
465
|
|
|
|
|
|
|
|
|
466
|
|
|
|
|
|
|
=item rows (number) |
|
467
|
|
|
|
|
|
|
|
|
468
|
|
|
|
|
|
|
This attribute defines the number of lines of output to present before |
|
469
|
|
|
|
|
|
|
inserting a blank line (for readability) when formatting the topology |
|
470
|
|
|
|
|
|
|
attribute, or the solution to a puzzle. |
|
471
|
|
|
|
|
|
|
|
|
472
|
|
|
|
|
|
|
=item status_text (text, read-only) |
|
473
|
|
|
|
|
|
|
|
|
474
|
|
|
|
|
|
|
This attribute is a short piece of text corresponding to the |
|
475
|
|
|
|
|
|
|
status_value. |
|
476
|
|
|
|
|
|
|
|
|
477
|
|
|
|
|
|
|
=item status_value (number) |
|
478
|
|
|
|
|
|
|
|
|
479
|
|
|
|
|
|
|
The solution() method sets a status, which can be retrieved via this |
|
480
|
|
|
|
|
|
|
attribute. The retrieved value is one of |
|
481
|
|
|
|
|
|
|
|
|
482
|
|
|
|
|
|
|
SUDOKU_SUCCESS |
|
483
|
|
|
|
|
|
|
This means what you think it does. |
|
484
|
|
|
|
|
|
|
SUDOKU_NO_SOLUTION |
|
485
|
|
|
|
|
|
|
This means the method exhausted all possible |
|
486
|
|
|
|
|
|
|
soltions without finding one |
|
487
|
|
|
|
|
|
|
SUDOKU_TOO_HARD |
|
488
|
|
|
|
|
|
|
This means the iteration_limit attribute was |
|
489
|
|
|
|
|
|
|
set to a positive number and the solution() |
|
490
|
|
|
|
|
|
|
method hit the limit without finding a solution. |
|
491
|
|
|
|
|
|
|
|
|
492
|
|
|
|
|
|
|
=item sudoku (number, write-only) |
|
493
|
|
|
|
|
|
|
|
|
494
|
|
|
|
|
|
|
This attribute is a convenience, which causes the object to be |
|
495
|
|
|
|
|
|
|
configured to handle a standard Sudoku square. The value gives the size |
|
496
|
|
|
|
|
|
|
of the small squares into which the big square is divided. The big |
|
497
|
|
|
|
|
|
|
square's side is the square of the value. |
|
498
|
|
|
|
|
|
|
|
|
499
|
|
|
|
|
|
|
For example, the customary Sudoku topology is set by |
|
500
|
|
|
|
|
|
|
|
|
501
|
|
|
|
|
|
|
$su->set (sudoku => 3); |
|
502
|
|
|
|
|
|
|
|
|
503
|
|
|
|
|
|
|
This attribute is implemented in terms of C ... )>, |
|
504
|
|
|
|
|
|
|
and modifies the same "real" attributes. See the C attribute for |
|
505
|
|
|
|
|
|
|
the details. |
|
506
|
|
|
|
|
|
|
|
|
507
|
|
|
|
|
|
|
=item sudokux (number, write-only) |
|
508
|
|
|
|
|
|
|
|
|
509
|
|
|
|
|
|
|
This attribute is a convenience. It is similar to the 'sudoku' |
|
510
|
|
|
|
|
|
|
attribute, but the topology includes both main diagonals (set names |
|
511
|
|
|
|
|
|
|
'd0' and 'd1') in addition to the standard sets. See the |
|
512
|
|
|
|
|
|
|
C attribute for the details, since that's ultimately how this |
|
513
|
|
|
|
|
|
|
attribute is implemented. |
|
514
|
|
|
|
|
|
|
|
|
515
|
|
|
|
|
|
|
=item symbols (string) |
|
516
|
|
|
|
|
|
|
|
|
517
|
|
|
|
|
|
|
This attribute defines the symbols to be used in the puzzle. Any |
|
518
|
|
|
|
|
|
|
printing characters may be used except ",". Multi-character symbols |
|
519
|
|
|
|
|
|
|
are supported. The value of the attribute is a whitespace-delimited |
|
520
|
|
|
|
|
|
|
list of the symbols, though the whitespace is optional if all symbols |
|
521
|
|
|
|
|
|
|
(and symbol constraints if any) are a single character. See the |
|
522
|
|
|
|
|
|
|
L documentation for full details. |
|
523
|
|
|
|
|
|
|
|
|
524
|
|
|
|
|
|
|
The first symbol in the list is the one that represents an empty cell. |
|
525
|
|
|
|
|
|
|
Except for this, the order of the symbols is immaterial. |
|
526
|
|
|
|
|
|
|
|
|
527
|
|
|
|
|
|
|
The symbols defined here are used only for input or output. It is |
|
528
|
|
|
|
|
|
|
perfectly legitimate to set symbols, call the problem() method, and |
|
529
|
|
|
|
|
|
|
then change the symbols. The solution() method will return solutions |
|
530
|
|
|
|
|
|
|
in the new symbol set. I have no idea why you would want to do this. |
|
531
|
|
|
|
|
|
|
|
|
532
|
|
|
|
|
|
|
=item topology (string) |
|
533
|
|
|
|
|
|
|
|
|
534
|
|
|
|
|
|
|
This attribute defines the topology of the puzzle, in terms of what |
|
535
|
|
|
|
|
|
|
sets each cell belongs to. Each cell is defined in terms of a |
|
536
|
|
|
|
|
|
|
comma-delimited list of the names of the sets it belongs to, and |
|
537
|
|
|
|
|
|
|
the string is a whitespace-delimited list of cell definitions. For |
|
538
|
|
|
|
|
|
|
example, a three-by-three grid with diagonals can be defined as |
|
539
|
|
|
|
|
|
|
follows in terms of sets r1, r2, and r3 for the rows, c1, c2, and |
|
540
|
|
|
|
|
|
|
c3 for the columns, and d1 and d2 for the diagonals: |
|
541
|
|
|
|
|
|
|
|
|
542
|
|
|
|
|
|
|
r1,c1,d1 r1,c2 r1,c3,d2 |
|
543
|
|
|
|
|
|
|
r2,c1 r2,c2,d1,d2 r2,c3 |
|
544
|
|
|
|
|
|
|
r3,c1,d2 r3,c2 r3,c3,d1 |
|
545
|
|
|
|
|
|
|
|
|
546
|
|
|
|
|
|
|
The parser treats line breaks as whitespace. That is to say, the |
|
547
|
|
|
|
|
|
|
above definition would be the same if it were all on one line. |
|
548
|
|
|
|
|
|
|
|
|
549
|
|
|
|
|
|
|
You do not need to define the sets themselves anywhere. The |
|
550
|
|
|
|
|
|
|
package defines each set as it encounters it in the topology |
|
551
|
|
|
|
|
|
|
definition. |
|
552
|
|
|
|
|
|
|
|
|
553
|
|
|
|
|
|
|
For certain topologies (e.g. the London Times Quincunx) it may be |
|
554
|
|
|
|
|
|
|
convenient to include in the definition cells that are not part of the |
|
555
|
|
|
|
|
|
|
puzzle. Such unused cells are defined by specifying just a comma, |
|
556
|
|
|
|
|
|
|
without any set names. |
|
557
|
|
|
|
|
|
|
|
|
558
|
|
|
|
|
|
|
Setting the topology invalidates any currently-set-up problem. |
|
559
|
|
|
|
|
|
|
|
|
560
|
|
|
|
|
|
|
=back |
|
561
|
|
|
|
|
|
|
|
|
562
|
|
|
|
|
|
|
=head2 Methods |
|
563
|
|
|
|
|
|
|
|
|
564
|
|
|
|
|
|
|
This package provides the following public methods: |
|
565
|
|
|
|
|
|
|
|
|
566
|
|
|
|
|
|
|
=cut |
|
567
|
|
|
|
|
|
|
|
|
568
|
|
|
|
|
|
|
package Games::Sudoku::General; |
|
569
|
|
|
|
|
|
|
|
|
570
|
2
|
|
|
2
|
|
2801
|
use 5.006002; # For 'our', at least. |
|
|
2
|
|
|
|
|
11
|
|
|
571
|
|
|
|
|
|
|
|
|
572
|
2
|
|
|
2
|
|
8
|
use strict; |
|
|
2
|
|
|
|
|
3
|
|
|
|
2
|
|
|
|
|
35
|
|
|
573
|
2
|
|
|
2
|
|
8
|
use warnings; |
|
|
2
|
|
|
|
|
3
|
|
|
|
2
|
|
|
|
|
64
|
|
|
574
|
|
|
|
|
|
|
|
|
575
|
2
|
|
|
2
|
|
17
|
use Exporter qw{ import }; |
|
|
2
|
|
|
|
|
3
|
|
|
|
2
|
|
|
|
|
181
|
|
|
576
|
|
|
|
|
|
|
|
|
577
|
|
|
|
|
|
|
our $VERSION = '0.027'; |
|
578
|
|
|
|
|
|
|
our @EXPORT_OK = qw{ |
|
579
|
|
|
|
|
|
|
SUDOKU_SUCCESS |
|
580
|
|
|
|
|
|
|
SUDOKU_NO_SOLUTION |
|
581
|
|
|
|
|
|
|
SUDOKU_TOO_HARD |
|
582
|
|
|
|
|
|
|
SUDOKU_MULTIPLE_SOLUTIONS |
|
583
|
|
|
|
|
|
|
}; |
|
584
|
|
|
|
|
|
|
our %EXPORT_TAGS = ( |
|
585
|
|
|
|
|
|
|
all => \@EXPORT_OK, |
|
586
|
|
|
|
|
|
|
status => \@EXPORT_OK, |
|
587
|
|
|
|
|
|
|
); |
|
588
|
2
|
|
|
2
|
|
12
|
use Carp; |
|
|
2
|
|
|
|
|
3
|
|
|
|
2
|
|
|
|
|
140
|
|
|
589
|
2
|
|
|
2
|
|
1047
|
use Data::Dumper; |
|
|
2
|
|
|
|
|
11997
|
|
|
|
2
|
|
|
|
|
143
|
|
|
590
|
2
|
|
|
2
|
|
13
|
use List::Util qw{first max reduce}; |
|
|
2
|
|
|
|
|
4
|
|
|
|
2
|
|
|
|
|
200
|
|
|
591
|
2
|
|
|
2
|
|
841
|
use POSIX qw{floor}; |
|
|
2
|
|
|
|
|
12271
|
|
|
|
2
|
|
|
|
|
9
|
|
|
592
|
|
|
|
|
|
|
|
|
593
|
2
|
|
|
2
|
|
2564
|
use constant SUDOKU_SUCCESS => 0; |
|
|
2
|
|
|
|
|
4
|
|
|
|
2
|
|
|
|
|
112
|
|
|
594
|
2
|
|
|
2
|
|
10
|
use constant SUDOKU_NO_SOLUTION => 1; |
|
|
2
|
|
|
|
|
2
|
|
|
|
2
|
|
|
|
|
93
|
|
|
595
|
2
|
|
|
2
|
|
10
|
use constant SUDOKU_TOO_HARD => 2; |
|
|
2
|
|
|
|
|
4
|
|
|
|
2
|
|
|
|
|
71
|
|
|
596
|
2
|
|
|
2
|
|
9
|
use constant SUDOKU_MULTIPLE_SOLUTIONS => 3; |
|
|
2
|
|
|
|
|
2
|
|
|
|
2
|
|
|
|
|
148
|
|
|
597
|
|
|
|
|
|
|
|
|
598
|
|
|
|
|
|
|
my @status_values = ( |
|
599
|
|
|
|
|
|
|
'Success', |
|
600
|
|
|
|
|
|
|
'No solution found', |
|
601
|
|
|
|
|
|
|
'No solution found before exceeding iteration limit', |
|
602
|
|
|
|
|
|
|
'Multiple solutions found', |
|
603
|
|
|
|
|
|
|
); |
|
604
|
|
|
|
|
|
|
|
|
605
|
2
|
|
|
2
|
|
11
|
use constant HASH_REF => ref {}; |
|
|
2
|
|
|
|
|
3
|
|
|
|
2
|
|
|
|
|
18582
|
|
|
606
|
|
|
|
|
|
|
|
|
607
|
|
|
|
|
|
|
=head2 new |
|
608
|
|
|
|
|
|
|
|
|
609
|
|
|
|
|
|
|
$su = Games::Sudoku::General->new () |
|
610
|
|
|
|
|
|
|
|
|
611
|
|
|
|
|
|
|
This method instantiates a new Games::Sudoku::General object. Any |
|
612
|
|
|
|
|
|
|
arguments are passed to the set() method. If, after processing |
|
613
|
|
|
|
|
|
|
the arguments, the object does not have a topology, |
|
614
|
|
|
|
|
|
|
|
|
615
|
|
|
|
|
|
|
$self->set (sudoku => 3) |
|
616
|
|
|
|
|
|
|
|
|
617
|
|
|
|
|
|
|
is called. If there is no symbols setting (which could happen |
|
618
|
|
|
|
|
|
|
if the user passed an explicit topology), |
|
619
|
|
|
|
|
|
|
|
|
620
|
|
|
|
|
|
|
$self->set (symbols => join ' ', '.', |
|
621
|
|
|
|
|
|
|
1 .. $self->get ('largest_set')) |
|
622
|
|
|
|
|
|
|
|
|
623
|
|
|
|
|
|
|
is called. If, after all this, there is still no columns setting, |
|
624
|
|
|
|
|
|
|
the number of columns is set to the number of symbols, excluding |
|
625
|
|
|
|
|
|
|
the "empty cell" symbol. |
|
626
|
|
|
|
|
|
|
|
|
627
|
|
|
|
|
|
|
The newly-instantiated object is returned. |
|
628
|
|
|
|
|
|
|
|
|
629
|
|
|
|
|
|
|
=cut |
|
630
|
|
|
|
|
|
|
|
|
631
|
|
|
|
|
|
|
sub new { |
|
632
|
2
|
|
|
2
|
1
|
550
|
my ($class, @args) = @_; |
|
633
|
2
|
50
|
|
|
|
9
|
ref $class and $class = ref $class; |
|
634
|
2
|
|
|
|
|
11
|
my $self = bless { |
|
635
|
|
|
|
|
|
|
debug => 0, |
|
636
|
|
|
|
|
|
|
generation_limit => 30, |
|
637
|
|
|
|
|
|
|
iteration_limit => 0, |
|
638
|
|
|
|
|
|
|
output_delimiter => ' ', |
|
639
|
|
|
|
|
|
|
}, $class; |
|
640
|
2
|
50
|
|
|
|
7
|
@args and $self->set (@args); |
|
641
|
2
|
50
|
|
|
|
18
|
$self->{cell} or $self->set (sudoku => 3); |
|
642
|
|
|
|
|
|
|
$self->{symbol_list} |
|
643
|
2
|
50
|
|
|
|
7
|
or $self->set (symbols => join ' ', '.', 1 .. $self->{largest_set}); |
|
644
|
|
|
|
|
|
|
defined $self->{columns} |
|
645
|
2
|
50
|
|
|
|
17
|
or $self->set (columns => @{$self->{symbol_list}} - 1); |
|
|
0
|
|
|
|
|
0
|
|
|
646
|
|
|
|
|
|
|
defined $self->{status_value} |
|
647
|
2
|
50
|
|
|
|
13
|
or $self->set (status_value => SUDOKU_SUCCESS); |
|
648
|
|
|
|
|
|
|
defined $self->{max_tuple} |
|
649
|
2
|
50
|
|
|
|
8
|
or $self->set (max_tuple => 4); |
|
650
|
2
|
|
|
|
|
11
|
return $self; |
|
651
|
|
|
|
|
|
|
} |
|
652
|
|
|
|
|
|
|
|
|
653
|
|
|
|
|
|
|
=head2 add_set |
|
654
|
|
|
|
|
|
|
|
|
655
|
|
|
|
|
|
|
$su->add_set ($name => $cell ...) |
|
656
|
|
|
|
|
|
|
|
|
657
|
|
|
|
|
|
|
This method adds to the current topology a new set with the given name, |
|
658
|
|
|
|
|
|
|
and consisting of the given cells. The set name must not already |
|
659
|
|
|
|
|
|
|
exist, but the cells must already exist. In other words, you can't |
|
660
|
|
|
|
|
|
|
modify an existing set with this method, nor can you add new cells. |
|
661
|
|
|
|
|
|
|
|
|
662
|
|
|
|
|
|
|
=cut |
|
663
|
|
|
|
|
|
|
|
|
664
|
|
|
|
|
|
|
sub add_set { |
|
665
|
21
|
|
|
21
|
1
|
45
|
my ($self, $name, @cells) = @_; |
|
666
|
21
|
50
|
|
|
|
43
|
$self->{set}{$name} and croak <
|
|
667
|
|
|
|
|
|
|
Error - Set '$name' already exists. |
|
668
|
|
|
|
|
|
|
eod |
|
669
|
21
|
|
|
|
|
38
|
foreach my $inx (@cells) { |
|
670
|
184
|
50
|
|
|
|
282
|
$self->{cell}[$inx] or croak <
|
|
671
|
|
|
|
|
|
|
Error - Cell $inx does not exist. |
|
672
|
|
|
|
|
|
|
eod |
|
673
|
|
|
|
|
|
|
} |
|
674
|
21
|
|
|
|
|
27
|
foreach my $inx (@cells) { |
|
675
|
184
|
|
|
|
|
236
|
my $cell = $self->{cell}[$inx]; |
|
676
|
184
|
50
|
|
|
|
192
|
@{$cell->{membership}} or --$self->{cells_unused}; |
|
|
184
|
|
|
|
|
275
|
|
|
677
|
184
|
|
|
|
|
208
|
foreach my $other (@{$cell->{membership}}) { |
|
|
184
|
|
|
|
|
262
|
|
|
678
|
468
|
|
|
|
|
740
|
my $int = join ',', sort $other, $name; |
|
679
|
468
|
|
100
|
|
|
1301
|
$self->{intersection}{$int} ||= []; |
|
680
|
468
|
|
|
|
|
512
|
push @{$self->{intersection}{$int}}, $inx; |
|
|
468
|
|
|
|
|
802
|
|
|
681
|
|
|
|
|
|
|
} |
|
682
|
184
|
|
|
|
|
208
|
@{$cell->{membership}} = sort $name, @{$cell->{membership}}; |
|
|
184
|
|
|
|
|
446
|
|
|
|
184
|
|
|
|
|
273
|
|
|
683
|
|
|
|
|
|
|
} |
|
684
|
21
|
|
|
|
|
129
|
$self->{set}{$name} = { |
|
685
|
|
|
|
|
|
|
name => $name, |
|
686
|
|
|
|
|
|
|
membership => [sort @cells], |
|
687
|
|
|
|
|
|
|
}; |
|
688
|
|
|
|
|
|
|
$self->{largest_set} = max ($self->{largest_set}, |
|
689
|
21
|
|
|
|
|
32
|
scalar @{$self->{set}{$name}{membership}}); |
|
|
21
|
|
|
|
|
52
|
|
|
690
|
21
|
|
|
|
|
27
|
delete $self->{backtrack_stack}; # Force setting of new problem. |
|
691
|
21
|
|
|
|
|
51
|
return $self; |
|
692
|
|
|
|
|
|
|
} |
|
693
|
|
|
|
|
|
|
|
|
694
|
|
|
|
|
|
|
=head2 constraints_used |
|
695
|
|
|
|
|
|
|
|
|
696
|
|
|
|
|
|
|
%constraints_used = $su->constraints_used; |
|
697
|
|
|
|
|
|
|
|
|
698
|
|
|
|
|
|
|
This method returns a hash containing the constraints used in the most |
|
699
|
|
|
|
|
|
|
recent call to solution(), and the number of times each was used. The |
|
700
|
|
|
|
|
|
|
constraint codes are the same as for the steps() method. If called in |
|
701
|
|
|
|
|
|
|
scalar context it returns a string representing the constraints used |
|
702
|
|
|
|
|
|
|
at least once, in canonical order (i.e. in the order documented in the |
|
703
|
|
|
|
|
|
|
steps() method). |
|
704
|
|
|
|
|
|
|
|
|
705
|
|
|
|
|
|
|
B As of version 0.002, the string returned by the scalar has |
|
706
|
|
|
|
|
|
|
spaces delimiting the constraint names. They were not delimited in |
|
707
|
|
|
|
|
|
|
version 0.001 |
|
708
|
|
|
|
|
|
|
|
|
709
|
|
|
|
|
|
|
=cut |
|
710
|
|
|
|
|
|
|
|
|
711
|
|
|
|
|
|
|
sub constraints_used { |
|
712
|
6
|
|
|
6
|
1
|
16
|
my ( $self ) = @_; |
|
713
|
6
|
50
|
33
|
|
|
33
|
return unless $self->{constraints_used} && defined wantarray; |
|
714
|
6
|
50
|
|
|
|
14
|
return %{$self->{constraints_used}} if wantarray; |
|
|
0
|
|
|
|
|
0
|
|
|
715
|
|
|
|
|
|
|
my $rslt = join ' ', grep { |
|
716
|
6
|
|
|
|
|
16
|
$self->{constraints_used}{$_}} qw{F N B T X Y W ?}; |
|
|
48
|
|
|
|
|
80
|
|
|
717
|
6
|
|
|
|
|
24
|
return $rslt; |
|
718
|
|
|
|
|
|
|
} |
|
719
|
|
|
|
|
|
|
|
|
720
|
|
|
|
|
|
|
=head2 copy |
|
721
|
|
|
|
|
|
|
|
|
722
|
|
|
|
|
|
|
$su->copy () |
|
723
|
|
|
|
|
|
|
|
|
724
|
|
|
|
|
|
|
This method copies the current problem to the clipboard. If solution() |
|
725
|
|
|
|
|
|
|
has been called, the current solution goes on the clipboard. |
|
726
|
|
|
|
|
|
|
|
|
727
|
|
|
|
|
|
|
See L for what is needed for this |
|
728
|
|
|
|
|
|
|
to work. |
|
729
|
|
|
|
|
|
|
|
|
730
|
|
|
|
|
|
|
=cut |
|
731
|
|
|
|
|
|
|
|
|
732
|
|
|
|
|
|
|
{ # Local symbol block. |
|
733
|
|
|
|
|
|
|
my $copier; |
|
734
|
|
|
|
|
|
|
sub copy { |
|
735
|
1
|
|
|
1
|
1
|
3
|
my ( $self ) = @_; |
|
736
|
1
|
50
|
33
|
|
|
4
|
( $copier ||= eval { |
|
737
|
1
|
|
|
|
|
373
|
require Clipboard; |
|
738
|
1
|
|
|
|
|
6
|
Clipboard->import(); |
|
739
|
|
|
|
|
|
|
sub { |
|
740
|
1
|
|
|
1
|
|
6
|
Clipboard->copy( join '', @_ ); |
|
741
|
1
|
|
|
|
|
2
|
return; |
|
742
|
1
|
|
|
|
|
8
|
}; |
|
743
|
|
|
|
|
|
|
} |
|
744
|
|
|
|
|
|
|
) or croak 'copy() unavailable; can not load Clipboard'; |
|
745
|
1
|
|
|
|
|
4
|
$copier->( $self->_unload() ); |
|
746
|
1
|
|
|
|
|
2
|
return $self; |
|
747
|
|
|
|
|
|
|
} |
|
748
|
|
|
|
|
|
|
} |
|
749
|
|
|
|
|
|
|
|
|
750
|
|
|
|
|
|
|
=head2 drop_set |
|
751
|
|
|
|
|
|
|
|
|
752
|
|
|
|
|
|
|
$su->drop_set( $name ) |
|
753
|
|
|
|
|
|
|
|
|
754
|
|
|
|
|
|
|
This method removes from the current topology the set with the given |
|
755
|
|
|
|
|
|
|
name. The set must exist, or an exception is raised. |
|
756
|
|
|
|
|
|
|
|
|
757
|
|
|
|
|
|
|
=cut |
|
758
|
|
|
|
|
|
|
|
|
759
|
|
|
|
|
|
|
sub drop_set { |
|
760
|
1
|
|
|
1
|
1
|
4
|
my ($self, $name) = @_; |
|
761
|
1
|
50
|
|
|
|
4
|
$self->{set}{$name} or croak <
|
|
762
|
|
|
|
|
|
|
Error - Set '$name' not defined. |
|
763
|
|
|
|
|
|
|
eod |
|
764
|
1
|
|
|
|
|
3
|
foreach my $inx (@{$self->{set}{$name}{membership}}) { |
|
|
1
|
|
|
|
|
5
|
|
|
765
|
4
|
|
|
|
|
8
|
my $cell = $self->{cell}[$inx]; |
|
766
|
4
|
|
|
|
|
6
|
my @mbr; |
|
767
|
4
|
|
|
|
|
5
|
foreach my $other (@{$cell->{membership}}) { |
|
|
4
|
|
|
|
|
8
|
|
|
768
|
12
|
100
|
|
|
|
20
|
if ($other ne $name) { |
|
769
|
8
|
|
|
|
|
11
|
push @mbr, $other; |
|
770
|
8
|
|
|
|
|
16
|
my $int = join ',', sort $other, $name; |
|
771
|
8
|
|
|
|
|
15
|
delete $self->{intersection}{$int}; |
|
772
|
|
|
|
|
|
|
} |
|
773
|
|
|
|
|
|
|
} |
|
774
|
4
|
50
|
|
|
|
9
|
if (@mbr) { |
|
775
|
4
|
|
|
|
|
7
|
@{$cell->{membership}} = sort @mbr; |
|
|
4
|
|
|
|
|
11
|
|
|
776
|
|
|
|
|
|
|
} else { |
|
777
|
0
|
|
|
|
|
0
|
@{$cell->{membership}} = (); |
|
|
0
|
|
|
|
|
0
|
|
|
778
|
0
|
|
|
|
|
0
|
$self->{cells_unused}++; |
|
779
|
|
|
|
|
|
|
} |
|
780
|
|
|
|
|
|
|
} |
|
781
|
1
|
|
|
|
|
3
|
delete $self->{set}{$name}; |
|
782
|
1
|
|
|
|
|
2
|
$self->{largest_set} = 0; |
|
783
|
1
|
|
|
|
|
3
|
foreach (keys %{$self->{set}}) { |
|
|
1
|
|
|
|
|
5
|
|
|
784
|
|
|
|
|
|
|
$self->{largest_set} = max ($self->{largest_set}, |
|
785
|
8
|
|
|
|
|
10
|
scalar @{$self->{set}{$_}{membership}}); |
|
|
8
|
|
|
|
|
19
|
|
|
786
|
|
|
|
|
|
|
} |
|
787
|
1
|
|
|
|
|
3
|
delete $self->{backtrack_stack}; # Force setting of new problem. |
|
788
|
1
|
|
|
|
|
3
|
return $self; |
|
789
|
|
|
|
|
|
|
} |
|
790
|
|
|
|
|
|
|
|
|
791
|
|
|
|
|
|
|
=head2 generate |
|
792
|
|
|
|
|
|
|
|
|
793
|
|
|
|
|
|
|
$problem = $su->generate( $min, $max, $const ); |
|
794
|
|
|
|
|
|
|
|
|
795
|
|
|
|
|
|
|
This method generates a problem and returns it. |
|
796
|
|
|
|
|
|
|
|
|
797
|
|
|
|
|
|
|
The $min argument is the minimum number of givens in the puzzle. You |
|
798
|
|
|
|
|
|
|
may (and probably will) get more. The default is the number of cells |
|
799
|
|
|
|
|
|
|
in the puzzle divided by the number of sets a cell belongs to. |
|
800
|
|
|
|
|
|
|
|
|
801
|
|
|
|
|
|
|
The value of this argument is critical to getting a puzzle: too large |
|
802
|
|
|
|
|
|
|
and you generate puzzles with no solution; too small and you spend all |
|
803
|
|
|
|
|
|
|
your time backtracking. There is no science behind the default, just an |
|
804
|
|
|
|
|
|
|
attempt to make a rational heuristic based on the number of degrees of |
|
805
|
|
|
|
|
|
|
freedom and the observation that about a third of the cells are given |
|
806
|
|
|
|
|
|
|
in a typical Sudoku puzzle. My experience with the default is: |
|
807
|
|
|
|
|
|
|
|
|
808
|
|
|
|
|
|
|
topology comment |
|
809
|
|
|
|
|
|
|
brick 3,2 default is OK |
|
810
|
|
|
|
|
|
|
corresponding 3 default is OK |
|
811
|
|
|
|
|
|
|
cube 3 default is too large |
|
812
|
|
|
|
|
|
|
cube half default is OK |
|
813
|
|
|
|
|
|
|
cube full default is OK |
|
814
|
|
|
|
|
|
|
quincunx 3 default is too large |
|
815
|
|
|
|
|
|
|
sudoku 3 default is OK |
|
816
|
|
|
|
|
|
|
sudoku 4 default is OK |
|
817
|
|
|
|
|
|
|
sudokux 3 default is OK |
|
818
|
|
|
|
|
|
|
|
|
819
|
|
|
|
|
|
|
Typically when I take the defaults I get a puzzle in anywhere from |
|
820
|
|
|
|
|
|
|
a few seconds (most of the listed topologies) to a couple minutes |
|
821
|
|
|
|
|
|
|
(sudoku 4) on an 800 Mhz G4. But I have never successfully generated |
|
822
|
|
|
|
|
|
|
a Dion cube (cube 3). C |
|
823
|
|
|
|
|
|
|
|
|
824
|
|
|
|
|
|
|
The $max argument is the maximum number of givens in the puzzle. You |
|
825
|
|
|
|
|
|
|
may get less. The default is 1.5 times the minimum. |
|
826
|
|
|
|
|
|
|
|
|
827
|
|
|
|
|
|
|
The $const argument specifies the constraints to be used in the |
|
828
|
|
|
|
|
|
|
generated puzzle. This may be specified either as a string or as a hash |
|
829
|
|
|
|
|
|
|
reference. If specified as a string, it is a whitespace-delimited list, |
|
830
|
|
|
|
|
|
|
with each constraint name possibly followed by an equals sign and a |
|
831
|
|
|
|
|
|
|
number to specify that that constraint can be used only a certain |
|
832
|
|
|
|
|
|
|
number of times. For example, 'F N ?=1' specifies a puzzle to be |
|
833
|
|
|
|
|
|
|
solved by use of any number of applications of the F and N constraints, |
|
834
|
|
|
|
|
|
|
and at most one guessed cell. If specified as a hash reference, the |
|
835
|
|
|
|
|
|
|
keys are the constraint names, and the values are the usage counts, |
|
836
|
|
|
|
|
|
|
with undef meaning no limit. The hash reference corresponding to |
|
837
|
|
|
|
|
|
|
'F N ?=1' is {F => undef, N => undef, '?' => 1}. The default for this |
|
838
|
|
|
|
|
|
|
argument is to allow all known constraints except '?'. |
|
839
|
|
|
|
|
|
|
|
|
840
|
|
|
|
|
|
|
In practice, the generator usually generates puzzles solvable using |
|
841
|
|
|
|
|
|
|
only the F constraint, or the F and N constraints. |
|
842
|
|
|
|
|
|
|
|
|
843
|
|
|
|
|
|
|
The algorithm used is to generate a puzzle with the minimum number of |
|
844
|
|
|
|
|
|
|
cells selected at random, and then solve it. If a solution does not |
|
845
|
|
|
|
|
|
|
exist, we try again until we have tried |
|
846
|
|
|
|
|
|
|
C times, then we return undef. |
|
847
|
|
|
|
|
|
|
B |
|
848
|
|
|
|
|
|
|
|
|
849
|
|
|
|
|
|
|
If we get a solution, we remove allowed constraints. If we run into |
|
850
|
|
|
|
|
|
|
a constraint that is not allowed, we either stop (if we're below the |
|
851
|
|
|
|
|
|
|
maximum number of givens) or turn it into a given value (if we're |
|
852
|
|
|
|
|
|
|
above the maximum). We stop unconditionally if we get down to the |
|
853
|
|
|
|
|
|
|
minimum number of givens. As a side effect, the generated puzzle is |
|
854
|
|
|
|
|
|
|
set up as a problem. |
|
855
|
|
|
|
|
|
|
|
|
856
|
|
|
|
|
|
|
Note that if you allow guesses you may get puzzles with more than |
|
857
|
|
|
|
|
|
|
one solution. |
|
858
|
|
|
|
|
|
|
|
|
859
|
|
|
|
|
|
|
=cut |
|
860
|
|
|
|
|
|
|
|
|
861
|
|
|
|
|
|
|
sub generate { |
|
862
|
0
|
|
|
0
|
1
|
0
|
my ( $self, $min, $max, $const ) = @_; |
|
863
|
0
|
|
|
|
|
0
|
my $size = @{$self->{cell}} - $self->{cells_unused}; |
|
|
0
|
|
|
|
|
0
|
|
|
864
|
0
|
|
0
|
|
|
0
|
$min ||= do { |
|
865
|
|
|
|
|
|
|
floor( $size * $size / |
|
866
|
0
|
|
|
|
|
0
|
( $self->{largest_set} * keys %{ $self->{set} } ) ); |
|
|
0
|
|
|
|
|
0
|
|
|
867
|
|
|
|
|
|
|
}; |
|
868
|
0
|
|
0
|
|
|
0
|
$max ||= floor( $min * 1.5 ); |
|
869
|
0
|
|
0
|
|
|
0
|
$const ||= 'F N B T'; |
|
870
|
0
|
0
|
0
|
|
|
0
|
croak <<"EOD" if ref $const && HASH_REF ne ref $const; |
|
871
|
|
|
|
|
|
|
Error - The constraints argument must be a string or a hash reference, |
|
872
|
0
|
|
|
|
|
0
|
not a @{[ref $const]} reference. |
|
873
|
|
|
|
|
|
|
EOD |
|
874
|
0
|
0
|
|
|
|
0
|
$const = {map {my @ret; $_ and do { |
|
|
0
|
0
|
|
|
|
0
|
|
|
|
0
|
|
|
|
|
0
|
|
|
875
|
0
|
|
|
|
|
0
|
@ret = split '=', $_, 2; push @ret, undef while @ret < 2}; @ret} |
|
|
0
|
|
|
|
|
0
|
|
|
|
0
|
|
|
|
|
0
|
|
|
876
|
|
|
|
|
|
|
split '\s+', $const} |
|
877
|
|
|
|
|
|
|
unless HASH_REF eq ref $const; |
|
878
|
0
|
0
|
|
|
|
0
|
$self->{debug} and do { |
|
879
|
0
|
|
|
|
|
0
|
local $Data::Dumper::Terse = 1; |
|
880
|
0
|
|
|
|
|
0
|
print <
|
|
881
|
0
|
|
|
|
|
0
|
Debug generate ($min, $max, @{[Dumper $const]}) |
|
882
|
|
|
|
|
|
|
eod |
|
883
|
|
|
|
|
|
|
}; |
|
884
|
0
|
|
|
|
|
0
|
my $syms = @{$self->{symbol_list}} - 1; |
|
|
0
|
|
|
|
|
0
|
|
|
885
|
0
|
0
|
|
|
|
0
|
croak < $size; |
|
886
|
|
|
|
|
|
|
Error - You specified a minimum of $min given values, but the puzzle |
|
887
|
|
|
|
|
|
|
only contains $size cells. |
|
888
|
|
|
|
|
|
|
eod |
|
889
|
0
|
|
|
|
|
0
|
my $tries = $self->{generation_limit}; |
|
890
|
0
|
|
|
|
|
0
|
$size = @{$self->{cell}}; # Note equivocation on $size. |
|
|
0
|
|
|
|
|
0
|
|
|
891
|
0
|
|
|
|
|
0
|
local $Data::Dumper::Terse = 1; |
|
892
|
|
|
|
|
|
|
my @universe = $self->{cells_unused} ? |
|
893
|
0
|
|
|
|
|
0
|
grep {@{$self->{cell}[$_]{membership}}} (0 .. @{$self->{cell}} - 1) : |
|
|
0
|
|
|
|
|
0
|
|
|
|
0
|
|
|
|
|
0
|
|
|
894
|
0
|
0
|
|
|
|
0
|
(0 .. @{$self->{cell}} - 1); |
|
|
0
|
|
|
|
|
0
|
|
|
895
|
0
|
|
|
|
|
0
|
while (--$tries >= 0) { |
|
896
|
0
|
|
|
|
|
0
|
$self->problem (); # We rely on this specifying an empty problem. |
|
897
|
|
|
|
|
|
|
## my @ix = (0 .. $size - 1); |
|
898
|
0
|
|
|
|
|
0
|
my @ix = @universe; |
|
899
|
0
|
|
|
|
|
0
|
my $gen = 0; |
|
900
|
0
|
|
|
|
|
0
|
while ($gen++ < $min) { |
|
901
|
0
|
|
|
|
|
0
|
my ($inx) = splice @ix, floor (rand scalar @ix), 1; |
|
902
|
0
|
|
|
|
|
0
|
my $cell = $self->{cell}[$inx]; |
|
903
|
|
|
|
|
|
|
## @{$cell->{membership}} or redo; # Ignore unused cells. |
|
904
|
0
|
0
|
|
|
|
0
|
my @pos = grep {!$cell->{possible}{$_}} 1 .. $syms or next; |
|
|
0
|
|
|
|
|
0
|
|
|
905
|
0
|
|
|
|
|
0
|
my $val = $pos[floor (rand scalar @pos)]; |
|
906
|
0
|
0
|
|
|
|
0
|
defined $val or confess <{possible}); |
|
907
|
|
|
|
|
|
|
Programming error - generate() selected an undefined value for cell $inx. |
|
908
|
|
|
|
|
|
|
Possible values hash is: |
|
909
|
|
|
|
|
|
|
eod |
|
910
|
|
|
|
|
|
|
$self->_try ($cell, $val) |
|
911
|
0
|
0
|
|
|
|
0
|
and confess <{possible}); |
|
912
|
|
|
|
|
|
|
Programming error - generate() tried to assign $val to cell $inx, |
|
913
|
|
|
|
|
|
|
but it was rejected. Possible values hash is: |
|
914
|
|
|
|
|
|
|
eod |
|
915
|
|
|
|
|
|
|
} |
|
916
|
0
|
0
|
|
|
|
0
|
$self->solution () or next; |
|
917
|
0
|
|
|
|
|
0
|
$self->_constraint_remove ($min, $max, $const); |
|
918
|
0
|
|
|
|
|
0
|
my $prob = $self->_unload ('', SUDOKU_SUCCESS); |
|
919
|
0
|
|
|
|
|
0
|
$self->problem ($prob); |
|
920
|
0
|
0
|
|
|
|
0
|
$self->copy ($prob) if $self->{autocopy}; |
|
921
|
0
|
|
|
|
|
0
|
return $prob; |
|
922
|
|
|
|
|
|
|
} |
|
923
|
0
|
|
|
|
|
0
|
return; |
|
924
|
|
|
|
|
|
|
} |
|
925
|
|
|
|
|
|
|
|
|
926
|
|
|
|
|
|
|
my %accessor = ( |
|
927
|
|
|
|
|
|
|
allowed_symbols => \&_get_allowed_symbols, |
|
928
|
|
|
|
|
|
|
autocopy => \&_get_value, |
|
929
|
|
|
|
|
|
|
columns => \&_get_value, |
|
930
|
|
|
|
|
|
|
debug => \&_get_value, |
|
931
|
|
|
|
|
|
|
generation_limit => \&_get_value, |
|
932
|
|
|
|
|
|
|
## ignore_unused => \&_get_value, |
|
933
|
|
|
|
|
|
|
iteration_limit => \&_get_value, |
|
934
|
|
|
|
|
|
|
largest_set => \&_get_value, |
|
935
|
|
|
|
|
|
|
name => \&_get_value, |
|
936
|
|
|
|
|
|
|
output_delimiter => \&_get_value, |
|
937
|
|
|
|
|
|
|
rows => \&_get_value, |
|
938
|
|
|
|
|
|
|
status_text => \&_get_value, |
|
939
|
|
|
|
|
|
|
status_value => \&_get_value, |
|
940
|
|
|
|
|
|
|
symbols => \&_get_symbols, |
|
941
|
|
|
|
|
|
|
topology => \&_get_topology, |
|
942
|
|
|
|
|
|
|
); |
|
943
|
|
|
|
|
|
|
|
|
944
|
|
|
|
|
|
|
=head2 get |
|
945
|
|
|
|
|
|
|
|
|
946
|
|
|
|
|
|
|
$value = $su->get( $name ); |
|
947
|
|
|
|
|
|
|
|
|
948
|
|
|
|
|
|
|
This method returns the value of the named attribute. An exception |
|
949
|
|
|
|
|
|
|
is thrown if the given name does not correspond to an attribute that |
|
950
|
|
|
|
|
|
|
can be read. That is, the given name must appear on the list of |
|
951
|
|
|
|
|
|
|
attributes above, and not be marked "write-only". |
|
952
|
|
|
|
|
|
|
|
|
953
|
|
|
|
|
|
|
If called in list context, you can pass multiple attribute names, |
|
954
|
|
|
|
|
|
|
and get back a list of their values. If called in scalar context, |
|
955
|
|
|
|
|
|
|
attribute names after the first are ignored. |
|
956
|
|
|
|
|
|
|
|
|
957
|
|
|
|
|
|
|
=cut |
|
958
|
|
|
|
|
|
|
|
|
959
|
|
|
|
|
|
|
sub get { |
|
960
|
21
|
|
|
21
|
1
|
70
|
my ($self, @args) = @_; |
|
961
|
21
|
|
|
|
|
37
|
my @rslt; |
|
962
|
21
|
50
|
|
|
|
76
|
wantarray or @args = ($args[0]); |
|
963
|
21
|
|
|
|
|
40
|
foreach my $name (@args) { |
|
964
|
21
|
50
|
|
|
|
48
|
exists $accessor{$name} or croak <
|
|
965
|
|
|
|
|
|
|
Error - Attribute $name does not exist, or is write-only. |
|
966
|
|
|
|
|
|
|
eod |
|
967
|
21
|
|
|
|
|
63
|
push @rslt, $accessor{$name}->($self, $name); |
|
968
|
|
|
|
|
|
|
} |
|
969
|
21
|
50
|
|
|
|
107
|
return wantarray ? @rslt : $rslt[0]; |
|
970
|
|
|
|
|
|
|
} |
|
971
|
|
|
|
|
|
|
|
|
972
|
|
|
|
|
|
|
sub _get_allowed_symbols { |
|
973
|
2
|
|
|
2
|
|
6
|
my ( $self ) = @_; |
|
974
|
2
|
|
|
|
|
4
|
my $rslt = ''; |
|
975
|
2
|
|
|
|
|
3
|
my $syms = @{$self->{symbol_list}}; |
|
|
2
|
|
|
|
|
5
|
|
|
976
|
2
|
|
|
|
|
4
|
foreach (sort keys %{$self->{allowed_symbols}}) { |
|
|
2
|
|
|
|
|
15
|
|
|
977
|
6
|
|
|
|
|
7
|
my @symlst; |
|
978
|
6
|
|
|
|
|
13
|
for (my $val = 1; $val < $syms; $val++) { |
|
979
|
|
|
|
|
|
|
push @symlst, $self->{symbol_list}[$val] |
|
980
|
54
|
100
|
|
|
|
123
|
if $self->{allowed_symbols}{$_}[$val]; |
|
981
|
|
|
|
|
|
|
} |
|
982
|
6
|
|
|
|
|
12
|
$rslt .= "$_=@{[join ',', @symlst]}\n"; |
|
|
6
|
|
|
|
|
21
|
|
|
983
|
|
|
|
|
|
|
} |
|
984
|
2
|
|
|
|
|
11
|
return $rslt; |
|
985
|
|
|
|
|
|
|
} |
|
986
|
|
|
|
|
|
|
|
|
987
|
|
|
|
|
|
|
sub _get_symbols { |
|
988
|
5
|
|
|
5
|
|
11
|
my ( $self ) = @_; |
|
989
|
5
|
|
|
|
|
10
|
return join ' ', @{$self->{symbol_list}}; |
|
|
5
|
|
|
|
|
28
|
|
|
990
|
|
|
|
|
|
|
} |
|
991
|
|
|
|
|
|
|
|
|
992
|
|
|
|
|
|
|
sub _get_topology { |
|
993
|
8
|
|
|
8
|
|
40
|
my ( $self ) = @_; |
|
994
|
8
|
|
|
|
|
22
|
my $rslt = ''; |
|
995
|
8
|
|
|
|
|
19
|
my $col = $self->{columns}; |
|
996
|
8
|
|
33
|
|
|
24
|
my $row = $self->{rows} ||= floor (@{$self->{cell}} / $col); |
|
|
0
|
|
|
|
|
0
|
|
|
997
|
8
|
50
|
|
|
|
16
|
foreach (map {join (',', @{$_->{membership}}) || ','} @{$self->{cell}}) { |
|
|
583
|
|
|
|
|
656
|
|
|
|
583
|
|
|
|
|
1126
|
|
|
|
8
|
|
|
|
|
21
|
|
|
998
|
583
|
|
|
|
|
643
|
$rslt .= $_; |
|
999
|
583
|
100
|
|
|
|
707
|
if (--$col > 0) { |
|
1000
|
522
|
|
|
|
|
616
|
$rslt .= ' ' |
|
1001
|
|
|
|
|
|
|
} else { |
|
1002
|
61
|
|
|
|
|
72
|
$rslt .= "\n"; |
|
1003
|
61
|
|
|
|
|
78
|
$col = $self->{columns}; |
|
1004
|
61
|
100
|
|
|
|
115
|
if (--$row <= 0) { |
|
1005
|
8
|
|
|
|
|
14
|
$rslt .= "\n"; |
|
1006
|
8
|
|
|
|
|
14
|
$row = $self->{rows}; |
|
1007
|
|
|
|
|
|
|
} |
|
1008
|
|
|
|
|
|
|
} |
|
1009
|
|
|
|
|
|
|
} |
|
1010
|
8
|
|
|
|
|
54
|
0 while chomp $rslt; |
|
1011
|
8
|
|
|
|
|
16
|
$rslt .= "\n"; |
|
1012
|
8
|
|
|
|
|
43
|
return $rslt; |
|
1013
|
|
|
|
|
|
|
} |
|
1014
|
|
|
|
|
|
|
|
|
1015
|
6
|
|
|
6
|
|
19
|
sub _get_value {return $_[0]->{$_[1]}} |
|
1016
|
|
|
|
|
|
|
|
|
1017
|
|
|
|
|
|
|
=head2 paste |
|
1018
|
|
|
|
|
|
|
|
|
1019
|
|
|
|
|
|
|
$su->paste() |
|
1020
|
|
|
|
|
|
|
|
|
1021
|
|
|
|
|
|
|
This method pastes a problem from the clipboard. |
|
1022
|
|
|
|
|
|
|
|
|
1023
|
|
|
|
|
|
|
See L for what is needed for this |
|
1024
|
|
|
|
|
|
|
to work. |
|
1025
|
|
|
|
|
|
|
|
|
1026
|
|
|
|
|
|
|
=cut |
|
1027
|
|
|
|
|
|
|
|
|
1028
|
|
|
|
|
|
|
{ # Begin local symbol block |
|
1029
|
|
|
|
|
|
|
|
|
1030
|
|
|
|
|
|
|
my $paster; |
|
1031
|
|
|
|
|
|
|
sub paste { |
|
1032
|
1
|
|
|
1
|
1
|
3
|
my ( $self ) = @_; |
|
1033
|
1
|
50
|
33
|
|
|
5
|
( $paster ||= eval { |
|
1034
|
1
|
|
|
|
|
5
|
require Clipboard; |
|
1035
|
1
|
|
|
|
|
4
|
Clipboard->import(); |
|
1036
|
|
|
|
|
|
|
return sub { |
|
1037
|
1
|
|
|
1
|
|
3
|
return Clipboard->paste(); |
|
1038
|
1
|
|
|
|
|
6
|
}; |
|
1039
|
|
|
|
|
|
|
} |
|
1040
|
|
|
|
|
|
|
) or croak 'paste() unavailable; can not load Clipboard'; |
|
1041
|
|
|
|
|
|
|
|
|
1042
|
1
|
|
|
|
|
4
|
$self->problem( $paster->() ); |
|
1043
|
1
|
|
|
|
|
3
|
$self->_unload(); |
|
1044
|
1
|
|
|
|
|
3
|
return $self; |
|
1045
|
|
|
|
|
|
|
} |
|
1046
|
|
|
|
|
|
|
|
|
1047
|
|
|
|
|
|
|
} # End local symbol block |
|
1048
|
|
|
|
|
|
|
|
|
1049
|
|
|
|
|
|
|
=head2 problem |
|
1050
|
|
|
|
|
|
|
|
|
1051
|
|
|
|
|
|
|
$su->problem( $string ); |
|
1052
|
|
|
|
|
|
|
|
|
1053
|
|
|
|
|
|
|
This method specifies the problem to be solved, and sets the object |
|
1054
|
|
|
|
|
|
|
up to solve the problem. |
|
1055
|
|
|
|
|
|
|
|
|
1056
|
|
|
|
|
|
|
The problem is specified by a whitespace-delimited list of the symbols |
|
1057
|
|
|
|
|
|
|
contained by each cell. You can format the puzzle definition into a |
|
1058
|
|
|
|
|
|
|
square grid (e.g. the SYNOPSIS section), but to the parser a line |
|
1059
|
|
|
|
|
|
|
break is no different than spaces. If you pass an empty string, an |
|
1060
|
|
|
|
|
|
|
empty problem will be set up - that is, one in which all cells are |
|
1061
|
|
|
|
|
|
|
empty. |
|
1062
|
|
|
|
|
|
|
|
|
1063
|
|
|
|
|
|
|
An exception will be thrown if: |
|
1064
|
|
|
|
|
|
|
|
|
1065
|
|
|
|
|
|
|
* The puzzle definition uses an unknown symbol; |
|
1066
|
|
|
|
|
|
|
* The puzzle definition has a different number |
|
1067
|
|
|
|
|
|
|
of cells from the topology definition; |
|
1068
|
|
|
|
|
|
|
* There exists a set with more members than the |
|
1069
|
|
|
|
|
|
|
number of symbols, excluding the "empty" |
|
1070
|
|
|
|
|
|
|
symbol. |
|
1071
|
|
|
|
|
|
|
|
|
1072
|
|
|
|
|
|
|
The whitespace delimiter is optional, provided that all symbol names |
|
1073
|
|
|
|
|
|
|
are exactly one character long, B that you have not defined any |
|
1074
|
|
|
|
|
|
|
symbol constraint names more than one character long since the last |
|
1075
|
|
|
|
|
|
|
time you set the symbol names. |
|
1076
|
|
|
|
|
|
|
|
|
1077
|
|
|
|
|
|
|
=cut |
|
1078
|
|
|
|
|
|
|
|
|
1079
|
|
|
|
|
|
|
sub problem { |
|
1080
|
19
|
|
|
19
|
1
|
73
|
my ( $self, $val ) = @_; |
|
1081
|
19
|
|
50
|
|
|
48
|
$val ||= ''; |
|
1082
|
|
|
|
|
|
|
$val =~ m/\S/ or |
|
1083
|
|
|
|
|
|
|
$val = "$self->{symbol_list}[0] " x |
|
1084
|
19
|
50
|
|
|
|
96
|
(scalar @{$self->{cell}} - $self->{cells_unused}); |
|
|
0
|
|
|
|
|
0
|
|
|
1085
|
19
|
100
|
|
|
|
294
|
$val =~ s/\s+//g unless $self->{biggest_spec} > 1; |
|
1086
|
19
|
|
|
|
|
58
|
$val =~ s/^\s+//; |
|
1087
|
19
|
|
|
|
|
77
|
$val =~ s/\s+$//; |
|
1088
|
19
|
50
|
|
|
|
69
|
$self->{debug} and print <
|
|
1089
|
|
|
|
|
|
|
Debug problem - Called with $val |
|
1090
|
|
|
|
|
|
|
eod |
|
1091
|
|
|
|
|
|
|
|
|
1092
|
19
|
|
|
|
|
41
|
local $Data::Dumper::Terse = 1; |
|
1093
|
19
|
50
|
|
|
|
31
|
$self->{largest_set} >= @{$self->{symbol_list}} and croak <
|
|
|
19
|
|
|
|
|
53
|
|
|
1094
|
|
|
|
|
|
|
Error - The largest set has $self->{largest_set} cells, but there are only @{[ |
|
1095
|
0
|
|
|
|
|
0
|
@{$self->{symbol_list}} - 1]} symbols. |
|
|
0
|
|
|
|
|
0
|
|
|
1096
|
|
|
|
|
|
|
Either the set definition is in error or the list of symbols is |
|
1097
|
|
|
|
|
|
|
incomplete. |
|
1098
|
|
|
|
|
|
|
eod |
|
1099
|
|
|
|
|
|
|
|
|
1100
|
19
|
|
|
|
|
26
|
my $syms = @{$self->{symbol_list}}; |
|
|
19
|
|
|
|
|
31
|
|
|
1101
|
19
|
|
|
|
|
27
|
foreach (@{$self->{cell}}) { |
|
|
19
|
|
|
|
|
42
|
|
|
1102
|
1651
|
|
|
|
|
2400
|
$_->{content} = $_->{chosen} = 0; |
|
1103
|
1651
|
|
|
|
|
2226
|
$_->{possible} = {map {$_ => 0} (1 .. $syms - 1)}; |
|
|
17167
|
|
|
|
|
26053
|
|
|
1104
|
|
|
|
|
|
|
} |
|
1105
|
19
|
|
|
|
|
44
|
foreach (values %{$self->{set}}) { |
|
|
19
|
|
|
|
|
77
|
|
|
1106
|
511
|
|
|
|
|
536
|
$_->{free} = @{$_->{membership}}; |
|
|
511
|
|
|
|
|
639
|
|
|
1107
|
511
|
|
|
|
|
842
|
$_->{content} = [$_->{free}]; |
|
1108
|
|
|
|
|
|
|
} |
|
1109
|
19
|
|
|
|
|
29
|
$self->{cells_unassigned} = scalar @{$self->{cell}} - $self->{cells_unused}; |
|
|
19
|
|
|
|
|
40
|
|
|
1110
|
|
|
|
|
|
|
|
|
1111
|
19
|
|
|
|
|
31
|
my $hash = $self->{symbol_hash}; |
|
1112
|
19
|
|
|
|
|
34
|
my $inx = 0; |
|
1113
|
19
|
|
|
|
|
30
|
my $max = @{$self->{cell}}; |
|
|
19
|
|
|
|
|
38
|
|
|
1114
|
19
|
100
|
|
|
|
485
|
foreach (split (($self->{biggest_spec} > 1 ? '\s+' : ''), $val)) { |
|
1115
|
1651
|
50
|
|
|
|
2316
|
$inx >= $max and croak <
|
|
1116
|
|
|
|
|
|
|
Error - Too many cell specifications. The topology allows only $max. |
|
1117
|
|
|
|
|
|
|
eod |
|
1118
|
1651
|
50
|
|
|
|
2272
|
next unless defined $_; |
|
1119
|
|
|
|
|
|
|
# was $self->{ignore_unused} |
|
1120
|
0
|
|
|
|
|
0
|
($self->{cells_unused} && !@{$self->{cell}[$inx]{membership}}) |
|
1121
|
1651
|
50
|
33
|
|
|
2466
|
and do {$inx++; redo}; |
|
|
0
|
|
|
|
|
0
|
|
|
|
0
|
|
|
|
|
0
|
|
|
1122
|
1651
|
100
|
|
|
|
2407
|
$self->{allowed_symbols}{$_} and do { |
|
1123
|
195
|
50
|
|
|
|
320
|
$self->{debug} > 1 and print <
|
|
1124
|
|
|
|
|
|
|
Debug problem - Cell $inx allows symbol set $_ |
|
1125
|
|
|
|
|
|
|
eod |
|
1126
|
195
|
|
|
|
|
228
|
my $cell = $self->{cell}[$inx]; |
|
1127
|
195
|
50
|
|
|
|
200
|
@{$cell->{membership}} or croak <
|
|
|
195
|
|
|
|
|
324
|
|
|
1128
|
|
|
|
|
|
|
Error - Cell $inx is unused, and must be specified as empty. |
|
1129
|
|
|
|
|
|
|
eod |
|
1130
|
195
|
|
|
|
|
322
|
for (my $val = 1; $val < $syms; $val++) { |
|
1131
|
1692
|
100
|
|
|
|
2740
|
next if $self->{allowed_symbols}{$_}[$val]; |
|
1132
|
749
|
|
|
|
|
1210
|
$cell->{possible}{$val} = 1; |
|
1133
|
|
|
|
|
|
|
} |
|
1134
|
|
|
|
|
|
|
}; |
|
1135
|
1651
|
100
|
|
|
|
2425
|
defined $hash->{$_} or $_ = $self->{symbol_list}[0]; |
|
1136
|
1651
|
|
|
|
|
2826
|
(@{$self->{cell}[$inx]{membership}} || |
|
1137
|
1651
|
50
|
33
|
|
|
1682
|
$_ eq $self->{symbol_list}[0]) |
|
1138
|
|
|
|
|
|
|
or croak <
|
|
1139
|
|
|
|
|
|
|
Error - Cell $inx is unused, and must be specified as empty. |
|
1140
|
|
|
|
|
|
|
eod |
|
1141
|
1651
|
50
|
|
|
|
2440
|
$self->{debug} > 1 and print <
|
|
1142
|
|
|
|
|
|
|
Debug problem - Cell $inx specifies symbol $_ |
|
1143
|
|
|
|
|
|
|
eod |
|
1144
|
1651
|
50
|
|
|
|
2438
|
$self->_try ($inx, $hash->{$_}) and croak <
|
|
1145
|
|
|
|
|
|
|
Error - Symbol '$_' appears more than once in a set. |
|
1146
|
|
|
|
|
|
|
The problem loaded thus far is: |
|
1147
|
0
|
|
|
|
|
0
|
@{[$self->_unload (' ')]} |
|
1148
|
|
|
|
|
|
|
eod |
|
1149
|
1651
|
100
|
|
|
|
2745
|
$self->{cell}[$inx]{chosen} = $hash->{$_} ? 1 : 0; |
|
1150
|
|
|
|
|
|
|
} continue { |
|
1151
|
1651
|
|
|
|
|
2127
|
$inx++; |
|
1152
|
|
|
|
|
|
|
} |
|
1153
|
|
|
|
|
|
|
|
|
1154
|
19
|
50
|
|
|
|
138
|
unless ($inx == $max) { |
|
1155
|
|
|
|
|
|
|
# was $self->{ignore_unused} |
|
1156
|
0
|
0
|
|
|
|
0
|
$self->{cells_unused} and do { |
|
1157
|
0
|
|
|
|
|
0
|
$inx -= $self->{cells_unused}; |
|
1158
|
0
|
|
|
|
|
0
|
$max -= $self->{cells_unused}; |
|
1159
|
|
|
|
|
|
|
}; |
|
1160
|
0
|
|
|
|
|
0
|
croak <
|
|
1161
|
|
|
|
|
|
|
Error - Not enough cell specifications. you gave $inx but the topology |
|
1162
|
|
|
|
|
|
|
defined $max. |
|
1163
|
|
|
|
|
|
|
eod |
|
1164
|
|
|
|
|
|
|
} |
|
1165
|
|
|
|
|
|
|
|
|
1166
|
19
|
|
|
|
|
62
|
$self->{constraints_used} = {}; |
|
1167
|
|
|
|
|
|
|
|
|
1168
|
19
|
50
|
|
|
|
42
|
$self->{debug} and print <
|
|
1169
|
|
|
|
|
|
|
Debug problem - problem loaded. |
|
1170
|
|
|
|
|
|
|
eod |
|
1171
|
|
|
|
|
|
|
|
|
1172
|
19
|
|
|
|
|
149
|
$self->{backtrack_stack} = []; |
|
1173
|
19
|
|
|
|
|
43
|
$self->{cell_order} = []; |
|
1174
|
19
|
|
|
|
|
29
|
delete $self->{no_more_solutions}; |
|
1175
|
|
|
|
|
|
|
|
|
1176
|
19
|
50
|
|
|
|
38
|
$self->{debug} > 1 and print " object = ", Dumper ($self); |
|
1177
|
|
|
|
|
|
|
|
|
1178
|
19
|
|
|
|
|
62
|
return $self; |
|
1179
|
|
|
|
|
|
|
} |
|
1180
|
|
|
|
|
|
|
|
|
1181
|
|
|
|
|
|
|
my %mutator = ( |
|
1182
|
|
|
|
|
|
|
allowed_symbols => \&_set_allowed_symbols, |
|
1183
|
|
|
|
|
|
|
autocopy => \&_set_value, |
|
1184
|
|
|
|
|
|
|
brick => \&_set_brick, |
|
1185
|
|
|
|
|
|
|
columns => \&_set_number, |
|
1186
|
|
|
|
|
|
|
debug => \&_set_number, |
|
1187
|
|
|
|
|
|
|
corresponding => \&_set_corresponding, |
|
1188
|
|
|
|
|
|
|
cube => \&_set_cube, |
|
1189
|
|
|
|
|
|
|
generation_limit => \&_set_number, |
|
1190
|
|
|
|
|
|
|
## ignore_unused => \&_set_value, |
|
1191
|
|
|
|
|
|
|
iteration_limit => \&_set_number, |
|
1192
|
|
|
|
|
|
|
latin => \&_set_latin, |
|
1193
|
|
|
|
|
|
|
max_tuple => \&_set_number, |
|
1194
|
|
|
|
|
|
|
name => \&_set_value, |
|
1195
|
|
|
|
|
|
|
null => \&_set_null, |
|
1196
|
|
|
|
|
|
|
output_delimiter => \&_set_value, |
|
1197
|
|
|
|
|
|
|
quincunx => \&_set_quincunx, |
|
1198
|
|
|
|
|
|
|
rows => \&_set_number, |
|
1199
|
|
|
|
|
|
|
status_value => \&_set_status_value, |
|
1200
|
|
|
|
|
|
|
sudoku => \&_set_sudoku, |
|
1201
|
|
|
|
|
|
|
sudokux => \&_set_sudokux, |
|
1202
|
|
|
|
|
|
|
symbols => \&_set_symbols, |
|
1203
|
|
|
|
|
|
|
topology => \&_set_topology, |
|
1204
|
|
|
|
|
|
|
); |
|
1205
|
|
|
|
|
|
|
|
|
1206
|
|
|
|
|
|
|
=head2 set |
|
1207
|
|
|
|
|
|
|
|
|
1208
|
|
|
|
|
|
|
$su->set( $name => $value ); |
|
1209
|
|
|
|
|
|
|
|
|
1210
|
|
|
|
|
|
|
This method sets the value of the named attribute. An exception |
|
1211
|
|
|
|
|
|
|
is thrown if the given name does not correspond to an attribute that |
|
1212
|
|
|
|
|
|
|
can be written. That is, the given name must appear on the list of |
|
1213
|
|
|
|
|
|
|
attributes above, and not be marked "read-only". An exception is |
|
1214
|
|
|
|
|
|
|
also thrown if the value is invalid, e.g. a non-numeric value for |
|
1215
|
|
|
|
|
|
|
an attribute marked "number". |
|
1216
|
|
|
|
|
|
|
|
|
1217
|
|
|
|
|
|
|
You can pass multiple name-value pairs. If an exception is thrown, |
|
1218
|
|
|
|
|
|
|
all settings before the exception will be made, and all settings |
|
1219
|
|
|
|
|
|
|
after the exception will not be made. |
|
1220
|
|
|
|
|
|
|
|
|
1221
|
|
|
|
|
|
|
The object itself is returned. |
|
1222
|
|
|
|
|
|
|
|
|
1223
|
|
|
|
|
|
|
=cut |
|
1224
|
|
|
|
|
|
|
|
|
1225
|
|
|
|
|
|
|
sub set { |
|
1226
|
62
|
|
|
62
|
1
|
183
|
my ( $self, @args ) = @_; |
|
1227
|
62
|
|
|
|
|
128
|
while ( @args ) { |
|
1228
|
90
|
|
|
|
|
204
|
my ( $name, $val ) = splice @args, 0, 2; |
|
1229
|
90
|
50
|
|
|
|
220
|
exists $mutator{$name} or croak <
|
|
1230
|
|
|
|
|
|
|
Error - Attribute $name does not exist, or is read-only. |
|
1231
|
|
|
|
|
|
|
eod |
|
1232
|
90
|
|
|
|
|
215
|
$mutator{$name}->($self, $name, $val ); |
|
1233
|
|
|
|
|
|
|
} |
|
1234
|
62
|
|
|
|
|
101
|
return $self; |
|
1235
|
|
|
|
|
|
|
} |
|
1236
|
|
|
|
|
|
|
|
|
1237
|
|
|
|
|
|
|
sub _set_allowed_symbols { |
|
1238
|
|
|
|
|
|
|
## my ( $self, $name, $value ) = @_; |
|
1239
|
4
|
|
|
4
|
|
11
|
my ( $self, undef, $value ) = @_; # Name unused |
|
1240
|
4
|
50
|
|
|
|
11
|
defined $value or $value = ''; |
|
1241
|
4
|
|
|
|
|
9
|
my $maxlen = 0; |
|
1242
|
4
|
50
|
|
|
|
13
|
$self->{debug} and print <
|
|
1243
|
|
|
|
|
|
|
Debug allowed_symbols being set to '$value' |
|
1244
|
|
|
|
|
|
|
eod |
|
1245
|
4
|
50
|
|
|
|
10
|
if ($value) { |
|
1246
|
4
|
|
|
|
|
32
|
foreach (split '\s+', $value) { |
|
1247
|
22
|
|
|
|
|
48
|
my ($name, $value) = split '=', $_, 2; |
|
1248
|
22
|
50
|
|
|
|
46
|
croak <{symbol_hash}{$name}; |
|
1249
|
|
|
|
|
|
|
Error - You can not use '$name' as a symbol constraint name, because |
|
1250
|
|
|
|
|
|
|
it is a valid symbol name. |
|
1251
|
|
|
|
|
|
|
eod |
|
1252
|
22
|
100
|
|
|
|
37
|
$value or do {delete $self->{allowed_symbols}{$name}; next}; |
|
|
2
|
|
|
|
|
14
|
|
|
|
2
|
|
|
|
|
4
|
|
|
1253
|
20
|
|
|
|
|
39
|
$maxlen = max ($maxlen, length ($name)); |
|
1254
|
20
|
50
|
|
|
|
36
|
$self->{debug} > 1 and print <
|
|
1255
|
|
|
|
|
|
|
Debug allowed_symbols - $_ |
|
1256
|
0
|
|
|
|
|
0
|
set name '$name' has length @{[length ($name)]}. Maxlen now $maxlen. |
|
1257
|
|
|
|
|
|
|
eod |
|
1258
|
20
|
|
|
|
|
53
|
my $const = $self->{allowed_symbols}{$name} = []; |
|
1259
|
20
|
|
|
|
|
45
|
foreach (split ',', $value) { |
|
1260
|
93
|
50
|
|
|
|
146
|
$self->{debug} > 1 and print <
|
|
1261
|
|
|
|
|
|
|
Debug allowed_symbols - Adding symbol '$_' to set '$name'. |
|
1262
|
|
|
|
|
|
|
eod |
|
1263
|
93
|
50
|
|
|
|
134
|
$self->{symbol_hash}{$_} or croak <
|
|
1264
|
|
|
|
|
|
|
Error - '$_' is not a valid symbol. |
|
1265
|
|
|
|
|
|
|
eod |
|
1266
|
93
|
|
|
|
|
157
|
$const->[$self->{symbol_hash}{$_}] = 1; |
|
1267
|
|
|
|
|
|
|
} |
|
1268
|
|
|
|
|
|
|
} |
|
1269
|
|
|
|
|
|
|
} else { |
|
1270
|
0
|
|
|
|
|
0
|
$self->{allowed_symbols} = {}; |
|
1271
|
|
|
|
|
|
|
} |
|
1272
|
4
|
100
|
|
|
|
16
|
$self->{biggest_spec} = $maxlen if $maxlen > $self->{biggest_spec}; |
|
1273
|
4
|
|
|
|
|
13
|
return; |
|
1274
|
|
|
|
|
|
|
} |
|
1275
|
|
|
|
|
|
|
|
|
1276
|
|
|
|
|
|
|
sub _set_brick { |
|
1277
|
6
|
|
|
6
|
|
20
|
my ( $self, undef, $value ) = @_; # $name unused |
|
1278
|
6
|
100
|
|
|
|
24
|
my ($horiz, $vert, $size) = ref $value ? @$value : split ',', $value; |
|
1279
|
6
|
50
|
|
|
|
21
|
defined $size |
|
1280
|
|
|
|
|
|
|
and $self->_deprecation_notice( 'brick_third_argument' ); |
|
1281
|
6
|
|
33
|
|
|
50
|
$size ||= $horiz * $vert; |
|
1282
|
6
|
50
|
33
|
|
|
41
|
($size % $horiz || $size % $vert) and croak <
|
|
1283
|
|
|
|
|
|
|
Error - The puzzle size $size must be a multiple of both the horizontal |
|
1284
|
|
|
|
|
|
|
brick size $horiz and the vertical brick size $vert. |
|
1285
|
|
|
|
|
|
|
eod |
|
1286
|
6
|
|
|
|
|
66
|
my $rowmul = floor ($size / $horiz); |
|
1287
|
6
|
|
|
|
|
17
|
my $syms = '.'; |
|
1288
|
6
|
|
|
|
|
12
|
my $topo = ''; |
|
1289
|
6
|
|
|
|
|
36
|
for (my $row = 0; $row < $size; $row++) { |
|
1290
|
58
|
|
|
|
|
76
|
$syms .= " @{[$row + 1]}"; |
|
|
58
|
|
|
|
|
124
|
|
|
1291
|
58
|
|
|
|
|
111
|
for (my $col = 0; $col < $size; $col++) { |
|
1292
|
616
|
|
|
|
|
1789
|
$topo .= sprintf ' r%d,c%d,s%d', $row, $col, |
|
1293
|
|
|
|
|
|
|
floor ($row / $vert) * $rowmul + floor ($col / $horiz); |
|
1294
|
|
|
|
|
|
|
} |
|
1295
|
|
|
|
|
|
|
} |
|
1296
|
6
|
|
|
|
|
18
|
substr ($topo, 0, 1, ''); |
|
1297
|
6
|
|
|
|
|
23
|
$self->set (columns => $size, rows => $size, symbols => $syms, |
|
1298
|
|
|
|
|
|
|
topology => $topo); |
|
1299
|
6
|
|
|
|
|
18
|
return; |
|
1300
|
|
|
|
|
|
|
} |
|
1301
|
|
|
|
|
|
|
|
|
1302
|
|
|
|
|
|
|
sub _set_corresponding { |
|
1303
|
|
|
|
|
|
|
## my ( $self, $name, $order ) = @_; |
|
1304
|
1
|
|
|
1
|
|
4
|
my ( $self, undef, $order ) = @_; # Name unused |
|
1305
|
1
|
|
|
|
|
4
|
my $size = $order * $order; |
|
1306
|
1
|
|
|
|
|
5
|
$self->set (sudoku => $order); |
|
1307
|
1
|
|
|
|
|
3
|
my $order_minus_1 = $order - 1; |
|
1308
|
1
|
|
|
|
|
3
|
my $offset = $size * $order; |
|
1309
|
1
|
|
|
|
|
6
|
for (my $inx = 0; $inx < $size; $inx++) { |
|
1310
|
9
|
|
|
|
|
27
|
my $base = floor ($inx / $order) * $size + $inx % $order; |
|
1311
|
|
|
|
|
|
|
$self->add_set ("u$inx", map { |
|
1312
|
9
|
|
|
|
|
19
|
my $g = $_ * $offset + $base; |
|
|
27
|
|
|
|
|
32
|
|
|
1313
|
27
|
|
|
|
|
30
|
(map {$_ * $order + $g} 0 .. $order_minus_1)} 0 .. $order_minus_1); |
|
|
81
|
|
|
|
|
120
|
|
|
1314
|
|
|
|
|
|
|
} |
|
1315
|
1
|
|
|
|
|
3
|
return; |
|
1316
|
|
|
|
|
|
|
} |
|
1317
|
|
|
|
|
|
|
|
|
1318
|
|
|
|
|
|
|
my %cube = ( |
|
1319
|
|
|
|
|
|
|
full => <
|
|
1320
|
|
|
|
|
|
|
c0,r0,s0 c1,r0,s0 c2,r0,s0 c3,r0,s0 |
|
1321
|
|
|
|
|
|
|
c0,r1,s0 c1,r1,s0 c2,r1,s0 c3,r1,s0 |
|
1322
|
|
|
|
|
|
|
c0,r2,s0 c1,r2,s0 c2,r2,s0 c3,r2,s0 |
|
1323
|
|
|
|
|
|
|
c0,r3,s0 c1,r3,s0 c2,r3,s0 c3,r3,s0 |
|
1324
|
|
|
|
|
|
|
p0,r0,s1 p0,r1,s1 p0,r2,s1 p0,r3,s1 |
|
1325
|
|
|
|
|
|
|
p1,r0,s1 p1,r1,s1 p1,r2,s1 p1,r3,s1 |
|
1326
|
|
|
|
|
|
|
p2,r0,s1 p2,r1,s1 p2,r2,s1 p2,r3,s1 |
|
1327
|
|
|
|
|
|
|
p3,r0,s1 p3,r1,s1 p3,r2,s1 p3,r3,s1 |
|
1328
|
|
|
|
|
|
|
c0,p0,s2 c1,p0,s2 c2,p0,s2 c3,p0,s2 |
|
1329
|
|
|
|
|
|
|
c0,p1,s2 c1,p1,s2 c2,p1,s2 c3,p1,s2 |
|
1330
|
|
|
|
|
|
|
c0,p2,s2 c1,p2,s2 c2,p2,s2 c3,p2,s2 |
|
1331
|
|
|
|
|
|
|
c0,p3,s2 c1,p3,s2 c2,p3,s2 c3,p3,s2 |
|
1332
|
|
|
|
|
|
|
p0,r3,s3 p0,r2,s3 p0,r1,s3 p0,r0,s3 |
|
1333
|
|
|
|
|
|
|
p1,r3,s3 p1,r2,s3 p1,r1,s3 p1,r0,s3 |
|
1334
|
|
|
|
|
|
|
p2,r3,s3 p2,r2,s3 p2,r1,s3 p2,r0,s3 |
|
1335
|
|
|
|
|
|
|
p3,r3,s3 p3,r2,s3 p3,r1,s3 p3,r0,s3 |
|
1336
|
|
|
|
|
|
|
c0,r3,s4 c1,r3,s4 c2,r3,s4 c3,r3,s4 |
|
1337
|
|
|
|
|
|
|
c0,r2,s4 c1,r2,s4 c2,r2,s4 c3,r2,s4 |
|
1338
|
|
|
|
|
|
|
c0,r1,s4 c1,r1,s4 c2,r1,s4 c3,r1,s4 |
|
1339
|
|
|
|
|
|
|
c0,r0,s4 c1,r0,s4 c2,r0,s4 c3,r0,s4 |
|
1340
|
|
|
|
|
|
|
c0,p3,s5 c1,p3,s5 c2,p3,s5 c3,p3,s5 |
|
1341
|
|
|
|
|
|
|
c0,p2,s5 c1,p2,s5 c2,p2,s5 c3,p2,s5 |
|
1342
|
|
|
|
|
|
|
c0,p1,s5 c1,p1,s5 c2,p1,s5 c3,p1,s5 |
|
1343
|
|
|
|
|
|
|
c0,p0,s5 c1,p0,s5 c2,p0,s5 c3,p0,s5 |
|
1344
|
|
|
|
|
|
|
eod |
|
1345
|
|
|
|
|
|
|
half => <
|
|
1346
|
|
|
|
|
|
|
r0,c0,s0 r0,c1,s0 r0,c2,s0 r0,c3,s0 |
|
1347
|
|
|
|
|
|
|
r1,c0,s0 r1,c1,s0 r1,c2,s0 r1,c3,s0 |
|
1348
|
|
|
|
|
|
|
r2,c0,s1 r2,c1,s1 r2,c2,s1 r2,c3,s1 |
|
1349
|
|
|
|
|
|
|
r3,c0,s1 r3,c1,s1 r3,c2,s1 r3,c3,s1 |
|
1350
|
|
|
|
|
|
|
p0,c0,s2 p0,c1,s2 p0,c2,s3 p0,c3,s3 |
|
1351
|
|
|
|
|
|
|
p1,c0,s2 p1,c1,s2 p1,c2,s3 p1,c3,s3 |
|
1352
|
|
|
|
|
|
|
p2,c0,s2 p2,c1,s2 p2,c2,s3 p2,c3,s3 |
|
1353
|
|
|
|
|
|
|
p3,c0,s2 p3,c1,s2 p3,c2,s3 p3,c3,s3 |
|
1354
|
|
|
|
|
|
|
p0,r3,s4 p0,r2,s4 p0,r1,s4 p0,r0,s4 |
|
1355
|
|
|
|
|
|
|
p1,r3,s4 p1,r2,s4 p1,r1,s4 p1,r0,s4 |
|
1356
|
|
|
|
|
|
|
p2,r3,s5 p2,r2,s5 p2,r1,s5 p2,r0,s5 |
|
1357
|
|
|
|
|
|
|
p3,r3,s5 p3,r2,s5 p3,r1,s5 p3,r0,s5 |
|
1358
|
|
|
|
|
|
|
eod |
|
1359
|
|
|
|
|
|
|
); |
|
1360
|
|
|
|
|
|
|
|
|
1361
|
|
|
|
|
|
|
sub _set_cube { |
|
1362
|
|
|
|
|
|
|
## my ( $self, $name, $type ) = @_; |
|
1363
|
2
|
|
|
2
|
|
19
|
my ( $self, undef, $type ) = @_; # Name unused |
|
1364
|
2
|
50
|
|
|
|
13
|
if ($type =~ m/\D/) { |
|
1365
|
2
|
50
|
|
|
|
7
|
$cube{$type} or croak <
|
|
1366
|
|
|
|
|
|
|
Error - Cube type '$type' is not defined. Legal values are numeric (for |
|
1367
|
0
|
|
|
|
|
0
|
Dion cube), or one of @{[join ', ', map {"'$_'"} sort keys %cube]} |
|
|
0
|
|
|
|
|
0
|
|
|
1368
|
|
|
|
|
|
|
eod |
|
1369
|
2
|
|
|
|
|
7
|
$self->set (topology => $cube{$type}, columns => 4, rows => 4); |
|
1370
|
|
|
|
|
|
|
} else { |
|
1371
|
0
|
|
|
|
|
0
|
my $size = $type * $type; |
|
1372
|
0
|
|
|
|
|
0
|
my $topo = ''; |
|
1373
|
0
|
|
|
|
|
0
|
for (my $x = 0; $x < $size; $x++) { |
|
1374
|
0
|
|
|
|
|
0
|
for (my $y = 0; $y < $size; $y++) { |
|
1375
|
0
|
|
|
|
|
0
|
for (my $z = 0; $z < $size; $z++) { |
|
1376
|
0
|
|
|
|
|
0
|
$topo .= join (',', |
|
1377
|
|
|
|
|
|
|
_cube_set_names ($type, x => $x, $y, $z), |
|
1378
|
|
|
|
|
|
|
_cube_set_names ($type, y => $y, $z, $x), |
|
1379
|
|
|
|
|
|
|
_cube_set_names ($type, z => $z, $x, $y)) . ' '; |
|
1380
|
|
|
|
|
|
|
} |
|
1381
|
|
|
|
|
|
|
} |
|
1382
|
|
|
|
|
|
|
} |
|
1383
|
0
|
|
|
|
|
0
|
$self->set (topology => $topo, columns => $size, rows => $size); |
|
1384
|
|
|
|
|
|
|
} |
|
1385
|
2
|
|
|
|
|
18
|
$self->set (symbols => join ' ', '.', 1 .. $self->{largest_set}); |
|
1386
|
2
|
|
|
|
|
5
|
return; |
|
1387
|
|
|
|
|
|
|
} |
|
1388
|
|
|
|
|
|
|
|
|
1389
|
|
|
|
|
|
|
sub _cube_set_names { |
|
1390
|
0
|
|
|
0
|
|
0
|
my ( $order, $name, $x, $y, $z ) = @_; |
|
1391
|
0
|
|
|
|
|
0
|
my $tplt = sprintf '%s%d%%s%%d', $name, $x; |
|
1392
|
0
|
|
|
|
|
0
|
return map {sprintf $tplt, @$_} [r => $y], [c => $z], |
|
|
0
|
|
|
|
|
0
|
|
|
1393
|
|
|
|
|
|
|
[s => floor ($y / $order) * $order + floor ($z / $order)] |
|
1394
|
|
|
|
|
|
|
} |
|
1395
|
|
|
|
|
|
|
|
|
1396
|
|
|
|
|
|
|
sub _set_latin { |
|
1397
|
|
|
|
|
|
|
## my ( $self, $name, $size ) = @_; |
|
1398
|
2
|
|
|
2
|
|
6
|
my ( $self, undef, $size ) = @_; # Name unused |
|
1399
|
2
|
|
|
|
|
3
|
my $syms = '.'; |
|
1400
|
2
|
|
|
|
|
4
|
my $topo = ''; |
|
1401
|
2
|
|
|
|
|
4
|
my $letter = 'A'; |
|
1402
|
2
|
|
|
|
|
8
|
for (my $row = 0; $row < $size; $row++) { |
|
1403
|
13
|
|
|
|
|
17
|
$syms .= " @{[$letter++]}"; |
|
|
13
|
|
|
|
|
28
|
|
|
1404
|
13
|
|
|
|
|
24
|
for (my $col = 0; $col < $size; $col++) { |
|
1405
|
97
|
|
|
|
|
176
|
$topo .= sprintf ' r%d,c%d', $row, $col; |
|
1406
|
|
|
|
|
|
|
} |
|
1407
|
|
|
|
|
|
|
} |
|
1408
|
2
|
|
|
|
|
5
|
substr ($topo, 0, 1, ''); |
|
1409
|
2
|
|
|
|
|
7
|
$self->set (columns => $size, rows => $size, symbols => $syms, |
|
1410
|
|
|
|
|
|
|
topology => $topo); |
|
1411
|
2
|
|
|
|
|
5
|
return; |
|
1412
|
|
|
|
|
|
|
} |
|
1413
|
|
|
|
|
|
|
|
|
1414
|
|
|
|
|
|
|
sub _set_null { |
|
1415
|
|
|
|
|
|
|
## my ( $self, $name, $value ) = @_; |
|
1416
|
0
|
|
|
0
|
|
0
|
my ( $self, undef, $value ) = @_; # Name unused |
|
1417
|
0
|
0
|
|
|
|
0
|
my ($size, $columns, $rows) = ref $value ? @$value : split ',', $value; |
|
1418
|
0
|
|
|
|
|
0
|
$self->{cell} = []; # The cells themselves. |
|
1419
|
0
|
|
|
|
|
0
|
$self->{set} = {}; # The sets themselves. |
|
1420
|
0
|
|
|
|
|
0
|
$self->{largest_set} = 0; |
|
1421
|
0
|
|
|
|
|
0
|
$self->{intersection} = {}; |
|
1422
|
0
|
|
|
|
|
0
|
$self->{cells_unused} = $size; |
|
1423
|
0
|
|
|
|
|
0
|
foreach my $cell_inx (0 .. $size - 1) { |
|
1424
|
0
|
|
|
|
|
0
|
my $cell = {membership => [], index => $cell_inx}; |
|
1425
|
0
|
|
|
|
|
0
|
push @{$self->{cell}}, $cell; |
|
|
0
|
|
|
|
|
0
|
|
|
1426
|
|
|
|
|
|
|
} |
|
1427
|
0
|
|
|
|
|
0
|
delete $self->{backtrack_stack}; # Force setting of new problem. |
|
1428
|
0
|
0
|
|
|
|
0
|
defined $columns and $self->set (columns => $columns); |
|
1429
|
0
|
0
|
|
|
|
0
|
defined $rows and $self->set (rows => $rows); |
|
1430
|
0
|
|
|
|
|
0
|
return; |
|
1431
|
|
|
|
|
|
|
} |
|
1432
|
|
|
|
|
|
|
|
|
1433
|
|
|
|
|
|
|
sub _set_number { |
|
1434
|
23
|
|
|
23
|
|
43
|
my ( $self, $name, $value ) = @_; |
|
1435
|
23
|
50
|
|
|
|
53
|
_looks_like_number ($value) or croak <
|
|
1436
|
|
|
|
|
|
|
Error - Attribute $name must be numeric. |
|
1437
|
|
|
|
|
|
|
eod |
|
1438
|
23
|
|
|
|
|
56
|
$self->{$name} = $value; |
|
1439
|
23
|
|
|
|
|
55
|
return; |
|
1440
|
|
|
|
|
|
|
} |
|
1441
|
|
|
|
|
|
|
|
|
1442
|
|
|
|
|
|
|
sub _set_quincunx { |
|
1443
|
|
|
|
|
|
|
## my ( $self, $name, $value ) = @_; |
|
1444
|
0
|
|
|
0
|
|
0
|
my ( $self, undef, $value ) = @_; # Name unused |
|
1445
|
0
|
0
|
|
|
|
0
|
my ($order, $gap) = ref $value ? @$value : split ',', $value; |
|
1446
|
0
|
0
|
|
|
|
0
|
$order =~ m/\D/ and croak <
|
|
1447
|
|
|
|
|
|
|
Error - The quincunx order must be an integer. |
|
1448
|
|
|
|
|
|
|
eod |
|
1449
|
0
|
0
|
|
|
|
0
|
if (defined $gap) { |
|
1450
|
0
|
0
|
|
|
|
0
|
$gap =~ m/\D/ and croak <
|
|
1451
|
|
|
|
|
|
|
Error - The quincunx gap must be an integer. |
|
1452
|
|
|
|
|
|
|
eod |
|
1453
|
0
|
0
|
|
|
|
0
|
$gap > $order - 2 and croak <
|
|
1454
|
|
|
|
|
|
|
Error - The quincunx gap must not be greater than the order ($order) - 2. |
|
1455
|
|
|
|
|
|
|
eod |
|
1456
|
0
|
0
|
|
|
|
0
|
$gap % 2 == $order % 2 or croak <
|
|
1457
|
|
|
|
|
|
|
Error - The gap must be the same parity (odd or even) as the order. |
|
1458
|
|
|
|
|
|
|
eod |
|
1459
|
|
|
|
|
|
|
} else { |
|
1460
|
0
|
|
|
|
|
0
|
$gap = $order % 2; |
|
1461
|
|
|
|
|
|
|
} |
|
1462
|
0
|
|
|
|
|
0
|
my $cols = ($order * 2 + $gap) * $order; |
|
1463
|
0
|
|
|
|
|
0
|
$self->set(null => [$cols * $cols, $cols, $cols]); |
|
1464
|
0
|
|
|
|
|
0
|
my $osq = $order * $order; |
|
1465
|
0
|
|
|
|
|
0
|
$self->set(symbols => join (' ', '.', 1 .. $osq)); |
|
1466
|
0
|
|
|
|
|
0
|
my @squares = do { # Squares in terms of index of top left corner |
|
1467
|
0
|
|
|
|
|
0
|
my $offset = ($order + $gap) * $order; |
|
1468
|
0
|
|
|
|
|
0
|
my $inset = ($order - ($order - $gap) / 2) * $order; |
|
1469
|
|
|
|
|
|
|
( |
|
1470
|
0
|
|
|
|
|
0
|
0, # Top left square |
|
1471
|
|
|
|
|
|
|
$offset, # Top right square |
|
1472
|
|
|
|
|
|
|
$inset * $cols + $inset, # Middle square |
|
1473
|
|
|
|
|
|
|
$offset * $cols, # Bottom left square |
|
1474
|
|
|
|
|
|
|
$offset * ($cols + 1), # Bottom right square |
|
1475
|
|
|
|
|
|
|
) |
|
1476
|
|
|
|
|
|
|
}; |
|
1477
|
0
|
|
|
|
|
0
|
my $limit = $osq - 1; |
|
1478
|
0
|
|
|
|
|
0
|
my @colinx = map {$_ * $cols} 0 .. $limit; |
|
|
0
|
|
|
|
|
0
|
|
|
1479
|
0
|
|
|
|
|
0
|
my @sqinx = map {$_ .. $_ + $order - 1} map {$_ * $cols} 0 .. $order - 1; |
|
|
0
|
|
|
|
|
0
|
|
|
|
0
|
|
|
|
|
0
|
|
|
1480
|
0
|
|
|
|
|
0
|
my @sqloc = map {$_ * $order} @sqinx; |
|
|
0
|
|
|
|
|
0
|
|
|
1481
|
0
|
|
|
|
|
0
|
my @sqgened; # 's' sets generated, by origin cell. |
|
1482
|
|
|
|
|
|
|
# Crete the row, column, and square sets. These have the same names |
|
1483
|
|
|
|
|
|
|
# as those created by the corresponding 'sudoku' topology, but with |
|
1484
|
|
|
|
|
|
|
# 'g0' .. 'g4' prepended, representing the five individual |
|
1485
|
|
|
|
|
|
|
# 'standard' sudoku grids. For topology 'quincunx 3', the top left |
|
1486
|
|
|
|
|
|
|
# cell is in sets g0c0,g0r0,g0s0, the top right in g1c8,g1r0,g1s2, |
|
1487
|
|
|
|
|
|
|
# and so on. Because some of the 's' sets are duplicates, the |
|
1488
|
|
|
|
|
|
|
# higher-numbered ones are supressed. In topology 'quincunx 3', set |
|
1489
|
|
|
|
|
|
|
# g0s8 is the same as g2s0, so the latter is supressed. |
|
1490
|
0
|
|
|
|
|
0
|
foreach my $grid (0 .. $#squares) { |
|
1491
|
0
|
|
|
|
|
0
|
my $sqr = $squares[$grid]; |
|
1492
|
0
|
|
|
|
|
0
|
foreach my $inx (0 .. $limit) { |
|
1493
|
0
|
|
|
|
|
0
|
my $offset = $inx * $cols; |
|
1494
|
0
|
|
|
|
|
0
|
my $o1 = $offset + $sqr; |
|
1495
|
0
|
|
|
|
|
0
|
$self->add_set("g${grid}r$inx" => $o1 .. $o1 + $limit); |
|
1496
|
0
|
|
|
|
|
0
|
$self->add_set("g${grid}c$inx" => map {$_ + $inx + $sqr} |
|
|
0
|
|
|
|
|
0
|
|
|
1497
|
|
|
|
|
|
|
@colinx); |
|
1498
|
0
|
|
|
|
|
0
|
$o1 = $sqloc[$inx] + $sqr; |
|
1499
|
|
|
|
|
|
|
$sqgened[$o1]++ |
|
1500
|
0
|
0
|
|
|
|
0
|
or $self->add_set("g${grid}s$inx" => map {$_ + $o1} |
|
|
0
|
|
|
|
|
0
|
|
|
1501
|
|
|
|
|
|
|
@sqinx); |
|
1502
|
|
|
|
|
|
|
} |
|
1503
|
|
|
|
|
|
|
} |
|
1504
|
0
|
|
|
|
|
0
|
return; |
|
1505
|
|
|
|
|
|
|
} |
|
1506
|
|
|
|
|
|
|
|
|
1507
|
|
|
|
|
|
|
sub _set_status_value { |
|
1508
|
21
|
|
|
21
|
|
56
|
my ( $self, $name, $value ) = @_; |
|
1509
|
21
|
50
|
|
|
|
60
|
_looks_like_number ($value) or croak <
|
|
1510
|
|
|
|
|
|
|
Error - Attribute $name must be numeric. |
|
1511
|
|
|
|
|
|
|
eod |
|
1512
|
21
|
50
|
33
|
|
|
126
|
($value < 0 || $value >= @status_values) and croak <
|
|
1513
|
|
|
|
|
|
|
Error - Attribute $name must be greater than or equal to 0 and |
|
1514
|
0
|
|
|
|
|
0
|
less than @{[scalar @status_values]} |
|
1515
|
|
|
|
|
|
|
eod |
|
1516
|
21
|
|
|
|
|
49
|
$self->{status_value} = $value; |
|
1517
|
21
|
|
|
|
|
52
|
$self->{status_text} = $status_values[$value]; |
|
1518
|
21
|
|
|
|
|
51
|
return; |
|
1519
|
|
|
|
|
|
|
} |
|
1520
|
|
|
|
|
|
|
|
|
1521
|
|
|
|
|
|
|
sub _set_sudoku { |
|
1522
|
|
|
|
|
|
|
## my ( $self, $name, $order ) = @_; |
|
1523
|
5
|
|
|
5
|
|
16
|
my ( $self, undef, $order ) = @_; # Name unused |
|
1524
|
5
|
|
|
|
|
25
|
$self->set( brick => [ $order, $order ] ); |
|
1525
|
5
|
|
|
|
|
11
|
return; |
|
1526
|
|
|
|
|
|
|
} |
|
1527
|
|
|
|
|
|
|
|
|
1528
|
|
|
|
|
|
|
sub _set_sudokux { |
|
1529
|
|
|
|
|
|
|
## my ( $self, $name, $order ) = @_; |
|
1530
|
1
|
|
|
1
|
|
3
|
my ( $self, undef, $order ) = @_; # Name unused |
|
1531
|
1
|
|
|
|
|
5
|
$self->set (sudoku => $order); |
|
1532
|
1
|
|
|
|
|
3
|
my $size = $order * $order; |
|
1533
|
1
|
|
|
|
|
3
|
my $size_minus_1 = $size - 1; |
|
1534
|
1
|
|
|
|
|
3
|
my $size_plus_1 = $size + 1; |
|
1535
|
1
|
|
|
|
|
4
|
$self->add_set (d0 => map {$_ * $size_plus_1} 0 .. $size_minus_1); |
|
|
9
|
|
|
|
|
17
|
|
|
1536
|
1
|
|
|
|
|
4
|
$self->add_set (d1 => map {$_ * $size_minus_1} 1 .. $size); |
|
|
9
|
|
|
|
|
15
|
|
|
1537
|
1
|
|
|
|
|
3
|
return; |
|
1538
|
|
|
|
|
|
|
} |
|
1539
|
|
|
|
|
|
|
|
|
1540
|
|
|
|
|
|
|
sub _set_symbols { |
|
1541
|
|
|
|
|
|
|
## my ( $self, $name, $value ) = @_; |
|
1542
|
13
|
|
|
13
|
|
35
|
my ( $self, undef, $value ) = @_; # Name unused |
|
1543
|
13
|
|
|
|
|
140
|
my @lst = split '\s+', $value; |
|
1544
|
13
|
|
|
|
|
23
|
my %hsh; |
|
1545
|
13
|
|
|
|
|
20
|
my $inx = 0; |
|
1546
|
13
|
|
|
|
|
22
|
my $maxlen = 0; |
|
1547
|
13
|
|
|
|
|
30
|
foreach (@lst) { |
|
1548
|
142
|
50
|
|
|
|
208
|
defined $_ or next; |
|
1549
|
142
|
50
|
|
|
|
228
|
m/,/ and croak <
|
|
1550
|
|
|
|
|
|
|
Error - Symbols may not contain commas. |
|
1551
|
|
|
|
|
|
|
eod |
|
1552
|
142
|
50
|
|
|
|
206
|
exists $hsh{$_} and croak <
|
|
1553
|
|
|
|
|
|
|
Error - Symbol '$_' specified more than once. |
|
1554
|
|
|
|
|
|
|
eod |
|
1555
|
142
|
|
|
|
|
253
|
$hsh{$_} = $inx++; |
|
1556
|
142
|
|
|
|
|
241
|
$maxlen = max ($maxlen, length ($_)); |
|
1557
|
|
|
|
|
|
|
} |
|
1558
|
13
|
|
|
|
|
53
|
$self->{symbol_list} = \@lst; |
|
1559
|
13
|
|
|
|
|
46
|
$self->{symbol_hash} = \%hsh; |
|
1560
|
13
|
|
|
|
|
29
|
$self->{symbol_number} = scalar @lst; |
|
1561
|
13
|
|
|
|
|
32
|
$self->{biggest_spec} = $self->{biggest_symbol} = $maxlen; |
|
1562
|
13
|
|
|
|
|
37
|
$self->{allowed_symbols} = {}; |
|
1563
|
13
|
|
|
|
|
34
|
return; |
|
1564
|
|
|
|
|
|
|
} |
|
1565
|
|
|
|
|
|
|
|
|
1566
|
|
|
|
|
|
|
sub _set_topology { |
|
1567
|
|
|
|
|
|
|
## my ( $self, $name, @args ) = @_; |
|
1568
|
11
|
|
|
11
|
|
28
|
my ( $self, undef, @args ) = @_; # Name unused |
|
1569
|
11
|
|
|
|
|
988
|
$self->{cell} = []; # The cells themselves. |
|
1570
|
11
|
|
|
|
|
361
|
$self->{set} = {}; # The sets themselves. |
|
1571
|
11
|
|
|
|
|
24
|
$self->{largest_set} = 0; |
|
1572
|
11
|
|
|
|
|
496
|
$self->{intersection} = {}; |
|
1573
|
11
|
|
|
|
|
23
|
$self->{cells_unused} = 0; |
|
1574
|
11
|
|
|
|
|
18
|
my $cell_inx = 0; |
|
1575
|
11
|
|
|
|
|
32
|
foreach my $cell_def (map {split '\s+', $_} @args) { |
|
|
11
|
|
|
|
|
360
|
|
|
1576
|
938
|
|
|
|
|
1707
|
my $cell = {membership => [], index => $cell_inx}; |
|
1577
|
938
|
|
|
|
|
1084
|
push @{$self->{cell}}, $cell; |
|
|
938
|
|
|
|
|
1310
|
|
|
1578
|
938
|
|
|
|
|
1731
|
foreach my $name (sort grep {$_ ne ''} split ',', $cell_def) { |
|
|
2717
|
|
|
|
|
4775
|
|
|
1579
|
2717
|
|
|
|
|
3009
|
foreach my $other (@{$cell->{membership}}) { |
|
|
2717
|
|
|
|
|
3640
|
|
|
1580
|
2620
|
|
|
|
|
3421
|
my $int = "$other,$name"; |
|
1581
|
2620
|
|
100
|
|
|
7975
|
$self->{intersection}{$int} ||= []; |
|
1582
|
2620
|
|
|
|
|
3243
|
push @{$self->{intersection}{$int}}, $cell_inx; |
|
|
2620
|
|
|
|
|
4354
|
|
|
1583
|
|
|
|
|
|
|
} |
|
1584
|
2717
|
|
|
|
|
2915
|
push @{$cell->{membership}}, $name; |
|
|
2717
|
|
|
|
|
4076
|
|
|
1585
|
2717
|
|
100
|
|
|
5019
|
my $set = $self->{set}{$name} ||= |
|
1586
|
|
|
|
|
|
|
{name => $name, membership => []}; |
|
1587
|
2717
|
|
|
|
|
2870
|
push @{$set->{membership}}, $cell_inx; |
|
|
2717
|
|
|
|
|
5173
|
|
|
1588
|
|
|
|
|
|
|
$self->{largest_set} = max ($self->{largest_set}, |
|
1589
|
2717
|
|
|
|
|
3158
|
scalar @{$set->{membership}}); |
|
|
2717
|
|
|
|
|
5357
|
|
|
1590
|
|
|
|
|
|
|
} |
|
1591
|
938
|
50
|
|
|
|
1206
|
@{$cell->{membership}} or $self->{cells_unused}++; |
|
|
938
|
|
|
|
|
1522
|
|
|
1592
|
938
|
|
|
|
|
1260
|
$cell_inx++; |
|
1593
|
|
|
|
|
|
|
} |
|
1594
|
11
|
|
|
|
|
335
|
delete $self->{backtrack_stack}; # Force setting of new problem. |
|
1595
|
11
|
|
|
|
|
44
|
return; |
|
1596
|
|
|
|
|
|
|
} |
|
1597
|
|
|
|
|
|
|
|
|
1598
|
1
|
|
|
1
|
|
2
|
sub _set_value {$_[0]->{$_[1]} = $_[2]; return;} |
|
|
1
|
|
|
|
|
3
|
|
|
1599
|
|
|
|
|
|
|
|
|
1600
|
|
|
|
|
|
|
=head2 solution |
|
1601
|
|
|
|
|
|
|
|
|
1602
|
|
|
|
|
|
|
$string = $su->solution(); |
|
1603
|
|
|
|
|
|
|
|
|
1604
|
|
|
|
|
|
|
This method returns the next solution to the problem, or undef if there |
|
1605
|
|
|
|
|
|
|
are no further solutions. The solution is a blank-delimited list of the |
|
1606
|
|
|
|
|
|
|
symbols each cell contains, with line breaks as specified by the |
|
1607
|
|
|
|
|
|
|
'columns' attribute. If the problem() method has not been called, |
|
1608
|
|
|
|
|
|
|
an exception is thrown. |
|
1609
|
|
|
|
|
|
|
|
|
1610
|
|
|
|
|
|
|
Status values set: |
|
1611
|
|
|
|
|
|
|
|
|
1612
|
|
|
|
|
|
|
SUDOKU_SUCCESS |
|
1613
|
|
|
|
|
|
|
SUDOKU_NO_SOLUTION |
|
1614
|
|
|
|
|
|
|
SUDOKU_TOO_HARD |
|
1615
|
|
|
|
|
|
|
|
|
1616
|
|
|
|
|
|
|
=cut |
|
1617
|
|
|
|
|
|
|
|
|
1618
|
|
|
|
|
|
|
sub solution { |
|
1619
|
19
|
|
|
19
|
1
|
46
|
my ( $self ) = @_; |
|
1620
|
|
|
|
|
|
|
|
|
1621
|
19
|
50
|
|
|
|
50
|
$self->{backtrack_stack} or croak <
|
|
1622
|
|
|
|
|
|
|
Error - You cannot call the solution() method unless you have specified |
|
1623
|
|
|
|
|
|
|
the problem via the problem() method. |
|
1624
|
|
|
|
|
|
|
eod |
|
1625
|
|
|
|
|
|
|
|
|
1626
|
19
|
50
|
|
|
|
43
|
$self->{debug} and print <
|
|
1627
|
|
|
|
|
|
|
Debug solution - entering method. Stack depth = @{[ |
|
1628
|
0
|
|
|
|
|
0
|
scalar @{$self->{backtrack_stack}}]} |
|
|
0
|
|
|
|
|
0
|
|
|
1629
|
|
|
|
|
|
|
eod |
|
1630
|
|
|
|
|
|
|
|
|
1631
|
19
|
|
|
|
|
43
|
return $self->_constrain (); |
|
1632
|
|
|
|
|
|
|
} |
|
1633
|
|
|
|
|
|
|
|
|
1634
|
|
|
|
|
|
|
=head2 steps |
|
1635
|
|
|
|
|
|
|
|
|
1636
|
|
|
|
|
|
|
$string = $su->steps(); |
|
1637
|
|
|
|
|
|
|
|
|
1638
|
|
|
|
|
|
|
=for comment help syntax-highlighting editor " |
|
1639
|
|
|
|
|
|
|
|
|
1640
|
|
|
|
|
|
|
This method returns the steps taken to solve the problem. If no |
|
1641
|
|
|
|
|
|
|
solution was found, it returns the steps taken to determine this. If |
|
1642
|
|
|
|
|
|
|
called in list context, you get an actual copy of the list. The first |
|
1643
|
|
|
|
|
|
|
element is the name of the constraint applied: |
|
1644
|
|
|
|
|
|
|
|
|
1645
|
|
|
|
|
|
|
F = forced: only one value works in this cell; |
|
1646
|
|
|
|
|
|
|
N = numeration or necessary: this is the only cell |
|
1647
|
|
|
|
|
|
|
that can supply the given value; |
|
1648
|
|
|
|
|
|
|
B = box claim: if a candidate number appears in only |
|
1649
|
|
|
|
|
|
|
one row or column of a given box, it can be |
|
1650
|
|
|
|
|
|
|
eliminated as a candidate in that row or column |
|
1651
|
|
|
|
|
|
|
but outside that box; |
|
1652
|
|
|
|
|
|
|
T = tuple, which is a generalization of the concept |
|
1653
|
|
|
|
|
|
|
pair, triple, and so on. These come in two |
|
1654
|
|
|
|
|
|
|
varieties for a given size of the tuple N: |
|
1655
|
|
|
|
|
|
|
naked: N cells contain among them N values, so |
|
1656
|
|
|
|
|
|
|
no cells outside the tuple can supply those |
|
1657
|
|
|
|
|
|
|
values. |
|
1658
|
|
|
|
|
|
|
hidden: N cells contain N values which do not |
|
1659
|
|
|
|
|
|
|
occur outside those cells, so any other values |
|
1660
|
|
|
|
|
|
|
in the tuple are supressed. |
|
1661
|
|
|
|
|
|
|
? = no constraint: generated in backtrack mode. |
|
1662
|
|
|
|
|
|
|
|
|
1663
|
|
|
|
|
|
|
See C and |
|
1664
|
|
|
|
|
|
|
L for fuller |
|
1665
|
|
|
|
|
|
|
definitions of the constraints and how they are applied. The |
|
1666
|
|
|
|
|
|
|
L section addresses why the former |
|
1667
|
|
|
|
|
|
|
URL is not an actual POD link. |
|
1668
|
|
|
|
|
|
|
|
|
1669
|
|
|
|
|
|
|
The second value is the cell number, as defined by the topology |
|
1670
|
|
|
|
|
|
|
setting. For the 'sudoku' and 'latin' settings, the cells are |
|
1671
|
|
|
|
|
|
|
numbered from zero, row-by-row. If you did your own topology, the |
|
1672
|
|
|
|
|
|
|
first cell you defined is 0, the second is 1, and so on. |
|
1673
|
|
|
|
|
|
|
|
|
1674
|
|
|
|
|
|
|
The third value is the value assigned to the cell. If returned in |
|
1675
|
|
|
|
|
|
|
list context, it is the number assigned to the cell's symbol. If |
|
1676
|
|
|
|
|
|
|
in scalar context, it is the symbol itself. |
|
1677
|
|
|
|
|
|
|
|
|
1678
|
|
|
|
|
|
|
=for comment help syntax-highlighting editor " |
|
1679
|
|
|
|
|
|
|
|
|
1680
|
|
|
|
|
|
|
=cut |
|
1681
|
|
|
|
|
|
|
|
|
1682
|
|
|
|
|
|
|
sub steps { |
|
1683
|
1
|
|
|
1
|
1
|
2
|
my ( $self ) = @_; |
|
1684
|
0
|
|
|
|
|
0
|
return wantarray ? (@{$self->{backtrack_stack}}) : |
|
1685
|
|
|
|
|
|
|
defined wantarray ? |
|
1686
|
1
|
50
|
|
|
|
4
|
$self->_format_constraint (@{$self->{backtrack_stack}}) : |
|
|
1
|
50
|
|
|
|
4
|
|
|
1687
|
|
|
|
|
|
|
undef; |
|
1688
|
|
|
|
|
|
|
} |
|
1689
|
|
|
|
|
|
|
|
|
1690
|
|
|
|
|
|
|
=head2 unload |
|
1691
|
|
|
|
|
|
|
|
|
1692
|
|
|
|
|
|
|
$string = $su->unload(); |
|
1693
|
|
|
|
|
|
|
|
|
1694
|
|
|
|
|
|
|
This method returns either the current puzzle or the current solution, |
|
1695
|
|
|
|
|
|
|
depending on whether the solution() method has been called since the |
|
1696
|
|
|
|
|
|
|
puzzle was loaded. |
|
1697
|
|
|
|
|
|
|
|
|
1698
|
|
|
|
|
|
|
=cut |
|
1699
|
|
|
|
|
|
|
|
|
1700
|
|
|
|
|
|
|
sub unload { |
|
1701
|
2
|
|
|
2
|
1
|
5
|
my ( $self ) = @_; |
|
1702
|
2
|
|
|
|
|
5
|
return $self->_unload () |
|
1703
|
|
|
|
|
|
|
} |
|
1704
|
|
|
|
|
|
|
|
|
1705
|
|
|
|
|
|
|
######################################################################## |
|
1706
|
|
|
|
|
|
|
|
|
1707
|
|
|
|
|
|
|
# Private methods and subroutines. |
|
1708
|
|
|
|
|
|
|
|
|
1709
|
|
|
|
|
|
|
# $status_value = $su->_constrain (); |
|
1710
|
|
|
|
|
|
|
|
|
1711
|
|
|
|
|
|
|
# This method applies all possible constraints to the current |
|
1712
|
|
|
|
|
|
|
# problem, placing them on the backtrack stack. The backtrack |
|
1713
|
|
|
|
|
|
|
# algorithm needs to remove these when backtracking. The return |
|
1714
|
|
|
|
|
|
|
# is false if we ran out of constraints, or true if we found |
|
1715
|
|
|
|
|
|
|
# a constraint that could not be satisfied. |
|
1716
|
|
|
|
|
|
|
|
|
1717
|
|
|
|
|
|
|
my %constraint_method = ( |
|
1718
|
|
|
|
|
|
|
'?' => '_constraint_backtrack', |
|
1719
|
|
|
|
|
|
|
); |
|
1720
|
|
|
|
|
|
|
|
|
1721
|
|
|
|
|
|
|
sub _constrain { |
|
1722
|
19
|
|
|
19
|
|
32
|
my ( $self ) = @_; |
|
1723
|
19
|
|
50
|
|
|
50
|
my $stack = $self->{backtrack_stack} ||= []; # May hit this |
|
1724
|
|
|
|
|
|
|
# when initializing. |
|
1725
|
19
|
|
50
|
|
|
44
|
my $used = $self->{constraints_used} ||= {}; |
|
1726
|
19
|
|
|
|
|
25
|
my $iterations; |
|
1727
|
|
|
|
|
|
|
$iterations = $self->{iteration_limit} |
|
1728
|
19
|
50
|
|
|
|
45
|
if $self->{iteration_limit} > 0; |
|
1729
|
|
|
|
|
|
|
|
|
1730
|
|
|
|
|
|
|
$self->{no_more_solutions} and |
|
1731
|
19
|
50
|
|
|
|
41
|
return $self->_unload (undef, SUDOKU_NO_SOLUTION); |
|
1732
|
|
|
|
|
|
|
|
|
1733
|
19
|
100
|
|
|
|
27
|
@{$self->{backtrack_stack}} and do { |
|
|
19
|
|
|
|
|
37
|
|
|
1734
|
1
|
50
|
|
|
|
4
|
$self->_constraint_remove and |
|
1735
|
|
|
|
|
|
|
return $self->_unload (undef, SUDOKU_NO_SOLUTION); |
|
1736
|
|
|
|
|
|
|
}; |
|
1737
|
|
|
|
|
|
|
|
|
1738
|
18
|
50
|
|
|
|
39
|
$self->{cells_unassigned} or do { |
|
1739
|
0
|
|
|
|
|
0
|
$self->{no_more_solutions} = 1; |
|
1740
|
0
|
|
|
|
|
0
|
return $self->_unload ('', SUDOKU_SUCCESS); |
|
1741
|
|
|
|
|
|
|
}; |
|
1742
|
|
|
|
|
|
|
|
|
1743
|
18
|
|
|
|
|
32
|
my $number_of_cells = @{$self->{cell}}; |
|
|
18
|
|
|
|
|
27
|
|
|
1744
|
|
|
|
|
|
|
|
|
1745
|
|
|
|
|
|
|
constraint_loop: |
|
1746
|
|
|
|
|
|
|
{ # Begin outer constraint loop. |
|
1747
|
|
|
|
|
|
|
|
|
1748
|
18
|
|
|
|
|
25
|
foreach my $constraint (qw{F N B T ?}) { |
|
|
356
|
|
|
|
|
593
|
|
|
1749
|
685
|
50
|
|
|
|
789
|
confess <{cell}} != $number_of_cells; |
|
|
685
|
|
|
|
|
1346
|
|
|
1750
|
|
|
|
|
|
|
Programming error - Before trying $constraint constraint. |
|
1751
|
|
|
|
|
|
|
We started with $number_of_cells cells, but now have @{[ |
|
1752
|
0
|
|
|
|
|
0
|
scalar @{$self->{cell}}]}. |
|
|
0
|
|
|
|
|
0
|
|
|
1753
|
|
|
|
|
|
|
eod |
|
1754
|
685
|
|
33
|
|
|
2138
|
my $method = $constraint_method{$constraint} || |
|
1755
|
|
|
|
|
|
|
"_constraint_$constraint"; |
|
1756
|
685
|
50
|
|
|
|
1714
|
my $rslt = $self->$method () or next; |
|
1757
|
685
|
100
|
|
|
|
1344
|
@$rslt or next; |
|
1758
|
356
|
|
|
|
|
530
|
foreach my $constr (@$rslt) { |
|
1759
|
1149
|
50
|
|
|
|
1647
|
if (ref $constr) { |
|
1760
|
1149
|
|
|
|
|
1529
|
push @$stack, $constr; |
|
1761
|
1149
|
|
|
|
|
1701
|
$used->{$constr->[0]}++ |
|
1762
|
|
|
|
|
|
|
} else { |
|
1763
|
0
|
0
|
|
|
|
0
|
my $rslt = $self->_constraint_remove or |
|
1764
|
|
|
|
|
|
|
redo constraint_loop; |
|
1765
|
0
|
|
|
|
|
0
|
return $self->_unload ('', $rslt); |
|
1766
|
|
|
|
|
|
|
} |
|
1767
|
|
|
|
|
|
|
} |
|
1768
|
|
|
|
|
|
|
$self->{cells_unassigned} or |
|
1769
|
356
|
100
|
|
|
|
703
|
return $self->_unload ('', SUDOKU_SUCCESS); |
|
1770
|
338
|
|
|
|
|
700
|
redo constraint_loop; |
|
1771
|
|
|
|
|
|
|
} |
|
1772
|
|
|
|
|
|
|
|
|
1773
|
|
|
|
|
|
|
} # end outer constraint loop. |
|
1774
|
|
|
|
|
|
|
|
|
1775
|
0
|
|
|
|
|
0
|
$self->set (status_value => SUDOKU_TOO_HARD); |
|
1776
|
0
|
|
|
|
|
0
|
return; |
|
1777
|
|
|
|
|
|
|
} |
|
1778
|
|
|
|
|
|
|
|
|
1779
|
|
|
|
|
|
|
# Constraint executors: |
|
1780
|
|
|
|
|
|
|
# These all return a reference to the constraints to be stacked, |
|
1781
|
|
|
|
|
|
|
# provided progress was made. Otherwise they return 0. At the |
|
1782
|
|
|
|
|
|
|
# point a contradiction is found, they push 'backtrack' on the |
|
1783
|
|
|
|
|
|
|
# end of the list to be returned, and return immediately. |
|
1784
|
|
|
|
|
|
|
|
|
1785
|
|
|
|
|
|
|
# F constraint - only one value possible. Unlike the other |
|
1786
|
|
|
|
|
|
|
# constraints, we keep iterating this one until we make no |
|
1787
|
|
|
|
|
|
|
# progress. |
|
1788
|
|
|
|
|
|
|
|
|
1789
|
|
|
|
|
|
|
sub _constraint_F { |
|
1790
|
356
|
|
|
356
|
|
563
|
my ( $self ) = @_; |
|
1791
|
356
|
|
|
|
|
464
|
my @stack; |
|
1792
|
356
|
|
|
|
|
432
|
my $done = 1; |
|
1793
|
|
|
|
|
|
|
|
|
1794
|
356
|
|
|
|
|
641
|
while ($done) { |
|
1795
|
547
|
|
|
|
|
632
|
$done = 0; |
|
1796
|
547
|
|
|
|
|
660
|
my $inx = 0; # Cell index. |
|
1797
|
547
|
|
|
|
|
620
|
foreach my $cell (@{$self->{cell}}) { |
|
|
547
|
|
|
|
|
882
|
|
|
1798
|
65630
|
100
|
|
|
|
94422
|
next if $cell->{content}; # Skip already-assigned cells. |
|
1799
|
30499
|
50
|
|
|
|
32243
|
next unless @{$cell->{membership}}; # Skip unused cells. |
|
|
30499
|
|
|
|
|
45535
|
|
|
1800
|
30499
|
|
|
|
|
33386
|
my $pos = 0; |
|
1801
|
30499
|
100
|
|
|
|
32180
|
foreach (values %{$cell->{possible}}) {$_ or $pos++}; |
|
|
30499
|
|
|
|
|
51903
|
|
|
|
385548
|
|
|
|
|
539419
|
|
|
1802
|
30499
|
100
|
|
|
|
41186
|
if ($pos > 1) { # > 1 possibility. Can't apply. |
|
|
|
50
|
|
|
|
|
|
|
1803
|
|
|
|
|
|
|
} elsif ($pos == 1) { # Exactly 1 possibility. Apply. |
|
1804
|
856
|
|
|
|
|
952
|
my $val; |
|
1805
|
856
|
|
|
|
|
921
|
foreach (keys %{$cell->{possible}}) { |
|
|
856
|
|
|
|
|
2338
|
|
|
1806
|
4582
|
100
|
|
|
|
7032
|
next if $cell->{possible}{$_}; |
|
1807
|
856
|
|
|
|
|
1019
|
$val = $_; |
|
1808
|
856
|
|
|
|
|
945
|
last; |
|
1809
|
|
|
|
|
|
|
} |
|
1810
|
856
|
50
|
|
|
|
1781
|
$self->_try ($cell, $val) and confess <
|
|
1811
|
|
|
|
|
|
|
Programming error - Passed 'F' constraint but _try failed. |
|
1812
|
|
|
|
|
|
|
eod |
|
1813
|
856
|
|
|
|
|
1708
|
my $constraint = [F => [$inx, $val]]; |
|
1814
|
|
|
|
|
|
|
$self->{debug} and |
|
1815
|
856
|
50
|
|
|
|
1455
|
print '# ', $self->_format_constraint ($constraint); |
|
1816
|
856
|
|
|
|
|
986
|
$done++; |
|
1817
|
856
|
|
|
|
|
1284
|
push @stack, $constraint; |
|
1818
|
856
|
100
|
|
|
|
1498
|
$self->{cells_unassigned} or do {$done = 0; last}; |
|
|
18
|
|
|
|
|
22
|
|
|
|
18
|
|
|
|
|
41
|
|
|
1819
|
|
|
|
|
|
|
} else { # No possibilities. Backtrack. |
|
1820
|
0
|
0
|
|
|
|
0
|
$self->{debug} and print <
|
|
1821
|
|
|
|
|
|
|
Debug - Cell $inx has no possible values. Backtracking. |
|
1822
|
|
|
|
|
|
|
eod |
|
1823
|
0
|
0
|
|
|
|
0
|
$self->{debug} > 1 and do { |
|
1824
|
0
|
|
|
|
|
0
|
local $Data::Dumper::Terse = 1; |
|
1825
|
0
|
|
|
|
|
0
|
print Dumper $cell; |
|
1826
|
|
|
|
|
|
|
}; |
|
1827
|
0
|
|
|
|
|
0
|
push @stack, 'backtrack'; |
|
1828
|
0
|
|
|
|
|
0
|
$done = 0; |
|
1829
|
0
|
|
|
|
|
0
|
last; |
|
1830
|
|
|
|
|
|
|
} |
|
1831
|
|
|
|
|
|
|
} continue { |
|
1832
|
65612
|
|
|
|
|
79445
|
$inx++; |
|
1833
|
|
|
|
|
|
|
} |
|
1834
|
|
|
|
|
|
|
} |
|
1835
|
356
|
|
|
|
|
1028
|
return \@stack; |
|
1836
|
|
|
|
|
|
|
} |
|
1837
|
|
|
|
|
|
|
|
|
1838
|
|
|
|
|
|
|
# N constraint - the only cell which supplies a necessary value. |
|
1839
|
|
|
|
|
|
|
|
|
1840
|
|
|
|
|
|
|
sub _constraint_N { |
|
1841
|
293
|
|
|
293
|
|
528
|
my ( $self ) = @_; |
|
1842
|
293
|
|
|
|
|
370
|
while (my ($name, $set) = each %{$self->{set}}) { |
|
|
4062
|
|
|
|
|
9680
|
|
|
1843
|
4032
|
|
|
|
|
4615
|
my @suppliers; |
|
1844
|
4032
|
|
|
|
|
4582
|
foreach my $inx (@{$set->{membership}}) { |
|
|
4032
|
|
|
|
|
6494
|
|
|
1845
|
51063
|
|
|
|
|
60948
|
my $cell = $self->{cell}[$inx]; |
|
1846
|
51063
|
100
|
|
|
|
76886
|
next if $cell->{content}; |
|
1847
|
|
|
|
|
|
|
# No need to check @{$cell->{membership}}, since the cell is |
|
1848
|
|
|
|
|
|
|
# known to be a member of set $name. |
|
1849
|
26004
|
|
|
|
|
27939
|
while (my ($val, $count) = each %{$cell->{possible}}) { |
|
|
375887
|
|
|
|
|
677040
|
|
|
1850
|
349883
|
100
|
|
|
|
516066
|
next if $count; |
|
1851
|
102657
|
|
100
|
|
|
182638
|
$suppliers[$val] ||= []; |
|
1852
|
102657
|
|
|
|
|
105963
|
push @{$suppliers[$val]}, $inx; |
|
|
102657
|
|
|
|
|
171296
|
|
|
1853
|
|
|
|
|
|
|
} |
|
1854
|
|
|
|
|
|
|
} |
|
1855
|
4032
|
|
|
|
|
5120
|
my $limit = @suppliers; |
|
1856
|
4032
|
|
|
|
|
6931
|
for (my $val = 1; $val < $limit; $val++) { |
|
1857
|
43333
|
100
|
100
|
|
|
68853
|
next unless $suppliers[$val] && @{$suppliers[$val]} == 1; |
|
|
25292
|
|
|
|
|
63949
|
|
|
1858
|
263
|
|
|
|
|
409
|
my $inx = $suppliers[$val][0]; |
|
1859
|
263
|
0
|
|
|
|
645
|
$self->_try ($inx, $val) and confess <{debug} ? <
|
|
|
|
50
|
|
|
|
|
|
|
1860
|
|
|
|
|
|
|
Programming error - Cell $inx passed 'N' constraint but try of |
|
1861
|
|
|
|
|
|
|
$self->{symbol_list}[$val] failed. |
|
1862
|
|
|
|
|
|
|
eod |
|
1863
|
0
|
|
|
|
|
0
|
@{[$self->_unload |
|
1864
|
|
|
|
|
|
|
]} set: $name |
|
1865
|
0
|
|
|
|
|
0
|
cell: @{[Dumper ($self->{cell}[$inx])]} |
|
1866
|
|
|
|
|
|
|
eod |
|
1867
|
263
|
|
|
|
|
678
|
my $constraint = [N => [$inx, $val]]; |
|
1868
|
|
|
|
|
|
|
$self->{debug} and |
|
1869
|
263
|
50
|
|
|
|
519
|
print '# ', $self->_format_constraint ($constraint); |
|
1870
|
263
|
|
|
|
|
292
|
keys %{$self->{set}}; # Reset iterator. |
|
|
263
|
|
|
|
|
379
|
|
|
1871
|
263
|
|
|
|
|
1067
|
return [$constraint]; |
|
1872
|
|
|
|
|
|
|
} |
|
1873
|
|
|
|
|
|
|
} |
|
1874
|
30
|
|
|
|
|
111
|
return []; |
|
1875
|
|
|
|
|
|
|
} |
|
1876
|
|
|
|
|
|
|
|
|
1877
|
|
|
|
|
|
|
# B constraint - "box claim". Given two sets whose intersection |
|
1878
|
|
|
|
|
|
|
# contains more than one cell, if all cells which can contribute |
|
1879
|
|
|
|
|
|
|
# a given value to one set are in the intersection, no cell in |
|
1880
|
|
|
|
|
|
|
# the second set can contribute that value. Note that this |
|
1881
|
|
|
|
|
|
|
# constraint does NOT actually assign a value to a cell, it just |
|
1882
|
|
|
|
|
|
|
# eliminates possible values. The name is because on the |
|
1883
|
|
|
|
|
|
|
# "standard" sudoku layout one of the sets is always a box; the |
|
1884
|
|
|
|
|
|
|
# other can be a row or a column. |
|
1885
|
|
|
|
|
|
|
|
|
1886
|
|
|
|
|
|
|
sub _constraint_B { |
|
1887
|
30
|
|
|
30
|
|
62
|
my ( $self ) = @_; |
|
1888
|
30
|
|
|
|
|
42
|
my $done = 0; |
|
1889
|
30
|
|
|
|
|
64
|
while (my ($int, $cells) = each %{$self->{intersection}}) { |
|
|
5002
|
|
|
|
|
11530
|
|
|
1890
|
4996
|
100
|
|
|
|
8524
|
next unless @$cells > 1; |
|
1891
|
1680
|
|
|
|
|
1930
|
my @int_supplies; # Values supplied by the intersection |
|
1892
|
|
|
|
|
|
|
my %int_cells; # Cells in the intersection |
|
1893
|
1680
|
|
|
|
|
2549
|
foreach my $inx (@$cells) { |
|
1894
|
6404
|
100
|
|
|
|
11312
|
next if $self->{cell}[$inx]{content}; |
|
1895
|
|
|
|
|
|
|
# No need to check @{$cell->{membership}}, since the cell is |
|
1896
|
|
|
|
|
|
|
# known to be a member of at least two sets. |
|
1897
|
3548
|
|
|
|
|
6230
|
$int_cells{$inx} = 1; |
|
1898
|
3548
|
|
|
|
|
3937
|
while (my ($val, $imposs) = each %{ |
|
1899
|
57367
|
|
|
|
|
113042
|
$self->{cell}[$inx]{possible}}) { |
|
1900
|
53819
|
100
|
|
|
|
85566
|
$int_supplies[$val] = 1 unless $imposs; |
|
1901
|
|
|
|
|
|
|
} |
|
1902
|
|
|
|
|
|
|
} |
|
1903
|
1680
|
|
|
|
|
2034
|
my %ext_supplies; # Intersection values also supplied outside. |
|
1904
|
|
|
|
|
|
|
my %ext_cells; # Cells not in the intersection. |
|
1905
|
1680
|
|
|
|
|
3756
|
my @set_names = split ',', $int; |
|
1906
|
1680
|
|
|
|
|
2248
|
foreach my $set (@set_names) { |
|
1907
|
3360
|
|
|
|
|
5736
|
$ext_supplies{$set} = []; |
|
1908
|
3360
|
|
|
|
|
4750
|
$ext_cells{$set} = []; |
|
1909
|
3360
|
|
|
|
|
3875
|
foreach my $inx (@{$self->{set}{$set}{membership}}) { |
|
|
3360
|
|
|
|
|
6211
|
|
|
1910
|
49362
|
100
|
|
|
|
72547
|
next if $int_cells{$inx}; # Skip cells in intersection. |
|
1911
|
42266
|
100
|
|
|
|
70518
|
next if $self->{cell}[$inx]{content}; |
|
1912
|
20236
|
|
|
|
|
21806
|
push @{$ext_cells{$set}}, $inx; |
|
|
20236
|
|
|
|
|
33960
|
|
|
1913
|
20236
|
|
|
|
|
23596
|
while (my ($val, $imposs) = each %{ |
|
1914
|
332372
|
|
|
|
|
644509
|
$self->{cell}[$inx]{possible}}) { |
|
1915
|
312136
|
100
|
100
|
|
|
564387
|
$ext_supplies{$set}[$val] = 1 |
|
1916
|
|
|
|
|
|
|
if !$imposs && $int_supplies[$val]; |
|
1917
|
|
|
|
|
|
|
} |
|
1918
|
|
|
|
|
|
|
} |
|
1919
|
|
|
|
|
|
|
} |
|
1920
|
1680
|
|
|
|
|
4125
|
for (my $val = 1; $val < @int_supplies; $val++) { |
|
1921
|
19614
|
100
|
|
|
|
32212
|
next unless $int_supplies[$val]; |
|
1922
|
8261
|
|
|
|
|
9998
|
my @occurs_in = grep {$ext_supplies{$_}[$val]} @set_names; |
|
|
16522
|
|
|
|
|
26924
|
|
|
1923
|
8261
|
100
|
100
|
|
|
31253
|
next unless @occurs_in && @occurs_in < @set_names; |
|
1924
|
24
|
|
|
|
|
42
|
my %cells_claimed; |
|
1925
|
24
|
|
|
|
|
63
|
foreach my $set (@occurs_in) { |
|
1926
|
24
|
|
|
|
|
29
|
foreach my $inx (@{$ext_cells{$set}}) { |
|
|
24
|
|
|
|
|
57
|
|
|
1927
|
127
|
100
|
|
|
|
243
|
next if $self->{cell}[$inx]{possible}{$val}; |
|
1928
|
60
|
|
|
|
|
103
|
$cells_claimed{$inx} = 1; |
|
1929
|
60
|
|
|
|
|
79
|
$self->{cell}[$inx]{possible}{$val} = 1; |
|
1930
|
60
|
|
|
|
|
83
|
$done++; |
|
1931
|
|
|
|
|
|
|
} |
|
1932
|
|
|
|
|
|
|
} |
|
1933
|
24
|
50
|
|
|
|
89
|
next unless $done; |
|
1934
|
24
|
|
|
|
|
256
|
my $constraint = [B => [[sort keys %cells_claimed], $val]]; |
|
1935
|
|
|
|
|
|
|
$self->{debug} and |
|
1936
|
24
|
50
|
|
|
|
95
|
print '# ', $self->_format_constraint ($constraint); |
|
1937
|
24
|
|
|
|
|
42
|
keys %{$self->{intersection}}; # Reset iterator. |
|
|
24
|
|
|
|
|
52
|
|
|
1938
|
24
|
|
|
|
|
166
|
return [$constraint]; |
|
1939
|
|
|
|
|
|
|
} |
|
1940
|
|
|
|
|
|
|
} |
|
1941
|
6
|
|
|
|
|
36
|
return [] |
|
1942
|
|
|
|
|
|
|
} |
|
1943
|
|
|
|
|
|
|
|
|
1944
|
|
|
|
|
|
|
# T constraint - "tuple" (double, triple, quad). These come in |
|
1945
|
|
|
|
|
|
|
# two flavors, "naked" and "hidden". Considering only pairs for |
|
1946
|
|
|
|
|
|
|
# the moment: |
|
1947
|
|
|
|
|
|
|
# A "naked pair" is two cells in the same set which contain the same |
|
1948
|
|
|
|
|
|
|
# pair of possibilities, and only those possibilities. These |
|
1949
|
|
|
|
|
|
|
# possibilities are then excluded from other cells in the set. |
|
1950
|
|
|
|
|
|
|
# A "hidden pair" is when there is a pair of values which can only |
|
1951
|
|
|
|
|
|
|
# be contributed to the set by one or the other of a pair of |
|
1952
|
|
|
|
|
|
|
# cells. These cells then must supply these values, and any other |
|
1953
|
|
|
|
|
|
|
# values supplied by cells in the pair can be eliminated. |
|
1954
|
|
|
|
|
|
|
# For higher groups (triples, quads ...) the rules generalize, except |
|
1955
|
|
|
|
|
|
|
# that all of the candidate values need not be present in all of |
|
1956
|
|
|
|
|
|
|
# the cells under consideration; it is only necessary that none |
|
1957
|
|
|
|
|
|
|
# of the candidate values appears outside the cells under |
|
1958
|
|
|
|
|
|
|
# consideration. |
|
1959
|
|
|
|
|
|
|
# |
|
1960
|
|
|
|
|
|
|
# Glenn Fowler of AT&T (http://www.research.att.com/~gsf/sudoku/) |
|
1961
|
|
|
|
|
|
|
# lumps all these together. But he refers to Angus Johnson |
|
1962
|
|
|
|
|
|
|
# (http://www.angusj.com/sudoku/hints.php) for the details, and |
|
1963
|
|
|
|
|
|
|
# Angus separates naked and hidden tuples. |
|
1964
|
|
|
|
|
|
|
|
|
1965
|
|
|
|
|
|
|
sub _constraint_T { |
|
1966
|
6
|
|
|
6
|
|
15
|
my ( $self ) = @_; |
|
1967
|
6
|
|
|
|
|
21
|
my @tuple; # Tuple indices |
|
1968
|
|
|
|
|
|
|
my %vacant; # Empty cells by set. $vacant{$set} = [$cell ...] |
|
1969
|
6
|
|
|
|
|
0
|
my %contributors; # Number of cells which can contrib value, by set. |
|
1970
|
6
|
|
|
|
|
11
|
my $syms = @{$self->{symbol_list}}; |
|
|
6
|
|
|
|
|
16
|
|
|
1971
|
|
|
|
|
|
|
|
|
1972
|
6
|
|
|
|
|
11
|
while (my ($name, $set) = each %{$self->{set}}) { |
|
|
233
|
|
|
|
|
603
|
|
|
1973
|
3051
|
|
|
|
|
4607
|
my @open = grep {!$_->{content}} |
|
1974
|
227
|
100
|
|
|
|
257
|
map {$self->{cell}[$_]} @{$set->{membership}} |
|
|
3051
|
|
|
|
|
3773
|
|
|
|
227
|
|
|
|
|
335
|
|
|
1975
|
|
|
|
|
|
|
or next; |
|
1976
|
|
|
|
|
|
|
# No need to check @{$_->{membership}} in the grep, since cell |
|
1977
|
|
|
|
|
|
|
# $_ is known to be a member of set $name. |
|
1978
|
214
|
|
|
|
|
392
|
foreach my $cell (@open) { |
|
1979
|
1614
|
|
|
|
|
2382
|
for (my $val = 1; $val < $syms; $val++) { |
|
1980
|
23556
|
100
|
|
|
|
40523
|
$cell->{possible}{$val} and next; |
|
1981
|
6344
|
|
100
|
|
|
9043
|
$contributors{$name} ||= []; |
|
1982
|
6344
|
|
|
|
|
9555
|
$contributors{$name}[$val]++; |
|
1983
|
|
|
|
|
|
|
} |
|
1984
|
|
|
|
|
|
|
} |
|
1985
|
214
|
100
|
|
|
|
260
|
@{$contributors{$name}} = map {$_ || 0} @{$contributors{$name}}; |
|
|
214
|
|
|
|
|
398
|
|
|
|
2912
|
|
|
|
|
4879
|
|
|
|
214
|
|
|
|
|
340
|
|
|
1986
|
214
|
|
|
|
|
400
|
$vacant{$name} = \@open; |
|
1987
|
214
|
|
100
|
|
|
534
|
$tuple[scalar @open] ||= [map {[$_]} 0 .. $#open]; |
|
|
332
|
|
|
|
|
632
|
|
|
1988
|
|
|
|
|
|
|
} |
|
1989
|
|
|
|
|
|
|
|
|
1990
|
6
|
|
|
|
|
29
|
for (my $order = 2; $order <= $self->{max_tuple}; $order++) { |
|
1991
|
6
|
|
|
|
|
22
|
for (my $inx = 1; $inx < @tuple; $inx++) { |
|
1992
|
61
|
100
|
|
|
|
111
|
next unless $tuple[$inx]; |
|
1993
|
45
|
|
|
|
|
61
|
my $max = $inx - 1; |
|
1994
|
287
|
|
|
|
|
393
|
$tuple[$inx] = [map {my @tpl = @$_; |
|
1995
|
287
|
|
|
|
|
386
|
map {[@tpl, $_]} $tpl[-1] + 1 .. $max} |
|
|
1327
|
|
|
|
|
2252
|
|
|
1996
|
45
|
|
|
|
|
55
|
grep {$_->[-1] < $max} @{$tuple[$inx]}]; |
|
|
332
|
|
|
|
|
449
|
|
|
|
45
|
|
|
|
|
78
|
|
|
1997
|
45
|
50
|
|
|
|
122
|
$tuple[$inx] = undef unless @{$tuple[$inx]}; |
|
|
45
|
|
|
|
|
122
|
|
|
1998
|
|
|
|
|
|
|
} |
|
1999
|
|
|
|
|
|
|
|
|
2000
|
|
|
|
|
|
|
# Okay, I have generated the blasted tuples. Now I need to take |
|
2001
|
|
|
|
|
|
|
# the union of all values provided by the tuple of cells. If the |
|
2002
|
|
|
|
|
|
|
# number of values in this union is equal to the current order, I |
|
2003
|
|
|
|
|
|
|
# have potentially found a naked tuple, and if this lets me |
|
2004
|
|
|
|
|
|
|
# eliminate any values outside the tuple I can apply the |
|
2005
|
|
|
|
|
|
|
# constraint. If the number of values inside the union is greater |
|
2006
|
|
|
|
|
|
|
# than the current order, I need to consider whether any tuple of |
|
2007
|
|
|
|
|
|
|
# supplied values is not represented outside the cell tuple; if |
|
2008
|
|
|
|
|
|
|
# so, I have a hidden tuple and can eliminate the superfluous |
|
2009
|
|
|
|
|
|
|
# values. |
|
2010
|
|
|
|
|
|
|
|
|
2011
|
6
|
|
|
|
|
49
|
foreach my $name (keys %vacant) { |
|
2012
|
49
|
|
|
|
|
72
|
my $open = $vacant{$name}; |
|
2013
|
49
|
50
|
|
|
|
91
|
next unless $tuple[@$open]; |
|
2014
|
49
|
|
|
|
|
60
|
my $contributed = $contributors{$name}; |
|
2015
|
49
|
|
|
|
|
57
|
foreach my $tuple (@{$tuple[@$open]}) { |
|
|
49
|
|
|
|
|
84
|
|
|
2016
|
998
|
|
|
|
|
1117
|
my @tcontr; # number of times each value |
|
2017
|
|
|
|
|
|
|
# contributed by the tuple. |
|
2018
|
998
|
|
|
|
|
1305
|
foreach my $inx (@$tuple) { |
|
2019
|
1996
|
|
|
|
|
2404
|
my $cell = $open->[$inx]; |
|
2020
|
1996
|
|
|
|
|
2969
|
for (my $val = 1; $val < $syms; $val++) { |
|
2021
|
29990
|
100
|
|
|
|
50683
|
next if $cell->{possible}{$val}; |
|
2022
|
7803
|
|
|
|
|
11792
|
$tcontr[$val]++; |
|
2023
|
|
|
|
|
|
|
} |
|
2024
|
|
|
|
|
|
|
} |
|
2025
|
998
|
100
|
|
|
|
1335
|
@tcontr = map {$_ || 0} @tcontr; |
|
|
14866
|
|
|
|
|
25182
|
|
|
2026
|
|
|
|
|
|
|
|
|
2027
|
|
|
|
|
|
|
# At this point, @tcontr contains how many cells in the tuple |
|
2028
|
|
|
|
|
|
|
# contribute each value. Calculate the number of discrete values |
|
2029
|
|
|
|
|
|
|
# the tuple can contribute. |
|
2030
|
|
|
|
|
|
|
|
|
2031
|
|
|
|
|
|
|
# If the number of discrete values contributed by the tuple is |
|
2032
|
|
|
|
|
|
|
# equal to the current order, we have a naked tuple. We have an |
|
2033
|
|
|
|
|
|
|
# "effective" naked tuple if at least one of the values |
|
2034
|
|
|
|
|
|
|
# contributed by the tuple occurs outside the tuple. We can |
|
2035
|
|
|
|
|
|
|
# determine this by subtracting the values in @tcontr from the |
|
2036
|
|
|
|
|
|
|
# corresponding values in @$contributed; if we get a positive |
|
2037
|
|
|
|
|
|
|
# result for any cell, we have an "effective" naked tuple. |
|
2038
|
|
|
|
|
|
|
|
|
2039
|
998
|
|
|
|
|
1387
|
my $discrete = grep {$_} @tcontr; |
|
|
14866
|
|
|
|
|
16395
|
|
|
2040
|
998
|
|
|
|
|
1171
|
my $constraint; |
|
2041
|
|
|
|
|
|
|
my @tuple_member; |
|
2042
|
998
|
100
|
|
|
|
1747
|
if ($discrete == $order) { |
|
|
|
50
|
|
|
|
|
|
|
2043
|
11
|
|
|
|
|
24
|
for (my $val = 1; $val < @tcontr; $val++) { |
|
2044
|
70
|
100
|
100
|
|
|
155
|
next unless $tcontr[$val] && |
|
2045
|
|
|
|
|
|
|
$contributed->[$val] > $tcontr[$val]; |
|
2046
|
|
|
|
|
|
|
|
|
2047
|
|
|
|
|
|
|
# At this point we know we have an "effective" naked tuple. |
|
2048
|
|
|
|
|
|
|
|
|
2049
|
4
|
|
100
|
|
|
16
|
$constraint ||= ['T', 'naked', $order]; |
|
2050
|
4
|
100
|
|
|
|
9
|
@tuple_member or map {$tuple_member[$_] = 1} @$tuple; |
|
|
4
|
|
|
|
|
9
|
|
|
2051
|
4
|
|
|
|
|
7
|
my @ccl; |
|
2052
|
4
|
|
|
|
|
11
|
for (my $inx = 0; $inx < @$open; $inx++) { |
|
2053
|
|
|
|
|
|
|
next if $tuple_member[$inx] || |
|
2054
|
20
|
100
|
100
|
|
|
52
|
$open->[$inx]{possible}{$val}; |
|
2055
|
10
|
|
|
|
|
14
|
$open->[$inx]{possible}{$val} = 1; |
|
2056
|
10
|
|
|
|
|
11
|
--$contributed->[$val]; |
|
2057
|
10
|
|
|
|
|
25
|
push @ccl, $open->[$inx]{index}; |
|
2058
|
|
|
|
|
|
|
} |
|
2059
|
4
|
50
|
|
|
|
37
|
push @$constraint, [\@ccl, $val] if @ccl; |
|
2060
|
|
|
|
|
|
|
} |
|
2061
|
|
|
|
|
|
|
|
|
2062
|
|
|
|
|
|
|
# If the number of discrete values is greater than the current |
|
2063
|
|
|
|
|
|
|
# order, we may have a hidden tuple. The test for an "effective" |
|
2064
|
|
|
|
|
|
|
# hidden tuple involves massaging @tcontr against @$contributed in |
|
2065
|
|
|
|
|
|
|
# some way to find a tuple of values within the tuple of cells |
|
2066
|
|
|
|
|
|
|
# which do not occur outside it. |
|
2067
|
|
|
|
|
|
|
|
|
2068
|
|
|
|
|
|
|
} elsif ($discrete > $order) { |
|
2069
|
987
|
|
|
|
|
1070
|
my $within = 0; # Number of values occuring only |
|
2070
|
|
|
|
|
|
|
# within tuple. |
|
2071
|
987
|
|
|
|
|
1562
|
for (my $val = 1; $val < @tcontr; $val++) { |
|
2072
|
13798
|
100
|
100
|
|
|
28806
|
$within++ if $tcontr[$val] && |
|
2073
|
|
|
|
|
|
|
$contributed->[$val] == $tcontr[$val]; |
|
2074
|
|
|
|
|
|
|
} |
|
2075
|
987
|
100
|
|
|
|
1995
|
next unless $within >= $order; |
|
2076
|
4
|
|
|
|
|
18
|
$constraint = ['T', 'hidden', $order]; |
|
2077
|
4
|
|
|
|
|
8
|
map {$tuple_member[$_] = 1} @$tuple; |
|
|
8
|
|
|
|
|
13
|
|
|
2078
|
4
|
|
|
|
|
15
|
for (my $val = 1; $val < @tcontr; $val++) { |
|
2079
|
51
|
100
|
100
|
|
|
124
|
next unless $tcontr[$val] && |
|
2080
|
|
|
|
|
|
|
$contributed->[$val] > $tcontr[$val]; |
|
2081
|
11
|
|
|
|
|
18
|
my @ccl; |
|
2082
|
11
|
|
|
|
|
30
|
for (my $inx = 0; $inx < @$open; $inx++) { |
|
2083
|
|
|
|
|
|
|
next unless $tuple_member[$inx] |
|
2084
|
84
|
100
|
100
|
|
|
187
|
&& !$open->[$inx]{possible}{$val} |
|
2085
|
|
|
|
|
|
|
; |
|
2086
|
12
|
|
|
|
|
19
|
$open->[$inx]{possible}{$val} = 1; |
|
2087
|
12
|
|
|
|
|
19
|
--$contributed->[$val]; |
|
2088
|
12
|
|
|
|
|
42
|
--$tcontr[$val]; |
|
2089
|
12
|
|
|
|
|
34
|
push @ccl, $open->[$inx]{index}; |
|
2090
|
|
|
|
|
|
|
} |
|
2091
|
|
|
|
|
|
|
|
|
2092
|
11
|
50
|
|
|
|
55
|
push @$constraint, [\@ccl, $val] if @ccl; |
|
2093
|
|
|
|
|
|
|
} |
|
2094
|
|
|
|
|
|
|
} |
|
2095
|
|
|
|
|
|
|
|
|
2096
|
15
|
100
|
|
|
|
34
|
next unless $constraint; |
|
2097
|
|
|
|
|
|
|
$self->{debug} and |
|
2098
|
6
|
50
|
|
|
|
20
|
print '# ', $self->_format_constraint ($constraint); |
|
2099
|
6
|
|
|
|
|
288
|
return [$constraint]; |
|
2100
|
|
|
|
|
|
|
} # Next tuple |
|
2101
|
|
|
|
|
|
|
} # Next set containing vacant cells |
|
2102
|
|
|
|
|
|
|
} # Next order |
|
2103
|
|
|
|
|
|
|
|
|
2104
|
0
|
|
|
|
|
0
|
return []; |
|
2105
|
|
|
|
|
|
|
} |
|
2106
|
|
|
|
|
|
|
|
|
2107
|
|
|
|
|
|
|
# ? constraint - initiate backtracking. |
|
2108
|
|
|
|
|
|
|
|
|
2109
|
|
|
|
|
|
|
sub _constraint_backtrack { |
|
2110
|
0
|
|
|
0
|
|
0
|
my ( $self ) = @_; |
|
2111
|
|
|
|
|
|
|
## --$iterations >= 0 or return $self->_unload ('', SUDOKU_TOO_HARD) |
|
2112
|
|
|
|
|
|
|
## if defined $iterations; |
|
2113
|
0
|
|
|
|
|
0
|
my @try; |
|
2114
|
0
|
|
|
|
|
0
|
my $syms = @{$self->{symbol_list}}; |
|
|
0
|
|
|
|
|
0
|
|
|
2115
|
0
|
|
|
|
|
0
|
foreach my $cell (@{$self->{cell}}) { |
|
|
0
|
|
|
|
|
0
|
|
|
2116
|
0
|
0
|
|
|
|
0
|
next if $cell->{content}; |
|
2117
|
0
|
0
|
|
|
|
0
|
next unless @{$cell->{membership}}; |
|
|
0
|
|
|
|
|
0
|
|
|
2118
|
0
|
|
|
|
|
0
|
my $possible = 0; |
|
2119
|
0
|
|
|
|
|
0
|
for (my $val = 1; $val < $syms; $val++) { |
|
2120
|
0
|
0
|
|
|
|
0
|
$possible++ unless $cell->{possible}{$val}; |
|
2121
|
|
|
|
|
|
|
} |
|
2122
|
0
|
0
|
|
|
|
0
|
$possible or return ['backtrack']; |
|
2123
|
0
|
|
|
|
|
0
|
push @try, [$cell, $possible]; |
|
2124
|
|
|
|
|
|
|
} |
|
2125
|
0
|
|
|
|
|
0
|
@try = map {$_->[0]} sort { |
|
2126
|
0
|
0
|
|
|
|
0
|
$a->[1] <=> $b->[1] || $a->[0]{index} <=> $b->[0]{index}} @try; |
|
|
0
|
|
|
|
|
0
|
|
|
2127
|
0
|
|
|
|
|
0
|
my $cell = $try[0]; |
|
2128
|
0
|
|
|
|
|
0
|
for (my $val = 1; $val < $syms; $val++) { |
|
2129
|
0
|
0
|
|
|
|
0
|
next if $cell->{possible}{$val}; |
|
2130
|
0
|
0
|
|
|
|
0
|
$self->_try ($cell, $val) and confess <
|
|
2131
|
|
|
|
|
|
|
Programming error - Value $val illegal in cell $cell->{index} for ? constraint, but |
|
2132
|
|
|
|
|
|
|
\$self->{possible}{$val} = $self->{possible}{$val} |
|
2133
|
|
|
|
|
|
|
eod |
|
2134
|
0
|
|
|
|
|
0
|
my $constraint = ['?' => [$cell->{index}, $val]]; |
|
2135
|
|
|
|
|
|
|
$self->{debug} |
|
2136
|
0
|
0
|
|
|
|
0
|
and print '# ', $self->_format_constraint ($constraint); |
|
2137
|
0
|
|
|
|
|
0
|
return [$constraint]; |
|
2138
|
|
|
|
|
|
|
} |
|
2139
|
0
|
|
|
|
|
0
|
return []; |
|
2140
|
|
|
|
|
|
|
} |
|
2141
|
|
|
|
|
|
|
|
|
2142
|
|
|
|
|
|
|
# $status_value = $su->_constraint_remove (); |
|
2143
|
|
|
|
|
|
|
|
|
2144
|
|
|
|
|
|
|
# This method removes the topmost constraints from the backtrack |
|
2145
|
|
|
|
|
|
|
# stack. It continues until the next item is a backtrack item or |
|
2146
|
|
|
|
|
|
|
# the stack is empty. It returns true (SUDOKU_NO_SOLUTION, |
|
2147
|
|
|
|
|
|
|
# actually) if the stack is emptied, or false (SUDOKU_SUCCESS, |
|
2148
|
|
|
|
|
|
|
# actually) if it stops because it found a backtrack item. |
|
2149
|
|
|
|
|
|
|
|
|
2150
|
|
|
|
|
|
|
# The following arguments may be passed, for use in preparing |
|
2151
|
|
|
|
|
|
|
# a generated problem: |
|
2152
|
|
|
|
|
|
|
# - minimum number of cells to leave occupied (no lower limit |
|
2153
|
|
|
|
|
|
|
# if this is undefined); |
|
2154
|
|
|
|
|
|
|
# - maximum number of cells to leave occupied (no upper limit |
|
2155
|
|
|
|
|
|
|
# if this is undefined); |
|
2156
|
|
|
|
|
|
|
# - a reference to a hash of constraints that it is legal to |
|
2157
|
|
|
|
|
|
|
# remove. The hash value is the number of times it is |
|
2158
|
|
|
|
|
|
|
# legal to remove that constraint, or undef if it can |
|
2159
|
|
|
|
|
|
|
# be removed any number of times. |
|
2160
|
|
|
|
|
|
|
|
|
2161
|
|
|
|
|
|
|
sub _constraint_remove { |
|
2162
|
1
|
|
|
1
|
|
4
|
my ( $self, $min, $max, $removal_ok ) = @_; |
|
2163
|
1
|
50
|
|
|
|
5
|
$min and $min = @{$self->{cell}} - $min; |
|
|
0
|
|
|
|
|
0
|
|
|
2164
|
1
|
50
|
|
|
|
3
|
$max and $max = @{$self->{cell}} - $max; |
|
|
0
|
|
|
|
|
0
|
|
|
2165
|
1
|
50
|
|
|
|
4
|
$self->{no_more_solutions} and return SUDOKU_NO_SOLUTION; |
|
2166
|
1
|
50
|
|
|
|
4
|
my $stack = $self->{backtrack_stack} or return SUDOKU_NO_SOLUTION; |
|
2167
|
1
|
|
50
|
|
|
4
|
my $used = $self->{constraints_used} ||= {}; |
|
2168
|
1
|
|
|
|
|
3
|
my $inx = @$stack; |
|
2169
|
1
|
|
|
|
|
2
|
my $syms = @{$self->{symbol_list}}; |
|
|
1
|
|
|
|
|
3
|
|
|
2170
|
1
|
50
|
33
|
|
|
4
|
($self->{debug} && $inx) and print <
|
|
2171
|
|
|
|
|
|
|
# Debug - Backtracking |
|
2172
|
|
|
|
|
|
|
eod |
|
2173
|
1
|
|
|
|
|
2
|
my $old = $inx; |
|
2174
|
1
|
|
|
|
|
4
|
while (--$inx >= 0) { |
|
2175
|
45
|
50
|
33
|
|
|
76
|
($min && $self->{cells_unassigned} >= $min) and do { |
|
2176
|
0
|
0
|
|
|
|
0
|
$self->{debug} and print <
|
|
2177
|
|
|
|
|
|
|
Debug - Hit minimum occupied cells - returning. |
|
2178
|
|
|
|
|
|
|
eod |
|
2179
|
0
|
|
|
|
|
0
|
return SUDOKU_SUCCESS; |
|
2180
|
|
|
|
|
|
|
}; |
|
2181
|
45
|
|
|
|
|
58
|
my $constraint = $stack->[$inx][0]; |
|
2182
|
45
|
50
|
|
|
|
70
|
if ($removal_ok) { |
|
2183
|
|
|
|
|
|
|
($max && $self->{cells_unassigned} <= $max && |
|
2184
|
|
|
|
|
|
|
## !$removal_ok->{$constraint} and next; |
|
2185
|
0
|
0
|
0
|
|
|
0
|
!exists $removal_ok->{$constraint}) and next; |
|
|
|
|
0
|
|
|
|
|
|
2186
|
|
|
|
|
|
|
|
|
2187
|
0
|
0
|
0
|
|
|
0
|
if (!exists $removal_ok->{$constraint}) { |
|
|
|
0
|
|
|
|
|
|
|
2188
|
0
|
0
|
|
|
|
0
|
$self->{debug} and print <
|
|
2189
|
|
|
|
|
|
|
Debug - Encountered constraint $constraint - returning. |
|
2190
|
|
|
|
|
|
|
eod |
|
2191
|
0
|
|
|
|
|
0
|
return SUDOKU_SUCCESS; |
|
2192
|
|
|
|
|
|
|
} elsif (defined $removal_ok->{$constraint} && |
|
2193
|
|
|
|
|
|
|
--$removal_ok->{$constraint}) { |
|
2194
|
0
|
0
|
|
|
|
0
|
$self->{debug} and print <
|
|
2195
|
|
|
|
|
|
|
Debug - Reached usage limit on $constraint - returning. |
|
2196
|
|
|
|
|
|
|
eod |
|
2197
|
0
|
|
|
|
|
0
|
return SUDOKU_SUCCESS; |
|
2198
|
|
|
|
|
|
|
} |
|
2199
|
|
|
|
|
|
|
} else { |
|
2200
|
45
|
0
|
33
|
|
|
72
|
($max && $self->{cells_unassigned} <= $max && |
|
|
|
|
33
|
|
|
|
|
|
2201
|
|
|
|
|
|
|
$constraint eq '?') |
|
2202
|
|
|
|
|
|
|
and next; |
|
2203
|
|
|
|
|
|
|
} |
|
2204
|
45
|
|
|
|
|
61
|
--$used->{$constraint}; |
|
2205
|
45
|
50
|
66
|
|
|
74
|
if ($constraint eq 'F' || $constraint eq 'N') { |
|
|
|
0
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
2206
|
45
|
|
|
|
|
55
|
foreach my $ref (reverse @{$stack->[$inx]}) { |
|
|
45
|
|
|
|
|
64
|
|
|
2207
|
90
|
100
|
|
|
|
161
|
$self->_try ($ref->[0], 0) if ref $ref; |
|
2208
|
|
|
|
|
|
|
} |
|
2209
|
|
|
|
|
|
|
} elsif ($constraint eq 'B' || $constraint eq 'T') { |
|
2210
|
0
|
|
|
|
|
0
|
foreach my $ref (reverse @{$stack->[$inx]}) { |
|
|
0
|
|
|
|
|
0
|
|
|
2211
|
0
|
0
|
|
|
|
0
|
next unless ref $ref; |
|
2212
|
0
|
|
|
|
|
0
|
my $val = $ref->[1]; |
|
2213
|
0
|
|
|
|
|
0
|
foreach my $inx (@{$ref->[0]}) { |
|
|
0
|
|
|
|
|
0
|
|
|
2214
|
0
|
|
|
|
|
0
|
$self->{cell}[$inx]{possible}{$val} = 0; |
|
2215
|
|
|
|
|
|
|
} |
|
2216
|
|
|
|
|
|
|
} |
|
2217
|
|
|
|
|
|
|
} elsif ($constraint eq '?') { |
|
2218
|
0
|
|
|
|
|
0
|
my $start = $stack->[$inx][1][1] + 1; |
|
2219
|
0
|
|
|
|
|
0
|
my $cell = $self->{cell}[$stack->[$inx][1][0]]; |
|
2220
|
0
|
|
|
|
|
0
|
$self->_try ($cell, 0); |
|
2221
|
0
|
0
|
|
|
|
0
|
next if $removal_ok; |
|
2222
|
0
|
|
|
|
|
0
|
for (my $val = $start; $val < $syms; $val++) { |
|
2223
|
0
|
0
|
|
|
|
0
|
next if $cell->{possible}{$val}; |
|
2224
|
0
|
0
|
|
|
|
0
|
$self->_try ($cell, $val) and confess <
|
|
2225
|
|
|
|
|
|
|
Programming error - Try of $val in cell $cell->{index} failed, but |
|
2226
|
|
|
|
|
|
|
\$cell->{possible}[$inx] = $cell->{possible}[$inx] |
|
2227
|
|
|
|
|
|
|
eod |
|
2228
|
0
|
|
|
|
|
0
|
$used->{$constraint}++; |
|
2229
|
0
|
|
|
|
|
0
|
$stack->[$inx][1][0] = $cell->{index}; |
|
2230
|
0
|
|
|
|
|
0
|
$stack->[$inx][1][1] = $val; |
|
2231
|
0
|
0
|
|
|
|
0
|
$self->{debug} and do { |
|
2232
|
0
|
|
|
|
|
0
|
my $x = $self->_format_constraint ($stack->[$inx]); |
|
2233
|
0
|
|
|
|
|
0
|
chomp $x; |
|
2234
|
0
|
|
|
|
|
0
|
print <
|
|
2235
|
0
|
|
|
|
|
0
|
# Debug - Backtrack complete. @{[$old - @$stack]} constraints removed. |
|
2236
|
0
|
|
|
|
|
0
|
# Resuming puzzle at stack depth @{[$inx + 1]} with |
|
2237
|
|
|
|
|
|
|
# $self->{cells_unassigned} unassigned cells, guessing |
|
2238
|
|
|
|
|
|
|
# $x |
|
2239
|
|
|
|
|
|
|
eod |
|
2240
|
|
|
|
|
|
|
}; |
|
2241
|
0
|
|
|
|
|
0
|
return SUDOKU_SUCCESS; |
|
2242
|
|
|
|
|
|
|
} |
|
2243
|
0
|
|
|
|
|
0
|
} else {confess <
|
|
2244
|
|
|
|
|
|
|
Programming Error - No code provided to remove constraint '$constraint' from stack. |
|
2245
|
|
|
|
|
|
|
eod |
|
2246
|
|
|
|
|
|
|
} |
|
2247
|
45
|
|
|
|
|
87
|
pop @$stack; |
|
2248
|
|
|
|
|
|
|
} |
|
2249
|
1
|
50
|
|
|
|
5
|
$self->{debug} and print <
|
|
2250
|
0
|
|
|
|
|
0
|
# Debug - Backtrack complete. @{[$old - @$stack]} constraints removed. |
|
2251
|
|
|
|
|
|
|
# No more solutions to the puzzle exist. |
|
2252
|
|
|
|
|
|
|
eod |
|
2253
|
1
|
|
|
|
|
2
|
$self->{no_more_solutions} = 1; |
|
2254
|
1
|
|
|
|
|
6
|
return SUDOKU_NO_SOLUTION; |
|
2255
|
|
|
|
|
|
|
} |
|
2256
|
|
|
|
|
|
|
|
|
2257
|
|
|
|
|
|
|
# $self->_deprecation_notice( $name ); |
|
2258
|
|
|
|
|
|
|
# |
|
2259
|
|
|
|
|
|
|
# This method centralizes deprecation. Deprecation is driven of |
|
2260
|
|
|
|
|
|
|
# the %deprecate hash. Level values are: |
|
2261
|
|
|
|
|
|
|
# false - no warning |
|
2262
|
|
|
|
|
|
|
# 1 - warn on first use |
|
2263
|
|
|
|
|
|
|
# 2 - warn on each use |
|
2264
|
|
|
|
|
|
|
# 3 - die on each use. |
|
2265
|
|
|
|
|
|
|
|
|
2266
|
|
|
|
|
|
|
{ |
|
2267
|
|
|
|
|
|
|
|
|
2268
|
|
|
|
|
|
|
my %deprecate = ( |
|
2269
|
|
|
|
|
|
|
brick_third_argument => { |
|
2270
|
|
|
|
|
|
|
message => 'Specifying 3 values for set( brick => ... ) is no longer allowed', |
|
2271
|
|
|
|
|
|
|
level => 3, |
|
2272
|
|
|
|
|
|
|
}, |
|
2273
|
|
|
|
|
|
|
); |
|
2274
|
|
|
|
|
|
|
|
|
2275
|
|
|
|
|
|
|
sub _deprecation_notice { |
|
2276
|
0
|
|
|
0
|
|
0
|
my ( undef, $name ) = @_; # Invocant unused |
|
2277
|
0
|
0
|
|
|
|
0
|
my $info = $deprecate{$name} |
|
2278
|
|
|
|
|
|
|
or return; |
|
2279
|
|
|
|
|
|
|
$info->{level} |
|
2280
|
0
|
0
|
|
|
|
0
|
or return; |
|
2281
|
|
|
|
|
|
|
$info->{level} >= 3 |
|
2282
|
0
|
0
|
|
|
|
0
|
and croak $info->{message}; |
|
2283
|
|
|
|
|
|
|
warnings::enabled( 'deprecated' ) |
|
2284
|
0
|
0
|
|
|
|
0
|
and carp $info->{message}; |
|
2285
|
|
|
|
|
|
|
$info->{level} == 1 |
|
2286
|
0
|
0
|
|
|
|
0
|
and $info->{level} = 0; |
|
2287
|
0
|
|
|
|
|
0
|
return; |
|
2288
|
|
|
|
|
|
|
} |
|
2289
|
|
|
|
|
|
|
|
|
2290
|
|
|
|
|
|
|
} |
|
2291
|
|
|
|
|
|
|
|
|
2292
|
|
|
|
|
|
|
# _format_constraint formats the given constraint for output. |
|
2293
|
|
|
|
|
|
|
|
|
2294
|
|
|
|
|
|
|
sub _format_constraint { |
|
2295
|
1
|
|
|
1
|
|
5
|
my ($self, @args) = @_; |
|
2296
|
1
|
|
|
|
|
2
|
my @steps; |
|
2297
|
1
|
|
|
|
|
3
|
foreach (@args) { |
|
2298
|
52
|
|
|
|
|
58
|
my @stuff; |
|
2299
|
52
|
|
|
|
|
70
|
foreach (@$_) { |
|
2300
|
104
|
50
|
|
|
|
145
|
last unless $_; |
|
2301
|
|
|
|
|
|
|
push @stuff, ref $_ ? |
|
2302
|
|
|
|
|
|
|
'[' . join (' ', |
|
2303
|
0
|
|
|
|
|
0
|
ref $_->[0] ? '[' . join (', ', @{$_->[0]}) . ']' : $_->[0], |
|
2304
|
|
|
|
|
|
|
ref $_->[1] ? '[' . join (', ', |
|
2305
|
0
|
|
|
|
|
0
|
map {$self->{symbol_list}[$_]} @{$_->[1]}) . ']' : |
|
|
0
|
|
|
|
|
0
|
|
|
2306
|
104
|
50
|
|
|
|
237
|
$self->{symbol_list}[$_->[1]], |
|
|
|
50
|
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
2307
|
|
|
|
|
|
|
) . ']' : |
|
2308
|
|
|
|
|
|
|
$_; |
|
2309
|
|
|
|
|
|
|
} |
|
2310
|
52
|
|
|
|
|
126
|
push @steps, join (' ', @stuff) . "\n"; |
|
2311
|
|
|
|
|
|
|
} |
|
2312
|
1
|
|
|
|
|
10
|
return join '', @steps; |
|
2313
|
|
|
|
|
|
|
} |
|
2314
|
|
|
|
|
|
|
|
|
2315
|
|
|
|
|
|
|
# _looks_like_number is cribbed heavily from |
|
2316
|
|
|
|
|
|
|
# Scalar::Util::looks_like_number by Graham Barr. This version |
|
2317
|
|
|
|
|
|
|
# only accepts integers, but it is really here because |
|
2318
|
|
|
|
|
|
|
# ActivePerl's Scalar::Util is too ancient to export |
|
2319
|
|
|
|
|
|
|
# looks_like_number. |
|
2320
|
|
|
|
|
|
|
|
|
2321
|
|
|
|
|
|
|
sub _looks_like_number { |
|
2322
|
44
|
|
|
44
|
|
83
|
( local $_ ) = @_; |
|
2323
|
44
|
50
|
33
|
|
|
160
|
return 0 if !defined ($_) || ref ($_); |
|
2324
|
44
|
50
|
|
|
|
343
|
return 1 if m/^[+-]?\d+$/; |
|
2325
|
0
|
|
|
|
|
0
|
return 0; |
|
2326
|
|
|
|
|
|
|
} |
|
2327
|
|
|
|
|
|
|
|
|
2328
|
|
|
|
|
|
|
# _set_* subroutines are found right after the set() method. |
|
2329
|
|
|
|
|
|
|
|
|
2330
|
|
|
|
|
|
|
# $su->_try ($cell, $value) |
|
2331
|
|
|
|
|
|
|
|
|
2332
|
|
|
|
|
|
|
# This method inserts the given value in the given cell, |
|
2333
|
|
|
|
|
|
|
# replacing the previous value if any, and doing all the |
|
2334
|
|
|
|
|
|
|
# bookkeeping. If the given value is legal (meaning, if |
|
2335
|
|
|
|
|
|
|
# it is zero or if it is unique in all sets the cell |
|
2336
|
|
|
|
|
|
|
# belongs to), it returns 0. If not, it returns 1, but |
|
2337
|
|
|
|
|
|
|
# does not undo the trial. |
|
2338
|
|
|
|
|
|
|
|
|
2339
|
|
|
|
|
|
|
sub _try { |
|
2340
|
2815
|
|
|
2815
|
|
4067
|
my ( $self, $cell, $new ) = @_; |
|
2341
|
2815
|
100
|
|
|
|
4550
|
$cell = $self->{cell}[$cell] unless ref $cell; |
|
2342
|
2815
|
50
|
|
|
|
4328
|
defined $new |
|
2343
|
|
|
|
|
|
|
or _fatal ( |
|
2344
|
|
|
|
|
|
|
"_try called for cell $cell->{index} with new value undefined"); |
|
2345
|
2815
|
50
|
|
|
|
4239
|
defined (my $old = $cell->{content}) or _fatal ( |
|
2346
|
|
|
|
|
|
|
"_try called with old cell $cell->{index} value undefined"); |
|
2347
|
2815
|
|
|
|
|
3241
|
my $rslt = eval { |
|
2348
|
2815
|
100
|
|
|
|
4534
|
return 0 if $old == $new; |
|
2349
|
1644
|
100
|
|
|
|
2449
|
if ($new) { |
|
2350
|
1599
|
|
|
|
|
1788
|
foreach my $set (@{$cell->{membership}}) { |
|
|
1599
|
|
|
|
|
2529
|
|
|
2351
|
4833
|
50
|
|
|
|
9123
|
return 1 if $self->{set}{$set}{content}[$new]; |
|
2352
|
|
|
|
|
|
|
} |
|
2353
|
|
|
|
|
|
|
} |
|
2354
|
1644
|
|
|
|
|
2377
|
$cell->{content} = $new; |
|
2355
|
1644
|
100
|
|
|
|
2291
|
$old and $self->{cells_unassigned}++; |
|
2356
|
1644
|
100
|
|
|
|
2580
|
$new and --$self->{cells_unassigned}; |
|
2357
|
1644
|
|
|
|
|
1765
|
foreach my $name (@{$cell->{membership}}) { |
|
|
1644
|
|
|
|
|
2334
|
|
|
2358
|
4968
|
|
|
|
|
6327
|
my $set = $self->{set}{$name}; |
|
2359
|
4968
|
|
|
|
|
5679
|
--$set->{content}[$old]; |
|
2360
|
4968
|
100
|
|
|
|
6791
|
$old and do { |
|
2361
|
135
|
|
|
|
|
149
|
$set->{free}++; |
|
2362
|
135
|
|
|
|
|
143
|
foreach (@{$set->{membership}}) { |
|
|
135
|
|
|
|
|
173
|
|
|
2363
|
1215
|
|
|
|
|
1567
|
--$self->{cell}[$_]{possible}{$old}; |
|
2364
|
|
|
|
|
|
|
} |
|
2365
|
|
|
|
|
|
|
}; |
|
2366
|
4968
|
|
|
|
|
6663
|
$set->{content}[$new]++; |
|
2367
|
4968
|
100
|
|
|
|
6936
|
$new and do { |
|
2368
|
4833
|
|
|
|
|
5371
|
--$set->{free}; |
|
2369
|
4833
|
|
|
|
|
5255
|
foreach (@{$set->{membership}}) { |
|
|
4833
|
|
|
|
|
6740
|
|
|
2370
|
50421
|
|
|
|
|
69566
|
$self->{cell}[$_]{possible}{$new}++; |
|
2371
|
|
|
|
|
|
|
} |
|
2372
|
|
|
|
|
|
|
}; |
|
2373
|
|
|
|
|
|
|
} |
|
2374
|
1644
|
|
|
|
|
2218
|
return 0; |
|
2375
|
|
|
|
|
|
|
}; |
|
2376
|
2815
|
50
|
|
|
|
4094
|
$@ and _fatal ("Eval failed in _try", $@); |
|
2377
|
2815
|
|
|
|
|
4552
|
return $rslt; |
|
2378
|
|
|
|
|
|
|
} |
|
2379
|
|
|
|
|
|
|
|
|
2380
|
|
|
|
|
|
|
# $string = $self->_unload (prefix, status_value) |
|
2381
|
|
|
|
|
|
|
|
|
2382
|
|
|
|
|
|
|
# This method unloads the current cell contents into a string. |
|
2383
|
|
|
|
|
|
|
# The prefix is prefixed to the string, and defaults to ''. |
|
2384
|
|
|
|
|
|
|
# If status_value is specified, it is set. If status_value is |
|
2385
|
|
|
|
|
|
|
# specified and it is a failure status, undef is returned, and |
|
2386
|
|
|
|
|
|
|
# the current cell contents are ignored. |
|
2387
|
|
|
|
|
|
|
|
|
2388
|
|
|
|
|
|
|
sub _unload { |
|
2389
|
23
|
|
|
23
|
|
58
|
my ($self, $prefix, @args) = @_; |
|
2390
|
23
|
100
|
|
|
|
64
|
defined $prefix or $prefix = ''; |
|
2391
|
23
|
100
|
|
|
|
52
|
@args and do { |
|
2392
|
19
|
|
|
|
|
90
|
$self->set (status_value => $args[0]); |
|
2393
|
19
|
100
|
|
|
|
51
|
$args[0] and return; |
|
2394
|
|
|
|
|
|
|
}; |
|
2395
|
22
|
|
|
|
|
39
|
my $rslt = ''; |
|
2396
|
22
|
|
|
|
|
39
|
my $col = $self->{columns}; |
|
2397
|
22
|
|
33
|
|
|
46
|
my $row = $self->{rows} ||= floor (@{$self->{cell}} / $col); |
|
|
0
|
|
|
|
|
0
|
|
|
2398
|
22
|
|
|
|
|
70
|
my $fmt = "%$self->{biggest_symbol}s"; |
|
2399
|
22
|
|
|
|
|
30
|
foreach (@{$self->{cell}}) { |
|
|
22
|
|
|
|
|
54
|
|
|
2400
|
1894
|
100
|
|
|
|
2614
|
$col == $self->{columns} and $rslt .= $prefix; |
|
2401
|
|
|
|
|
|
|
# was $self->{ignore_unused} |
|
2402
|
|
|
|
|
|
|
$rslt .= ($self->{cells_unused} && !@{$_->{membership}}) ? |
|
2403
|
|
|
|
|
|
|
sprintf ($fmt, ' ') : |
|
2404
|
1894
|
50
|
33
|
|
|
4503
|
sprintf ($fmt, $self->{symbol_list}[$_->{content} || 0]); |
|
|
|
|
100
|
|
|
|
|
|
2405
|
1894
|
100
|
|
|
|
2457
|
if (--$col > 0) { |
|
2406
|
|
|
|
|
|
|
$rslt .= $self->{output_delimiter} |
|
2407
|
1674
|
|
|
|
|
2207
|
} else { |
|
2408
|
|
|
|
|
|
|
# was $self->{ignore_unused} |
|
2409
|
220
|
50
|
|
|
|
344
|
$self->{cells_unused} and $rslt =~ s/\s+$//m; |
|
2410
|
220
|
|
|
|
|
262
|
$rslt .= "\n"; |
|
2411
|
220
|
|
|
|
|
264
|
$col = $self->{columns}; |
|
2412
|
220
|
100
|
|
|
|
355
|
if (--$row <= 0) { |
|
2413
|
29
|
|
|
|
|
38
|
$rslt .= "\n"; |
|
2414
|
29
|
|
|
|
|
52
|
$row = $self->{rows}; |
|
2415
|
|
|
|
|
|
|
} |
|
2416
|
|
|
|
|
|
|
} |
|
2417
|
|
|
|
|
|
|
} |
|
2418
|
22
|
|
|
|
|
86
|
0 while chomp $rslt; |
|
2419
|
22
|
|
|
|
|
30
|
$rslt .= "\n"; |
|
2420
|
22
|
|
|
|
|
257
|
return $rslt; |
|
2421
|
|
|
|
|
|
|
} |
|
2422
|
|
|
|
|
|
|
|
|
2423
|
|
|
|
|
|
|
1; |
|
2424
|
|
|
|
|
|
|
|
|
2425
|
|
|
|
|
|
|
__END__ |