Mastermind

The aim of the game is to discover the code made up of four 'pegs' randomly selected from 6 possible colours. To place your guess, first click on the colour you want, then click on the black square at the foot of the column you wish to place it in. When you have completed a row, a score will appear on the left. Each black dot means you have a peg of the right colour in the right place. Each white dot means you have the right colour in the wrong place. Until you complete a row, you can overwite a peg which you have already placed with a different colour. The game ends when your guess is correct or your 9th attempt is wrong. Immediately, the correct code replaces the grey circles at the top. Also a button appears inviting you to play another game. Solving the puzzle in less than 5 tries is excellent (or lucky!); 5 or 6 is good; more than 6 suggests that your logic could be improved..
from math import pi from random import randint from pyodide import create_proxy from js import document global clrsel,guess,rownum canvas = document.getElementById("ctx") ctx = canvas.getContext("2d") top = 100 ls = 30 lp = 160 hlft = 134 # hidden code dimensions htop = 16 hw = 232 hh = 52 hth = 1 hcx = 160 hco = 60 hcy = 42 hcr = 20 phy = hcy + hco bdr = 4 # width of border rows = 9 # layout pegsperrow = 4 wht = '#fff' # colours blk = '#000' red = '#f00' yel = '#fd0' grn = '#0c2' blu = '#06f' colours = [wht,blk,red,yel,grn,blu] mgr = '#aaa' bgc = '#ccf' scr = 3 # score hole dimensions scth = 2 phr = 8 # peg hole radius pcx = 40 # piece colour location pcy = 690 prl = 24 rownum = 0 guess = [-1,-1,-1,-1] ### def assess(): global clrsel,endofgame,guess,rownum gf = [0,0,0,0,0,0] # calculate color frequencies in guess for n in guess: gf[n] += 1 wf = [0,0,0,0,0,0] for i in range(len(gf)): # determine total hits if gf[i]<=cf[i]: wf[i] = gf[i] else: wf[i] = cf[i] tp = 0 for n in wf: tp = tp + n bp = 0 # determine number of black pegs for i in range(pegsperrow): if guess[i]==code[i]: bp += 1 wp = tp - bp # & white pegs for i in range(wp): # display pegs circle(4*ls-i*ls,phy+rownum*60,6,wht) for i in range(bp): circle(ls+i*ls,phy+rownum*60,6,blk) guess = [-1,-1,-1,-1] rownum += 1 if bp==pegsperrow: reveal(code) nwg.classList.remove("hide") endofgame = True ### def clickwht(event): global clrsel, endofgame if clrsel==-1 and not endofgame: clrsel = 0 circle(pcx,pcy,prl,wht) ### def clickblk(event): global clrsel, endofgame if clrsel==-1 and not endofgame: clrsel = 1 circle(pcx+hco,pcy,prl,blk) ### def clickred(event): global clrsel, endofgame if clrsel==-1 and not endofgame: clrsel = 2 circle(pcx+2*hco,pcy,prl,red) ### def clickyel(event): global clrsel, endofgame if clrsel==-1 and not endofgame: clrsel = 3 circle(pcx+3*hco,pcy,prl,yel) ## def clickgrn(event): global clrsel, endofgame if clrsel==-1 and not endofgame: clrsel = 4 circle(pcx+4*hco,pcy,prl,grn) ### def clickblu(event): global clrsel, endofgame if clrsel==-1 and not endofgame: clrsel = 5 circle(pcx+5*hco,pcy,prl,blu) ### def clickc0(event): global clrsel,guess,rownum if clrsel>-1: circle(hcx,phy+rownum*hco,hcr,clrsel) circle(pcx+clrsel*hco,pcy,prl+1,bgc) guess[0] = clrsel if -1 not in guess: assess() clrsel = -1 ### def clickc1(event): global clrsel,guess,rownum if clrsel>-1: circle(hcx+hco,phy+rownum*hco,hcr,clrsel) circle(pcx+clrsel*hco,pcy,prl+1,bgc) guess[1] = clrsel if -1 not in guess: assess() clrsel = -1 ### def clickc2(event): global clrsel,guess,rownum if clrsel>-1: circle(hcx+2*hco,phy+rownum*hco,hcr,clrsel) circle(pcx+clrsel*hco,pcy,prl+1,bgc) guess[2] = clrsel if -1 not in guess: assess() clrsel = -1 ### def clickc3(event): global clrsel,guess,rownum if clrsel>-1: circle(hcx+3*hco,phy+rownum*hco,hcr,clrsel) circle(pcx+clrsel*hco,pcy,prl+1,bgc) guess[3] = clrsel if -1 not in guess: assess() clrsel = -1 ### def circle(x,y,r,clr): ctx.beginPath() ctx.arc(x, y, r, 0, 2 * pi) ctx.fillStyle = clr ctx.fill() ### def end(): global clrsel nwg.classList.remove("hide") clrsel = -2 ctx.font = "30px Arial"; ctx.fillText(clrsel, 10, 550); ### def getcode(): lc = len(colours) - 1 code = [randint(0,lc), randint(0,lc), randint(0,lc), randint(0,lc)] cf = [0,0,0,0,0,0] for n in code: cf[n] += 1 return code, cf ### def hidden(): ctx.beginPath(); # hidden code rectangle ctx.rect(hlft,htop,hw,hh); ctx.stroke(); for i in range(pegsperrow): # hidden code circles ctx.beginPath() ctx.arc(hcx+i*hco, hcy, hcr, 0, 2 * pi) ctx.fillStyle = mgr ctx.fill() ### def holesscr(): for i in range(rows): # score holes for j in range(pegsperrow): ctx.beginPath() ctx.arc(ls+j*ls,phy+i*hco, scr, 0, 2 * pi) ctx.stroke() ### def holespeg(): for i in range(rows): # peg holes for j in range(pegsperrow): ctx.beginPath() ctx.arc(hcx+j*hco, phy+i*hco, phr, 0, 2 * pi) ctx.fillStyle = blk ctx.fill() ### def newgame(event): nwg.classList.add("hide") ctx.fillStyle = bgc ctx.fillRect(2,2,576,736) init() ### def reveal(code): for i in range(pegsperrow): circle(hcx+i*hco,hcy,hcr,colours[code[i]]) ### def init(): global cf, clrsel, code, endofgame, guess global rownum, textsur endofgame = False code, cf = getcode() clrsel = -1 rownum = 0 guess = [-1,-1,-1,-1] hidden() # board layout holesscr() holespeg() ## init() click_proxy = create_proxy(newgame) nwg = document.getElementById("ng") nwg.addEventListener("click",click_proxy) click_proxy = create_proxy(clickc0) document.getElementById("c0").addEventListener("click",click_proxy) click_proxy = create_proxy(clickc1) document.getElementById("c1").addEventListener("click",click_proxy) click_proxy = create_proxy(clickc2) document.getElementById("c2").addEventListener("click",click_proxy) click_proxy = create_proxy(clickc3) document.getElementById("c3").addEventListener("click",click_proxy) click_proxy = create_proxy(clickwht) document.getElementById("bwht").addEventListener("click",click_proxy) click_proxy = create_proxy(clickblk) document.getElementById("bblk").addEventListener("click",click_proxy) click_proxy = create_proxy(clickred) document.getElementById("bred").addEventListener("click",click_proxy) click_proxy = create_proxy(clickyel) document.getElementById("byel").addEventListener("click",click_proxy) click_proxy = create_proxy(clickgrn) document.getElementById("bgrn").addEventListener("click",click_proxy) click_proxy = create_proxy(clickblu) document.getElementById("bblu").addEventListener("click",click_proxy)