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