#! /Bin/bash # Tetris game #10.21.2003 xhchen <[email] xhchen@winbond.com.tw [/Email]> # app declarationapp_name = "$ {0 # * [\/]}" app_version = "1.0" # color definition cred = 1 cgreen = 2 cyellow = 3 cblue = 4 cfuchsia = 5 ccyan = 6 cwhite = 7 colortable = ($ cred $ cgreen $ cyellow $ cblue $ cfuchsia $ ccyan $ cwhite) # position and size ileft = 3 iTOP = 2 (itrayleft = ileft + 2) (itraytop = iTOP + 1) (itraywidth = 10 )) (itrayheight = 15) # color settings cborder = $ cgreencscore = $ cfuch Siacscorevalue = $ ccyan # control signal # change the game to use two processes, one for receiving input, the other for the game process and display interface; # When the former receives buttons such as top, bottom, and left, notify the latter by sending signal to the backend. Sigrotate = 25 sigleft = 26 sigright = 27 sigdown = 28 sigalldown = 29 sigexit = 30 # definitions of different squares in seven # by rotation, there may be several kinds of box0 = (0 0 0 1 1 0 1) box1 = (0 2 1 2 2 2 2 2 1 1 1 1 1 2 1 3) box2 = (0 0 0 1 1 1 1 1 2 0 1 1 1 1 1 1 2 0) box3 = (0 1 0 2 1 0 1 1 0 1 0 1 1 1 2 1) box4 = (0 1 0 2 1 1 1 1 1 1 1 1 1 2 2 2 2 0 1 1 2 0 2 1 0 1 0 1 1 1 1 1 2) box5 = (0 1 1 1 1 2 2 2 1 1 1 1 1 2 2 0 0 0 0 1 1 1 2 1 0 2 1 1 1 1 2) box6 = (0 1 1 1 1 2 2 1 1 0 1 1 1 2 2 1 0 1 1 1 1 1 2 1 0 1 1 1 1 2) # Put all the definitions of squares in the box variable box = ($ {box0 [@] }$ {box1 [@] }$ {box2 [@] }$ {box3 [@] }$ {box4 [@] }$ {box5 [@] }$ {box6 [@]}) # Number of possible styles after rotation of various squares countbox = (1 2 2 2 4 4 4) # offset offsetbox = (0 1 3 5 7 11 15) in the box array of various squares) # The score iscoreeachlevel = 50 # be greater than 7 # runtime data Sig = 0 # received signaliscore = 0 # Total ilevel = 0 # speed level boxnew = () # cboxnew = 0 # iboxnewtype = 0 # iboxnewtype = 0 # iboxnewr Otate = 0 # Rotation Angle of the new falling square boxcur = () # define the position of the current square cboxcur = 0 # the color of the current square iboxcurtype = 0 # the type of the current square iboxcurrotate = 0 # The Rotation Angle of the current square boxcurx =-1 # The X of the current square coordinate Position boxcury =-1 # Y coordinate position of the current square IMAP = () # # Initialize all the background blocks as-1, indicating that there is no square for (I = 0; I <itrayheight * itraywidth; I ++ )); do IMAP [$ I] =-1; done # receives the input process's main function runaskeycycler () {local piddisplayer key akey sig Cesc stty piddisplayer = $1 akey = (0 0 0) Cesc = 'echo-ne "\ 033" 'cspace = 'e Cho-ne "\ 040" '# Save terminal properties. When read-s reads the terminal key, the properties of the terminal are temporarily changed. # If the program is killed unfortunately during read-S, terminal confusion may occur. # restore terminal attributes when the program exits. Stty = 'stty-G' # capture exit signal trap "myexit;" int term trap "myexitnosub;" $ sigexit # Hide the cursor echo-ne "\ 033 [? 25l "while: Do # Read input. Note-S does not show back, -N returns read-S-N 1 key akey [0] =$ {akey [1]} akey [1] =$ {akey [2]} akey [2] = $ key Sig = 0 # determine the Input key. If [[$ key = $ Cesc & $ {akey [1] }== $ Cesc] then # ESC key myexit Elif [[$ {akey [0] }=$ Cesc & $ {akey [1] }== "["] Then if [[$ key = = "A"]; then Sig = $ sigrotate # <up key> Elif [[$ key = "B"]; then Sig = $ sigdown # <down key> Elif [[$ key = "D"]; then Sig = $ sigleft # <left arrow> Elif [[$ key = "C"]; then Sig = $ sigright # <right-click> fi Elif [[$ key = "W" | $ key = "W"]; then Sig = $ sigrotate # W, W Elif [[$ key = "S" | $ key = "S"]; then Sig = $ sigdown # s, s Elif [[$ key = "A" | $ key = "A"]; then Sig = $ sigleft #, A Elif [[$ key = "D" | $ key = "D"]; then Sig = $ sigright # D, d Elif [["[$ key]" = "[]"]; then Sig = $ sigalldown # Space key Elif [[$ key = "Q" | $ key = "Q"] # Q, Q then myexit Fi if [[$ Sig! = 0] Then # send the message kill-$ sig $ piddisplayer fi done} to another process # restore function myexitnosub () before exiting () {local y # restore terminal attributes stty $ stty (y = iTOP + itrayheight + 4) # display the cursor echo-e "\ 033 [? 25h \ 033 [$ {y}; 0 h "Exit} function myexit () {# The Notification display process needs to exit kill-$ sigexit $ piddisplayer myexitnosub} # Main Function function runasdisplayer () for processing display and game flows () {local sigthis initdraw # handler for mounting various signals trap "Sig = $ sigrotate;" $ sigrotate trap "Sig = $ sigleft;" $ sigleft trap "Sig = $ sigright; "$ sigright trap" Sig = $ sigdown; "$ sigdown trap" Sig = $ sigalldown; "$ sigalldown trap" showexit; "$ sigexit while: do # set the number of cycles according to the current speed level ilevel R (I = 0; I <21-ilevel; I ++ )) do sleep 0.02 sigthis = $ sig = 0 # determine whether to receive the corresponding signal if (sigthis = sigrotate) based on the SIG variable; then boxrotate; # rotating Elif (sigthis = sigleft); then boxleft; # moving the left column of Elif (sigthis = sigright); then boxright; # shift a column to the right of Elif (sigthis = sigdown); then boxdown; # Drop a row of Elif (sigthis = sigalldown); then boxalldown; # Drop fi done # Kill-$ sigdown $ boxdown # Drop a row done} # boxmove (Y, X) To test whether the moving block can be moved to the position (x, y). If 0 is returned, the moving block can be used. If 1 is returned, the function boxmove () cannot be used () {local J I x y xtest ytest = $1 xtest = $2 for (j = 0; j <8; j ++ = 2 )) do (I = J + 1) (Y =$ {boxcur [$ J]} + ytest )) (x =$ {boxcur [$ I]} + xtest )) if (Y <0 | Y> = itrayheight | x <0 | x> = itraywidth )) then # hit the wall and return 1 Fi if ($ {IMAP [y * itraywidth + x]}! =-1) then # Hit another existing square, return 1 fi done return 0;} # Put the square in the current movement in the background square, # Calculate the new score and speed level. (That is, once the square falls to the bottom) function box2map () {local J I x y XP YP line # Place the square in the current movement in the background square for (j = 0; j <8; j + = 2) do (I = J + 1) (Y =$ {boxcur [$ J]} + boxcury )) (x =$ {boxcur [$ I]} + boxcurx) (I = y * itraywidth + x )) IMAP [$ I] = $ cboxcur done # Delete rows that can be eliminated line = 0 for (j = 0; j <itraywidth * itrayheight; j + = itraywidth )) do for (I = J + itraywidth-1; I >=j; I --) do if ($ {IMAP [$ I] }==-1 )); then break; FI done if (I> = J); then continue; FI (line ++) for (I = J-1; I> = 0; I --) do (x = I + itraywidth) IMAP [$ X] =$ {IMAP [$ I]} done for (I = 0; I <itraywidth; I ++) do IMAP [$ I] =-1 done if (line = 0); then return; FI # Calculate the score and speed level based on the line of the deleted rows (x = ileft + itraywidth * 2 + 7) (y = iTOP + 11 )) (iscore + = line * 2-1 )) # display the new score echo-ne "\ 033 [1 m \ 033 [3 $ {cscorevalue} m \ 033 [$ {y}; $ {x} h $ {iscore} "If (iscore % iscoreeachlevel <line * 2-1) then if (ilevel <20) Then (ilevel ++ )) (y = iTOP + 14) # display the new speed echo-ne "\ 033 [3 $ {cscorevalue} m \ 033 [$ {y }; $ {x} h $ {ilevel} "fi echo-ne" \ 033 [0 m "# re-display the background square for (y = 0; y <itrayheight; Y ++) do (Yp = Y + itraytop + 1) (XP = itrayleft + 1) (I = y * itraywidth )) echo-ne "\ 033 [$ {YP}; $ {XP} H" for (x = 0; x <itraywidth; X ++ )) do (j = I + X) if ($ {IMAP [$ J]} =-1 )) then ECHO-ne "" else echo-ne "\ 033 [1 m \ 033 [7m \ 033 [3 $ {IMAP [$ J]} m \ 033 [4 $ {IMAP [$ J]} m [] \ 033 [0 m "fi done} # function boxdown () {local y s (y = boxcury + 1 )) # New Y coordinate if boxmove $ y $ boxcurx # test whether a row can be dropped by then S = "'drawcurbox 0'" # erase the old square (boxcury = y )) S = "$ s 'drawcurbox 1'" # display the new lower-left square echo-ne $ s else # Go here, if box2map cannot be dropped # paste the square currently being moved to the background box randombox # generate a new box fi} # Move a column left of Fu Nction boxleft () {local x s (x = boxcurx-1) If boxmove $ boxcury $ X then S = 'drawcurbox 0' (boxcurx = x )) S = $ s 'drawcurbox 1' echo-ne $ s fi} # Right Shift a column function boxright () {local x s (x = boxcurx + 1 )) if boxmove $ boxcury $ X then S = 'drawcurbox 0' (boxcurx = x )) S = $ s 'drawcurbox 1' echo-ne $ s fi} # function boxalldown () {local k J I x y idown s idown = $ itrayheight # calculate the total number of rows to be dropped for (j = 0; j <8; j + = 2) do (I = J + 1) (Y =$ {boxcur [$ J]} + boxcury )) (x =$ {boxcur [$ I]} + boxcurx) for (k = Y + 1; k <itrayheight; k ++ )) do (I = K * itraywidth + x) if ($ {IMAP [$ I]}! =-1); then break; FI done (k-= Y + 1) if ($ idown> $ K); then idown = $ K; FI done S = 'drawcurbox 0' # erase the old square (boxcury + = idown )) S = $ s 'drawcurbox 1' # display the new backward square echo-ne $ s box2map # paste the square in the current movement to the background box randombox # generate a new square }# rotate the box function boxrotate () {local icount itestrotate boxtest J I s icount =$ {countbox [$ iboxcurtype]} # Number of styles that can be generated when the current square is rotated # Calculate the new style after rotation (( itestrotate = iboxcurrotate + 1 )) if (itestro Tate> = icount) Then (itestrotate = 0) Fi # update to the new style and save the old style (but not displayed) for (j = 0, I = ($ {offsetbox [$ iboxcurtype]} + $ itestrotate) * 8; j <8; j ++, I ++ )) do boxtest [$ J] =$ {boxcur [$ J]} boxcur [$ J] =$ {box [$ I]} done if boxmove $ boxcury $ boxcurx # After testing Rotation whether there is space to put then # erase the old square for (j = 0; j <8; j ++ )) do boxcur [$ J] =$ {boxtest [$ J]} done S = 'drawcurbox 0' # draw a new square for (j = 0, I = ($ {offsetbox [$ iboxcurtype]} + $ it Estrotate) * 8; j <8; j ++, I ++ )) do boxcur [$ J] =$ {box [$ I]} done S = $ s 'drawcurbox 1 'echo-ne $ s iboxcurrotate = $ itestrotate else # cannot be rotated, continue to use the old style for (j = 0; j <8; j ++ )) do boxcur [$ J] =$ {boxtest [$ J]} done fi} # drawcurbox (bdraw), draw the square in the current movement, set bdraw to 1, draw, and bdraw to 0, erase the square. Function drawcurbox () {local I j t bdraw sbox s bdraw = $1 S = "" If (bdraw = 0 )) then sbox = "\ 040 \ 040" else sbox = "[]" s = $ s "\ 033 [1 m \ 033 [7m \ 033 [3 $ {cboxcur} m \ 033 [4 $ {cboxcur} m "fi for (j = 0; j <8; j + = 2) do (I = itraytop + 1 + $ {boxcur [$ J]} + boxcury )) (t = itrayleft + 1 + 2 * (boxcurx + $ {boxcur [$ J + 1]}) # \ 033 [Y; XH, cursor to (X, y) S = $ s "\ 033 [$ {I }; $ {t} h $ {sbox} "done S = $ s" \ 033 [0 m "echo-N $ s} # updated party Block Function randombox () {local I j t # update the currently moved box iboxcurtype =$ {iboxnewtype} iboxcurrotate =$ {iboxnewrotate} cboxcur =$ {cboxnew} For (j = 0; j <$ {# boxnew [@]}; j ++ )) do boxcur [$ J] =$ {boxnew [$ J]} done # display the currently moved box if ($ {# boxcur [@] }== 8 )) then # Calculate the row from the top of the current square for (j = 0, T = 4; j <8; j + = 2 )) do if ($ {boxcur [$ J]} <t); then t =$ {boxcur [$ J]}; FI done (boxcury =-t )) for (j = 1, I =-4, t = 20; j <8; j + = 2) do if ($ {boxcur [$ J]}> I); then I =$ {boxcur [$ J]}; fi if ($ {boxcur [$ J]} <t); then t =$ {boxcur [$ J]}; FI done (boxcurx = (itraywidth-1-I-t)/2 )) # display the currently moved square echo-ne 'drawcurbox 1' # If the square is displayed, it will not be placed. Game over! If! Boxmove $ boxcury $ boxcurx then kill-$ sigexit $ {ppid} showexit fi # Clear the pre-displayed box for (j = 0; j <4; j ++) on the right )) do (I = iTOP + 1 + J) (t = ileft + 2 * itraywidth + 7) echo-ne "\ 033 [$ {I }; $ {t} H "Done # randomly generate new blocks (iboxnewtype = random % $ {# offsetbox [@]}) (iboxnewrotate = random % $ {countbox [$ iboxnewtype]}) for (j = 0, I = ($ {offsetbox [$ iboxnewtype]} + $ iboxnewrotate) * 8; j <8; j ++, I ++) do box New [$ J] =$ {box [$ I]}; done (cboxnew =$ {colortable [random % $ {# colortable [@]}) # display the pre-displayed box echo-ne "\ 033 [1 m \ 033 [7m \ 033 [3 $ {cboxnew} m \ 033 [4 $ {cboxnew} M" ((j = 0; j <8; j + = 2) do (I = iTOP + 1 + $ {boxnew [$ J]}) (t = ileft + 2 * itraywidth + 7 + 2 * $ {boxnew [$ J + 1]}) echo-ne "\ 033 [$ {I }; $ {t} H [] "done echo-ne" \ 033 [0 m "} # initial draw function initdraw () {clear randombox # random generate blocks, in this case, the right-side pre-display window shows a fast rando. Mbox # generate blocks randomly, and the blocks in the pre-display window on the right are updated, the original square will start to fall into the local I T1 T2 T3 # display the border echo-ne "\ 033 [1 m" echo-ne "\ 033 [3 $ {cborder} m \ 033 [4 $ {cborder} m "(t2 = ileft + 1 )) (t3 = ileft + itraywidth * 2 + 3) for (I = 0; I <itrayheight; I ++ )) do (T1 = I + iTOP + 2) echo-ne "\ 033 [$ {T1 }; $ {T2} H | "echo-ne" \ 033 [$ {T1}; $ {T3} H | "done (t2 = iTOP + itrayheight + 2 )) for (I = 0; I <itraywidth + 2; I ++) do (T1 = I * 2 + Ileft + 1) echo-ne "\ 033 [$ {itraytop };; {T1} H =" echo-ne "\ 033 [$ {T2 }; $ {T1} H = "done echo-ne" \ 033 [0 m "# Show" score "and" level "echo-ne" \ 033 [1 m "( (T1 = ileft + itraywidth * 2 + 7 )) (t2 = iTOP + 10) echo-ne "\ 033 [3 $ {cscore} m \ 033 [$ {T2 }; $ {T1} hscore "(t2 = iTOP + 11) echo-ne" \ 033 [3 $ {cscorevalue} m \ 033 [$ {T2 }; $ {T1} h $ {iscore} "(t2 = iTOP + 13) echo-ne" \ 033 [3 $ {cscore} m \ 033 [$ {T2 }; $ {T1} hlevel "(t2 = ITOP + 14) echo-ne "\ 033 [3 $ {cscorevalue} m \ 033 [$ {T2 }; $ {T1} h $ {ilevel} "echo-ne" \ 033 [0 m "} # gameover displayed when exiting! Function showexit () {local Y (y = itrayheight + itraytop + 3) echo-e "\ 033 [$ {y}; 0 hgameover! \ 033 [0 m "Exit} # display usage. function usage {cat <eofusage: $ app_namestart Tetris game. -H, -- help display this help and exit -- version output version information and exiteof} # The main game program starts here. if [["$1" = "-h" | "$1" = "-- Help"]; then usageelif [["$1" = "-- version"]; then ECHO "$ app_name $ app_version" Elif [["$1" = "-- Show"]; then # When the -- show parameter is found, run runasdisplayerelse bash $0 -- show & # Use the -- show Parameter Run this program again. runaskeycycler $! # The process ID generated by the preceding line is used as the parameter fi
Powerful. You can study it.