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.