在这里详述 源码库/sudoku.py。
1 # -*- coding: utf8 -*-
2 """
3 MoinMoin - sudoku
4
5 This parser is used to show and resolve sudoku puzzles
6 Improving is encouraged, but please send me a copy
7 Email: znight@tom.com
8
9 @copyright: 2009 by znight@tom.com
10 @license: GNU GPL
11 """
12
13 import random
14 from MoinMoin import wikiutil
15
16 Dependiencies=[]
17 sudoku_id=0
18
19 def sudoku_settings(gen=28):
20 return locals()
21
22 class SudStack:
23 _data=[]
24 _cand=[]
25
26 def __init__(self):
27 self._data=[]
28 self._cand=[]
29
30 def push(self,x,y):
31 self._data.append(x[:])
32 self._cand.append(y[:])
33
34 def pop(self):
35 if len(self._data)>0:
36 return self._data.pop(),self._cand.pop()
37 return [],[]
38 def clear(self):
39 self._data=[]
40 self._cand=[]
41
42 def pos2seq(x,y):
43 return x+y*9
44 def blk2seq(b,s):
45 return (b//3*3+s//3)*9+b%3*3+s%3
46 def seq2pos(p):
47 return p%9,p//9
48 def seq2blk(p):
49 return p%9//3+p//9//3*3,p%9%3+p//9%3*3
50
51 def remove(s,c):
52 r=s
53 s&=~c
54 return 0 if r==s else 1,s
55
56 def count1(s):
57 c=0
58 for i in range (0,9):
59 if s&1: c+=1
60 s>>=1
61 return c
62
63 def subset(s,c):
64 return (s&c)==c
65
66 def find1(s,k):
67 c=0
68 for i in range(0,9):
69 if s&1: c+=1
70 if c>k: break
71 s>>=1
72 return i
73
74 def getcand(dt,cand):
75 for i in range(0,81):
76 if dt[i]!=0:
77 cand[i]=(1<<(dt[i]-1))
78 continue
79 cc,cl=seq2pos(i)
80 cb,bs=seq2blk(i)
81 for j in range(0,9):
82 d=dt[pos2seq(j,cl)]
83 if d!=0: cand[i]&=~(1<<(d-1))
84 d=dt[pos2seq(cc,j)]
85 if d!=0: cand[i]&=~(1<<(d-1))
86 d=dt[blk2seq(cb,j)]
87 if d!=0: cand[i]&=~(1<<(d-1))
88 cx=1
89 while cx>0:
90 cx=0
91 for i in range(0,81):
92 cs=count1(cand[i])
93 if cs<2: continue
94 cc,cl=seq2pos(i)
95 cb,bs=seq2blk(i)
96 k=0
97 for k in range(0,9):
98 if not (cand[i]&(1<<k)): continue;
99 for j in range(0,9):
100 if j!=cc and (cand[pos2seq(j,cl)]&(1<<k)): break
101 else: j=9
102 if j<9:
103 for j in range(0,9):
104 if j!=cl and (cand[pos2seq(cc,j)]&(1<<k)): break
105 else: j=9
106 if j<9:
107 for j in range(0,9):
108 if blk2seq(cb,j)!=i and (cand[blk2seq(cb,j)]&(1<<k)): break
109 else: j=9
110 if j>=9:
111 cand[i]=(1<<k)
112 cx+=1
113 break
114 """some other code will insert here"""
115
116 for i in range(0,81):
117 cc,cl=seq2pos(i)
118 cb,bs=seq2blk(i)
119 c=cand[i]
120 cs=count1(cand[i])
121 if cs==2:
122 for j in range(0,9):
123 if j!=cc and cand[pos2seq(j,cl)]==c: break
124 else: j=9
125 if j<9:
126 for k in range(0,9):
127 if k!=cc and k!=j:
128 xx,cand[pos2seq(k,cl)]=remove(cand[pos2seq(k,cl)],c)
129 cx+=xx
130 for j in range(0,9):
131 if j!=cl and cand[pos2seq(cc,j)]==c: break
132 else: j=9
133 if j<9:
134 for k in range(0,9):
135 if k!=cl and k!=j:
136 xx,cand[pos2seq(cc,k)]=remove(cand[pos2seq(cc,k)],c)
137 cx+=xx
138 for j in range(0,9):
139 if blk2seq(cb,j)!=i and cand[blk2seq(cb,j)]==c: break
140 else: j=9
141 if j<9:
142 for k in range(0,9):
143 if k!=j and k!=bs:
144 xx,cand[blk2seq(cb,k)]=remove(cand[blk2seq(cb,k)],c)
145 cx+=xx
146
147 elif cs==3:
148 j1=-1
149 j2=-1
150 for j in range(0,9):
151 if j!=cc and subset(c,cand[pos2seq(j,cl)]): j1=j; break
152 j+=1
153 while j<9:
154 if j!=cc and subset(c,cand[pos2seq(j,cl)]): j2=j; break
155 j+=1
156 if j1>=0 and j2>=0:
157 for k in range(0,9):
158 if k!=cc and k!=j1 and k!=j2:
159 xx,cand[pos2seq(k,cl)]=remove(cand[pos2seq(k,cl)],c)
160 cx+=xx
161
162 j1=-1
163 j2=-1
164 for j in range(0,9):
165 if j!=cl and subset(c,cand[pos2seq(cc,j)]): j1=j; break
166 j+=1
167 while j<9:
168 if j!=cl and subset(c,cand[pos2seq(cc,j)]): j2=j; break
169 j+=1
170 if j1>=0 and j2>=0:
171 for k in range(0,9):
172 if k!=cl and k!=j1 and k!=j2:
173 xx,cand[pos2seq(cc,k)]=remove(cand[pos2seq(cc,k)],c)
174 cx+=xx
175
176 j1=-1
177 j2=-1
178 for j in range(0,9):
179 if blk2seq(cb,j)!=i and subset(c,cand[blk2seq(cb,j)]): j1=j; break
180 j+=1
181 while j<9:
182 if blk2seq(cb,j)!=i and subset(c,cand[blk2seq(cb,j)]): j2=j; break
183 j+=1
184 if j1>=0 and j2>=0:
185 for k in range(0,9):
186 if k!=bs and k!=j1 and k!=j2:
187 xx,cand[blk2seq(cb,k)]=remove(cand[blk2seq(cb,k)],c)
188 cx+=xx
189
190
191 def check(dt,cd):
192 #print """check""",cd
193 x=0
194 for i in range(0,81):
195 if dt[i]==0:
196 x=1
197 continue
198 cc,cl=seq2pos(i)
199 cb,bs=seq2blk(i)
200 for j in range(0,9):
201 v=pos2seq(j,cl)
202 d=dt[v]
203 if v!=i and d==dt[i]: break
204 v=pos2seq(cc,j)
205 d=dt[v]
206 if v!=i and d==dt[i]: break
207 v=blk2seq(cb,j)
208 d=dt[v]
209 if v!=i and d==dt[i]: break
210 else: j=9
211 if j<9: break
212 else: i=81
213 #print "check result",i>=81,x
214 if i>=81:
215 if x: return 2
216 #print "test ok"
217 return 0
218 return 1
219
220 def resolve(tab,gen=False):
221 ss=SudStack()
222 ss.clear()
223 count_back=0
224 count_step=0
225 count_test=0
226 tcc=0
227 rtab=tab
228 dt=[]
229 cd=[]
230 for i in range(0,81):
231 dt.append(ord(tab[i])-48)
232 cd.append((1<<9)-1)
233 getcand(dt,cd)
234 while 1:
235 cc=1
236 mv=-1
237 mp=-1
238 i=0
239 while cc>0:
240 cc=0
241 mv=-1
242 mp=-1
243 for i in range(0,81):
244 l=count1(cd[i])
245 if l==0: break
246 if l==1 and dt[i]==0:
247 dt[i]=find1(cd[i],0)+1
248 cc+=1
249 elif l>=2 and (mv==-1 or l<mv):
250 mv=l
251 mp=i
252 else: i=81
253 if i<81: break
254 getcand(dt,cd)
255 if i<81 or check(dt,cd)==1:
256 if tcc<=0: count_back+=1
257 dt,cd=ss.pop()
258 if len(dt)==0: break
259 else:
260 count_step+=1
261 if (count_step>10000): """may break"""
262 if mp>=0:
263 cs=count1(cd[mp])
264 if cs>1: count_test+=1
265 sk=0
266 if gen: sk=random.randint(0,cs-1)
267 c=find1(cd[mp],sk)
268 cd[mp]&=~(1<<c)
269 ss.push(dt,cd)
270 dt[mp]=c+1
271 getcand(dt,cd)
272 elif check(dt,cd)==0:
273 tcc+=1
274 #print "ssss...."
275 if tcc<=1:
276 rtab='';
277 for i in range(0,81):
278 """success"""
279 rtab+=chr(dt[i]+48)
280 if gen or tcc>1: break
281 if tcc<=0: count_back+=1
282 dt,cd=ss.pop()
283 if len(dt)==0: break
284 else:
285 if tcc<=0: count_back+=1
286 dt,cd=ss.pop()
287 if len(dt)==0: break
288 if tcc<=0:
289 if count_step>10000: """more than 10000 steps"""
290 else: """cann't resolve"""
291 elif tcc==1: """resolved"""
292 elif tcc>1: """more than one results"""
293 else: """...results"""
294 return tcc,rtab
295
296 def genquest(nf):
297 nd=81
298 tt=[0]*81
299 while nd>nf:
300 xcd='0'*81
301 tcc,rtab=resolve(xcd,True)
302 xcd=rtab
303 for i in range(0,81):
304 tt[i]=i
305 for i in range(0,81-1):
306 v=random.randint(0,80-i)
307 t=tt[v]
308 tt[v]=tt[81-i-1]
309 tt[81-i-1]=t
310 nd=81
311 for i in range(0,81):
312 cc=xcd
313 v=tt[i]
314 cc=xcd[0:v]+'0'+xcd[v+1:]
315 tcc,rtab=resolve(cc)
316 if tcc==1:
317 xcd=xcd[0:v]+'0'+xcd[v+1:]
318 nd-=1
319 if (nd<=nf):
320 break
321 return xcd
322
323 class Parser:
324 extensions='sud'
325 Dependencies=[]
326
327 def __init__(self,raw,request,**kw):
328 self.raw=raw
329 self.request=request
330 args=kw.get('format_args','')
331 try:
332 settings=wikiutil.invoke_extension_function(request,sudoku_settings,args)
333 for key,value in settings.items():
334 setattr(self,key,value)
335 except ValueError,err:
336 msg=u"sudoku: %s"%err.args[0]
337 setattr(self,"gen",0)
338
339 def render(self,formatter):
340
341 global sudoku_id
342 sudoku_id+=1
343 #text=(formatter.preformatted(1))
344 #if not self.raw is str: return 'xxx'
345 text=''
346 k=0
347 tab=''
348
349 self.gen=0
350 if self.gen==0:
351 for i in range(0,9):
352 for j in range(0,9):
353 while k<len(self.raw) and self.raw[k].isspace(): k=k+1
354 c='0'
355 if k<len(self.raw): c=self.raw[k]
356 if c<'1' or c>'9': c='0'
357 tab+=c
358 k+=1
359 else:
360 tab=genquest(self.gen)
361
362 tcc,rtab=resolve(tab)
363 #text+=rtab+"<br>"
364 #text+=tab+"<br>"
365
366 msgshowanswer=self.request.getText("Show Answer");
367 msghideanswer=self.request.getText("Hide Answer");
368
369 text+="""<script langauge=javascript>
370 <!--
371 var rtab"""+str(sudoku_id)+"""='"""+rtab+"""';
372 var tab"""+str(sudoku_id)+"""='"""+tab+"""';
373 var showresolve"""+str(sudoku_id)+"""=false;
374 function show_resolve"""+str(sudoku_id)+"""()
375 {
376 var showmsg;
377 var f=document.getElementById('sudoku"""+str(sudoku_id)+"""')
378 var t=f.getElementsByTagName('td');
379 var i;
380 showresolve"""+str(sudoku_id)+"""=!showresolve"""+str(sudoku_id)+""";
381 if (showresolve"""+str(sudoku_id)+""")
382 showmsg='"""+msghideanswer+"""';
383 else
384 showmsg='"""+msgshowanswer+"""';
385 var sh=document.getElementById('showhide"""+str(sudoku_id)+"""')
386 sh.innerText=showmsg;
387
388 for(i=0;i<81;i++)
389 {
390 var c;
391 if (showresolve"""+str(sudoku_id)+""")
392 c=rtab"""+str(sudoku_id)+""".substr(i,1);
393 else
394 c=tab"""+str(sudoku_id)+""".substr(i,1);
395 if (c=='0') c='<INPUT SIZE=2 AUTOCOMPLETE=off NAME=n"""+str(sudoku_id)+'_'+str(i)+' MAXLENGTH=1 ID=d'+str(sudoku_id)+'_'+str(i)+""" style="border:0pt solid; COLOR: #7777dd; FONT-SIZE: 12pt;TEXT-ALIGN: center;MARGIN: 0pt; WIDTH: 20pt; ">';
396 t[i].innerHTML=c;
397 }
398 }
399 //--></script>"""
400 text+="<a href='javascript:show_resolve"+str(sudoku_id)+"()' id='showhide"+str(sudoku_id)+"'>"+msgshowanswer+"</a>"
401 text+="<table id=sudoku"+str(sudoku_id)+" style=\"border:2pt solid black;\">"
402 for i in range(0,9):
403 text+="<tr>"
404 for j in range(0,9):
405 k=pos2seq(j,i)
406 c=tab[k]
407 if c<'1' or c>'9': c='<INPUT SIZE=2 AUTOCOMPLETE=off NAME=n'+str(sudoku_id)+'_'+str(i*9+j)+' MAXLENGTH=1 ID=d'+str(sudoku_id)+'_'+str(i*9+j)+' style="border:0pt solid; COLOR: #7777dd; FONT-SIZE: 12pt;TEXT-ALIGN: center;vertical-align:middle;MARGIN: 0pt; WIDTH: 20pt;">'
408 text+="<td style=\"width:12px;height:20px;"
409 if (tab[k]==rtab[k]):
410 text+="font-weight:bold;"
411 else:
412 text+="color:gray;"
413 #text+="border-left:1pt solid lightblue;";
414 #text+="border-top:1pt solid light blye;";
415 if j%3==2 and j!=8: text+="border-right: 2pt solid lightblue;"
416 else: text+="border-right:1pt solid lightblue;"
417 if i%3==2 and i!=8: text+="border-bottom: 2pt solid lightblue;"
418 else: text+="border-bottom:1pt solid lightblue;"
419 text+="margin:0pt;padding:0pt;padding-top:1pt;padding-bottom:1pt;width:22pt;height:20pt;\" valign=middle align=center>"+c+"</td>"
420 text+="</tr>"
421 text+="</table>"
422 #self.request.write(formatter.preformatted(0))
423 return text
424
425 def format(self,formatter):
426 self.request.write(self.render(formatter))
427
428 """
429 class x:
430 y=1
431 def write(self,str):
432 print str
433 b=x()
434 a=Parser("xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",b)
435 a.format(b)
436 """