comparison nono.py @ 13:70993b538ddb

works on 5a
author Henry S. Thompson <ht@inf.ed.ac.uk>
date Fri, 20 Mar 2020 19:15:42 +0000
parents ede2551ae7d9
children 1e961cf10f88
comparison
equal deleted inserted replaced
12:ede2551ae7d9 13:70993b538ddb
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.m=m
28 self.margin0=0 # a run can start here, so if >0 then self[m0-1].val must be False 29 self.margin0=0 # a run can start here, so if >0 then self[m0-1].val must be False
29 self.marginN=n-1 # a run can end here, so if <n-1 then self[mN+1].val ditto 30 self.marginN=n-1 # a run can end here, so if <n-1 then self[mN+1].val ditto
30 self.runs=self.origRuns=runs 31 self.runs=self.origRuns=runs
31 self.initialComplete=[] 32 self.initialComplete=[]
32 self.finalComplete=[] 33 self.finalComplete=[]
68 r=self.runs[j] 69 r=self.runs[j]
69 for v in range(i,bound): 70 for v in range(i,bound):
70 for sub in self.seedList(1,j+1,pos0+v+r,posN,runLen-(r+1)): 71 for sub in self.seedList(1,j+1,pos0+v+r,posN,runLen-(r+1)):
71 yield [-v,r]+sub 72 yield [-v,r]+sub
72 73
73 def step(self): 74 def step(self,pos):
74 if self.runs==[]: 75 if self.runs==[]:
75 return 0 76 return 0
76 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]
77 wins=0 78 wins=0
78 changed=0 79 changed=0
79 for k,runs in enumerate(self.allRuns): 80 for k,runs in enumerate(self.allRuns):
80 dprint('=====pass %s======'%k) 81 dprint('=====pass %s.%s.%s.%s======'%(self.m.loop,self.cLet,pos,k))
81 if self.onepass(self.margin0,self.marginN+1,scratch,runs.copy()): 82 if self.onepass(self.margin0,self.marginN+1,scratch,runs.copy()):
82 wins+=1 83 wins+=1
83 dprint(wins, scratch) 84 dprint(wins, scratch)
85 dprint('r40',self.m.rows[4])
84 maybeSeq=None 86 maybeSeq=None
85 inSeq=None 87 inSeq=None
86 for i in range(self.n): 88 for i in range(self.n):
87 if scratch[i]==wins: 89 if scratch[i]>=wins:
88 # If blobby in _every_ pass, then must be a blob 90 # If blobby in _every_ pass, then must be a blob
89 if inSeq is None: 91 if inSeq is None:
90 inSeq=i if maybeSeq is None else maybeSeq 92 inSeq=i if maybeSeq is None else maybeSeq
91 dprint('s1',inSeq) 93 dprint('s1',inSeq)
92 maybeSeq=None 94 maybeSeq=None
109 dprint('s3',i) 111 dprint('s3',i)
110 maybeSeq=i 112 maybeSeq=i
111 dprint('s4',inSeq,i) 113 dprint('s4',inSeq,i)
112 if inSeq is not None: 114 if inSeq is not None:
113 self.checkNew(i) 115 self.checkNew(i)
116 dprint('r41',self.m.rows[4])
114 return changed 117 return changed
115 118
116 def onepass(self,i0,iBound,scratch,stack): 119 def onepass(self,i0,iBound,scratch,stack):
117 """note that stack is not a simple run, but one with _negative_ numbers between 120 """note that stack is not a simple run, but one with _negative_ numbers between
118 and possibly before the positive ones, indicating obligatory skips 121 and possibly before the positive ones, indicating obligatory skips
181 for k in range(iBound): 184 for k in range(iBound):
182 scratch[k]+=maybe[k] 185 scratch[k]+=maybe[k]
183 return True 186 return True
184 print("Shouldn't happen? - fell through",stack,i,iBound) 187 print("Shouldn't happen? - fell through",stack,i,iBound)
185 188
189 def checkX(self,pos,crosspos):
190 # New x at pos, are there others that can be xed out now?
191 # Overkill as is?
192 dprint('cx',self.cLet+str(crosspos),pos)
193 dprint('cxr1',self.m.rows[4])
194 if self.runs:
195 start0=0 if self.margin0==0 else self.margin0+1
196 if self.marginal(range(start0,pos+1),self.runs[0]):
197 dprint('cx1a')
198 else:
199 dprint('cx1b')
200 # if len(self.runs)>1:
201 startN=self.marginN if (self.marginN==self.n-1) else self.marginN-1
202 if self.marginal(range(startN,pos,-1),self.runs[-1]):
203 dprint('cx2a')
204 else:
205 dprint('cx2b')
206 dprint('cxr2',self.m.rows[4])
207
186 def checkNew(self,pos): 208 def checkNew(self,pos):
187 # New blob at pos, can we complete anything? 209 # New blob at pos, can we complete anything?
188 # Assuming @@ FTTB that it's never possible to definitively complete a 210 # Assuming @@ FTTB that it's never possible to definitively complete a
189 # non-marginal run, which is wrong if the length is unique and it's bounded... 211 # non-marginal run, which is wrong if the length is unique and it's bounded...
190 start0=0 if self.margin0==0 else self.margin0+1 212 start0=self.margin0 #0 if self.margin0==0 else self.margin0+1
191 startN=self.marginN if (self.marginN==self.n-1) else self.marginN-1 213 startN=self.marginN # self.marginN if (self.marginN==self.n-1) else self.marginN-1
214 dprint('cn',start0,pos,startN)
192 changed=False 215 changed=False
193 # First, find our boundaries: 216 # First, find our boundaries:
194 s=pos # start index of our run 217 s=pos # start index of our run
195 while s>start0 and self[s-1].val is True: 218 if s>start0:
196 s-=1 219 while s>start0 and self[s-1].val is True:
220 s-=1
197 f=pos # finish index of our run 221 f=pos # finish index of our run
198 while f<startN and self[f+1].val is True: 222 if f<startN:
199 f+=1 223 while f<startN and self[f+1].val is True:
224 f+=1
200 l=(f-s)+1 # our length 225 l=(f-s)+1 # our length
201 print('%s:%s,%s,%s,%s:<%s>'%(str(self.__class__.__name__)[0],s,f,l,self.runs,self)) 226 dprint('%s:%s,%s,%s,%s:<%s>'%(self.cLet,s,f,l,self.runs,self))
202 c=self.runs.count(l) 227 c=self.runs.count(l)
203 if c==0: 228 if c==0:
204 # not big enough yet 229 # not big enough yet
205 dprint('x0') 230 dprint('x0')
206 return 231 return
208 # is it safely left marginal, i.e. no blobs or big enough gaps before us? 233 # is it safely left marginal, i.e. no blobs or big enough gaps before us?
209 if self.marginal(range(start0,s),l): 234 if self.marginal(range(start0,s),l):
210 changed=True 235 changed=True
211 dprint('n1') 236 dprint('n1')
212 # mark our margins 237 # mark our margins
213 for i in range(start0,s): 238 for i in range(start0,s): # not sure this is still needed since
239 # added gap-filling in self.marginal
214 if self[i].val is None: 240 if self[i].val is None:
215 self[i].setVal(False) 241 self.newX(i,False)
216 if f<startN: 242 if f<startN:
217 if self[f+1].val is None: 243 if self[f+1].val is None:
218 self[f+1].setVal(False) 244 self.newX(f+1,True)
219 self.found0(f) # pull in the start margin at least to f+2 245 self.found0(f) # pull in the start margin at least to f+2
220 else: 246 else:
221 dprint('x1a') 247 dprint('x1a')
222 if not changed: 248 if not changed:
223 if self.runs[-1]==l: 249 if self.runs[-1]==l:
224 # is it safely _right_ marginal, i.e. no blobs or big enough gaps _after_ us? 250 # is it safely _right_ marginal, i.e. no blobs or big enough gaps _after_ us?
225 if self.marginal(range(startN,f,-1),l): 251 if self.marginal(range(startN,f,-1),l):
226 changed=True 252 changed=True
227 dprint('n2') 253 dprint('n2')
228 # mark our margins 254 # mark our margins: still needed? see above
229 for i in range(startN,f,-1): 255 for i in range(startN,f,-1):
230 if self[i].val is None: 256 if self[i].val is None:
231 self[i].setVal(False) 257 self.newX(i,False)
232 if s>start0: 258 if s>start0:
233 if self[s-1].val is None: 259 if self[s-1].val is None:
234 self[s-1].setVal(False) 260 self.newX(s-1,True)
235 self.foundN(s) # pull in the finish margin at least to s-2 261 self.foundN(s) # pull in the finish margin at least to s-2
236 else: 262 else:
237 dprint('x2a') 263 dprint('x2a')
238 else: 264 else:
239 dprint('x2b') 265 dprint('x2b')
240 if changed: 266 if changed:
241 self.resetAllRuns() 267 self.resetAllRuns()
242 268
243 def marginal(self,rng,l): 269 def marginal(self,rng,l):
244 print('m',rng.start,rng.stop,rng.step) 270 dprint('m',rng.start,rng.stop,rng.step)
245 g=0 # length of a gap 271 g=0 # length of a gap
246 for i in rng: 272 for i in rng:
247 if self[i].val is True: 273 if self[i].val is True:
274 # Shouldn't be possible?
248 dprint('mx0') 275 dprint('mx0')
249 return False 276 return False
250 if self[i].val is False: 277 if self[i].val is False:
251 g=0 278 if g>0:
279 # Block a too-small gap
280 for i in (i-g,i):
281 self.newX(i,True)
282 g=0
252 else: 283 else:
253 # None 284 # None
254 g+=1 285 g+=1
255 if g==l: 286 if g==l:
256 dprint('mx1') 287 dprint('mx1')
257 return False 288 return False
289 if g>0:
290 # Block a too-small gap
291 for j in (i-g,i):
292 self.newX(j,True)
258 return True 293 return True
259 294
260 def found0(self,i): 295 def found0(self,i):
261 print('found0 called on %s at %s'%(self,i)) 296 dprint('found0 called on %s at %s'%(self,i))
262 i=self.margin0 297 i=self.margin0
263 while self[i].val is False: 298 while self[i].val is False:
264 i+=1 299 i+=1
265 if self[i].val is True: 300 if self[i].val is True:
266 r=self.runs.pop(0) 301 r=self.runs.pop(0)
267 self.initialComplete.append(r) 302 self.initialComplete.append(r)
268 self.margin0+=r+1 303 self.margin0+=r+1
269 self.updateHeader(r=r,pre=True) 304 self.updateHeader(r=r,pre=True)
270 305
271 def foundN(self,i): 306 def foundN(self,i):
272 print('foundN called on %s at %s'%(self,i)) 307 dprint('foundN called on %s at %s'%(self,i))
273 i=self.marginN 308 i=self.marginN
274 while self[i].val is False: 309 while self[i].val is False:
275 i-=1 310 i-=1
276 if self[i].val is True: 311 if self[i].val is True:
277 r=self.runs.pop() 312 r=self.runs.pop()
279 self.marginN-=r+1 314 self.marginN-=r+1
280 self.updateHeader(r=r,pre=False) 315 self.updateHeader(r=r,pre=False)
281 316
282 317
283 class Row(Vector): 318 class Row(Vector):
319 cLet='R'
284 def __init__(self,n,m,runs,pos): 320 def __init__(self,n,m,runs,pos):
285 Vector.__init__(self,n,m,runs) 321 Vector.__init__(self,n,m,runs)
286 self.y=pos 322 self.y=pos
287 self.width=self.myPrintSize() 323 self.width=self.myPrintSize()
288 324
291 Vector.__str__(self)) 327 Vector.__str__(self))
292 328
293 def updateHeader(self,*,maxWidth=None,r=None,pre=None): 329 def updateHeader(self,*,maxWidth=None,r=None,pre=None):
294 if maxWidth is None: 330 if maxWidth is None:
295 # update 331 # update
332 spacer=(" " if self.runs else "")
296 if pre: 333 if pre:
297 self.infix+=(RedFmt%r)+(" " if self.runs else "") 334 self.infix+=(RedFmt%r)+spacer
298 else: 335 else:
299 # post 336 # post
300 self.suffix=" "+RedFmt%r+self.suffix 337 self.suffix=spacer+RedFmt%r+self.suffix
301 self.fmt="%s%s%%s%s|"%(self.prespace,self.infix,self.suffix) 338 self.fmt="%s%s%%s%s|"%(self.prespace,self.infix,self.suffix)
302 else: 339 else:
303 # init 340 # init
304 self.maxWidth=maxWidth 341 self.maxWidth=maxWidth
305 self.prespace=' '*(maxWidth-self.width) 342 self.prespace=' '*(maxWidth-self.width)
310 def newBlob(self,x,crossCheck=False): 347 def newBlob(self,x,crossCheck=False):
311 self[x].setVal(True) 348 self[x].setVal(True)
312 if crossCheck: 349 if crossCheck:
313 self[x].column.checkNew(self.y) 350 self[x].column.checkNew(self.y)
314 351
352 def newX(self,x,crossCheck=False):
353 dprint('nx %s%s@%s'%('R',self.y,x))
354 self[x].setVal(False)
355 if crossCheck:
356 self[x].column.checkX(self.y,x)
357
315 class Column(Vector): 358 class Column(Vector):
359 cLet='C'
316 def __init__(self,n,m,runs,pos): 360 def __init__(self,n,m,runs,pos):
317 Vector.__init__(self,n,m,runs) 361 Vector.__init__(self,n,m,runs)
318 self.x=pos 362 self.x=pos
319 self.height=self.myPrintSize() 363 self.height=self.myPrintSize()
320 364
351 def newBlob(self,y,crossCheck=False): 395 def newBlob(self,y,crossCheck=False):
352 self[y].setVal(True) 396 self[y].setVal(True)
353 if crossCheck: 397 if crossCheck:
354 self[y].row.checkNew(self.x) 398 self[y].row.checkNew(self.x)
355 399
400 def newX(self,y,crossCheck=False):
401 dprint('nx %s%s@%s'%('C',self.x,y))
402 self[y].setVal(False)
403 if crossCheck:
404 self[y].row.checkX(self.x,y)
405
356 class Cell: 406 class Cell:
357 def __init__(self,row,y,column,x): 407 def __init__(self,row,y,column,x):
358 # At the intersection of row and column Vectors 408 # At the intersection of row and column Vectors
359 self.row=row 409 self.row=row
360 self.column=column 410 self.column=column
380 self.val=v 430 self.val=v
381 else: 431 else:
382 if self.val is not None: 432 if self.val is not None:
383 print("Warning: %s -> %s at %s,%s"%(self.val,v,self.x,self.y),file=sys.stderr) 433 print("Warning: %s -> %s at %s,%s"%(self.val,v,self.x,self.y),file=sys.stderr)
384 self.val=v 434 self.val=v
435
385 436
386 class Nono(dict): 437 class Nono(dict):
387 # 0,0 is upper left, so increasing y goes _downwards_, to match the standard layout 438 # 0,0 is upper left, so increasing y goes _downwards_, to match the standard layout
388 def __init__(self,runsPerRow,runsPerCol): 439 def __init__(self,runsPerRow,runsPerCol):
440 self.loop=0
389 n=self.n=len(runsPerCol) 441 n=self.n=len(runsPerCol)
390 if n!=len(runsPerRow): 442 if n!=len(runsPerRow):
391 print("losing r:%s x c:%s"%(len(runsPerRow),n),sys.stderr) 443 print("losing r:%s x c:%s"%(len(runsPerRow),n),sys.stderr)
392 exit(1) 444 exit(1)
393 self.rc=runsPerRow 445 self.rc=runsPerRow
413 return "\n".join(lines) 465 return "\n".join(lines)
414 466
415 def solve(self): 467 def solve(self):
416 someChanged=1 468 someChanged=1
417 while someChanged>0: 469 while someChanged>0:
470 self.loop+=1
418 someChanged=0 471 someChanged=0
472 dprint("***Solve C %s***"%self.loop)
419 for c in self.columns: 473 for c in self.columns:
420 someChanged+=c.step() 474 someChanged+=c.step(c.x)
421 print(someChanged) 475 print(someChanged)
422 print(self) 476 print(self)
477 dprint("***Solve R %s***"%self.loop)
423 for r in self.rows: 478 for r in self.rows:
424 someChanged+=r.step() 479 someChanged+=r.step(r.y)
425 print(someChanged) 480 print(someChanged)
426 print(self) 481 print(self)
427 482
428 def dprint(*args): 483 def dprint(*args):
429 print(*args) 484 pass # print(*args)
430 485
431 if __name__ == '__main__': 486 if __name__ == '__main__':
432 if len(sys.argv)>1: 487 if len(sys.argv)>1:
433 f=open(sys.argv[1]) 488 f=open(sys.argv[1])
434 else: 489 else: