usunfish_chess.py

Created by fizban

Created on January 13, 2025

8.22 KB

Grafical user interface of the chess program based on Sunfish. It has 7 levels of difficulty (use Ln to change levels) and you can undo the last move (only the last one, with backspace). Use Pi to switch to black (rotates the board and makes the engine move) You select the piece to move with the cursor keys and OK. You can cancel the move selecting again the piece.

Requires the usunfish_engine.py to be also loaded in the calculator.

github repository


try:import usunfish_engine as u
except:assert False,'\nusunfish_engine.py missing'
from kandinsky import fill_rect as fr,draw_string as ds
from ion import*
from ion import keydown as kd
from time import sleep as slp
pc_colors=65535,0,63422,52889,65535,0,38066,50646
bg=59225,31883
tcol=59225,38066
pcs='DhLA3AweHIHmGIHmGIHmGIHmGMHhyB5hhB7hRJ6UweHMHhyBPiGEE+oUAT7iEBPuoRPuod3V','EhXEHMjAEhzEwanIweZcCBYeYcgSXqGEHu4UQe7hQB7uYQHmXqEOJUHmEBZYHqEAXB7hDEHuIQwB7iFMHuYUzd1IH/8QQe7mFB7uYU3d0A==','DxbA3ExWXAweHAxWHEwBIcjBYUHIGhASGEGhQWFEGhAaFAHhAeEAHhAeEAHp4QAe6hAB7qEAHuoQQe4hSB6hhN2UAf+xAe7iEe7iHd2Q','ExXMzIzMyImFiUgWEBYQFhSBaWlhSB7uFIHu4Uwe5hjAHuHMAe4cwB7hzAHuHMAe4cwB7hzAHuHMAe4czd2IH/8UQe7mEEHu5hBN3dA=','FhXEnJxMAaFBocDAGhQaHAwBoUVhwAlBIcEhSQGhAWFBYQGhGhAWFBYQGhFhQWFBYUFhAWEBYUFhAWEEFlpaVhRBoe4aFIHu5hiB7uYYwe7hzB7uHMAe5hwMDd3Awf/xyB7uYYge7mGI3d2A','FhbMDcwMwBYcwMheXIyB5hyMheXIzB4cyNXtWEHu7hQB7u5hAenmnhGhgeGBoRoYHhgaEBoUHhQaEAHhAeEB4QQeHmHhSB7uYYwe7hzA3dwMH/8cge7mGIHu5hiN3dg='
font=15329376,7968529,6887776,15310472,14809440,2257452,261003744,10065681,7479858,15820950,6915222,4519268,6915871,6919958,2237583,6919830
_HIGHLIGHT=const(11615)
def rgb(c):return(c>>11&31)*255//31,(c>>5&63)*255//63,(c&31)*255//31
def font_gen(n):
  for i in range(0,28):yield(1,(1-(font[n]>>i)&1)*65535)
def gcl(data,colors,off):yield from(((nibble>>2)+1,colors[(nibble&3)+off])for byte in data for nibble in[byte>>4,byte&15])
def dr_ln(x,y,lx,ly,c):
  if c!=(255,255,255):fr(x,y,lx,ly,c)
def dr_img(w,h,x,y,gdc):
  x0,y0,pc,tl=x,y,-1,0
  while True:
    l,c=next(gdc);c=rgb(c)
    if x+l>x0+w:
      if tl>0:dr_ln(x-tl,y,tl,1,pc)
      y,x,pc,tl=y+1,x0,c,0
    if pc!=c:
      if pc!=-1 and tl>0:dr_ln(x-tl,y,tl,1,pc)
      pc,tl=c,l
    else:tl+=l
    x+=l
    if x==x0+w and y==y0+h-1:break
  dr_ln(x-tl,y,tl,1,pc)
def dr_sq(x,y):fr(x*26+11,y*26+1,26,26,rgb(bg[x%2^y%2]))
def draw_board():
  i=1;fr(0,0,222,222,(255,255,255));fr(10,0,210,210,(0,0,0));fr(11,1,208,208,rgb(bg[0]))
  for x in range(8):
    for y in range(i,8,2):dr_sq(x,y)
    i=1-i
def cur(c,x,y):fr(x,y,24,2,c);fr(x,y+24,24,2,c);fr(x,y,2,24,c);fr(x+24,y,2,26,c)
def dr_cur(sq,c=None):
  y,x=divmod(sq,8)
  if c is None:c=bg[x%2^y%2]
  cur(rgb(c),x*26+11,y*26+1)
def move_cur(v):
  global cind,gm
  if not gm:return
  dr_cur(gm[cind],_HIGHLIGHT)
  if abs(v)==1:cind=(cind+v)%len(gm)
  else:
    match=False;sq=gm[cind]
    while not match:
      sq+=v;y=sq//8;isq=-1
      for isq in(sq for sq in sorted(gm)if sq//8==y):
        if isq==sq:cind=gm.index(isq);match=True;break
        elif isq!=-1 and isq>sq:match=True;cind=gm.index(isq);break
      if not match:
        if isq!=-1:cind=gm.index(isq);match=True
        elif sq+v>63 or sq+v<0:match=True
  dr_cur(gm[cind],0)
def dr_high(on):
  global cind,gm
  for cind in range(len(gm)):
    if on:dr_cur(gm[cind],_HIGHLIGHT)
    else:dr_cur(gm[cind])
def g_gm1():global gm;gm=list(set((m&16383)>>8 for m in u.gen_moves()if not u.can_kill_king(m&16383)));gm.sort()
def set_initial_sq(i=0):
  global gm,cind,origin;origin=True;g_gm1();dr_high(True)
  if len(gm)>i:cind=i
  else:cind=0
  if len(gm)==0 and u.can_kill_king(0):ds('Checkmate!',225,20);return
  elif len(gm)==0:ds('Stalemate!',225,20);return
  dr_cur(gm[cind],0)
def dr_pc(x,y,p):piece=pcs[p&7];data=u.fb64(piece);w,h=next(data),next(data);y=y*26+26-h-1;x=x*26+(24-w)//2+12;dr_img(w,h,x,y,gcl(data,pc_colors,(p>>3)*4))
def dr_mv(o,d,p):
  if p|8==13 and abs(o-d)==2:
    if u.g_trn()==1 and not invert or u.g_trn()==0 and invert:d2=63-d;inv=-1
    else:d2=d;inv=1
    r,c=divmod(d,8)
    for i in range(-2,3):
      if 0<=c+i<8:
        if 0<c+i<8:
          dr_sq(c+i,r);rk=u.board[d2+i*inv]
          if rk|8!=14:
            if u.g_trn()==0:rk=rk|8
            else:rk=rk&7
            dr_pc(c+i,r,rk)
  else:dr_sq(o&7,o>>3);dr_sq(d&7,d>>3);dr_pc(d&7,d>>3,p)
def is_end_game():
  global gm
  if u.can_kill_king(0,ccheck=False):
    g_gm1()
    if not gm:ds('Checkmate!',220,18);return True
    else:ds('  Check!',220,18)
  else:
    g_gm1()
    if not gm:ds('Stalemate',220,18);return True
  if u.threefold():ds('Draw-rep',220,18);gm=[];return True
def dr_trn(trn,ply):
  if ply%2==0:ds(str(ply//2),220,0)
  fr(234,2,13,13,rgb(tcol[trn]))
def upd_moves(mv):
  global prev_movs,undo,trn
  if mv!=0:undo.append(u.pscore);undo.append(u.wc_bc_ep_kp);undo.append(u.last_mv<<16|u.op_mode<<15|u.op_ind);dif=u.mk_mv(mv);trn=u.g_trn();undo.append(dif);undo=undo[-8:];w_mv=u.render_mv(mv,1-trn);prev_movs=mv.to_bytes(2,'big')+prev_movs[:6]
  else:trn=u.g_trn()
  fr(224,2,13,13,rgb(tcol[trn]));ds('          ',240,0);ds('          ',220,20)
  for i in range(0,8,2):
    if i<len(prev_movs):
      w_mv=u.render_mv(int.from_bytes(prev_movs[i:i+2],'big'),(1+trn+i//2)%2)
      if(u.ply-i//2)%2!=0:ds(str((u.ply-i//2)//2+1)+'.',222,38+i*10)
      else:ds('          ',220,38+i*10)
      fr(250,40+i*10,13,13,rgb(tcol[(1+trn+i//2)%2]));ds(w_mv,265,38+i*10)
    else:ds('          ',220,38+i*10)
def think():
  global gm,trn;ds('Thinking',240,0);gm=[m&16383 for m in u.gen_moves()if not u.can_kill_king(m&16383)];best=0
  for(_depth,gamma,score,mv)in u.search():
    if score>=gamma:best=mv
    if u.nodes>125*2**lvl:
      if best==0:best=mv
      if best in gm:gm=None;break
      elif lvl==0:
        if len(gm)>0:best=gm[-1];break
        else:best=0;break
      elif _depth>1:best=0;break
  ds('#Nod:'+(str(u.nodes)+'  'if u.nodes>0 else'opng'),222,125);isqb=0;dsqb=0
  if best!=0:
    dsqb=best&63;isqb=best>>8;dsqb=63-dsqb;isqb=63-isqb;upd_moves(best)
    if trn==1:p=u.board[dsqb]^8
    else:p=u.board[dsqb]
    dr_mv(isqb,dsqb,p);dr_cur(isqb,65348);dr_cur(dsqb,65348)
    if not is_end_game():set_initial_sq(0)
    u.upd_hist()
  else:ds('Resign',220,18);gm=[]
  return isqb,dsqb
def draw_pcs():
  r=0;c=0
  if invert:
    u.board.reverse()
    if u.g_trn()==1:u.reverse()
  elif u.g_trn()==1:u.reverse()
  for p in u.board:
    if p&7<6:dr_pc(c,r,p)
    c=c+1 if c<7 else 0
    if c==0:y=r*26+18;fnt=8+r if invert else 15-r;dr_img(4,7,3,y,font_gen(fnt));r=r+1
  for i in range(8):x=i*26+20;fnt=7-i if invert else i;dr_img(4,7,x,212,font_gen(fnt))
  if invert:
    u.board.reverse()
    if u.g_trn()==1:u.reverse()
  elif u.g_trn()==1:u.reverse()
def dr_lvl(lvl):ds('[Ln] Lvl '+str(lvl),220,195)
invert=False
draw_board()
cind=0
draw_pcs()
keys='\x00\x03\x01\x02\x13\x1b\x04\x11'
key_pressing='\x00\x00\x00\x00\x00\x00\x00\x00'
prev_movs=b''
undo=[]
lvl=0
ds('[Bk] Undo',220,155)
ds('['+chr(960)+']  Rot',220,175)
dr_lvl(lvl)
set_initial_sq(4)
trn=0
upd_moves(0)
dsqb=-1
isqb=-1
while True:
  for(ik,k)in enumerate(keys):
    k=ord(k)
    if kd(k):
      if not ord(key_pressing[ik]):
        key_pressing=key_pressing[:ik]+'\x01'+key_pressing[ik+1:]
        if k==KEY_LEFT:move_cur(-1)
        elif k==KEY_RIGHT:move_cur(1)
        elif k==KEY_UP:move_cur(-8)
        elif k==KEY_DOWN:move_cur(8)
        elif k==KEY_BACKSPACE:
          if len(undo)>7:
            for i in range(2):
              u.reverse();u.restore(int.from_bytes(prev_movs[i*2:i*2+2],'big'),undo.pop());v=undo.pop();u.last_mv=v>>16;u.op_ind=v&16383;u.op_mode=v>>15&1;u.wc_bc_ep_kp=undo.pop();u.pscore=undo.pop();u.ply-=1
              if i==0 and trn==u.g_trn():break
            prev_movs=prev_movs[4:];draw_board();draw_pcs();upd_moves(0);u.history=u.history[:-2];set_initial_sq(0)
        elif k==KEY_LN:lvl=(lvl+1)%7;dr_lvl(lvl)
        elif k==KEY_PI:
          if not is_end_game():draw_board();invert=not invert;draw_pcs();isqb,dsqb=think()
        elif k==KEY_EXE or k==KEY_OK:
          if origin:ds(u.render(gm[cind]),245,0);origin=False;iind=cind;isq=gm[cind];dr_high(False);gm=list(set(m&63 for m in u.gen_moves()if(m&16383)>>8==isq and not u.can_kill_king(m&16383)));gm.append(isq);gm.sort();dr_high(True);cind=gm.index(isq);dr_cur(isq,0)
          else:
            origin=True;dind=cind;dr_high(False)
            if dind==gm.index(isq):set_initial_sq(iind);ds('     ',225,40)
            else:
              if isqb!=-1:dr_cur(isqb);dr_cur(dsqb)
              mv=isq<<8|gm[dind];trn=1-u.g_trn();w_mv=None;dsq=gm[dind];mv=isq<<8|dsq|192;upd_moves(mv)
              if u.g_trn()==0:p=u.board[63-dsq]
              else:p=u.board[63-dsq]^8
              dr_mv(isq,dsq,p);u.upd_hist()
              if not is_end_game():isqb,dsqb=think()
              else:trn=u.g_trn()^1
    else:key_pressing=key_pressing[:ik]+'\x00'+key_pressing[ik+1:]
  slp(.05)

During your visit to our site, NumWorks needs to install "cookies" or use other technologies to collect data about you in order to:

With the exception of Cookies essential to the operation of the site, NumWorks leaves you the choice: you can accept Cookies for audience measurement by clicking on the "Accept and continue" button, or refuse these Cookies by clicking on the "Continue without accepting" button or by continuing your browsing. You can update your choice at any time by clicking on the link "Manage my cookies" at the bottom of the page. For more information, please consult our cookies policy.