Very powerful shell-written Tetris
On the Internet, you can see a Russian box written using linux shell scripts. It is the most awesome shell I have ever written. Share.
The original author information is included in the comments of the script.
: Click to download
#! /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 = $ CFuchsiacScoreValue = $ cCyan # control signal # change the game to use two processes, one for receiving input, and 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 1 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 # iBoxN EwRotate = 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) cESC = 'echo-ne "\ 033" 'cspa Ce = 'echo-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 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 based on the current speed level iLevel For (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 square in the movement can be moved to the position of (x, y). If 0 is returned, 1 cannot be function BoxMove () {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 box in the current movement to the background box, RandomBox # generate a new box fi} # Move the column to the left Function 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 (iTestR Otate> = 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]} + $ I TestRotate) * 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} # update the new Square 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, at this time, there is a square in the pre-display window on the right, Rand OmBox # 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 "(t 2 = 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 the display function RunAsDisplayerelse bash $0 -- show & # With the -- sh Parameter Ow runs this program again. runaskeycycler $! # The process ID generated by the preceding line is used as the parameter fi