How to use shell script to write Tetris games
This article mainly introduces how to use shell script to write Tetris games, the article is very detailed, has a certain reference value, interested friends must read it!
Paste the following code into an empty Shell script file and run it in Bash!
The code is as follows:
#! / bin/bash
# Tetris Game
# 10.21.2003 xhchen
# APP declaration
APP_NAME= "${0,000,500 * [\\ /]}"
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)
# location and size
ILeft=3
ITop=2
((iTrayLeft = iLeft + 2))
((iTrayTop = iTop + 1))
((iTrayWidth = 10))
((iTrayHeight = 15))
# Color Settings
CBorder=$cGreen
CScore=$cFuchsia
CScoreValue=$cCyan
# Control signal
# change the game to use two processes, one for receiving input and one for the game flow and display interface
# when the current user receives the up and down keys, notify the latter by sending signal to the latter.
SigRotate=25
SigLeft=26
SigRight=27
SigDown=28
SigAllDown=29
SigExit=30
# definition of different squares in # 7
# by rotation, there may be several styles for the display of each square
Box0= (0 0 1 1 0 1 1)
Box1= (0 2 1 2 2 2 3 2 1 0 1 1 1 2 1 3)
Box2= (0 0 1 1 1 2 0 1 1 0 1 1 2 0)
Box3= (0 1 0 2 1 0 1 0 1 0 1 1 1 2 1)
Box4= (0 1 0 2 1 1 1 0 1 1 1 2 2 2 0 1 1 1 2 0 2 1 0 1 0 1 1 1 12)
Box5= (0 1 1 1 2 1 2 1 0 1 1 1 2 2 2000 0 0 1 1 1 0 2 1 0 1 1 1 12)
Box6= (0 1 1 1 2 2 1 1 0 1 1 1 2 2 1 0 1 1 0 1 1 2 1 0 1 0 1 1 1 12)
# all the definitions of squares are put 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 of various squares in the box array
OffsetBox= (0 1 3 5 7 11 15)
# points accumulated for each increase in speed level
IScoreEachLevel=50 # be greater than 7
# Runtime data
Sig=0 # received signal
IScore=0 # Total score
ILevel=0 # speed class
BoxNew= () # the location definition of the newly fallen square
CBoxNew=0 # the color of the newly fallen square
IBoxNewType=0 # types of newly fallen squares
IBoxNewRotate=0 # rotation angle of the newly fallen square
BoxCur= () # the location definition of the current square
CBoxCur=0 # Color of the current square
IBoxCurType=0 # the category of the current square
IBoxCurRotate=0 # rotation angle of the current square
BoxCurX=-1 # the x coordinate position of the current square
BoxCurY=-1 # the y coordinate position of the current square
IMap= () # background square chart
# initialize all background squares as-1, which means there are no squares
For ((I = 0; I)
< iTrayHeight * iTrayWidth; i++)); do iMap[$i]=-1; done #接收输入的进程的主函数 function RunAsKeyReceiver() { local pidDisplayer key aKey sig cESC sTTY pidDisplayer=$1 aKey=(0 0 0) cESC=`echo -ne "\033"` cSpace=`echo -ne "\040"` #保存终端属性。在read -s读取终端键时,终端的属性会被暂时改变。 #如果在read -s时程序被不幸杀掉,可能会导致终端混乱, #需要在程序退出时恢复终端属性。 sTTY=`stty -g` #捕捉退出信号 trap "MyExit;" INT TERM trap "MyExitNoSub;" $sigExit #隐藏光标 echo -ne "\033[?25l" while : do #读取输入。注-s不回显,-n读到一个字符立即返回 read -s -n 1 key aKey[0]=${aKey[1]} aKey[1]=${aKey[2]} aKey[2]=$key sig=0 #判断输入了何种键 if [[ $key == $cESC && ${aKey[1]} == $cESC ]] then #ESC键 MyExit elif [[ ${aKey[0]} == $cESC && ${aKey[1]} == "[" ]] then if [[ $key == "A" ]]; then sig=$sigRotate # elif [[ $key == "B" ]]; then sig=$sigDown # elif [[ $key == "D" ]]; then sig=$sigLeft # elif [[ $key == "C" ]]; then sig=$sigRight # 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, a elif [[ $key == "D" || $key == "d" ]]; then sig=$sigRight #D, d elif [[ "[$key]" == "[]" ]]; then sig=$sigAllDown #空格键 elif [[ $key == "Q" || $key == "q" ]] #Q, q then MyExit fi if [[ $sig != 0 ]] then #向另一进程发送消息 kill -$sig $pidDisplayer fi done } #退出前的恢复 function MyExitNoSub() { local y #恢复终端属性 stty $sTTY ((y = iTop + iTrayHeight + 4)) #显示光标 echo -e "\033[?25h\033[${y};0H" exit } function MyExit() { #通知显示进程需要退出 kill -$sigExit $pidDisplayer MyExitNoSub } #处理显示和游戏流程的主函数 function RunAsDisplayer() { local sigThis InitDraw #挂载各种信号的处理函数 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 #根据当前的速度级iLevel不同,设定相应的循环的次数 for ((i = 0; i < 21 - iLevel; i++)) do sleep 0.02 sigThis=$sig sig=0 #根据sig变量判断是否接受到相应的信号 if ((sigThis == sigRotate)); then BoxRotate; #旋转 elif ((sigThis == sigLeft)); then BoxLeft; #左移一列 elif ((sigThis == sigRight)); then BoxRight; #右移一列 elif ((sigThis == sigDown)); then BoxDown; #下落一行 elif ((sigThis == sigAllDown)); then BoxAllDown; #下落到底 fi done #kill -$sigDown $$ BoxDown #下落一行 done } #BoxMove(y, x), 测试是否可以把移动中的方块移到(x, y)的位置, 返回0则可以, 1不可以 function BoxMove() { local j i x y xTest yTest 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
Return 1
Fi
If ((${iMap [y * iTrayWidth + x]}! =-1))
Then
# bumped into other existing squares
Return 1
Fi
Done
Return 0
}
# put the currently moving square into the background box
# and 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
# put the currently moving square into the background box
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 #消去可被消去的行 line=0 for ((j = 0; j < iTrayWidth * iTrayHeight; j += iTrayWidth)) do for ((i = j + iTrayWidth - 1; i >= j; iMurt -))
Do
If ((${iMap [$I]} =-1)); then break; fi
Done
If ((I > = j)); then continue; fi
((line++))
For ((I = j-1; I > = 0; iMel -))
Do
((X = I + iTrayWidth))
IMap [$x] = ${iMap [$I]}
Done
For ((I = 0; I)
< iTrayWidth; i++)) do iMap[$i]=-1 done done if ((line == 0)); then return; fi #根据消去的行数line计算分数和速度级 ((x = iLeft + iTrayWidth * 2 + 7)) ((y = iTop + 11)) ((iScore += line * 2 - 1)) #显示新的分数 echo -ne "\033[1m\033[3${cScoreValue}m\033[${y};${x}H${iScore} " if ((iScore % iScoreEachLevel < line * 2 - 1)) then if ((iLevel < 20)) then ((iLevel++)) ((y = iTop + 14)) #显示新的速度级 echo -ne "\033[3${cScoreValue}m\033[${y};${x}H${iLevel} " fi fi echo -ne "\033[0m" #重新显示背景方块 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[1m\033[7m\033[3${iMap[$j]}m\033[4${iMap[$j]}m[]\033[0m" fi done done } #下落一行 function BoxDown() { local y s ((y = boxCurY + 1)) #新的y坐标 if BoxMove $y $boxCurX #测试是否可以下落一行 then s="`DrawCurBox 0`" #将旧的方块抹去 ((boxCurY = y)) s="$s`DrawCurBox 1`" #显示新的下落后方块 echo -ne $s else #走到这儿, 如果不能下落了 Box2Map #将当前移动中的方块贴到背景方块中 RandomBox #产生新的方块 fi } #左移一列 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 } #右移一列 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 #计算一共需要下落多少行 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))
Slots $s`DrawCurBox 1` # shows the square after the new drop
Echo-ne $s
Box2Map # pastes the currently moving square to the background square
RandomBox # generates new squares
}
# rotate the square
Function BoxRotate ()
{
Local iCount iTestRotate boxTest j i s
ICount=$ {countBox [$iBoxCurType]} # the number of styles that the current square can be rotated to produce
# calculate the new style after rotation
((iTestRotate = iBoxCurRotate + 1))
If ((iTestRotate > = iCount))
Then
((iTestRotate = 0))
Fi
# Update to the new style and save the old style (but not shown)
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 #测试旋转后是否有空间放的下 then #抹去旧的方块 for ((j = 0; j < 8; j++)) do boxCur[$j]=${boxTest[$j]} done s=`DrawCurBox 0` #画上新的方块 for ((j = 0, i = (${offsetBox[$iBoxCurType]} + $iTestRotate) * 8; j < 8; j++, i++)) do boxCur[$j]=${box[$i]} done s=$s`DrawCurBox 1` echo -ne $s iBoxCurRotate=$iTestRotate else #不能旋转,还是继续使用老的样式 for ((j = 0; j < 8; j++)) do boxCur[$j]=${boxTest[$j]} done fi } #DrawCurBox(bDraw), 绘制当前移动中的方块, bDraw为1, 画上, bDraw为0, 抹去方块。 function DrawCurBox() { local i j t bDraw sBox s bDraw=$1 s="" if (( bDraw == 0 )) then sBox="\040\040" else sBox="[]" s=$s"\033[1m\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, 光标到(x, y)处 s=$s"\033[${i};${t}H${sBox}" done s=$s"\033[0m" echo -n $s } #更新新的方块 function RandomBox() { local i j t #更新当前移动的方块 iBoxCurType=${iBoxNewType} iBoxCurRotate=${iBoxNewRotate} cBoxCur=${cBoxNew} for ((j = 0; j < ${#boxNew[@]}; j++)) do boxCur[$j]=${boxNew[$j]} done #显示当前移动的方块 if (( ${#boxCur[@]} == 8 )) then #计算当前方块该从顶端哪一行"冒"出来 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 iTunes ${boxCur [$j]}; fi
If ((${boxCur [$j]} < t)); then tasking ${boxCur [$j]}; fi
Done
((boxCurX = (iTrayWidth-1-I-t) / 2))
# display the currently moved square
Echo-ne `DrawCurBox 1`
# if there is no place to put the square as soon as it comes out, Game over!
If! BoxMove $boxCurY $boxCurX
Then
Kill-$sigExit ${PPID}
ShowExit
Fi
Fi
# clear the pre-displayed square on the right
For ((j = 0; j < 4; jacks +))
Do
((I = iTop + 1 + j))
((t = iLeft + 2 * iTrayWidth + 7))
Echo-ne "\ 033 [${I}; ${t} H"
Done
# randomly generate new squares
((iBoxNewType = RANDOM% ${# offsetBox [@]}))
((iBoxNewRotate = RANDOM% ${countBox [$iBoxNewType]}))
For ((j = 0, I = (${offsetBox [$iBoxNewType]} + $iBoxNewRotate) * 8; j < 8; jacks, iTunes +)
Do
BoxNew [$j] = ${box [$I]}
Done
((cBoxNew = ${colorTable [RANDOM% ${# colorTable [@]}]}))
# display the pre-displayed square on the right
Echo-ne "\ 033 [1m\ 033 [7m\ 033 [3 ${cBoxNew} m\ 033 [4 ${cBoxNew} m"
For ((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 [0m"
}
# initial drawing
Function InitDraw ()
{
Clear
RandomBox # randomly generates squares, and the square in the right preview window is faster.
RandomBox # randomly generates squares, the squares in the right pre-display window are updated, and the original squares will begin to fall
Local i t1 t2 t3
# display border
Echo-ne "\ 033 [1m"
Echo-ne "\ 033 [3 ${cBorder} m\ 033 [4 ${cBorder} m"
((T2 = iLeft + 1))
(T3 = iLeft + iTrayWidth * 2 + 3))
For ((I = 0; I < iTrayHeight; iTunes +))
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; iTunes +))
Do
(T1 = I * 2 + iLeft + 1))
Echo-ne "\ 033 [${iTrayTop}; ${T1} hinge ="
Echo-ne "\ 033 [${T2}; ${T1} hinge ="
Done
Echo-ne "\ 033 [0m"
# display the words "Score" and "Level"
Echo-ne "\ 033 [1m"
(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 [0m"
}
# display GameOVer when exiting!
Function ShowExit ()
{
Local y
((y = iTrayHeight + iTrayTop + 3))
Echo-e "\ 033 [${y}; 0HGameOver!\ 033 [0m"
Exit
}
# display usage.
Function Usage
{
Cat