Mercurial > hg > python
comparison nono.py @ 57:057b52746850 simple
use Run and Space classes in Vector.runs
author | Henry Thompson <ht@markup.co.uk> |
---|---|
date | Thu, 01 Jun 2023 18:21:56 +0100 |
parents | 40273cb7c7fc |
children | a3aaf6c085f4 |
comparison
equal
deleted
inserted
replaced
56:40273cb7c7fc | 57:057b52746850 |
---|---|
11 | 11 |
12 def interleave(*args): | 12 def interleave(*args): |
13 for vals in zip(*args): | 13 for vals in zip(*args): |
14 yield from vals | 14 yield from vals |
15 | 15 |
16 class Run: | |
17 def __init__(self,n,i,j): | |
18 # A run of length n, starting within [i,j] | |
19 self.n=n | |
20 self.i=i | |
21 self.j=j | |
22 self.done=False | |
23 | |
24 def __str__(self): | |
25 return str(self.n) | |
26 | |
27 class Space: | |
28 def __init__(self,n,i,j): | |
29 # A space of length 1..n, starting within [i,j] | |
30 # Once done, length == n | |
31 assert n>0 | |
32 self.n=n | |
33 self.i=i | |
34 self.j=j | |
35 self.done=False | |
36 | |
37 def __str__(self): | |
38 return '-' | |
39 | |
16 class Vector(list): | 40 class Vector(list): |
17 # reads top-to-bottom or left-to-right | 41 # reads top-to-bottom or left-to-right |
18 def __init__(self,n,m,runs): | 42 def __init__(self,n,m): |
19 list.__init__(self,list(range(n))) | 43 list.__init__(self,list(range(n))) |
20 self.n=n # size | 44 self.n=n # size |
21 self.m=m # parent NoNo | 45 self.m=m # parent NoNo |
22 self.margin0=0 # a run can start here, so if >0 then self[m0-1].val must be False | 46 self.margin0=0 # a run can start here, so if >0 then self[m0-1].val must be False |
23 self.marginN=n-1 # a run can end here, so if <n-1 then self[mN+1].val ditto | 47 self.marginN=n-1 # a run can end here, so if <n-1 then self[mN+1].val ditto |
24 self.runs=self.origRuns=runs | 48 |
49 def initRuns(self,runs): | |
50 # Set runs to alternating list of Run and Skip | |
25 self.rn=len(runs) | 51 self.rn=len(runs) |
26 self.initialComplete=[] | 52 self.runs=[] |
27 self.finalComplete=[] | 53 if self.rn==0: |
28 #self.resetAllRuns() | 54 # No runs means a vector of x |
55 for c in self: | |
56 c.setVal(False) | |
57 else: | |
58 need=sum(1+runs[k] for k in range(self.rn))-1 | |
59 space=self.n-need | |
60 pos=0 | |
61 while runs: | |
62 self.runs.append(Run(r:=runs.pop(0),pos,pos+space)) | |
63 pos+=r | |
64 if runs: | |
65 self.runs.append(Space(space,pos,pos+space)) | |
66 pos+=1 | |
67 self.initDisp() | |
29 | 68 |
30 def __repr__(self): | 69 def __repr__(self): |
31 return "V@%s%s:%s"%(self.x,self.origRuns,list.__repr__(self)) | 70 return "V@%s%s:%s"%(self.x,self.runs,list.__repr__(self)) |
32 | 71 |
33 def __str__(self): | 72 def __str__(self): |
34 return '%s|'%('|'.join(str(c) for c in self)) | 73 return '%s|'%('|'.join(str(c) for c in self)) |
35 | 74 |
36 def myPrintSize(self): | 75 def myPrintSize(self): |
37 return sum(len(str(run)) for run in self.runs)+len(self.runs)-1 | 76 return sum(len(str(run)) for run in self.runs) |
77 | |
78 #=========v-old-v============ | |
38 | 79 |
39 def resetAllRuns(self): | 80 def resetAllRuns(self): |
40 # compute the set of all possible layouts for runs | 81 # compute the set of all possible layouts for runs |
41 self.rn=len(self.runs) | 82 self.rn=len(self.runs) |
42 rtot=sum(self.runs) | 83 rtot=sum(self.runs) |
322 r=self.runs.pop() | 363 r=self.runs.pop() |
323 self.finalComplete=[r]+self.finalComplete | 364 self.finalComplete=[r]+self.finalComplete |
324 self.marginN-=r+1 | 365 self.marginN-=r+1 |
325 self.updateHeader(r=r,pre=False) | 366 self.updateHeader(r=r,pre=False) |
326 | 367 |
368 #=============new simple============ | |
369 def pass0(self): | |
370 need=sum(1+self.runs[k] for k in range(self.rn))-1 | |
371 space=self.n-need | |
372 p=0 | |
373 for run in self.runs: | |
374 if run>space: | |
375 for p in range(p+space,p+run): | |
376 self[p].setVal(True) | |
377 p+=1 | |
378 else: | |
379 p+=run | |
380 p+=1 | |
327 | 381 |
328 class Row(Vector): | 382 class Row(Vector): |
329 cLet='R' | 383 cLet='R' |
330 def __init__(self,n,m,runs,pos): | 384 def __init__(self,n,m,pos): |
331 Vector.__init__(self,n,m,runs) | 385 Vector.__init__(self,n,m) |
332 self.y=pos | 386 self.y=pos |
387 | |
388 def initDisp(self): | |
333 self.width=self.myPrintSize() | 389 self.width=self.myPrintSize() |
334 | 390 |
335 def __str__(self): | 391 def __str__(self): |
336 return ((self.fmt%(' '.join(str(r) for r in self.runs)))+ | 392 return ((self.fmt%(' '.join(str(r) for r in self.runs)))+ |
337 Vector.__str__(self)) | 393 Vector.__str__(self)) |
369 self[x].column.checkX(self.y,x) | 425 self[x].column.checkX(self.y,x) |
370 dpop('x_cc') | 426 dpop('x_cc') |
371 | 427 |
372 class Column(Vector): | 428 class Column(Vector): |
373 cLet='C' | 429 cLet='C' |
374 def __init__(self,n,m,runs,pos): | 430 def __init__(self,n,m,pos): |
375 Vector.__init__(self,n,m,runs) | 431 Vector.__init__(self,n,m) |
376 self.x=pos | 432 self.x=pos |
433 | |
434 def initDisp(self): | |
377 self.height=self.myPrintSize() | 435 self.height=self.myPrintSize() |
378 | |
379 #=============new simple============ | |
380 def pass0(self): | |
381 need=sum(1+self.runs[k] for k in range(self.rn))-1 | |
382 space=self.n-need | |
383 p=0 | |
384 for run in self.runs: | |
385 if run>space: | |
386 for p in range(p+space,p+run): | |
387 self[p].setVal(True) | |
388 p+=1 | |
389 else: | |
390 p+=run | |
391 p+=1 | |
392 | 436 |
393 def updateHeader(self,*,maxHeight=None,r=None,pre=None): | 437 def updateHeader(self,*,maxHeight=None,r=None,pre=None): |
394 dprint('CuH',r,pre) | 438 dprint('CuH',r,pre) |
395 if maxHeight is None: | 439 if maxHeight is None: |
396 # update | 440 # update |
452 | 496 |
453 def __str__(self): | 497 def __str__(self): |
454 return ' ' if self.val is None else ('\u25A0' if self.val else 'x') | 498 return ' ' if self.val is None else ('\u25A0' if self.val else 'x') |
455 | 499 |
456 def setVal(self,v): | 500 def setVal(self,v): |
457 if v is True: | 501 # Returns true iff old value was None |
458 if self.val is False: | 502 assert v in (True, False) |
459 wprint("Warning: x -> * at %s,%s"%(self.x,self.y)) | 503 if v == self.val: |
460 elif self.val is True: | 504 return False |
461 # No-op | 505 if self.val is not None: |
462 return | 506 eprint("Reset not allowed: %s -> %s at %s,%s"%(self.val, v, self.x,self.y),err=103) |
463 self.val=v | 507 self.val=v |
464 else: | 508 return True |
465 if self.val is not None: | |
466 wprint("Warning: %s -> %s at %s,%s"%(self.val,v,self.x,self.y)) | |
467 self.val=v | |
468 | |
469 | 509 |
470 class Nono(dict): | 510 class Nono(dict): |
471 # 0,0 is upper left, so increasing y goes _downwards_, to match the standard layout | 511 # 0,0 is upper left, so increasing y goes _downwards_, to match the standard layout |
472 def __init__(self,runsPerRow,runsPerCol): | 512 def __init__(self,runsPerRow,runsPerCol): |
473 global SOLVER | 513 global SOLVER |
480 print("losing r:%s x c:%s"%(len(runsPerRow),n),sys.stderr) | 520 print("losing r:%s x c:%s"%(len(runsPerRow),n),sys.stderr) |
481 exit(1) | 521 exit(1) |
482 self.rc=runsPerRow | 522 self.rc=runsPerRow |
483 self.cc=runsPerCol | 523 self.cc=runsPerCol |
484 # print col nums>9 vertically :-( | 524 # print col nums>9 vertically :-( |
485 self.columns=cc=[Column(n,self,runsPerCol[i],i) for i in range(n)] | 525 self.columns=cc=[Column(n,self,i) for i in range(n)] |
526 self.rows=rr=[Row(n,self,i) for i in range(n)] | |
527 for x in range(n): | |
528 for y in range(n): | |
529 self[(x,y)]=Cell(rr[y],y,cc[x],x) | |
530 # Need cells in place for the following | |
531 for row,runs in zip(rr,runsPerRow): | |
532 row.initRuns(runs) | |
533 for col,runs in zip(cc,runsPerCol): | |
534 col.initRuns(runs) | |
486 self.maxCRheight=maxCRheight=max(col.height for col in cc) | 535 self.maxCRheight=maxCRheight=max(col.height for col in cc) |
487 for c in cc: | 536 for c in cc: |
488 c.updateHeader(maxHeight=maxCRheight) | 537 c.updateHeader(maxHeight=maxCRheight) |
489 self.rows=rr=[Row(n,self,runsPerRow[i],i) for i in range(n)] | |
490 maxRRwidth=max(row.width for row in rr) | 538 maxRRwidth=max(row.width for row in rr) |
491 for r in rr: | 539 for r in rr: |
492 r.updateHeader(maxWidth=maxRRwidth) | 540 r.updateHeader(maxWidth=maxRRwidth) |
493 self.rowfmt="%s|%%s|"%(' '*maxRRwidth) | 541 self.rowfmt="%s|%%s|"%(' '*maxRRwidth) |
494 for x in range(n): | 542 |
495 for y in range(n): | |
496 self[(x,y)]=Cell(rr[y],y,cc[x],x) | |
497 | 543 |
498 def __str__(self): | 544 def __str__(self): |
499 lines=[self.rowfmt%('|'.join([(self.columns[i]).header[j] for i in range(self.n)])) # 'rotate' | 545 lines=[self.rowfmt%('|'.join([(self.columns[i]).header[j] for i in range(self.n)])) # 'rotate' |
500 for j in range(self.maxCRheight)] | 546 for j in range(self.maxCRheight)] |
501 lines+=[str(r) for r in self.rows] | 547 lines+=[str(r) for r in self.rows] |
503 | 549 |
504 #=============new simple============ | 550 #=============new simple============ |
505 def pass0(self): # do columns of empty layout | 551 def pass0(self): # do columns of empty layout |
506 for c in self.columns: | 552 for c in self.columns: |
507 c.pass0() | 553 c.pass0() |
554 for r in self.rows: | |
555 r.pass0() | |
508 | 556 |
509 def solve(self): | 557 def solve(self): |
510 self.pass0() | 558 self.pass0() |
511 print(self) | 559 print(self) |
512 return | 560 return |
559 print('%sx%s puzzle'%(n,n),file=sys.stderr) | 607 print('%sx%s puzzle'%(n,n),file=sys.stderr) |
560 cols=[[int(i) for i in v.split('.')] for v in vv[:n]] | 608 cols=[[int(i) for i in v.split('.')] for v in vv[:n]] |
561 rows=[[int(i) for i in v.split('.')] for v in vv[n:]] | 609 rows=[[int(i) for i in v.split('.')] for v in vv[n:]] |
562 | 610 |
563 solver=Nono(rows,cols) | 611 solver=Nono(rows,cols) |
612 breakpoint() | |
564 solver.solve() | 613 solver.solve() |
565 | 614 |