Mercurial > hg > python
comparison nono.py @ 25:0bbeb01a7681
combine from various sources
author | Henry Thompson <ht@markup.co.uk> |
---|---|
date | Sat, 29 May 2021 21:32:41 +0100 |
parents | 6c9e371b0325 |
children | 6543fcbb8abd 68004ce55703 |
comparison
equal
deleted
inserted
replaced
24:6df2f6dcc809 | 25:0bbeb01a7681 |
---|---|
23 class Vector(list): | 23 class Vector(list): |
24 # reads top-to-bottom or left-to-right | 24 # reads top-to-bottom or left-to-right |
25 def __init__(self,n,m,runs): | 25 def __init__(self,n,m,runs): |
26 list.__init__(self,list(range(n))) | 26 list.__init__(self,list(range(n))) |
27 self.n=n | 27 self.n=n |
28 self.runs=runs | 28 self.m=m |
29 self.margin0=0 # a run can start here, so if >0 then self[m0-1].val must be False | |
30 self.marginN=n-1 # a run can end here, so if <n-1 then self[mN+1].val ditto | |
31 self.runs=self.origRuns=runs | |
32 self.initialComplete=[] | |
33 self.finalComplete=[] | |
34 self.resetAllRuns() | |
35 | |
36 def __repr__(self): | |
37 return "V@%s%s:%s"%(self.x,self.origRuns,list.__repr__(self)) | |
38 | |
39 def __str__(self): | |
40 return '%s|'%('|'.join(str(c) for c in self)) | |
41 | |
42 def myPrintSize(self): | |
43 return sum(len(str(run)) for run in self.runs)+len(self.runs)-1 | |
44 | |
45 def resetAllRuns(self): | |
29 # compute the set of all possible layouts for runs | 46 # compute the set of all possible layouts for runs |
30 self.rn=len(self.runs) | 47 self.rn=len(self.runs) |
31 rtot=sum(self.runs) | 48 rtot=sum(self.runs) |
32 self.allRuns=list(self.seedList(0,0,0, | 49 self.allRuns=list(self.seedList(0,0,self.margin0,self.marginN+1, |
33 sum(1+self.runs[k] for k in range(self.rn))-1)) | 50 sum(1+self.runs[k] for k in range(self.rn))-1)) |
34 self.nar=len(self.allRuns) | 51 self.nar=len(self.allRuns) |
35 | 52 |
36 def seedList(self,i,j,pos,runLen): | 53 def seedList(self,i,j,pos0,posN,runLen): |
37 """ | 54 """ |
38 :param i: starting skip before next run | 55 :param i: starting skip before next run |
39 :type i: 0 if pos==0 else 1 | 56 :type i: 0 if pos==0 else 1 |
40 :param j: next run number | 57 :param j: next run number |
41 :type j: int | 58 :type j: int |
42 :param pos: left margin | 59 :param pos0: left margin |
43 :type pos: int | 60 :type pos0: int |
61 :param posN: right margin | |
62 :type posN: int | |
44 """ | 63 """ |
45 bound=self.n-(pos+runLen)+1 | 64 bound=posN-(pos0+runLen)+1 |
46 #dprint('s',i,j,pos,runLen,bound) | 65 dprint('s',i,j,pos0,posN,runLen,bound) |
47 if j==self.rn: | 66 if j==self.rn: |
48 yield [] | 67 yield [] |
49 return | 68 return |
50 r=self.runs[j] | 69 r=self.runs[j] |
51 for v in range(i,bound): | 70 for v in range(i,bound): |
52 for sub in self.seedList(1,j+1,pos+v+r,runLen-(r+1)): | 71 for sub in self.seedList(1,j+1,pos0+v+r,posN,runLen-(r+1)): |
53 yield [-v,r]+sub | 72 yield [-v,r]+sub |
54 | 73 |
55 def __repr__(self): | 74 def step(self,pos): |
56 return "V@%s%s:%s"%(self.x,self.runs,list.__repr__(self)) | 75 if self.runs==[]: |
57 | 76 return 0 |
58 def __str__(self): | |
59 return '%s|'%('|'.join(str(c) for c in self)) | |
60 | |
61 def step(self): | |
62 scratch=[0 if c.val is None else c.val for c in self] | 77 scratch=[0 if c.val is None else c.val for c in self] |
78 wins=0 | |
79 changed=0 | |
63 for k,runs in enumerate(self.allRuns): | 80 for k,runs in enumerate(self.allRuns): |
64 dprint('=====pass %s======'%k) | 81 if (self.m.loop,self.cLet,pos,k)==(1,'R',7,5): |
65 self.onepass(0,self.n,scratch,runs.copy()) | 82 print(self.m) |
66 dprint(scratch) | 83 dprint('=====pass %s.%s.%s.%s======'%(self.m.loop,self.cLet,pos,k)) |
84 if self.onepass(self.margin0,self.marginN+1,scratch,runs.copy()): | |
85 wins+=1 | |
86 dprint(wins, scratch) | |
87 maybeSeq=None | |
88 inSeq=None | |
89 j=None | |
67 for i in range(self.n): | 90 for i in range(self.n): |
68 if scratch[i]==self.nar: | 91 if scratch[i]>=wins: |
69 # If blobby in _every_ pass, then must be a blob | 92 # If blobby in _every_ pass, then must be a blob |
93 if inSeq is None: | |
94 inSeq=i if maybeSeq is None else maybeSeq | |
95 dprint('s1',inSeq) | |
96 maybeSeq=None | |
70 if self[i].val is None: | 97 if self[i].val is None: |
71 self[i].setVal(True) | 98 dprint('s1a',i) |
99 self.newBlob(i,True) # Check cross-vector | |
100 j=i | |
101 changed=1 | |
72 elif self[i].val is True: | 102 elif self[i].val is True: |
73 # already there | |
74 pass | 103 pass |
75 else: | 104 else: |
76 print("Shouldn't happen: attempt to blob where x already present! %s at %s"%(self,i),file=sys.stderr) | 105 eprint("Shouldn't happen: attempt to blob where x already present! %s at %s"%(self,i),err=101) |
77 exit(101) | 106 elif inSeq is not None: |
107 if self[i].val is not True: | |
108 inSeq=None | |
109 maybeSeq=None | |
110 dprint('s2',i-1,j) | |
111 if j is not None: | |
112 # Only if we actually added a blob at some point | |
113 dpush('b_s2') | |
114 self.checkNew(i-1) | |
115 dpop('b_s2') | |
116 elif self[i].val is True and maybeSeq is None: | |
117 dprint('s3',i) | |
118 maybeSeq=i | |
119 dprint('s4',inSeq,i) | |
120 if inSeq is not None: | |
121 dpush('b_s4') | |
122 self.checkNew(i) | |
123 dpop('b_s4') | |
124 return changed | |
78 | 125 |
79 def onepass(self,i0,iBound,scratch,stack): | 126 def onepass(self,i0,iBound,scratch,stack): |
80 """note that stack is not a simple run, but one with _negative_ numbers between | 127 """note that stack is not a simple run, but one with _negative_ numbers between |
81 and possibly before the positive ones, indicating obligatory skips | 128 and possibly before the positive ones, indicating obligatory skips |
82 """ | 129 """ |
83 i=i0 # starting index into self/scratch/maybe | 130 i=i0 # starting index into self/scratch/maybe |
84 j=-1 # index into run | 131 maybe=[0]*self.n |
85 maybe=[0]*iBound | 132 dprint('r: %s'%stack,i0,iBound) |
86 dprint('r: %s'%stack) | |
87 req=sum((-r if r<0 else r) for r in stack) | 133 req=sum((-r if r<0 else r) for r in stack) |
88 while stack and i<iBound: | 134 while stack and i<iBound: |
89 r=rr=stack.pop(0) | 135 r=rr=stack.pop(0) |
90 dprint('pop:',r) | 136 dprint('pop:',r) |
91 if r<1: | 137 if r<1: |
92 # obligatory skip | 138 # obligatory skip |
93 # (Above init of self.allRuns is easier if we allow a 0 to be ignored | 139 # (Above init of self.allRuns is easier if we allow a 0 to be ignored |
140 for k in range(i,i-r): | |
141 if self[k] is True: | |
142 # has to be False or None to skip | |
143 dprint('x0',k) | |
144 return False | |
94 i-=r | 145 i-=r |
95 req+=r | 146 req+=r |
96 r=rr=stack.pop(0) | 147 r=rr=stack.pop(0) |
97 # rr is run remaining -- how many we still need | 148 # rr is run remaining -- how many we still need |
98 j+=1 # index of current run in self.runs, we'll need to decorate that eventually | |
99 inOne=-1 # if non-neg, records the start point of a possible run | |
100 gapsFilled=0 | |
101 # First, check if we can start here: 0 is OK, and n>0 iff n-1 is None or False | 149 # First, check if we can start here: 0 is OK, and n>0 iff n-1 is None or False |
102 if i>0 and i<iBound: | 150 # if i>0 and self[i-1].val is True: |
103 while self[i-1].val: | 151 # dprint('x1',i) |
104 i+=1 | 152 # return |
105 if (iBound-i)<req: | 153 if (iBound-i)<req: |
106 # Can't win, give up altogether | 154 # Can't win, give up altogether |
107 dprint('c0',i,iBound,req) | 155 dprint('x2',i,iBound,req) |
108 return | 156 return False |
109 while i<iBound: | 157 j=i # start of run, if we find it |
158 gapsFilled=0 | |
159 dprint('top:',j) | |
160 while rr>0: | |
161 # guaranteed by check above that we won't run over the far end | |
110 c=self[i].val | 162 c=self[i].val |
111 dprint('top',i,c,inOne,rr) | 163 if c is False: |
164 dprint('x3',i) | |
165 return False | |
112 if c is None: | 166 if c is None: |
113 # we could add a blob here | 167 # we could add a blob here |
114 dprint('c1') | |
115 gapsFilled+=1 | 168 gapsFilled+=1 |
116 rr-=1 | 169 # and a blob already present is OK |
117 if inOne<0: | 170 rr-=1 |
118 dprint('c1a',i) | |
119 # starts here | |
120 inOne=i | |
121 # fall through to check for completion | |
122 else: | |
123 dprint('c2') | |
124 # c is a bool | |
125 if inOne<0: | |
126 dprint('c2a') | |
127 if c: | |
128 dprint('c2a1') | |
129 # a *, we can possible start something here | |
130 inOne=i | |
131 rr-=1 | |
132 # fall through to check for completion | |
133 else: | |
134 dprint('c2a2') | |
135 # an x, can't start here, just move along | |
136 i+=1 | |
137 continue | |
138 else: | |
139 dprint('c2b') | |
140 if c: | |
141 dprint('c2b1') | |
142 # a blob, extend or complete a partial | |
143 rr-=1 | |
144 # fall through to check for completion | |
145 else: | |
146 # abandon a partial | |
147 dprint('c2b2') | |
148 inOne=-1 | |
149 rr=r | |
150 i+=1 | |
151 continue | |
152 if rr>0: | |
153 dprint('c3') | |
154 # we're not done, carry on | |
155 i+=1 | |
156 continue | |
157 # Maybe a win? | |
158 # look ahead, can we stop here? | |
159 # NB _self_.n | |
160 if i+1<self.n and self[i+1].val: | |
161 dprint('c4') | |
162 # Nope | |
163 inOne=-1 | |
164 rr=r | |
165 gapsFilled=0 | |
166 i+=1 | |
167 continue | |
168 elif gapsFilled==0: | |
169 dprint('c5') | |
170 # We must have crossed at least on gap... | |
171 print("Shouldn't happen: no gap! me:%s i:%s j:%s rr:%s inOne:%s"%(self,i, j, rr, inOne),file=sys.stderr) | |
172 exit(100) | |
173 # Victory! | |
174 dprint('c6',r,inOne,i) | |
175 for k in range(inOne,i+1): | |
176 maybe[k]+=1 | |
177 i+=1 | 171 i+=1 |
178 req-=r | 172 # Maybe a win? |
179 break | 173 # look ahead, can we stop here? |
174 if i<self.n and self[i].val is True: | |
175 dprint('x4',i) | |
176 return False | |
177 # elif gapsFilled==0: | |
178 # # We must have crossed at least one gap... | |
179 # print("Shouldn't happen: no gap! me:%s i:%s j:%s rr:%s"%(self,i, j, rr),file=sys.stderr) | |
180 # raise Exception | |
181 # Victory! | |
182 dprint('c6',r,j,i) | |
183 for k in range(j,i): | |
184 maybe[k]+=1 | |
185 req-=r | |
180 # on to the next run | 186 # on to the next run |
181 # end of inner loop, did we win? | 187 # end of inner loop, did we win? |
182 if (not stack) or i==iBound: | 188 if (not stack) or i==iBound: |
183 # yes | 189 # yes |
184 dprint('win:',maybe) | 190 dprint('win:',maybe) |
185 for k in range(iBound): | 191 for k in range(iBound): |
186 scratch[k]+=maybe[k] | 192 scratch[k]+=maybe[k] |
193 return True | |
194 eprint("Shouldn't happen? - fell through",stack,i,iBound,err=102) | |
195 | |
196 def checkX(self,pos,crosspos): | |
197 # New x at pos, are there others that can be xed out now? | |
198 # Overkill as is? | |
199 dprint('cx',self.cLet+str(crosspos),pos) | |
200 if self.runs: | |
201 start0=self.margin0 # 0 if self.margin0==0 else self.margin0+1 | |
202 if self.marginal(range(start0,pos),self.runs[0]): | |
203 dprint('cx1a') | |
204 else: | |
205 dprint('cx1b') | |
206 # if len(self.runs)>1: | |
207 startN=self.marginN # self.marginN if (self.marginN==self.n-1) else self.marginN-1 | |
208 if self.marginal(range(startN,pos,-1),self.runs[-1]): | |
209 dprint('cx2a') | |
210 else: | |
211 dprint('cx2b') | |
212 | |
213 def checkNew(self,pos): | |
214 # New blob at pos, can we complete anything? | |
215 # Assuming @@ FTTB that it's never possible to definitively complete a | |
216 # non-marginal run, which is wrong if the length is unique and it's bounded... | |
217 start0=self.margin0 #0 if self.margin0==0 else self.margin0+1 | |
218 startN=self.marginN # self.marginN if (self.marginN==self.n-1) else self.marginN-1 | |
219 dprint('cn',start0,pos,startN) | |
220 changed=False | |
221 # First, find our boundaries: | |
222 s=pos # start index of our run | |
223 if s>start0: | |
224 while s>start0 and self[s-1].val is True: | |
225 s-=1 | |
226 f=pos # finish index of our run | |
227 if f<startN: | |
228 while f<startN and self[f+1].val is True: | |
229 f+=1 | |
230 l=(f-s)+1 # our length | |
231 dprint('%s:%s,%s,%s,%s:<%s>'%(self.cLet,s,f,l,self.runs,self)) | |
232 c=self.runs.count(l) | |
233 if c==0: | |
234 # not big enough yet | |
235 dprint('x0') | |
236 return | |
237 if self.runs[0]==l: | |
238 # is it safely left marginal, i.e. no blobs or big enough gaps before us? | |
239 if self.marginal(range(start0,s),l): | |
240 changed=True | |
241 dprint('n1') | |
242 # mark our margins | |
243 for i in range(start0,s): # not sure this is still needed since | |
244 # added gap-filling in self.marginal | |
245 if self[i].val is None: | |
246 self.newX(i,False) | |
247 if f<startN: | |
248 if self[f+1].val is None: | |
249 self.newX(f+1,True) | |
250 self.found0(f) # pull in the start margin at least to f+2 | |
251 else: | |
252 dprint('x1a') | |
253 if not changed: | |
254 if self.runs[-1]==l: | |
255 # is it safely _right_ marginal, i.e. no blobs or big enough gaps _after_ us? | |
256 if self.marginal(range(startN,f,-1),l): | |
257 changed=True | |
258 dprint('n2') | |
259 # mark our margins: still needed? see above | |
260 for i in range(startN,f,-1): | |
261 if self[i].val is None: | |
262 self.newX(i,False) | |
263 if s>start0: | |
264 if self[s-1].val is None: | |
265 self.newX(s-1,True) | |
266 self.foundN(s) # pull in the finish margin at least to s-2 | |
267 else: | |
268 dprint('x2a') | |
269 else: | |
270 dprint('x2b') | |
271 if changed: | |
272 self.resetAllRuns() | |
273 | |
274 def marginal(self,rng,l): | |
275 dprint('m%s'%self.cLet,rng.start,rng.stop,rng.step,l) | |
276 g=0 # length of a gap | |
277 for i in rng: | |
278 if self[i].val is True: | |
279 # Shouldn't be possible? | |
280 dprint('mx0') | |
281 return False | |
282 if self[i].val is False: | |
283 if g>0: | |
284 # Block a too-small gap | |
285 dprint('m1',i-g,i) | |
286 for j in range(i-g,i): | |
287 self.newX(j,True) | |
288 g=0 | |
289 else: | |
290 # None | |
291 g+=1 | |
292 if g==l: | |
293 # run could fit here, so no go | |
294 dprint('mx1') | |
295 return False | |
296 if g>0: | |
297 # Block a too-small gap | |
298 if rng.step==1: | |
299 # forward | |
300 dprint('m2f',i-g,i) | |
301 for j in range(i+1-g,i+1): | |
302 self.newX(j,True) | |
303 else: | |
304 # backward | |
305 dprint('m2b',i+g,i) | |
306 for j in range(i+g-1,i-1,-1): | |
307 self.newX(j,True) | |
308 return True | |
309 | |
310 def found0(self,i): | |
311 dprint('found0 called on %s at %s'%(self,i)) | |
312 i=self.margin0 | |
313 while self[i].val is False: | |
314 i+=1 | |
315 if self[i].val is True: | |
316 r=self.runs.pop(0) | |
317 self.initialComplete.append(r) | |
318 self.margin0+=r+1 | |
319 self.updateHeader(r=r,pre=True) | |
320 | |
321 def foundN(self,i): | |
322 dprint('foundN called on %s at %s'%(self,i)) | |
323 i=self.marginN | |
324 while self[i].val is False: | |
325 i-=1 | |
326 if self[i].val is True: | |
327 r=self.runs.pop() | |
328 self.finalComplete=[r]+self.finalComplete | |
329 self.marginN-=r+1 | |
330 self.updateHeader(r=r,pre=False) | |
331 | |
187 | 332 |
188 class Row(Vector): | 333 class Row(Vector): |
189 def __init__(self,n,m,runs,pos,dprintWidth): | 334 cLet='R' |
335 def __init__(self,n,m,runs,pos): | |
190 Vector.__init__(self,n,m,runs) | 336 Vector.__init__(self,n,m,runs) |
191 self.y=pos | 337 self.y=pos |
192 self.dprintWidth=dprintWidth | 338 self.width=self.myPrintSize() |
193 self.fmt="%%%ss|"%dprintWidth | |
194 | 339 |
195 def __str__(self): | 340 def __str__(self): |
196 return ((self.fmt%(' '.join(str(r) for r in self.runs)))+ | 341 return ((self.fmt%(' '.join(str(r) for r in self.runs)))+ |
197 Vector.__str__(self)) | 342 Vector.__str__(self)) |
198 | 343 |
344 def updateHeader(self,*,maxWidth=None,r=None,pre=None): | |
345 if maxWidth is None: | |
346 # update | |
347 spacer=(" " if self.runs else "") | |
348 if pre: | |
349 self.infix+=(RedFmt%r)+spacer | |
350 else: | |
351 # post | |
352 self.suffix=spacer+RedFmt%r+self.suffix | |
353 self.fmt="%s%s%%s%s|"%(self.prespace,self.infix,self.suffix) | |
354 else: | |
355 # init | |
356 self.maxWidth=maxWidth | |
357 self.prespace=' '*(maxWidth-self.width) | |
358 self.fmt="%s%%s|"%self.prespace | |
359 self.infix="" | |
360 self.suffix="" | |
361 | |
362 def newBlob(self,x,crossCheck=False): | |
363 self[x].setVal(True) | |
364 if crossCheck: | |
365 dpush('b_cc') | |
366 self[x].column.checkNew(self.y) | |
367 dpop('b_cc') | |
368 | |
369 def newX(self,x,crossCheck=False): | |
370 dprint('nx %s%s@%s'%('R',self.y,x)) | |
371 self[x].setVal(False) | |
372 if crossCheck: | |
373 dpush('x_cc') | |
374 self[x].column.checkX(self.y,x) | |
375 dpop('x_cc') | |
376 | |
199 class Column(Vector): | 377 class Column(Vector): |
200 def __init__(self,n,m,runs,pos,dprintHeight): | 378 cLet='C' |
379 def __init__(self,n,m,runs,pos): | |
201 Vector.__init__(self,n,m,runs) | 380 Vector.__init__(self,n,m,runs) |
202 self.x=pos | 381 self.x=pos |
203 self.dprintHeight=dprintHeight | 382 self.height=self.myPrintSize() |
204 self.fmt="%%%ss"%self.dprintHeight | 383 |
205 self.updateHeader() | 384 def updateHeader(self,*,maxHeight=None,r=None,pre=None): |
206 | 385 dprint('CuH',r,pre) |
207 def updateHeader(self): | 386 if maxHeight is None: |
208 header=('-'.join(str(c) for c in self.runs)) | 387 # update |
209 self.header=self.fmt%header # pad to same 'height' | 388 if pre: |
389 for rc in str(r): | |
390 self.infix.append(RedFmt%rc) | |
391 if self.runs: | |
392 self.infix.append('-') | |
393 else: | |
394 # post | |
395 ins=["-"] if self.runs else [] | |
396 for rc in str(r): | |
397 ins.append(RedFmt%r) | |
398 self.suffix=ins+self.suffix | |
399 dprint('CuH1: |%s|,%s,%s,%s'%(self.prespace,self.infix,self.suffix,self.runs)) | |
400 self.header=([" "]*self.prespace)+\ | |
401 self.infix+\ | |
402 (['-'.join(str(c) for c in self.runs)] if self.runs else [])+\ | |
403 self.suffix | |
404 else: | |
405 # init | |
406 self.maxHeight=maxHeight | |
407 self.infix=[] | |
408 self.suffix=[] | |
409 self.prespace=maxHeight - self.height # pad to same 'height' | |
410 self.fmt="%s%%s"%(' '*self.prespace) | |
411 header=('-'.join(str(c) for c in self.runs)) | |
412 self.header=self.fmt%header | |
413 dprint(self.header) | |
414 | |
415 def newBlob(self,y,crossCheck=False): | |
416 self[y].setVal(True) | |
417 if crossCheck: | |
418 dpush('b_cc') | |
419 self[y].row.checkNew(self.x) | |
420 dpop('b_cc') | |
421 | |
422 def newX(self,y,crossCheck=False): | |
423 dprint('nx %s%s@%s'%('C',self.x,y)) | |
424 self[y].setVal(False) | |
425 if crossCheck: | |
426 dpush('x_cc') | |
427 self[y].row.checkX(self.x,y) | |
428 dpop('x_cc') | |
210 | 429 |
211 class Cell: | 430 class Cell: |
212 def __init__(self,row,y,column,x): | 431 def __init__(self,row,y,column,x): |
213 # At the intersection of row and column Vectors | 432 # At the intersection of row and column Vectors |
214 self.row=row | 433 self.row=row |
226 return ' ' if self.val is None else ('\u25A0' if self.val else 'x') | 445 return ' ' if self.val is None else ('\u25A0' if self.val else 'x') |
227 | 446 |
228 def setVal(self,v): | 447 def setVal(self,v): |
229 if v is True: | 448 if v is True: |
230 if self.val is False: | 449 if self.val is False: |
231 dprint("Warning: x -> * at %s,%s"%(self.x,self.y)) | 450 wprint("Warning: x -> * at %s,%s"%(self.x,self.y)) |
232 elif self.val is True: | 451 elif self.val is True: |
233 # No-op | 452 # No-op |
234 return | 453 return |
235 # @@ check row/col completed | 454 self.val=v |
236 else: | 455 else: |
237 if self.val is not None: | 456 if self.val is not None: |
238 dprint("Warning: %s -> %s at %s,%s"%(self.val,v,self.x,self.y)) | 457 wprint("Warning: %s -> %s at %s,%s"%(self.val,v,self.x,self.y)) |
239 self.val=v | 458 self.val=v |
459 | |
240 | 460 |
241 class Nono(dict): | 461 class Nono(dict): |
242 # 0,0 is upper left, so increasing y goes _downwards_, to match the standard layout | 462 # 0,0 is upper left, so increasing y goes _downwards_, to match the standard layout |
243 def __init__(self,rows,cols): | 463 def __init__(self,runsPerRow,runsPerCol): |
244 n=self.n=len(cols) | 464 global SOLVER |
245 if n!=len(rows): | 465 self.loop=0 |
246 print("losing r:%s x c:%s"%(len(rows),n),sys.stderr) | 466 self.dp='' |
467 self.dstate=[] | |
468 SOLVER=self | |
469 n=self.n=len(runsPerCol) | |
470 if n!=len(runsPerRow): | |
471 print("losing r:%s x c:%s"%(len(runsPerRow),n),sys.stderr) | |
247 exit(1) | 472 exit(1) |
248 self.rc=rows | 473 self.rc=runsPerRow |
249 rowDprintWidth=max(sum(len(str(r)) for r in row)+len(row)-1 for row in rows) | 474 self.cc=runsPerCol |
250 self.rowfmt="%s|%%s"%(' '*rowDprintWidth) | 475 # print col nums>9 vertically :-( |
251 self.cc=cols | 476 self.columns=cc=[Column(n,self,runsPerCol[i],i) for i in range(n)] |
252 # dprint col nums>9 vertically :-( | 477 self.maxCRheight=maxCRheight=max(col.height for col in cc) |
253 self.colDprintHeight=max(sum(len(str(c)) for c in col)+len(col)-1 for col in cols) | 478 for c in cc: |
254 self.columns=cc=[Column(n,self,cols[i],i,self.colDprintHeight) for i in range(20)] | 479 c.updateHeader(maxHeight=maxCRheight) |
255 self.rows=rr=[Row(n,self,rows[i],i,rowDprintWidth) for i in range(20)] | 480 self.rows=rr=[Row(n,self,runsPerRow[i],i) for i in range(n)] |
256 for x in range(20): | 481 maxRRwidth=max(row.width for row in rr) |
257 for y in range(20): | 482 for r in rr: |
483 r.updateHeader(maxWidth=maxRRwidth) | |
484 self.rowfmt="%s|%%s|"%(' '*maxRRwidth) | |
485 for x in range(n): | |
486 for y in range(n): | |
258 self[(x,y)]=Cell(rr[y],y,cc[x],x) | 487 self[(x,y)]=Cell(rr[y],y,cc[x],x) |
259 | 488 |
260 def __str__(self): | 489 def __str__(self): |
261 lines=[self.rowfmt%('|'.join([(self.columns[i]).header[j] for i in range(self.n)])) # 'rotate' | 490 lines=[self.rowfmt%('|'.join([(self.columns[i]).header[j] for i in range(self.n)])) # 'rotate' |
262 for j in range(self.colDprintHeight)] | 491 for j in range(self.maxCRheight)] |
263 lines+=[str(r) for r in self.rows] | 492 lines+=[str(r) for r in self.rows] |
264 return "\n".join(lines) | 493 return "\n".join(lines) |
265 | 494 |
495 def solve(self): | |
496 someChanged=1 | |
497 while someChanged>0: | |
498 self.loop+=1 | |
499 someChanged=0 | |
500 dprint("***Solve C %s***"%self.loop) | |
501 for c in self.columns: | |
502 someChanged+=c.step(c.x) | |
503 print(someChanged) | |
504 print(self) | |
505 dprint("***Solve R %s***"%self.loop) | |
506 for r in self.rows: | |
507 someChanged+=r.step(r.y) | |
508 print(someChanged) | |
509 print(self) | |
510 | |
511 def dpush(s): | |
512 SOLVER.dp+=' ' | |
513 SOLVER.dstate.append(s) | |
514 | |
515 def dpop(s): | |
516 assert(SOLVER.dstate.pop()==s) | |
517 SOLVER.dp=SOLVER.dp[1:] | |
518 | |
266 def dprint(*args): | 519 def dprint(*args): |
267 pass | 520 print(SOLVER.dp,end='') |
521 print(*args) | |
522 sys.stdout.flush() | |
523 | |
524 def eprint(*args,**kw): | |
525 print(*args,file=sys.stderr) | |
526 sys.stderr.flush() | |
527 exit(kw['err']) | |
528 | |
529 def wprint(*args): | |
530 print(*args,file=sys.stderr) | |
531 sys.stderr.flush() | |
268 | 532 |
269 if __name__ == '__main__': | 533 if __name__ == '__main__': |
270 if len(sys.argv)>1: | 534 if len(sys.argv)>1: |
271 f=open(sys.argv[1]) | 535 f=open(sys.argv[1]) |
272 else: | 536 else: |
285 cols.append(vv) | 549 cols.append(vv) |
286 | 550 |
287 rows=[[int(s) for s in l.split()] for l in f] | 551 rows=[[int(s) for s in l.split()] for l in f] |
288 | 552 |
289 solver=Nono(rows,cols) | 553 solver=Nono(rows,cols) |
290 print(solver) | 554 solver.solve() |
291 for c in solver.columns: | |
292 c.step() | |
293 print() | |
294 print(solver) | |
295 for r in solver.rows: | |
296 r.step() | |
297 print() | |
298 print(solver) |