Sudoku

from random import randint from pyodide import create_proxy from js import console, document global es,hd,md,ng,ra,ro,sv,vh,stack,clr canvas = document.getElementById("ctx") ctx = canvas.getContext("2d") e0 = ' 42 63 3 2 3 4 89 4 3743 6296 1 47 1 8 9 1 27 19 ' e1 = '5 1 7 4 16 8 5986 7 4 45 29 18 921 8 4 36 6 158323 9 74 ' e2 = ' 2 9 5 465 132 2376 2 8 1 5981 4276 4 9 5 5236 839 726 1 9 2 ' ea = [e0,e1,e2] m0 = ' 281 6 53 6 5 4 3 9 92 7 6 8 6 92 39 7 4 1 5' m1 = '6 8 4 6 7 5 9 1 87 3 35 1 7 461 9 5 8 24 6 ' m2 = '13 8 2 94 35 42 36 2 1 9 4 16 45 59 38 4 2 47' m3 = ' 5 84 7 6859 64 1 3 7 2 2 3 79 7864 2 41 2 ' m4 = ' 7 6 32 5 81 9 7 3 9 6 4 2 1 4 27 8 41 5 7 8 ' m5 = ' 8 92 32 5 9 1 43 4 2 6 45 1 6 4 38 5139 7 ' m6 = ' 97 1 5 7 14 5 6 2 3 5 46 1 9 57 26 8 9 2 3 ' m7 = ' 9 85 3 3 95 1 8 3 9 5 17 6 1 68 2 3 1 8 9 76 ' mo = [m0,m1,m2,m3,m4,m5,m6,m7] h0 = '5 1 819 5 5 6 9 73 3 8 496 4 21 4 273 ' h1 = ' 1 27 17 5 9 3 2 3 6 9 8 2 8 1 6 2 9 3 4 79 76 1 ' h2 = '3 14 386 75 1 4 6 924 3 3 719 62 7 3 ' h3 = ' 7 1 64 5 98 31 57 8 2 4 9 56 56 21 3 93 4 5 ' h4 = ' 5 1 497 4 7 8 2 7 3 8 5 1 5 1 8 4 6 8 9 8 7 781 3 5 ' h5 = ' 452 8318 6 1 2 5 6 3 4 2 52 37 28 4 7 5 ' h6 = ' 7 5 1436 7 1 28 93 7 2 65 6 8 891 5 1 7' h7 = ' 29 32 59 5 46 4 19 1 7 4 8 5 3 1 7 5 3 4 5 2' ha = [h0,h1,h2,h3,h4,h5,h6,h7] v0 = ' 31 5 4 91 9 7 5 39 28 4 473 6 8 23 98 7 ' v1 = ' 2 4 3 7 5 9 9 7 2 8 9 56 34 5 31 9 8 4 2 6' v2 = ' 4 1 3 8 9 9 2 3 9 7 41 25 46 926 3 5 713 4' v3 = '3 1 6 5 1 2 4 63 5 4 2 9 1 82 13 9 4 7 7 6 ' v4 = ' 62 47 5 2 75 3 8 9 6 1 7 8 5 11 2 8 31 54 ' v5 = ' 49 38 5 2 9 17 94 5 921 4 2 4 6 3 1 2 63 7 ' v6 = '5 3 8 7 27 185 6 165 97 75 81 9 213 7 7 4 1 2' v7 = ' 3 2 7 6 9 1 3 8 4 7 2 5 8 9 6 8 5 4 1 4 6 4 9 ' ve = [v0,v1,v2,v3,v4,v5,v6,v7] font1 = '40px arial' font2 = '16px arial' cs = 60 bcells = 3 # cells in a row or column of a block ucells = 9 # cells in a block, row or column cells = 81 # total cells in the grid norm = ['1','2','3','4','5','6','7','8','9'] # validation list clr1 = '#f00' clr2 = '#0f0' clr = clr1 ### def back(evnt): # back button handler global added,savedstack,stack if len(stack)<=cells: stack = [] deladded() else: stack = stack[:-cells] added = stack[-cells:] dspadded() ### def board(): # display board for i in range(4): ctx.beginPath() ctx.rect(0,i*180,543,3) ctx.fill() for i in range(4): ctx.beginPath() ctx.rect(i*180,0,3,543) ctx.fill() for i in range(10): ctx.beginPath() ctx.rect(0,i*60,543,1) ctx.fill() for i in range(10): ctx.beginPath() ctx.rect(i*60,0,1,543) ctx.fill() ### def clic(): # for debugging global clr if clr==clr1: clr = clr2 else: clr = clr1 ctx.fillStyle = clr ctx.fillRect(0,0,20,20) ### def clik(evnt): # click handler global added,stack x = evnt.offsetX y = evnt.offsetY xcl = int(x/cs) ycl = int(y/cs) cl = xcl+ycl*ucells if added[cl]!='*': xn = int(3*(x/cs-xcl)) yn = int(3*(y/cs-ycl)) n = xn+3*yn+1 x0 = xcl*cs+19 y0 = ycl*cs+45 if evnt.button==0: # left click if added[cl]==' 'or added[cl]==' ': # insert big ctx.font = font1 ctx.fillStyle = '#0a0' ctx.fillText(str(n),x0,y0) added[cl] = str(n) else: # delete all ctx.fillStyle = '#fff' ctx.fillRect(x0-16,y0-41,56,56) added[cl] = ' ' elif evnt.button==1: # wheel click if len(added[cl])==1: # add small to blank if added[cl]==' ': m = (n-1)*' ' + str(n) + (ucells-n)*' ' xc,yc = dspk(x0+5,y0-10,str(n)) ctx.font = font2 ctx.fillStyle = '#000' ctx.fillText(str(n),xc,yc) added[cl] = m else: if added[cl][n-1]==' ': # add small to others m = added[cl][:n-1] + str(n) + added[cl][n:] xc,yc = dspk(x0+5,y0-10,str(n)) ctx.font = font2 ctx.fillStyle = '#000' ctx.fillText(str(n),xc,yc) else: m = added[cl][:n-1] + ' ' + added[cl][n:] # delete small xc,yc = dspk(x0+5,y0-10,str(n)) ctx.fillStyle = '#fff' ctx.fillRect(xc-3,yc-14,17,17) added[cl] = m stack = stack + added if ' ' not in added: if valid(): endofgame() ### def deladded(): # delete added cells global added for i in range(cells): if added[i]!=' ' and added[i]!='*': x = i % ucells y = int(i/ucells) x0 = x*cs+3 y0 = y*cs+4 ctx.fillStyle = '#fff' ctx.fillRect(x0,y0,56,56) added[i] = ' ' ### def dsp(sdk): # display the puzzle global added,es,hd,md,ra,sv,vh es.classList.add("hide") md.classList.add("hide") hd.classList.add("hide") vh.classList.add("hide") sv.classList.remove("hide") document.getElementById("bak").classList.remove("hide") ra.classList.remove("hide") added = list(' ' * cells) for i in range(cells): n = sdk[i] x = i % ucells y = int(i/ucells) x0 = x*cs+20 y0 = y*cs+45 ctx.font = font1 ctx.fillStyle = '#000' ctx.fillText(n,x0,y0) if sdk[i]!=' ': added[i] = '*' ### def dspadded(): # display the added cells global added for i in range(cells): x = i % ucells y = int(i/ucells) n = x+3*y+1 x0 = x*cs+19 y0 = y*cs+45 char = added[i] if len(char)==1: if char!='*': if char==' ': ctx.fillStyle = '#fff' ctx.fillRect(x0-15,y0-41,52,52) else: ctx.font = font1 ctx.fillStyle = '#0a0' ctx.fillText(char,x0,y0) else: for j in range(ucells): ch = char[j] xc,yc = dspk(x0+5,y0-10,str(j+1)) if ch==' ': ctx.fillStyle = '#fff' ctx.fillRect(xc-3,yc-14,17,17) else: ctx.font = font2 ctx.fillStyle = '#000' ctx.fillText(str(ch),xc,yc) ### def dspk(x0,y0,nu): # small number positions if nu=='1' or nu=='4' or nu=='7': x0 -= 20 elif nu=='3' or nu=='6' or nu=='9': x0 += 20 if nu=='1' or nu=='2' or nu=='3': y0 -= 20 elif nu=='7' or nu=='8' or nu=='9': y0 += 20 x0 += 2 y0 += 2 return x0,y0 ### def endofgame(): # end of game global added,bk,ng,ra,ro,sv added = list('*' * cells) sv.classList.add("hide") ra.classList.add("hide") ba.classList.add("hide") ro.classList.add("hide") ng.classList.remove("hide") ### def filhd(evnt): # hard button handler global gfile if evnt.button==0: n = randint(0,len(ha)-1) gfile = ha[n] gfile = transform(gfile) dsp(gfile) ### def filmd(evnt): # moderate button handler global gfile if evnt.button==0: n = randint(0,len(mo)-1) gfile = mo[n] gfile = transform(gfile) dsp(gfile) ### def filsy(evnt): # easy button handler global gfile if evnt.button==0: n = randint(0,len(ea)-1) gfile = ea[n] gfile = transform(gfile) dsp(gfile) ### def filvh(evnt): # very hard button handler global gfile if evnt.button==0: n = randint(0,len(ve)-1) gfile = ve[n] #gfile = '32691745897465813218542376926378591459813427674129638545236189783957264161784952 ' gfile = transform(gfile) dsp(gfile) ### def newgame(evnt): # new game button handler global es,hd,md,ng,vh for i in range(cells): # delete previous game x = i % ucells y = int(i/ucells) x0 = x*cs+19 y0 = y*cs+45 ctx.fillStyle = '#fff' ctx.fillRect(x0-16,y0-41,56,56) ng.classList.add("hide") # adjust buttons es.classList.remove("hide") md.classList.remove("hide") hd.classList.remove("hide") vh.classList.remove("hide") init() ### def restart(evnt): # restart button handler global added ro.classList.add("hide") deladded() ### def restore(evnt): # restore button handler global added,saved,savedstack,stack deladded() # delete added cells added = list(saved) stack = list(savedstack) dspadded() ### def save(evnt): global ro,saved,savedstack,stack,ye saved = tuple(added) savedstack = tuple(stack) ro.classList.remove("hide") ## def transform(dat): # transform puzzle dat = trnumeric(dat) # numeric transform dat = trcols(dat) # swap columns within blocks dat = trrows(dat) # swap rows of blocks dat = trrotate(dat) # rotate return dat ### def trcols(dt): a = randint(0,3) b = randint(0,3) c = randint(0,3) ds = '' for i in range(cells): x = i % ucells y = int(i/ucells) z = int(x/bcells) w = x % bcells if z==0: m = a elif z==1: m = b elif z==2: m = c if m==0 or (m==3 and w==1): n = 0 elif m==1 and w!=2: n = 1 elif (m==2 or m==3) and w==0: n = 2 elif m==2 and (w==1 or w==2): n = -1 elif (m==1 or m==3) and w==2: n = -2 ds += dt[i+n] return ds ### def trnumeric(dt): a = randint(0,8) b = randint(1,9) c = randint(1,9) ds = '' for i in range(cells): char = dt[i] if char!=' ': n = int(char) + a if n>ucells: n -= ucells if n==b: n = c elif n==c: n = b char = str(n) ds += char return ds ### def trrotate(dt): a = randint(0,3) ds = '' for i in range(cells): x = i % ucells y = int(i/ucells) if a==0: n = x + y*ucells if a==1: n = y + ucells*(8-x) if a==2: n = 8-x + ucells*(8-y) if a==3: n = 8-y + ucells*x ds += dt[n] return ds ### def trrows(dt): ran = [randint(0,5),randint(0,5),randint(0,5)] off = [[0,1,2],[0,2,1],[1,0,2],[1,2,0],[2,0,1],[2,1,0]] ds = '' for i in range(bcells): m = ran[i] n = (off[m][0] + i*bcells) * ucells ds += dt[n:(n+ucells)] n = (off[m][1] + i*bcells) * ucells ds += dt[n:(n+ucells)] n = (off[m][2] + i*bcells) * ucells ds += dt[n:(n+ucells)] return ds ### def valid(): # validate completed puzzle global added,gfile flag = True fs = list(" "*cells) # final state to check for i in range (cells): if added[i]=='*': fs[i] = gfile[i] else: fs[i] = added[i] for y in range(ucells): # check rows rw = list(fs[y*ucells:(y+1)*ucells]) rw.sort() if rw!=norm: flag=False for x in range(ucells): # check columns co = list(' '*ucells) for y in range(ucells): co[y] = fs[y*ucells+x] co.sort() if co!=norm: flag=False for y in range(0,ucells,bcells): # check blocks for x in range(0,ucells,bcells): bl = list(' '*ucells) for yb in range(bcells): for xb in range(bcells): a = x + xb + (y + yb)*ucells bl[xb+3*yb] = (fs[x + xb + (y + yb)*ucells]) bl.sort() if bl!=norm: flag=False return flag ### def init(): # initiallization global added,stack stack = [] added = list('*' * cells) ### board() document.getElementById("ctx").addEventListener("mousedown",create_proxy(clik)) on_click = create_proxy(filsy) es = document.getElementById("eas") es.addEventListener("mouseup",on_click) on_click = create_proxy(filmd) md = document.getElementById("mod") md.addEventListener("mouseup",on_click) on_click = create_proxy(filhd) hd = document.getElementById("hrd") hd.addEventListener("mouseup",on_click) on_click = create_proxy(filvh) vh = document.getElementById("vhd") vh.addEventListener("mouseup",on_click) on_click = create_proxy(save) sv = document.getElementById("sav") sv.addEventListener("mouseup",on_click) on_click = create_proxy(restore) ro = document.getElementById("ror") ro.addEventListener("mouseup",on_click) on_click = create_proxy(back) ba = document.getElementById("bak") ba.addEventListener("mouseup",on_click) on_click = create_proxy(restart) ra = document.getElementById("rar") ra.addEventListener("mouseup",on_click) on_click = create_proxy(newgame) ng = document.getElementById("new") ng.addEventListener("mouseup",on_click) init()
  The aim of the game is to fill in all blank cells so that each row, column & block (defined by thicker lines) contains each of the digits 1..9.
  First select the level of difficulty & a puzzle will be displayed. A number is entered by left clicking on a cell. The actual digit produced is determined by where in the cell you click. The cell location to produce each digit is as follows:
  1 2 3
  4 5 6
  7 8 9
Thus, to insert a 7, you click on the lower left hand corner of the cell. The given numbers in the puzzle are black & the entered numbers are dark green. A number can be deleted by clicking again anywhere in the cell.
  It is possible to enter small numbers representing possible candidates for a cell by clicking the scroll wheel. All 9 digits can be entered by clicking the same positions as shown above. Again, these small numbers can be deleted by clicking directly on them using the scroll wheel. If you left click the cell, all of the small numbers are deleted.
  When the valid solution is reached, a [NEW GAME] button appears.
  If you want to save temporarily a certain state which you may wish to come back to later, press [SAVE]. The [RESTORE] button will then appear to enable you to go back.
  The [BACK} button lets you remove your latest entries one by one.
  If you make mistakes & get in an impossible situation, you can click the [RESTART] button.