A Tetris that was written on the web with a Linux shell script. It's the best shell I've ever seen written. Share a bit.
The original author information is in the comments of the script.
#!/bin/bash # Tetris game# 10.21.2003 xhchen<[email][email protected][/email]> #APP declarationAPP_NAME= "$ {0##*[\\/]} "app_version=" 1.0 "#颜色定义cRed =1cgreen=2cyellow=3cblue=4cfuchsia=5ccyan=6cwhite=7colortable= ($cRed $ Cgreen $cYellow $cBlue $cFuchsia $cCyan $cWhite) #位置和大小iLeft =3itop=2 ((itrayleft = ILeft + 2)) ((Itraytop = iTop + 1)) (Itra Ywidth =)) ((Itrayheight =)) #颜色设置cBorder = $cGreencScore = $cFuchsiacScoreValue = $cCyan #控制信号 # change game use two processes, one for receiving input, One for the game flow and display interface; #当前者接收到上下左右等按键时 to inform the latter by sending signal to the latter. sigrotate=25sigleft=26sigright=27sigdown=28sigalldown=29sigexit=30 #七中不同的方块的定义 # by rotation, the display style of each block may have several box0= (0 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 0 1 1 1 1 2 0 1 1 0 1 1 2 0) box3= (0 1 0 2 1 0 1 1 0 0 1 0 1 1 2 1) box4 = (0 1 0 2 1 1 2 1 1 0 1 1 1 2 2 2 0 1 1 1 2 0 2 1 0 0 1 0 1 1 1 2) box5= (0 1 1 1 2 1 2 2 1 0 1 1 1 2 2 0 0 0 2 1 0 2 1 0 1 1 1 2) box6= (0 1 1 1 1 2 2 1 1 0 1 1 1 2 2 1 0 1 1 0 1 1 2 1 0 1 1 0 1 1 1 2) #所有其中方块的定义都放到box变量中box = (${box0[@ ]} ${box1[@]} ${box2[@]} ${box3[@]} ${box4[@] {${box5[@]} ${box6[@]}) #各种方块旋转后可能的样式数目countBox = (1 2 2 2 4 4 4) #各种方块再box数组中的偏移offsetBox = ( 0 1 3 5 7) #每提高一个速度级需要积累的分数iScoreEachLevel =50 #be greater than 7 #运行时数据sig =0 #接收到的signaliScore =0 #总分iLevel =0 #速度级boxNew = () #新下落的方块的位置定义cBoxNew =0 #新下落的方块的颜色iBoxNewType =0 #新下落的方块的种类iB Oxnewrotate=0 #新下落的方块的旋转角度boxCur = () #当前方块的位置定义cBoxCur =0 #当前方块的颜色iBoxCurType =0 #当前方块的种类iBoxCurR Otate=0 #当前方块的旋转角度boxCurX =-1 #当前方块的x坐标位置boxCurY =-1 #当前方块的y坐标位置iMap = () #背景方块图表 #初始化所有背景 The block is-1, which means no block 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" ' #保存终端属性. When Read-s reads the terminal key, the terminal's properties are temporarily changed. #如果在read-S program is unfortunately killed, may cause terminal confusion, #需要在程序退出时恢复终端Property. stty= ' stty-g ' #捕捉退出信号 trap "myexit;" INT term trap "myexitnosub;" $sigExit #隐藏光标 Echo-ne "\033[?25l" While:do #读取输入. Note-S does not echo,-N reads to a character immediately returns 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 #< up key > elif [[$key = = "B"]]; Then sig= $sigDown #< down arrow > elif [[$key = = "D"]]; Then sig= $sigLeft #< left arrow > elif [[$key = = "C"]]; Then sig= $sigRight #< right > 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不同, set the number of times for the corresponding loop 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), test whether the moving block can be moved to (x, y) position, return 0, 1 can not function Boxmove () {Local J I x y xTest ytest Ytest=$1 xtest=$2 for ((j = 0; J < 8; J + + 2)) does ((i = j + 1)) ((y = ${boxcur[$j]} + ytest)) ((x = ${boxcur[$i]} + xTest)) if ((Y < 0 | | y >= itrayheight | | x < 0 | | x >= itraywidth)) Then #撞到墙壁了 return 1 fi if (${ima P[y * itraywidth + x]} =-1) then #撞到其他已经存在的方块了 return 1 Fi done return 0;} #将当前移动中的方块放到背景方块中去, #并计算新的分数和速度级. (That is, once the block falls to the bottom) function Box2map () {Local J I x y xp YP line #将当前移动中的方块放到背景方块中去 for ((j= 0; J < 8; J + = 2)) do ((i = j + 1)) ((y = ${boxcur[$j]} + Boxcury)) ((x = ${boxc ur[$i]} + Boxcurx)) ((i = y * itraywidth + x)) imap[$i]= $cBoxCur done #消去可被消去 The line line=0 for ((j = 0; J < Itraywidth * Itrayheight; j + + Itraywidth)) does 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--)) Does (( x = i + itraywidth)) imap[$x]=${imap[$i]} done for ((i = 0; i < IT Raywidth; i++)) do imap[$i the]=-1 done if (line = = 0); then return; Fi #根据消去的行数line计算分数和速度级 ((x = ILEFT + itraywidth * 2 + 7) ((y = iTop + one)) ((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 <) and then ((ilevel++)) ((y = ITop +)) #显示新的速度级 Echo-ne "\033[3${cscorevalue}m\033[${y};${x}h${i Level} "Fi fi echo-ne" \033[0m "#重新显示背景方块 for ((y = 0; Y < Itrayheight; y++)) do ((YP = y + itraytop + 1)) ((XP = Itrayleft + 1)) ((i = y * IT raywidth)) Echo-ne "\033[${yp};${xp}h" for ((x = 0; x < Itraywidth + x + +)) Do ((j = i + x)) if ((${imap[$j]} = = 1) and then Echo-ne "" Else Echo-ne "\033[1m\033[7m\033[3${imap[$j] }m\033[4${imap[$j]}m[]\033[0m "fi Done" #下落一行function Boxdown () { Local y S ((y = Boxcury + 1)) #新的y坐标 if Boxmove $y $boxCurX #测试是否可以下落一行 Then s= "' Drawcurbox 0 '" #将旧的方块抹去 ((Boxcury = y)) s= "$s ' drawcurbox 1 '" #显示新的下落 The rear block Echo-ne $s else #走到这儿 if it cannot fall Box2map #将当前移动中的方块贴 To the Background box 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 = Boxcur X + 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= $iTrayHeig HT #计算一共需要下落多少行 for ((j = 0; J < 8, J + + 2)) do ((i = j + 1)) ((y = ${boxcur[$j]} + Boxcury)) ((x = ${boxcur[$i]} + Boxcurx)) for ((k = y + 1; k < Itrayheig Ht 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 ' #将旧的方块抹去 ((Boxcury + = Idown)) s= $s ' drawcurbox 1 ' #显示新的 Falling behind the block Echo-ne $s box2map #将当前移动中的方块贴到背景方块中 randombox #产生新的方块} #旋转方块function Boxrotate () {Local ICount itestrotate boxtest J i s icount=${countbox[$iBoxCurType]} #当前的方块经旋转可以产生的样式的数目 #计算旋转后的新 ((itestrotate = iboxcurrotate + 1)) if ((Itestrotate >= iCount) then (Itestro Tate = 0) fi #更新到新的样式, save old style (but not display) 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 #不能旋转, or continue to use the old style for (j = 0; J < 8; J + +)) do boxcur[$j]=${boxtest[$j]} done fi} #DrawCurBox (Bdraw), draw the currently moving block, Bdraw to 1, draw on, Bdraw to 0, erase the block. 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\0 33[4${cboxcur}m "fi for (j = 0; J < 8; J + + 2)) does ((i = itraytop + 1 + ${boxcur[ $J]} + Boxcury)) ((t = itrayleft + 1 + 2 * (Boxcurx + ${boxcur[$j + 1]})) #\033[y;xh, cursor to (x , y) at s= $s "\033[${i};${t}h${sbox}" done s= $s "\033[0m" echo-n $s} #更新新的方块function Ran Dombox () {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 #计算当前方块该从顶端哪一行 "Take" Out 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 =; 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)) #显示当前移动的方块 Echo-ne ' Drawcurbox 1 ' #如果方块一出来就没处放, Game over! if! Boxmove $boxCurY $boxCurX then kill-$sigExit ${pPID} showexit fi fi #清除右边预显示的方块 for ((j = 0; J < 4; J + +)) Do ((i = ITop + 1 + j)) ((t = ileft + 2 * itraywidth + 7)) Echo-ne " \033[${i};${t}h "Done #随机产生新的方块 ((Iboxnewtype = RANDOM% ${#offsetBox [@]})) ((iboxnew Rotate = RANDOM% ${countbox[$iBoxNewType]})) for ((j = 0, I = (${offsetbox[$iBoxNewType]} + $iBoxNewRotate) * 8; J < 8; J + +, i++)) do boxnew[$j]=${box[$i]}; Done ((cboxnew = ${colortable[random% ${#colorTable [@]}]}) #显示右边预显示的方块 Echo-ne "\033[1m\033[7m\0 33[3${cboxnew}m\033[4${cboxnew}m "for (j = 0; J < 8; J + + 2)) does ((i = iTop + 1 + ${boxne w[$j])) ((t = ileft + 2 * itraywidth + 7 + 2 * ${boxnew[$j + 1]})) Echo-ne "\033[${i};${t} H[] "Done Echo-ne" \033[0m "} #初始绘制function Initdraw () { Clear Randombox #随机产生方块, then the right pre-display window is fast Randombox #再随机产生方块, the right pre-display window in the box is updated, the original block will begin to fall Local i t1 T2 T3 #显示边框 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; 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++)) Does ((t 1 = i * 2 + ileft + 1) echo-ne "\033[${itraytop};${t1}h==" Echo-ne "\033[${t2};${t1}h==" Done Echo-ne "\033[0m" #显示 "score" and "Level" Echo-ne "\033[1m" (T1 = ileft + itrayw Idth * 2 + 7)) ((t2 = ITop +)) 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 +) Echo-ne "\033[3${cscore}m\033[${t2 };${t1}hlevel "((t2 = ITop +)) Echo-ne" \033[3${cscorevalue}m\033[${t2};${t1}h${ilevel} "Echo-ne "\033[0m"} #退出时显示GameOVer!function Showexit () {Local Y ((y = itrayheight + itraytop + 3)) Echo-e " \033[${y};0hgameover!\033[0m "Exit} #显示用法. function usage{cat << eofusage: $APP _namestart Tetris Game . -H,--help display this Help and exit--version output version information and exiteof} #游戏主程 The order starts here. if [["$" = = "-H" | | "$" = = "--help"]; Then Usageelif [["$" = = "--version"]]; Then echo "$APP _name $APP _version" Elif [["$" = = "--show"]]; Then #当发现具有参数--show, run the display function Runasdisplayerelse bash $--show& #以参数--show to run the program again Runaskeyreceiver $! #以上一行产生的进程的进程号作为参数fi
Very powerful shell written by Tetris