Tetris/Julia: Difference between revisions

m
Fixed syntax highlighting.
m (Fixed syntax highlighting.)
 
(5 intermediate revisions by 2 users not shown)
Line 1:
<span style='font-family: "Linux Libertine",Georgia,Times,serif;font-size:150%;'>[[Julia]]</span><hr>
=={{header|Julia}}==
This is a fairly complete, configurable pure Julia implementation of Tetris, played in the standard Julia terminal. No external packages are used.
 
Line 6:
{{works with|Julia|1.0}}
 
<syntaxhighlight lang="julia">struct B4 # Tetromino made of 4 Blocks
<lang julia>using Random
struct B4
bg::Int # ANSI code of tetromino color (bground of " ")
d::Array{Int,2} # d[b,1:2] = row,col/2 offset of block b
# constructor: Zset =centered B4(101,[0offsets 0;of 0 1; 1 1; 1 2])blocks
function B4(bg,A::Array{Int,2}) #= new(bg,A.- fills in centered offsets for tetrominosum.(extrema(A,dims=1)).>>1)
new(bg, A .- sum.(extrema(A,dims=1)).>>1)
end end
# print tetromino on playfield
function draw(b,r,c,clr=-1)
print("\e[$(clr & b.bg)m") # set color. (0 = default colors)
for i=1:4 print("\e[$(r+b.d[i,1]);$(c+2b.d[i,2])H ") end
end
# hitin otherloops B4sITERATE orover border?the blocks in B4
Base.iterate(B::B4,i=1) = i<5 ? (B.d[i,:],i+1) : nothing
function hit(B,r,c)
# printPRINT/CLEAR tetromino on playfieldterminal
for i=1:4 BD[r+B.d[i,1],c>>1+B.d[i,2]+2]>0 && return true end
drw(B,r,c)=(for(i,j)=B print("\e[$(B.bg)m\e[$(r+i);$(c+2j)H ") end)
return false
clr(B,r,c)=(for (i,j)=1:4B print("\e[0m\e[$(r+b.d[i,1]);$(c+2b.d[i,2]2j)H ") end)
end
# moveHIT tetrominosother left/right/down,B4s ifor no hitborder?
hit(B,r,c)=(for (i,j)=1:4B try BD[r+B.d[i,1],c>>1+B.d[i,2]+2j]>0 catch;true end&& return true end;false)
function mov(B,r,c,x,y)
# function for dialog confirmation
hit(B,r+x,c+y) || (draw(B,r,c,0);draw(B,r+=x,c+=y))
function mov(B,r,c,x,y) # rotateMOVE tetrominos left/right/down, if no hit
TMv || (global tmr = Timer(tm)); return (r,c)
hit(B,r+x,c+y) || (drawclr(B,r,c,0);draw drw(B,r+=x,c+=y))
TMv || (global tmr = Timer(tm)); return (r,c)
end
 
# rotate tetrominos left/right, if no hit
function rot(B,r,c,rt=0) # ROTATE tetrominos left/right, if no hit
TMv || (global tmr = Timer(tm))
A = deepcopy(B)
forA i=1:4 AB4(B.d[ibg,:]= rt>0 ? [AB.d[i:,2], (3 .-AB.d[i:,1])] : [(3 .-AB.d[i:,2],A) B.d[i:,1]] end)
A.d .-= sum.(extrema(A.d,dims=1)).>>1 # centralize
TMv || (global tmr = Timer(tm))
for j = c.+(0,+2,-2) # try shifted positions
hit(A,r,j) || (drawclr(B,r,c,0); drawdrw(A,r,j); return (A,j))
end; return (B,c) # cannot rotate: all 3 positions hit
end
return (B,c) # cannot rotate: all 3 positions hit
end
 
function mrk(B,r,c) # RECORD # record-place,clear-CLEAR full-lines,dropDROP-above,score
function mark(B,r,c)
global lines, score, level; ln n,l = 0,0
for (i,j)=1:4B BD[r+B.d[i,1],c>>1+B.d[i,2]+2j]=B.bg end # record stuck B4
for i in r.+sort(unique(B.d[:,1])) # in board: empty full rows in board; drop partall above
if all(BD[i,3:12].>0) lnn += 1; l = i # l = lowest line changed
for j = i-1:-1:1 BD[j+1,3:12] = BD[j,3:12] end
print("\e[0m\e[$i;2H$s20"); sleep(0.3)
end end # update changed display from board data ->
for i=1:22l,j=31:1210 print("\e[$(BD[i,j])m\e[$i;$(2j-4)H ") end
ln > 0 && (lines+=ln; score += (level+1)*(0,100,300,500,800)[lnn+1])
lines+=n; level=isqrt(lines>>3) # updatesupdate lines, score, level
end
 
# function for dialog confirmation
function confcnf(tp) # t:CONFIRMATION prompt,dialog. w/op new= 1-line! prompt
print("$tp - Enter confirms, other keys ignore")
t = take!(chnl); println("\e[2K"); t==[0xd]
end
# showSHOW basic usage- and status info
infoinf()=print("\e[0m\e[26H\e[2K i: Info, l: Locked drop (=$LDr), t: Timed move (=$TMv)")
 
# SetupSETUP nonblocking, non-echoed keyboard inputINPUT
ccall(:jl_tty_set_mode,Cint,(Ptr{Cvoid},Cint),stdin.handle,1)==0 ||
throw("Terminal cannot enter raw mode.") # raw terminal mode to catch keystrokes
const chnl = Channel{Array{UInt8,1}}(0) # unbuffered channel for key codes
@async while true put!(chnl,readavailable(stdin)) end # task, catching keystrokes
 
I = B4(106,[0 0; 0 1; 0 2; 0 3]) # define the 7 tetrominos
Line 74 ⟶ 66:
 
begin @label RESTART # @label - @goto: require begin..end
global lines,score,level,s20,eq = 0,0,0," "^20,"="^6
BD = fill(0,2422,1510); for j=3:12 BD[23,j]=BD[24,j] # empty BOARD. Screen_Col =1 end2*Board_Col
for i=1:24,j=∪(1,2,13:15) BD[i,j]=1 end # Board border. screen_C = 2j-4
 
print("\e[0m\e[2J\e[?25l\e[1H") # #Set default colors/clear screen/hide cursor
for i=1:22 print("\e[$i;H▐$(s20)▌\n"^22*"▝$("▀"^20)▘\n\n$s20\n$s20") end # screen BORDER, -> Cols=2:2:20
print("\e[23H▝$("▀"^20)▘") # ROWS=1:22, COLS=2:2:20, Center=10,11
print("\n\n$s20\n$s20"); X0 = (I,T,O,S,Z,J,L)[rand(1:7)]
 
print("\n\n$s20\n$s20"); X0 = (I,T,O,S,Z,J,L)[rand(1:7)]
while true # random B4, timer totimed drop, act on keystrokes
global lines,score,level,tm,tmr,LDr,TMv,Ifo
draw(X0,24,10,0)
global X0,X,r,c = (I,T,O,S,Z,J,L)[rand(1:7)],X0,2,10
drawclr(X,24,10); drw(X0,24,10); infoinf() # show next piece, information
print("\n Level =\t$level\n Lines filled =\t$lines\n Score =\t$score")
tm=(.8-level*.007)^level; tmr=Timer(0.5) # fixed 0.5s firstinitial delay
hit(X,r,c) && (while true confcnf("\e[31H Game Over/: RESTART")&&@goto RESTART end)
while isready(chnl) take!(chnl) end # flush queued keystrokes, max 1
while true
global X,r,c,tm,tmr
if !isopen(tmr) # time to drop tetromino by a line
hit(X,r+1,c) && (markmrk(X,r,c); break)
drawclr(X,r,c,0); drawdrw(X,r+=1,c); tmr=Timer(tm)
end
whileif isready(chnl) # areif there is a queued keystrokes?keystroke
global X,r,c,score
ch = take!(chnl) # take keys
Line 107 ⟶ 96:
elseif ch==[0x61] X,c=rot(X,r,c) # a
elseif ch==[0x64] X,c=rot(X,r,c,1) # d
elseif ch==[0x20] r0=r;draw clr(X,r,c,0); tmr=Timer(tm*!LDr) # SPACE
while !hit(X,r+=1,c) end; drawdrw(X,r-=1,c); score+=2r-2r0; continue
elseif ch==[0x1b,0x5b,0x31,0x7e] # HOME
confcnf("\e[0m\e[31H RESTART") && @goto RESTART
elseif ch==[0x1b,0x5b,0x34,0x7e] # END
confcnf("\e[0m\e[31H EXIT") && return
elseif ch == [0x6c] LDr=!LDr; infoinf() # l: Locked drop
elseif ch == [0x74] TMv=!TMv; infoinf() # t: Timed move
elseif ch == [0x69] if (Ifo=!Ifo) open(@__FILE__) do f # i: long-Info
if readuntil(Ifo = !Ifof,eq); io = openprintln(Base.source_path"\e[0m\e[32H$()); readuntil(iof,eq))"="^6) end
else printlnprint("\e[0m\e[32H$(readuntil(io,"="^6))\e[J"); close(io)# erase txt
else
print("\e[0m\e[32H\e[J") # erase txt
end end end
sleep(0.01) # not to take all CPU core time
end end end</langsyntaxhighlight>
 
The following long comment block has to be appended to program code:
 
<pre>#====== Julia-TETRIS by Laszlo Hars, Version 1.12 10/2131/2018
- (Set Terminal window to 4943+ rows, don't scroll!)
-- Key assignments --
↑: rotate +90° counterclockwise i: Info about the program on/off
↓: soft drop (one line) per press) l: Locked hard drop on/off
←: shift left t: restart Timer after move on/off
→: shift right SPACE: hard drop until hit
a: rotate +90° counterclockwise HOME: restart game
d: rotate -90° clockwise END: exit
I: Info about the program on/off
L: Locked-drop on/off(move after hard drop)
T: Timed move on/off (restart drop timer after move)
SPACE: hard drop until hit
HOME: restart game
END: exit
======
The game is played in the Julia standard Unicode character based Terminal
Line 163 ⟶ 144:
Number of LINES cleared
SCORE
Keys for short Info, Locked-drop-, and Timed -move status
 
Game LEVEL: 0,1...= [sqrt(LINES/8)]
Increase atafter 8,32,72,128,200... lines cleared
 
SCORE: Cleared_Line values: *(LEVEL+1)
Line 204 ⟶ 185:
White 107 L ██████
 
Julia specific notesspecifics
Terminal is switched to RAW mode to catch keystrokes.
Non-blocking readkeyboard isinput achievedis viaby asynchronous (maybe blocked) 2nd TASK.
Keystrokes are transferred to the main program through a CHANNEL.
STRUCTs (B4) describe Tetrominos, with color code and coordinates of blocks.
A TIMER controls the dropping speed of tetrominos.
At construction the coordinates get centralized.
In loops Julia 1.0 assumes local variables, when values get assigned,
Assigning Struct to a variable (X) only creates a referenceREFERENCE, not copied.
therefore many GLOBAL variables have to be declared and used
Memory used by orphan instance is cleared by internal GARBAGE COLLECTION.
Out of range indices are caught with TRY..CATCH, to keep tetrominos in board.
A TIMER controls the dropping speeddelay of tetrominos.
ITERATE is defined for tetrominos, looping over the coordinates of blocks.
In loops when variables get assigned before other use, Julia 1.0 assumes
local variables, therefore many GLOBAL variables have to beare declared and used
For simplicity Int variables are used; in 64-bit OS versions: 8 Bytes.
In most places 1 Byte Int8 (or Int16, Int32) would work. WhenTake changing:care
take special carewhen aschanging: Int8(x)+1 -> Int64, Int8(x)+0x1 -> UInt8. Use +Int8(1).
Only the"lines" variableand "score" needsneed to be UInt32 or longer.
LabelLABEL - GoToGOTO (for restart) require the main loop enclosed in Begin..End.
Assigning Struct to a variable only creates a reference.
For a new instance we need "deepcopy", and rely on garbage collection.
Label - GoTo (for restart) require the main loop enclosed in Begin..End.
Several instructions are written in single lines of at most 84 chars,
to keep the program lesswell thanunder 110100 non-comment/non-blank lines!
#.. Comments denote tasks, explain important points.
TheIn programstrings hassome embedded UnicodeUNICODE characters appear. They can be replaced with
\u{hex_digits} into strings,make andthe "∪"program replacedall withASCII "union"(for old editors).
to make the program all ASCII. Otherwise editing must handle Unicode.
======#</pre>
9,476

edits