The following script is powerful, make by xhchen, and add it to favorites.
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917 0171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303 3043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364 3743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957 0571572573574575576577578579580581582583584585586587588 #! /Bin/bash # Tetris Game #10.21.2003 xhchen & amp; lt; [email] xhchen@winbond.com.tw [/email] & amp; gt; # APP declarationAPP_NAME = & quot; ${0 # * [/]} & quot; APP_VERSION = & quot; 1.0 & quot; # 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 upper, lower, and left buttons, it sends a signal notification to the latter. 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 # iBoxNewRotate = 0 # 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 & amp; lt; iTrayHeight * iTrayWidth; I ++ )); doiMap [$ I] =-1; done # Main Function functionRunAsKeyReceiver () {localpidDisplayer key aKey sig cESC sTTYpidDisplayer = $1 aKey = (0 0 0) cESC = 'echo-ne & quot; & #92; & #48; 33 & quot; 'cspace = 'echo -Ne & quot; & #92; & #48; 40 & quot; '# 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 & quot; MyExit; & quot; INT TERMtrap & quot; MyExitNoSub; & quot; $ sigExit # Hide the cursor echo-ne & quot; & #92; & #48; 33 [? 25l & quot; while: do # reads the input. Note-s does not show back, -n returns read-s-n 1 keyaKey [0] =$ {aKey [1]} aKey [1] =$ {aKey [2]} aKey [2] = $ keysig = 0 # determine the Input key if [[$ key = $ cESC & amp; amp; & amp; $ {aKey [1] }=ccesc] then # ESC key MyExitelif [[$ {aKey [0] }== $ cESC & amp; amp; $ {aKey [1] }=& quot; [& quot;] thenif [[$ key ==& quot; A & quot;]; thensig = $ sigRotate # & amp; lt; up key & amp; gt; elif [[$ key = & quot; B & quot;]; thensig = $ sigDown # & amp; lt; down key & amp; gt; elif [[$ key = & Quot; D & quot;]; thensig = $ sigLeft # & amp; lt; left arrow & amp; gt; elif [[$ key = & quot; C & quot;]; thensig = $ sigRight # & amp; lt; right-click & amp; gt; fielif [[$ key = & quot; W & quot; | $ key ==& quot; w & quot;]; thensig = $ sigRotate # W, welif [[$ key ===& quot; S & quot; | $ key ==& quot; s & quot;]; thensig = $ sigDown # S, selif [[$ key ==& quot; A & quot; | $ key ==& quot; a & quot;]; thensig = $ sigLeft # A, aelif [[$ key ==& quot; D & quot; | $ key = & Quot; d & quot;]; thensig = $ sigRight # D, delif [[& quot; [$ key] & quot ;== & quot; [] & quot;]; thensig = $ sigAllDown # Space key elif [[$ key = & quot; Q & quot; | $ key = & quot; q & quot;] # Q, qthenMyExitfiif [[$ sig! = 0] then # send the message kill-$ sig $ pidDisplayerfidone} to another process # restore functionMyExitNoSub () before exiting () {localy # restore terminal attributes stty $ sTTY (y = iTop + iTrayHeight + 4) # display the cursor echo-e & quot; & #92; & #48; 33 [? 25 h & #92; & #48; 33 [$ {y}; 0 H & quot; exit} functionMyExit () {# The Notification display process needs to exit kill-$ sigExit $ pidDisplayerMyExitNoSub} # Main Function functionRunAsDisplayer () {localsigThisInitDraw # handler trap for mounting various signals; sig = $ sigRotate; & quot; $ sigRotatetrap & quot; sig = $ sigLeft; & quot; $ sigLefttrap & quot; sig = $ sigRight; & quot; $ sigRighttrap & quot; sig = $ sigDown; & quot; $ sigDowntrap & quot; sig = $ sigAllDown; & quot; $ sigAllDowntrap & quot; ShowExit; & quot; $ SigExitwhile: do # set the number of cycles for (I = 0; I & amp; lt; 21-iLevel; I ++) based on the current iLevel of speed )) dosleep0.02sigThis = $ sigsig = 0 # determine whether to receive the corresponding signal if (sigThis = sigRotate) based on the sig variable); thenBoxRotate; # rotating elif (sigThis = sigLeft); thenBoxLeft; # moving the left column of elif (sigThis = sigRight); thenBoxRight; # shift a column to the right of elif (sigThis = sigDown); thenBoxDown; # Drop a row of elif (sigThis = sigAllDown); thenBoxAllDown; # Drop to the end of fidone # kill-$ sigDown $ BoxDown # Drop a row of done} # BoxMove (y, x) to test whether to move the square in the movement to the position (x, y). If 0 is returned, yes. 1 cannot functionBoxMove () {localj I x y xTest yTestyTest = $1 xTest = $ 2for (j = 0; j & amp; lt; 8; j ++ = 2 )) do (I = j + 1) (y =$ {boxCur [$ j]} + yTest )) (x =$ {boxCur [$ I]} + xTest) if (y & amp; lt; 0 | y & amp; gt; = iTrayHeight | x & amp; lt; 0 | x & amp; gt; = iTrayWidth )) then # Hit the wall with return1fiif ($ {iMap [y * iTrayWidth + x]}! =-1) then # hits another existing square and return1fidonereturn0;} # places the currently moving square in the background square. # calculates the new score and speed level. (That is, once the square falls to the bottom) functionBox2Map () {localj I x y xp yp line # Put the square in the current movement in the background square for (j = 0; j & amp; lt; 8; j + = 2) do (I = j + 1) (y =$ {boxCur [$ j]} + boxCurY )) (x =$ {boxCur [$ I]} + boxCurX) (I = y * iTrayWidth + x )) iMap [$ I] = $ cBoxCurdone # Delete the line that can be deleted = 0for (j = 0; j & amp; lt; iTrayWidth * iTrayHeight; j + = iTrayWidth )) dofor (I = j + iTrayWidth-1; I & amp; gt; = j; I --)) doif ($ {iMap [$ I] }=- 1); thenbreak; fidon Eif (I & amp; gt; = j); thencontinue; fi (line ++) for (I = j-1; I & amp; gt; = 0; I --) do (x = I + iTrayWidth) iMap [$ x] =$ {iMap [$ I]} donefor (I = 0; I & amp; lt; iTrayWidth; I ++) doiMap [$ I] =-1 donedoneif (line = 0); thenreturn; 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 & quot; & #92; & #48; 33 [1 m & #92; & #48; 33 [3 $ {cScoreValue} m & #92; & #48; 33 [$ {y}; $ {X} H $ {iScore} & quot; if (iScore % iScoreEachLevel & amp; lt; line * 2-1) thenif (iLevel & amp; lt; 20) then (iLevel ++) (y = iTop + 14) # display the new speed echo-ne & quot; & #92; & #48; 33 [3 $ {cScoreValue} m & #92; & #48; 33 [$ {y}; $ {x} H $ {iLevel} & quot; incluiecho-ne & quot; & #92; & #48; 33 [0 m & quot; # re-display the background square for (y = 0; y & amp; lt; iTrayHeight; y ++) do (yp = y + iTrayTop + 1) (xp = iTrayLeft + 1) (I = y * iTrayWidth )) echo-ne & quot; & #92 ;& #48; 33 [$ {yp}; $ {xp} H & quot; for (x = 0; x & amp; lt; iTrayWidth; x ++ )) do (j = I + x) if ($ {iMap [$ j] }==-1) thenecho-ne & quot; elseecho-ne & quot; & #92; & #48; 33 [1 m & #92; & #48; 33 [7 m & #92; & #48; 33 [3 $ {iMap [$ j]} m & #92; & #48; 33 [4 $ {iMap [$ j]} m [] & #92; & #48; 33 [0 m & quot; fidonedone} # Drop a row functionBoxDown () {localy s (y = boxCurY + 1 )) # New y coordinate ifBoxMove $ y $ boxCurX # test whether a row can be dropped by thens = & quot; 'drawcurbox 0' & quot; # erase the old square (boxCurY = Y) s = & quot; $ s 'drawcurbox 1' & quot; # display the new lower-left box echo-ne $ selse # 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 left column to functionBoxLeft () {localx s (x = boxCurX-1) ifBoxMove $ boxCurY $ xthens = 'drawcurbox 0' (boxCurX = x )) s = $ s 'drawcurbox 1' echo-ne $ sfi} # Right Shift a column functionBoxRight () {localx s (x = boxCurX + 1 )) ifBoxMove $ boxCurY $ xthens = 'drawcurbox 0' (boxCurX = x) s = $ s' DrawCurBox 1 'echo-ne $ sfi} # Fall to functionB OxAllDown () {localk j I x y iDown siDown = $ iTrayHeight # calculate the total number of rows to be dropped for (j = 0; j & amp; lt; 8; j + = 2) do (I = j + 1) (y =$ {boxCur [$ j]} + boxCurY )) (x =$ {boxCur [$ I]} + boxCurX) for (k = y + 1; k & amp; lt; iTrayHeight; k ++ )) do (I = k * iTrayWidth + x) if ($ {iMap [$ I]}! =-1); thenbreak; fidone (k-= y + 1) if ($ iDown & amp; gt; $ k); theniDown = $ k; fidones = 'drawcurbox 0' # erase the old square (boxCurY + = iDown )) s = $ s 'drawcurbox 1' # display the new backward box echo-ne $ sBox2Map # paste the square in the current movement to the background box RandomBox # generate a new box} # rotate box functionBoxRotate () {localiCount iTestRotate boxTest j I siCount =$ {countBox [$ iBoxCurType]} # Number of styles generated by rotation of the current square # Calculate the new style after rotation (iTestRotate = iBoxCurRotate + 1 )) if (iTestRotate & amp; gt; = iCount) th En (iTestRotate = 0) fi # update to a new style and save the old style (but not displayed) for (j = 0, I = ($ {offsetBox [$ iBoxCurType]} + $ iTestRotate) * 8; j & amp; lt; 8; j ++, I ++ )) doboxTest [$ j] =$ {boxCur [$ j]} boxCur [$ j] =$ {box [$ I]} doneifBoxMove $ boxCurY $ boxCurX # test whether there is space after rotation put down then # erase the old square for (j = 0; j & amp; lt; 8; j ++ )) doboxCur [$ j] =$ {boxTest [$ j]} dones = 'drawcurbox 0' # draw a new square for (j = 0, I = ($ {offsetBox [$ iBoxCurType]} + $ iTestRotate) * 8; j & amp; lt; 8; J ++, I ++ )) doboxCur [$ j] =$ {box [$ I]} dones = $ s 'drawcurbox 1' echo-ne $ siBoxCurRotate = $ iTestRotateelse # It cannot be rotated, continue to use the old style for (j = 0; j & amp; lt; 8; j ++ )) doboxCur [$ j] =$ {boxTest [$ j]} donefi} # DrawCurBox (bDraw), draw the square in the current movement, set bDraw to 1, draw, and bDraw to 0, erase the square. FunctionDrawCurBox () {locali j t bDraw sBox sbDraw = $1 s = & quot; if (bDraw = 0) thensBox = & quot; & #92; & #48; 40 & #92; & #48; 40 & quot; elsesBox = & quot; [] & quot; s = $ s & quot; & #92; & #48; 33 [1 m & #92; & #48; 33 [7 m & #92; & #48; 33 [3 $ {cBoxCur} m & #92; & #48; 33 [4 $ {cBoxCur} m & quot; primary or (j = 0; j & amp; lt; 8; j ++ = 2 )) do (I = iTrayTop + 1 + $ {boxCur [$ j]} + boxCurY )) (t = iTrayLeft + 1 + 2 * (boxCurX + $ {boxCur [$ j + 1]}) # & #92; & #48; 3 3 [y; xH, cursor to (x, y) s =$ s & quot; & #92; & #48; 33 [$ {I }; $ {t} H $ {sBox} & quot; dones = $ s & quot; & #92; & #48; 33 [0 m & quot; echo-n $ s} # update the new box functionRandomBox () {locali j t # update the currently moved box iBoxCurType =$ {iBoxNewType} iBoxCurRotate =$ {iBoxNewRotate} cBoxCur =$ {cBoxNew} for (j = 0; j & amp; lt ;$ {# boxNew [@]}; j ++ )) doboxCur [$ j] =$ {boxNew [$ j]} done # display the currently moved box if ($ {# boxCur [@] }== 8 )) then # calculates the top row of the current square & quot; for (j = 0, t = 4; j & amp; lt; 8; j + = 2) doif ($ {boxCur [$ j]} & amp; lt; t); thent =$ {boxCur [$ j]}; fidone (boxCurY =-t) for (j = 1, I =-4, t = 20; j & amp; lt; 8; j + = 2 )) doif ($ {boxCur [$ j]} & amp; gt; I); theni =$ {boxCur [$ j]}; fiif ($ {boxCur [$ j]} & amp; lt; t); thent =ent {boxCur [$ j]}; fidone (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 $ boxCurXthenkill-$ sigExit $ {PPID} ShowExitfifi # Clear the pre-displayed block on the right for (j = 0; j & amp; lt; 4; j ++ )) do (I = iTop + 1 + j) (t = iLeft + 2 * iTrayWidth + 7) echo-ne & quot; & #92; & #48; 33 [$ {I };$ {t} H & quot; done # randomly generate new blocks (iBoxNewType = RANDOM % $ {# offsetBox [@]}) (iBoxNewRotate = RANDOM % $ {countBox [$ iBoxNewType]}) for (j = 0, I = ($ {offsetBox [$ iBoxNewType]} + $ iBoxNewRotate) * 8; j & amp; lt; 8; j ++, I ++) DoboxNew [$ j] =$ {box [$ I]}; done (cBoxNew =$ {colorTable [RANDOM % $ {# colorTable [@]}) # display the pre-displayed block echo-ne & quot; & #92; on the right; & #48; 33 [1 m & #92; & #48; 33 [7 m & #92; & #48; 33 [3 $ {cBoxNew} m & #92; & #48; 33 [4 $ {cBoxNew} m & quot; for (j = 0; j & amp; lt; 8; j ++ = 2 )) do (I = iTop + 1 + $ {boxNew [$ j]}) (t = iLeft + 2 * iTrayWidth + 7 + 2 * $ {boxNew [$ j + 1]}) echo-ne & quot; & #92; & #48; 33 [$ {I}; $ {t} H [] & quot; doneecho-ne & quot; & #92; & #48; 33 [0 m & Quot ;}# initially draw functionInitDraw () {clearRandomBox # randomly generate squares. Then, there is a square in the pre-display window on the right that is faster than RandomBox # Then randomly generate squares. the squares in the pre-display window on the right are updated, the original square will start to fall down locali t1 t2 t3 # display the border echo-ne & quot; & #92; & #48; 33 [1 m & quot; echo-ne & quot; & #92; & #48; 33 [3 $ {cBorder} m & #92; & #48; 33 [4 $ {cBorder} m & quot; (t2 = iLeft + 1) (t3 = iLeft + iTrayWidth * 2 + 3) for (I = 0; I & amp; lt; iTrayHeight; I ++) do (t1 = I + iTop + 2) echo-ne & quot; & #92; & #48; 33 [$ {t1 }; $ {t2} H | & quot; echo- Ne & quot; & #92; & #48; 33 [$ {t1 };$ {t3} H | & quot; done (t2 = iTop + iTrayHeight + 2) for (I = 0; I & amp; lt; iTrayWidth + 2; I ++ )) do (t1 = I * 2 + iLeft + 1) echo-ne & quot; & #92; & #48; 33 [$ {iTrayTop }; $ {t1} H ==& quot; echo-ne & quot; & #92; & #48; 33 [$ {t2 }; $ {t1} H ==& quot; doneecho-ne & quot; & #92; & #48; 33 [0 m & quot; # Show & quot; Score & quot; and & quot; Level & quot; echo-ne & quot; & #92; & #48; 33 [1 m & quot; (t1 = iLeft + iTrayWidth * 2 + 7) (t2 = ITop + 10) echo-ne & quot; & #92; & #48; 33 [3 $ {cScore} m & #92; & #48; 33 [$ {t2 };; {t1} HScore & quot; (t2 = iTop + 11) echo-ne & quot; & #92; & #48; 33 [3 $ {cScoreValue} m & #92; & #48; 33 [$ {t2} ;$ {t1} H $ {iScore} & quot; (t2 = iTop + 13) echo-ne & quot; & #92; & #48; 33 [3 $ {cScore} m & #92; & #48; 33 [$ {t2}; $ {t1} HLevel & quot; (t2 = iTop + 14) echo-ne & quot; & #92; & #48; 33 [3 $ {cScoreValue} m & #92; & #48; 33 [$ {t2} ;$ {t1} H $ {iLevel} & quot; echo-ne & quot; & #92; & #48; 33 [0 m & quot;} # GameOVer displayed when exiting! FunctionShowExit () {localy (y = iTrayHeight + iTrayTop + 3) echo-e & quot; & #92; & #48; 33 [$ {y}; 0 HGameOver! & #92; & #48; 33 [0 m & quot; exit} # display usage. functionUsage {cat & amp; lt; EOFUsage: $ APP_NAMEStart tetris game. -h, -- help display this help andexit -- version output version information andexitEOF} # The main game program starts here. if [[& quot; $1 & quot ;=& quot;-h & quot ;||& quot; $1 & quot ;=& quot; -- help & quot;]; thenUsageelif [[& quot; $1 & quot ;==& quot; -- version & quot;]; thenecho & quot; $ APP_NAME $ APP_VERSION & quot; elif [[& quot; $1 & quot ;=& Quot; -- show & quot;]; then # When the -- show parameter is found, run the display function RunAsDisplayerelsebash $0 -- show & amp; # Run the program again with the -- show parameter runaskeycycler $! # The process ID generated by the preceding line is used as the parameter fi