Tetris in the Julia REPL...in one line of code!
module Tetris begin global (pd=false, buf=Channel{Char}(0x737415), sc=0, hs=0, S=" "^2, rf=(ls=sc, bs=hs) -> ["┌" "────" "───" "T" "E" "T" "R" "I" "S" "────" "───" "┐"; "│" S S S S S S S S S S "│ ┌──NEXT──┐"; "│" S S S S S S S S S S "│ │$S$S$S$(S)│"; "│" S S S S S S S S S S "│ │$S$S$S$(S)│"; "│" S S S S S S S S S S "│ │$S$S$S$(S)│"; "│" S S S S S S S S S S "│ │$S$S$S$(S)│"; "│" S S S S S S S S S S "│ └────────┘"; "│" S S S S S S S S S S "│$(S)Score: $ls"; "│" S S S S S S S S S S "│$(S) High: $bs"; "│" S S S S S S S S S S "│";"│" S S S S S S S S S S "│";"│" S S S S S S S S S S "│"; "│" S S S S S S S S S S "│";"│" S S S S S S S S S S "│";"│" S S S S S S S S S S "│"; "│" S S S S S S S S S S "│";"│" S S S S S S S S S S "│";"│" S S S S S S S S S S "│"; "└" "──" "──" "──" "──" "──" "──" "──" "──" "──" "──" "┘"], f=rf(), b=nothing, bi=rand(1:7), nbi=rand(1:7), fw=12, fh=18, sX=div(fw,2)-1, sY=2, X=sX, Y=sY, alv=false, play(██="██", ■■="[]", R="\e[31m$██\e[0m", B="\e[31;1m$██\e[0m", G="\e[32m$██\e[0m", L="\e[33m$██\e[0m", N="\e[34m$██\e[0m", M="\e[35m$██\e[0m", C="\e[36m$██\e[0m", W="\e[37m$██\e[0m", bs=[[S S C S; S S C S; S S C S; S S C S], [S B S S; S B S S; S B B S; S S S S], [S S N S; S S N S; S N N S; S S S S], [S S S S; S L L S; S L L S; S S S S], [S G S S; S G G S; S S G S; S S S S], [S S R S; S R R S; S R S S; S S S S], [S S M S; S M M S; S S M S; S S S S]], nb=bs[nbi], rb=nothing, fd=false, esc=false, rtx=false, h=false, p=(X,Y), rcan=false, lr=[], fwlr=nothing, key=nothing, t=0, sp=10, af=copy(f), dn= (lf, lnb) -> [lf[y,end] = replace(lf[y,end], r" │.{8,100}│"=>" │"*join(lnb[i,:])*"│") for (i,y) in enumerate(3:6)], c=(lb, lf, xy, rt::Bool=false) -> ([rt ? nothing : (rt = (lb[j,i] != S && lf[y,max(1,x)] != S)) for (i,x) in enumerate(xy[1]:xy[1]+3) for (j,y) in enumerate(xy[2]:xy[2]+3)], rt)[end], gd=(lb, lf, lX, ly, lp, rt::Bool=false) -> ([rt ? nothing : (!c(lb, lf, lp) ? (()->lp=(lX,py))() : rt=true) for py in ly:fh], (()->ly=lp[2]-1)(), (lp, ly))[end], sh=(lb) -> map(s->replace(s,██=>■■), lb), m=(lb, lf, xy, nf=deepcopy(lf)) -> ([lb[j,i] != S ? (nf[y,x] = lb[j,i]) : nothing for (i,x) in enumerate(xy[1]:xy[1]+3) for (j,y) in enumerate(xy[2]:xy[2]+3)], nf)[end], ds=(lf, lb, laf, lX, ly, lp, sb=sh(lb), np=gd(sb,lf,lX,ly,lp)[1]) -> m(lb, m(sb, laf, (np[1], np[2]-1)), lp), df=(lf, lb, lX, ly, lp, laf=lf, naf=(!pd && !isempty(lp) ? ds(lf, lb, laf, lX, ly, lp) : laf)) -> println("\033[1;1H",join(join.([naf[i,:] for i in 1:size(naf,1)]), "\n"), "\033[$(fh+1);$(fw+1)H", "\n Hit ` to pause ", "\033[$(fh+1);$(fw+1)H"), cr=(lrb, lp::Tuple, rt::Tuple=(false,lp)) -> ([!c(lrb, f, (lp[1]+r,lp[2])) ? (r == 0 ? rt=(true, lp) : rt=(rt[1] ? rt : (true,(lp[1]+r, lp[2])))) : nothing for (i,r) in enumerate([0,-1,1,-2,2])], rt)[end], cl=(lf) -> [all(map(line->!occursin(S,line), lf[i,2:end-1])) for i in 2:size(lf,1)-1], cc=() -> ccall(:jl_tty_set_mode, Int32, (Ptr{Cvoid}, Int32), stdin.handle, true), kbr=() -> ccall(:jl_tty_set_mode, Int32, (Ptr{Cvoid}, Int32), stdin.handle, false), asy=begin @async begin for __ in 1:72000 pd ? break : put!(buf, read(stdin, Char)) end end end, ri=() -> (isready(buf) ? take!(buf) : nothing), fls=() -> try Base.throwto(asy, InterruptException()) catch end, shc=() -> println("\e[?25h"), ps=() -> (println("╔────────────────────╗\n", "║ Tetris.start() ║\n", "╚────────────────────╝"), kbr(), shc(), fls(), global pd=true)) = (begin global pd=false end, begin global X=X end, begin global Y=Y end, begin global b=isnothing(b) ? bs[bi] : b end, cc(), println("\33[2J"), print("\e[?25l"), (()->f=rf())(), dn(f, nb), [(sleep(0.05), (()->t+=1)(), (()->fd=(t==sp))(), (()->p=(X,Y))(), (!isempty(lr) ? (df(f,b,X,Y,p), begin global sc+=length(lr) end, begin global hs=max(sc,hs) end, (()->fwlr=f[setdiff(2:size(f,1)-1,lr),2:end-1])(), (()->f=rf(sc))(), (()->f[(2+length(lr)):end-1, 2:end-1]=fwlr)(), dn(f, nb), sleep(0.05), (()->lr=[])(), df(f,b,X,Y,p)) : nothing), (()->key=ri())(), begin global alv=alv ? true : !isnothing(key) end, ((key=='a') ? ((!rtx ? (()->p=(X-1,Y))() : nothing), (()->rtx=true)()) : ((key=='d') ? ((!rtx ? (()->p=(X+1,Y))() : nothing), (()->rtx=true)()) : ((key=='s') ? (()->p=(X,Y+1))() : ((key=='w') ? ((!h ? ((()->(p,Y)=gd(b,f,X,Y,p))(), (()->fd=true)()) : nothing), (()->h=true)()) : (((key=='q') || (key=='e')) ? (((()->rb=((key=='q') ? rotl90(b) : rotr90(b)))(), (!h ? (()->((()->(rcan,p)=cr(rb,p))(), (rcan ? (begin global b=rb end) : nothing)))() : nothing), (()->h=true)())) : ((key=='`') ? ((()->esc=true)()) : (()->h=false)())))))), rtx ? (sleep(0.025),(()->rtx=false)()) : nothing, !c(b,f,p) ? (begin global X=p[1] end, begin Y=p[2] end) : begin p=(X,Y) end, (()->af=m(b,f,p))(), fd ? (!c(b, f, (p[1], p[2]+1)) ? (()->global Y+=1)() : ((()->f[:]=af)(), (any(cl(f)) ? ((l=findall(cl(f)).+1)-> (f[l,2:end-1] .= W, (()->lr=l)()))() : (()->lr=[])()), ((()->global X=sX)(), (()->global Y=sY)(), (()->p=(X,Y))(), begin global bi=nbi end, begin b=nb end, begin global nbi=rand(1:7) end, begin nb=bs[nbi] end, dn(f, nb), (()->global pd=c(b,f,p))())), (()->t=0)()) : df(f,b,X,Y,p,af), pd && (fls(), begin global sc=0 end, begin global f=rf() end, alv ? play() : ps()), esc && ps()) for _ in 1:72000 if !pd]), start() = (print("\e[?25l"), play(), nothing)[end], __init__() = play()) end end
View the raw one-liner at Tetris.jl
and an exanded/indented version at TetrisExpanded.jl
. For an un-obfuscated version, see my JuliaGaming implementation.
] add Tetris
Works across operating systems—even on Android using Termux!
using Tetris
Running this will automatically start the game and exit the REPL at gameover.
a
andd
for left and right movements
to move downw
to snap downq
ande
for left and right rotations`
to pauseTetris.start()
to resume
In going from the full implementation (pushed to JuliaGaming) down to one line, I recorded my screen for others to witness the abomination. (Click to zoom-in)
Sidenote, this was actually recorded after I finished. I used the Sublime Text ctrl-z
buffer all the way back, then hit record and used crtl-y
to go forward.
Great question. I first wanted to create Tetris in Julia, then made it a goal to reduce it down to one functional line of Julia code. I'm not using semicolons to break up separate Julia lines. Instead, I'm using tricks to define one inner __init__()
function that encodes the game state variables and subroutines as function arguments and then runs the game loop when the Tetris
module is loaded.
It works cross-plaform in pure Julia as I did not want to rely on any external libraries. Games are limited to 1 hour (72,000 iterations with a game clock of 0.05 seconds), as while loops would break my "don't use semicolons to separate lines" rule (aside from Matrix row definitions). To play, run using Tetris
or copy and paste the one-liner into the Julia REPL and enjoy!