Simple tetris
# Tetris - NumWorks # Created by Alvar Laigna - https://alvarlaigna.com # L/R:move Down:soft Up:hard OK/EXE:rotate 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,monotonic as M from random import randint GW,GH=10,20;CS=11;OX=5;OY=1 BK=(0,)*3;GC=(25,)*3;BD=(50,)*3 # 7 tetrominoes: I O T S Z L J PC=[((0,-1),(0,0),(0,1),(0,2)), ((0,0),(1,0),(0,1),(1,1)), ((-1,0),(0,0),(1,0),(0,-1)), ((0,0),(1,0),(-1,1),(0,1)), ((-1,0),(0,0),(0,1),(1,1)), ((0,-1),(0,0),(0,1),(1,1)), ((0,-1),(0,0),(0,1),(-1,1))] CC=((0,200,200),(200,200,0),(160,0,200), (0,200,0),(220,30,30),(220,120,0),(30,30,220)) # Dimmed versions for ghost GG=tuple(tuple(c//4 for c in col)for col in CC) SX=OX+GW*CS+14 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,320,222,BK) D("T E T R I S",85,25,(0,200,200),BK) # Draw mini tetrominoes as decoration cols=[(220,30,30),(0,200,0),(200,200,0),(160,0,200)] for i,dx in enumerate([55,115,175,235]): F(dx,55,8,8,cols[i]);F(dx+9,55,8,8,cols[i]) F(dx,64,8,8,cols[i]);F(dx+9,64,8,8,cols[i]) D("Left / Right",85,95,(180,)*3,BK) D("move piece",100,113,(120,)*3,BK) D("Down = soft drop",70,138,(180,)*3,BK) D("Up = hard drop",78,156,(180,)*3,BK) D("OK = rotate",95,174,(180,)*3,BK) D("Press OK to start",70,205,(0,200,200),BK) while True: if okp():wup();return True if K(17):return False Z(0.05) def tetris(): gr=[[0]*GW for _ in range(GH)] sc=ln=lv=0 def cl(x,y,c): if 0<=y<GH and 0<=x<GW: F(OX+x*CS,OY+y*CS,CS-1,CS-1,c) def fit(p,px,py): for dx,dy in p: x,y=px+dx,py+dy if x<0 or x>=GW or y>=GH:return False if y>=0 and gr[y][x]:return False return True def drp(p,px,py,c): for dx,dy in p:cl(px+dx,py+dy,c) def ghost_y(p,px,py): gy=py while fit(p,px,gy+1):gy+=1 return gy def dgr(): for y in range(GH): for x in range(GW): cl(x,y,CC[gr[y][x]-1]if gr[y][x]else BK) def dui(): D("SCORE",SX,10,(120,)*3,BK) D(str(sc)+" ",SX,26,(255,)*3,BK) D("LEVEL",SX,50,(120,)*3,BK) D(str(lv)+" ",SX,66,(0,200,200),BK) D("LINES",SX,90,(120,)*3,BK) D(str(ln)+" ",SX,106,(200,200,0),BK) def dnx(ci): D("NEXT",SX,132,(120,)*3,BK) F(SX,150,48,48,(15,)*3) F(SX+1,151,46,46,BK) for dx,dy in PC[ci]: F(SX+15+dx*10,170+dy*10,9,9,CC[ci]) def lflash(rows): for _ in range(3): for y in rows: for x in range(GW):cl(x,y,(255,)*3) Z(0.06) for y in rows: for x in range(GW):cl(x,y,BK) Z(0.06) # Init screen F(0,0,320,222,BK) # Grid border F(OX-2,OY-2,GW*CS+4,GH*CS+4,BD) F(OX-1,OY-1,GW*CS+2,GH*CS+2,GC) F(OX,OY,GW*CS,GH*CS,BK) dgr();dui() ci=randint(0,6);ni=randint(0,6) p=list(PC[ci]);px,py=GW//2,1 dnx(ni) # Draw ghost + piece gy=ghost_y(p,px,py) if gy!=py:drp(p,px,gy,GG[ci]) drp(p,px,py,CC[ci]) tm=M();lt=M() das=0;das_dir=0;das_t=0 while True: if K(17):return False sp=max(0.05,0.5-lv*0.04) now=M() moved=False # DAS: hold L/R for auto-repeat lr=(-1 if K(0)else 1 if K(3)else 0) if lr!=0: go=False if lr!=das_dir: das_dir=lr;das_t=now;go=True elif now-das_t>0.18: go=True;das_t=now-0.12 if go and fit(p,px+lr,py): drp(p,px,gy if gy!=py else py,BK) if gy!=py:drp(p,px,gy,BK) drp(p,px,py,BK);px+=lr;moved=True else:das_dir=0 if K(2): # soft drop if fit(p,px,py+1): drp(p,px,gy if gy!=py else py,BK) if gy!=py:drp(p,px,gy,BK) drp(p,px,py,BK);py+=1;moved=True;tm=now Z(0.04) elif K(1): # hard drop drp(p,px,py,BK) if gy!=py:drp(p,px,gy,BK) py=ghost_y(p,px,py);drp(p,px,py,CC[ci]) tm=now-sp Z(0.05) elif okp(): # rotate nr=[(-dy,dx)for dx,dy in p] for ox in(0,1,-1,2,-2): if fit(nr,px+ox,py): drp(p,px,py,BK) if gy!=py:drp(p,px,gy,BK) p=nr;px+=ox;moved=True;break Z(0.14) # Redraw ghost + piece if moved if moved: gy=ghost_y(p,px,py) if gy!=py:drp(p,px,gy,GG[ci]) drp(p,px,py,CC[ci]) # Gravity if now-tm>=sp: tm=now if fit(p,px,py+1): drp(p,px,py,BK) if gy!=py:drp(p,px,gy,BK) py+=1;gy=ghost_y(p,px,py) if gy!=py:drp(p,px,gy,GG[ci]) drp(p,px,py,CC[ci]) else: # Lock piece for dx,dy in p: x,y=px+dx,py+dy if 0<=y<GH:gr[y][x]=ci+1 # Line clear full=[y for y in range(GH)if all(gr[y])] if full: lflash(full) n=len(full) sc+=(0,100,300,500,800)[n]*(lv+1) ln+=n;lv=ln//10 for y in sorted(full,reverse=True): gr.pop(y);gr.insert(0,[0]*GW) dgr() dui() # Spawn ci=ni;ni=randint(0,6) p=list(PC[ci]);px,py=GW//2,1 dnx(ni) if not fit(p,px,py): # Game over F(OX,OY+75,GW*CS,55,BK) F(OX+2,OY+77,GW*CS-4,51,(20,)*3) D("GAME OVER",OX+14,OY+80,(255,50,50),(20,)*3) D(str(sc)+" pts",OX+20,OY+100,(255,)*3,(20,)*3) D("OK=again Back=quit",2,210,(120,)*3,BK) while True: if K(17):return False if okp():wup();return True Z(0.05) gy=ghost_y(p,px,py) if gy!=py:drp(p,px,gy,GG[ci]) drp(p,px,py,CC[ci]) Z(0.025) # Main loop if title(): while tetris():pass