Mercurial > hg > python
changeset 73:63e87db2bc31 simple
update to modern syntax using ruff on lochinver
| author | Henry Thompson <ht@markup.co.uk> |
|---|---|
| date | Sat, 09 Aug 2025 15:56:58 -0400 |
| parents | 15e540c190d5 |
| children | 0245bc07f16c |
| files | nono.py |
| diffstat | 1 files changed, 228 insertions(+), 196 deletions(-) [+] |
line wrap: on
line diff
--- a/nono.py Sat Aug 09 15:48:34 2025 -0400 +++ b/nono.py Sat Aug 09 15:56:58 2025 -0400 @@ -14,105 +14,109 @@ # Change in the middle # New dead: If deterministic, split the run in two and requeue the cell (now in two runs) # fill: If deterministic single gap at any end, kill, split, and requeue the cell (now in two runs) -#---------- +# ---------- # Doesn't yet cover everything, e.g. killing gaps that are too small to be filled # Do we need to actually represent run-internal segments, e.g. skips and bars? import sys -Red='[31m' -eRed='[39m' -RedFmt=Red+'%s'+eRed +Red = "[31m" +eRed = "[39m" +RedFmt = Red + "%s" + eRed + class Run: '''What we're looking for, converted eventually into what - we've found. - "left" and "right" make sense for Rows, for Columns read "above" and "below"''' - def __init__(self,n,i,j): + we've found. + "left" and "right" make sense for Rows, for Columns read "above" and "below"''' + + def __init__(self, n, i, j): # A run of length n, starting within [i,j] - self.n=n - self.i=i - self.j=j - self.s=j-i - self.done=False - self.left=self.right=None + self.n = n + self.i = i + self.j = j + self.s = j - i + self.done = False + self.left = self.right = None def leftEdge(self): return self.i def rightEdge(self): if self.done: - return self.i+self.n-1 - return self.j+self.n-1 + return self.i + self.n - 1 + return self.j + self.n - 1 def __str__(self): return str(self.n) - + + class Space: - def __init__(self,n,i,j): + def __init__(self, n, i, j): # A space of length 1..n, starting within [i,j] # Once done, length == n # i and j don't change, but s, which is used by check0 # to decide where to look, is optimistic and may need to # be restricted if there is an overlap with the next (forward or backward) # run (see self.right and self.left, if any, respectively) - assert n>0 - self.n=n - self.i=i - self.j=j - self.s=j-i - self.done=False + assert n > 0 + self.n = n + self.i = i + self.j = j + self.s = j - i + self.done = False def __str__(self): - return '' + return "" + class Vector(list): # reads top-to-bottom or left-to-right - def __init__(self,n,m): - list.__init__(self,list(range(n))) - self.n=n # size - self.m=m # parent NoNo - self.margin0=0 # a run can start here, so if >0 then self[m0-1].val must be False - self.marginN=n-1 # a run can end here, so if <n-1 then self[mN+1].val ditto + def __init__(self, n, m): + list.__init__(self, list(range(n))) + self.n = n # size + self.m = m # parent NoNo + self.margin0 = 0 # a run can start here, so if >0 then self[m0-1].val must be False + self.marginN = n - 1 # a run can end here, so if <n-1 then self[mN+1].val ditto - def initRuns(self,runs): + def initRuns(self, runs): # Set runs to alternating list of Run and Skip - self.rn=len(runs) - self.runs=[] - if self.rn==0: + self.rn = len(runs) + self.runs = [] + if self.rn == 0: # No runs means a vector of x for c in self: c.setVal(False) else: - need=sum(1+runs[k] for k in range(self.rn))-1 - space=self.n-need - pos=0 + need = sum(1 + runs[k] for k in range(self.rn)) - 1 + space = self.n - need + pos = 0 while runs: - self.runs.append(Run(r:=runs.pop(0),pos,pos+space)) - pos+=r + self.runs.append(Run(r := runs.pop(0), pos, pos + space)) + pos += r if runs: - self.runs.append(Space(space,pos,pos+space)) - pos+=1 + self.runs.append(Space(space, pos, pos + space)) + pos += 1 # Adjacent pairs mutually restrict possible starting points - for i in range(0,self.rn-1): - left=self.runs[2*i] - right=self.runs[(2*i)+2] - left.right=right - right.left=left + for i in range(0, self.rn - 1): + left = self.runs[2 * i] + right = self.runs[(2 * i) + 2] + left.right = right + right.left = left self.initDisp() def __str__(self): - return '%s|'%('|'.join(str(c) for c in self)) + return "%s|" % ("|".join(str(c) for c in self)) def myPrintSize(self): - return sum((len(str(run)) if isinstance(run,Run) else 1) for run in self.runs) + return sum((len(str(run)) if isinstance(run, Run) else 1) for run in self.runs) def pass0(self): # simple first pass down/across a row - for i in range(0,len(self.runs),2): - run=self.runs[i] - for p in range(run.i+run.s,run.i+run.n): + for i in range(0, len(self.runs), 2): + run = self.runs[i] + for p in range(run.i + run.s, run.i + run.n): self[p].setVal(True) def check0(self): @@ -120,270 +124,298 @@ # Have to restrict lookahead/behind if there's overlap between runs... # Forwards # ****Assumes margin0 is 0***** and marginN is n - for i in range(0,len(self.runs),2): - run=self.runs[i] + for i in range(0, len(self.runs), 2): + run = self.runs[i] if not run.done: # look for first True cell - for p in range(run.i,min(run.i+run.s+1, - run.right.leftEdge()-run.n if (run.right and run.i>run.n) else 100)): + for p in range( + run.i, + min( + run.i + run.s + 1, + run.right.leftEdge() - run.n if (run.right and run.i > run.n) else 100, + ), + ): if self[p].val: - if p>0 and self[p-1].val==False: + if p > 0 and self[p - 1].val == False: # We're pinned, force start here from now on - self.i=p - self.s=p+1 - self.right=None #Maybe? - l=self.trueRun(p,run.n) - if l==run.n: - if p!=0: - self[p-1].setVal(False) - e=p+l - if e!=self.n: + self.i = p + self.s = p + 1 + self.right = None # Maybe? + l = self.trueRun(p, run.n) + if l == run.n: + if p != 0: + self[p - 1].setVal(False) + e = p + l + if e != self.n: self[e].setVal(False) break break # and backwards - m=len(self.runs)-1 -# if isinstance(self,Column) and self.x==3: -# breakpoint() - for i in range(0,m+1,2): - run=self.runs[m-i] + m = len(self.runs) - 1 + # if isinstance(self,Column) and self.x==3: + # breakpoint() + for i in range(0, m + 1, 2): + run = self.runs[m - i] if not run.done: # look for first True cell - for p in range(run.i+run.s+(run.n-1), - max(run.i-1,run.left.rightEdge()+run.n if (run.left and run.i<self.n-run.n) else 0),-1): + for p in range( + run.i + run.s + (run.n - 1), + max( + run.i - 1, + run.left.rightEdge() + run.n + if (run.left and run.i < self.n - run.n) + else 0, + ), + -1, + ): if self[p].val: - if p<self.n-1 and self[p+1]==False: - self.i=p - self.s=p+1 - self.left=None # Maybe? - l=self.trueRun(p,run.n,-1) - if l==run.n: - e=p-l - if e!=0: + if p < self.n - 1 and self[p + 1] == False: + self.i = p + self.s = p + 1 + self.left = None # Maybe? + l = self.trueRun(p, run.n, -1) + if l == run.n: + e = p - l + if e != 0: self[e].setVal(False) - if p+1!=self.n: - self[p+1].setVal(False) + if p + 1 != self.n: + self[p + 1].setVal(False) break break - def trueRun(self,p,n,delta=1): - res=0 - for i in range(p,p+(delta*n),delta): + def trueRun(self, p, n, delta=1): + res = 0 + for i in range(p, p + (delta * n), delta): if self[i].val: - res+=1 + res += 1 else: break return res + class Row(Vector): - cLet='R' - def __init__(self,n,m,pos): - Vector.__init__(self,n,m) - self.y=pos + cLet = "R" + + def __init__(self, n, m, pos): + Vector.__init__(self, n, m) + self.y = pos def __repr__(self): - return "R@%s%s:%s"%(self.y,self.runs,list.__repr__(self)) + return "R@%s%s:%s" % (self.y, self.runs, list.__repr__(self)) def initDisp(self): - self.width=self.myPrintSize() + self.width = self.myPrintSize() def __str__(self): - return ((self.fmt%(' '.join(str(r) for r in self.runs if isinstance(r,Run))))+ - Vector.__str__(self)) + return ( + self.fmt % (" ".join(str(r) for r in self.runs if isinstance(r, Run))) + ) + Vector.__str__(self) - def updateHeader(self,*,maxWidth=None,r=None,pre=None): + def updateHeader(self, *, maxWidth=None, r=None, pre=None): if maxWidth is None: # update - spacer=(" " if self.runs else "") + spacer = " " if self.runs else "" if pre: - self.infix+=(RedFmt%r)+spacer + self.infix += (RedFmt % r) + spacer else: # post - self.suffix=spacer+RedFmt%r+self.suffix - self.fmt="%s%s%%s%s|"%(self.prespace,self.infix,self.suffix) + self.suffix = spacer + RedFmt % r + self.suffix + self.fmt = "%s%s%%s%s|" % (self.prespace, self.infix, self.suffix) else: # init - self.maxWidth=maxWidth - self.prespace=' '*(maxWidth-self.width) - self.fmt="%s%%s|"%self.prespace - self.infix="" - self.suffix="" + self.maxWidth = maxWidth + self.prespace = " " * (maxWidth - self.width) + self.fmt = "%s%%s|" % self.prespace + self.infix = "" + self.suffix = "" + class Column(Vector): - cLet='C' - def __init__(self,n,m,pos): - Vector.__init__(self,n,m) - self.x=pos + cLet = "C" + + def __init__(self, n, m, pos): + Vector.__init__(self, n, m) + self.x = pos def __repr__(self): - return "C@%s%s:%s"%(self.x,self.runs,list.__repr__(self)) + return "C@%s%s:%s" % (self.x, self.runs, list.__repr__(self)) def initDisp(self): - self.height=self.myPrintSize() + self.height = self.myPrintSize() - def updateHeader(self,*,maxHeight=None,r=None,pre=None): - dprint('CuH',r,pre) + def updateHeader(self, *, maxHeight=None, r=None, pre=None): + dprint("CuH", r, pre) if maxHeight is None: # update if pre: for rc in str(r): - self.infix.append(RedFmt%rc) + self.infix.append(RedFmt % rc) if self.runs: - self.infix.append('-') + self.infix.append("-") else: # post - ins=["-"] if self.runs else [] + ins = ["-"] if self.runs else [] for rc in str(r): - ins.append(RedFmt%r) - self.suffix=ins+self.suffix - dprint('CuH1: |%s|,%s,%s,%s'%(self.prespace,self.infix,self.suffix,self.runs)) - self.header=([" "]*self.prespace)+\ - self.infix+\ - (['-'.join(str(c) for c in self.runs)] if self.runs else [])+\ - self.suffix + ins.append(RedFmt % r) + self.suffix = ins + self.suffix + dprint( + "CuH1: |%s|,%s,%s,%s" % (self.prespace, self.infix, self.suffix, self.runs) + ) + self.header = ( + ([" "] * self.prespace) + + self.infix + + (["-".join(str(c) for c in self.runs)] if self.runs else []) + + self.suffix + ) else: # init - self.maxHeight=maxHeight - self.infix=[] - self.suffix=[] - self.prespace=maxHeight - self.height # pad to same 'height' - self.fmt="%s%%s"%(' '*self.prespace) - header=('-'.join(str(c) for c in self.runs if isinstance(c,Run))) - self.header=self.fmt%header + self.maxHeight = maxHeight + self.infix = [] + self.suffix = [] + self.prespace = maxHeight - self.height # pad to same 'height' + self.fmt = "%s%%s" % (" " * self.prespace) + header = "-".join(str(c) for c in self.runs if isinstance(c, Run)) + self.header = self.fmt % header dprint(self.header) + class Cell: - def __init__(self,row,y,column,x): + def __init__(self, row, y, column, x): # At the intersection of row and column Vectors - self.row=row - self.column=column - self.x=x - self.y=y - self.val=None # three valued: None(unknown), True(filled), False(empty) - self.row[x]=self - self.column[y]=self + self.row = row + self.column = column + self.x = x + self.y = y + self.val = None # three valued: None(unknown), True(filled), False(empty) + self.row[x] = self + self.column[y] = self def __repr__(self): - return "c@(%s,%s):%s"%(self.x,self.y,self.val) + return "c@(%s,%s):%s" % (self.x, self.y, self.val) def __str__(self): - return ' ' if self.val is None else ('\u25A0' if self.val else 'x') + return " " if self.val is None else ("\u25a0" if self.val else "x") - def setVal(self,v): + def setVal(self, v): # Returns true iff old value was None assert v in (True, False) if v == self.val: return False if self.val is not None: - eprint("Reset not allowed: %s -> %s at %s,%s"%(self.val, v, self.x,self.y),err=103) - self.val=v + eprint( + "Reset not allowed: %s -> %s at %s,%s" % (self.val, v, self.x, self.y), + err=103, + ) + self.val = v return True + class Nono(list): - # 0,0 is upper left, so increasing y goes _downwards_, to match the standard layout - def __getitem__(self,xy): - return list.__getitem__(self,xy[1])[xy[0]] + def __getitem__(self, xy): + return list.__getitem__(self, xy[1])[xy[0]] - def __setitem__(self,xy,v): - list.__getitem__(self,xy[1])[xy[0]] = v + def __setitem__(self, xy, v): + list.__getitem__(self, xy[1])[xy[0]] = v - def __init__(self,runsPerRow: list[int], - runsPerCol: list[int], - debug: bool) -> list[list[Cell]]: + def __init__( + self, runsPerRow: list[int], runsPerCol: list[int], debug: bool + ) -> list[list[Cell]]: global SOLVER - self.loop=0 - self.dp='' # old depth hack + self.loop = 0 + self.dp = "" # old depth hack self.debug = debug - self.dstate=[] - SOLVER=self - n=self.n=len(runsPerCol) - list.__init__(self,list(list(list(range(n)) for _ in range(n)))) - if n!=len(runsPerRow): - print("losing r:%s x c:%s"%(len(runsPerRow),n),sys.stderr) + self.dstate = [] + SOLVER = self + n = self.n = len(runsPerCol) + if n != len(runsPerRow): + print("losing r:%s x c:%s" % (len(runsPerRow), n), sys.stderr) exit(1) - self.rc=runsPerRow - self.cc=runsPerCol + self.rc = runsPerRow + self.cc = runsPerCol # print col nums>9 vertically :-( - self.columns=cc=[Column(n,self,i) for i in range(n)] - self.rows=rr=[Row(n,self,i) for i in range(n)] + self.columns = cc = [Column(n, self, i) for i in range(n)] + self.rows = rr = [Row(n, self, i) for i in range(n)] for x in range(n): for y in range(n): - self[x,y]=Cell(rr[y],y,cc[x],x) + self[x, y] = Cell(rr[y], y, cc[x], x) # Need cells in place for the following - for row,runs in zip(rr,runsPerRow): + for row, runs in zip(rr, runsPerRow): row.initRuns(runs) - for col,runs in zip(cc,runsPerCol): + for col, runs in zip(cc, runsPerCol): col.initRuns(runs) - self.maxCRheight=maxCRheight=max(col.height for col in cc) + self.maxCRheight = maxCRheight = max(col.height for col in cc) for c in cc: c.updateHeader(maxHeight=maxCRheight) - maxRRwidth=max(row.width for row in rr) + maxRRwidth = max(row.width for row in rr) for r in rr: r.updateHeader(maxWidth=maxRRwidth) - self.rowfmt="%s|%%s|"%(' '*maxRRwidth) + self.rowfmt = "%s|%%s|" % (" " * maxRRwidth) def __str__(self): - lines=[self.rowfmt%('|'.join([(self.columns[i]).header[j] for i in range(self.n)])) # 'rotate' - for j in range(self.maxCRheight)] - lines+=[str(r) for r in self.rows] + lines = [ + self.rowfmt + % ("|".join([(self.columns[i]).header[j] for i in range(self.n)])) # 'rotate' + for j in range(self.maxCRheight) + ] + lines += [str(r) for r in self.rows] return "\n".join(lines) - def pass0(self): # do columns of empty layout + def pass0(self): # do columns of empty layout for c in self.columns: c.pass0() - dprint('After Pass 0 for columns', self, sep = '\n') + dprint("After Pass 0 for columns", self, sep="\n") for r in self.rows: r.pass0() - dprint('After Pass 0 for rows', self, sep = '\n') + dprint("After Pass 0 for rows", self, sep="\n") for c in self.columns: c.check0() - dprint('After Check 0 for columns', self, sep = '\n') + dprint("After Check 0 for columns", self, sep="\n") dprint(self) for r in self.rows: r.check0() - dprint('After Check 0 for rows', self, sep = '\n') + dprint("After Check 0 for rows", self, sep="\n") def solve(self): self.pass0() + def dprint(*args, **kwargs): - '''Debug print''' + """Debug print""" if SOLVER.debug: print(*args, **kwargs) sys.stdout.flush() -def eprint(*args,**kw): - '''error print''' - print(*args,file=sys.stderr) + +def eprint(*args, **kw): + """error print""" + print(*args, file=sys.stderr) sys.stderr.flush() print(SOLVER) breakpoint() - exit(kw['err']) + exit(kw["err"]) + -if __name__ == '__main__': - if sys.argv[1] == '-d': +if __name__ == "__main__": + if sys.argv[1] == "-d": sys.argv.pop(1) debug = True else: debug = False - if len(sys.argv)>1: - f=open(sys.argv[1]) + if len(sys.argv) > 1: + f = open(sys.argv[1]) else: - f=sys.stdin + f = sys.stdin l = f.readline().rstrip() - vv=l.split('/') - n=int(len(vv)/2) - print('%sx%s puzzle'%(n,n),file=sys.stderr) - cols=[[int(i) for i in v.split('.')] for v in vv[:n]] - rows=[[int(i) for i in v.split('.')] for v in vv[n:]] + vv = l.split("/") + n = int(len(vv) / 2) + print("%sx%s puzzle" % (n, n), file=sys.stderr) + cols = [[int(i) for i in v.split(".")] for v in vv[:n]] + rows = [[int(i) for i in v.split(".")] for v in vv[n:]] - solver=Nono(rows, cols, debug) + solver = Nono(rows, cols, debug) print(solver) print() solver.solve() - print('Done',solver, sep = '\n') - - - + print("Done", solver, sep="\n")
