tictac.py

Created by laigna

Created on March 14, 2026

4.58 KB

2-player X/O. Positions 1–9, detects wins/draws.


# Tic-Tac-Toe - NumWorks
# Created by Alvar Laigna - https://alvarlaigna.com
# Arrows:move OK:place Back:quit
from kandinsky import fill_rect as F,draw_string as D
from ion import keydown as K
from time import sleep as Z
from random import choice
SW,SH=320,222
BK=(0,)*3;WH=(255,)*3
BL=(50,110,230);GR=(60,60,60)
XC=(230,70,70);OC=(70,180,230)
WC=(0,220,100);DG=(100,)*3
CS=56;PAD=4
OX=(SW-CS*3)//2;OY=(SH-CS*3)//2+6

okv=1 if K(4)or K(52) else 0

def okd():
 return K(4)or K(52)

def okp():
 global okv
 d=okd()
 if d and not okv:
  okv=1
  return True
 if not d:okv=0
 return False

def wup():
 while okd():Z(0.02)
 Z(0.12)

def title():
 F(0,0,SW,SH,BK)
 D("TIC-TAC-TOE",80,18,(0,200,200),BK)
 # Draw mini board
 for i in range(1,3):
  F(OX+i*CS-1,OY-30,2,CS*3-30,(40,)*3)
  F(OX,OY+i*CS-1-30,CS*3,2,(40,)*3)
 dx(1,0,OY-30);do(2,0,OY-30)
 dx(0,1,OY-30);do(1,1,OY-30)
 do(0,2,OY-30);dx(2,2,OY-30)
 dx(1,2,OY-30)
 sel=1
 opts=["vs Friend","vs CPU"]
 while True:
  for i in range(2):
   y=132+i*28
   if i==sel:
    F(80,y-2,160,22,BL)
    D("> "+opts[i],85,y,WH,BL)
   else:
    F(80,y-2,160,22,BK)
    D("  "+opts[i],85,y,(120,)*3,BK)
  D("Up/Down + OK",90,200,(80,)*3,BK)
  while True:
   if K(17):return 0
   if K(1)or K(2):sel=1-sel;Z(0.15);break
   if okp():wup();return sel+1
   Z(0.04)

def dx(c,r,oy=None):
 if oy is None:oy=OY
 x,y=OX+c*CS+10,oy+r*CS+10
 s=CS-20
 for i in range(s):
  F(x+i,y+i,3,3,XC)
  F(x+s-i-1,y+i,3,3,XC)

def do(c,r,oy=None):
 if oy is None:oy=OY
 cx2,cy2=OX+c*CS+CS//2,oy+r*CS+CS//2
 rd=CS//2-10
 for a in range(-rd,rd+1):
  for b in range(-rd,rd+1):
   d=a*a+b*b
   if(rd-3)**2<=d<=rd*rd:
    F(cx2+a,cy2+b,1,1,OC)

def board():
 F(0,0,SW,SH,BK)
 D("TIC-TAC-TOE",90,2,(80,)*3,BK)
 for i in range(1,3):
  F(OX+i*CS-1,OY,2,CS*3,GR)
  F(OX,OY+i*CS-1,CS*3,2,GR)

def cell(c,r,bg):
 F(OX+c*CS+1,OY+r*CS+1,CS-2,CS-2,bg)

def cursor(c,r):
 x,y=OX+c*CS,OY+r*CS
 # Blue corners
 for ox,oy,dw,dh in((0,0,12,3),(0,0,3,12),
  (CS-12,0,12,3),(CS-3,0,3,12),
  (0,CS-3,12,3),(0,CS-12,3,12),
  (CS-12,CS-3,12,3),(CS-3,CS-12,3,12)):
  F(x+ox,y+oy,dw,dh,BL)

def clear_cursor(c,r):
 x,y=OX+c*CS,OY+r*CS
 for ox,oy,dw,dh in((0,0,12,3),(0,0,3,12),
  (CS-12,0,12,3),(CS-3,0,3,12),
  (0,CS-3,12,3),(0,CS-12,3,12),
  (CS-12,CS-3,12,3),(CS-3,CS-12,3,12)):
  F(x+ox,y+oy,dw,dh,BK)
 # Redraw grid lines if needed
 if c>0:F(OX+c*CS-1,OY+r*CS,2,CS,GR)
 if r>0:F(OX+c*CS,OY+r*CS-1,CS,2,GR)

def check(b,p):
 wins=[(0,1,2),(3,4,5),(6,7,8),
  (0,3,6),(1,4,7),(2,5,8),
  (0,4,8),(2,4,6)]
 for w in wins:
  if b[w[0]]==b[w[1]]==b[w[2]]==p:
   return w
 return None

def ai_move(b,me,op):
 # Win if possible
 for i in range(9):
  if b[i]==0:
   b[i]=me
   if check(b,me):b[i]=0;return i
   b[i]=0
 # Block opponent win
 for i in range(9):
  if b[i]==0:
   b[i]=op
   if check(b,op):b[i]=0;return i
   b[i]=0
 # Center
 if b[4]==0:return 4
 # Corners
 cn=[i for i in(0,2,6,8)if b[i]==0]
 if cn:return choice(cn)
 # Edges
 ed=[i for i in(1,3,5,7)if b[i]==0]
 if ed:return choice(ed)
 return -1

def hline(w):
 for i in w:
  c,r=i%3,i//3
  cell(c,r,BK)
  F(OX+c*CS+4,OY+r*CS+4,CS-8,CS-8,(20,50,20))

def game(mode):
 while True:
  b=[0]*9
  pl=1;cx,cy=1,1
  vs_cpu=mode==2
  board()
  cursor(cx,cy)
  msg=""
  while True:
   if K(17):return
   mv=False
   oc,or2=cx,cy
   if K(0)and cx>0:cx-=1;mv=True
   elif K(3)and cx<2:cx+=1;mv=True
   elif K(1)and cy>0:cy-=1;mv=True
   elif K(2)and cy<2:cy+=1;mv=True
   elif okp():
    idx=cy*3+cx
    if b[idx]==0:
     b[idx]=pl
     cell(cx,cy,BK)
     if pl==1:dx(cx,cy)
     else:do(cx,cy)
     w=check(b,pl)
     if w:
      hline(w)
      for i in w:
       c2,r2=i%3,i//3
       if b[i]==1:dx(c2,r2)
       else:do(c2,r2)
      Z(0.3)
      nm="X"if pl==1 else"O"
      msg=nm+" wins!"
      mc=XC if pl==1 else OC
      break
     if all(v!=0 for v in b):
      msg="Draw!";mc=DG;break
     pl=2 if pl==1 else 1
     if vs_cpu and pl==2:
      Z(0.3)
      ai=ai_move(b,2,1)
      if ai>=0:
       b[ai]=2
       ac,ar=ai%3,ai//3
       cell(ac,ar,BK)
       do(ac,ar)
       w=check(b,2)
       if w:
        hline(w)
        for i in w:
         c2,r2=i%3,i//3
         if b[i]==1:dx(c2,r2)
         else:do(c2,r2)
        Z(0.3)
        msg="O wins!";mc=OC;break
       if all(v!=0 for v in b):
        msg="Draw!";mc=DG;break
      pl=1
     cursor(cx,cy)
    Z(0.18)
   if mv:
    clear_cursor(oc,or2)
    idx2=or2*3+oc
    if b[idx2]==1:dx(oc,or2)
    elif b[idx2]==2:do(oc,or2)
    cursor(cx,cy)
    Z(0.12)
   Z(0.03)
  # Result
  F(60,SH-24,200,22,BK)
  D(msg,SW//2-len(msg)*5,SH-22,mc,BK)
  D("OK=again",SW//2-40,SH-10,(80,)*3,BK)
  while True:
   if K(17):return
   if okp():wup();break
   Z(0.05)

m=title()
if m:game(m)

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.