shell脚本俄罗斯方块游戏

亲自测试了一个大牛写的shell脚本,感兴趣可以看看,效果如下:

代码如下:

  1 #!/bin/bash
  2  
  3 # Tetris Game
  4 # 10.21.2003 xhchen<[email]xhchen@winbond.com.tw[/email]>
  5  
  6 #APP declaration
  7 APP_NAME="${0##*[\\/]}"
  8 APP_VERSION="1.0"
  9  
 10  
 11 #颜色定义
 12 cRed=1
 13 cGreen=2
 14 cYellow=3
 15 cBlue=4
 16 cFuchsia=5
 17 cCyan=6
 18 cWhite=7
 19 colorTable=($cRed $cGreen $cYellow $cBlue $cFuchsia $cCyan $cWhite)
 20  
 21 #位置和大小
 22 iLeft=3
 23 iTop=2
 24 ((iTrayLeft = iLeft + 2))
 25 ((iTrayTop = iTop + 1))
 26 ((iTrayWidth = 10))
 27 ((iTrayHeight = 15))
 28  
 29 #颜色设置
 30 cBorder=$cGreen
 31 cScore=$cFuchsia
 32 cScoreValue=$cCyan
 33  
 34 #控制信号
 35 #改游戏使用两个进程,一个用于接收输入,一个用于游戏流程和显示界面;
 36 #当前者接收到上下左右等按键时,通过向后者发送signal的方式通知后者。
 37 sigRotate=25
 38 sigLeft=26
 39 sigRight=27
 40 sigDown=28
 41 sigAllDown=29
 42 sigExit=30
 43  
 44 #七中不同的方块的定义
 45 #通过旋转,每种方块的显示的样式可能有几种
 46 box0=(0 0 0 1 1 0 1 1)
 47 box1=(0 2 1 2 2 2 3 2 1 0 1 1 1 2 1 3)
 48 box2=(0 0 0 1 1 1 1 2 0 1 1 0 1 1 2 0)
 49 box3=(0 1 0 2 1 0 1 1 0 0 1 0 1 1 2 1)
 50 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)
 51 box5=(0 1 1 1 2 1 2 2 1 0 1 1 1 2 2 0 0 0 0 1 1 1 2 1 0 2 1 0 1 1 1 2)
 52 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)
 53 #所有其中方块的定义都放到box变量中
 54 box=(${box0[@]} ${box1[@]} ${box2[@]} ${box3[@]} ${box4[@]} ${box5[@]} ${box6[@]})
 55 #各种方块旋转后可能的样式数目
 56 countBox=(1 2 2 2 4 4 4)
 57 #各种方块再box数组中的偏移
 58 offsetBox=(0 1 3 5 7 11 15)
 59  
 60 #每提高一个速度级需要积累的分数
 61 iScoreEachLevel=50        #be greater than 7
 62  
 63 #运行时数据
 64 sig=0                #接收到的signal
 65 iScore=0        #总分
 66 iLevel=0        #速度级
 67 boxNew=()        #新下落的方块的位置定义
 68 cBoxNew=0        #新下落的方块的颜色
 69 iBoxNewType=0        #新下落的方块的种类
 70 iBoxNewRotate=0        #新下落的方块的旋转角度
 71 boxCur=()        #当前方块的位置定义
 72 cBoxCur=0        #当前方块的颜色
 73 iBoxCurType=0        #当前方块的种类
 74 iBoxCurRotate=0        #当前方块的旋转角度
 75 boxCurX=-1        #当前方块的x坐标位置
 76 boxCurY=-1        #当前方块的y坐标位置
 77 iMap=()                #背景方块图表
 78  
 79 #初始化所有背景方块为-1, 表示没有方块
 80 for ((i = 0; i < iTrayHeight * iTrayWidth; i++)); do iMap[$i]=-1; done
 81  
 82  
 83 #接收输入的进程的主函数
 84 function RunAsKeyReceiver()
 85 {
 86         local pidDisplayer key aKey sig cESC sTTY
 87  
 88         pidDisplayer=$1
 89         aKey=(0 0 0)
 90  
 91         cESC=`echo -ne "\033"`
 92         cSpace=`echo -ne "\040"`
 93  
 94         #保存终端属性。在read -s读取终端键时,终端的属性会被暂时改变。
 95         #如果在read -s时程序被不幸杀掉,可能会导致终端混乱,
 96         #需要在程序退出时恢复终端属性。
 97         sTTY=`stty -g`
 98  
 99         #捕捉退出信号
100         trap "MyExit;" INT TERM
101         trap "MyExitNoSub;" $sigExit
102  
103         #隐藏光标
104         echo -ne "\033[?25l"
105  
106  
107         while :
108         do
109                 #读取输入。注-s不回显,-n读到一个字符立即返回
110                 read -s -n 1 key
111  
112                 aKey[0]=${aKey[1]}
113                 aKey[1]=${aKey[2]}
114                 aKey[2]=$key
115                 sig=0
116  
117                 #判断输入了何种键
118                 if [[ $key == $cESC && ${aKey[1]} == $cESC ]]
119                 then
120                         #ESC键
121                         MyExit
122                 elif [[ ${aKey[0]} == $cESC && ${aKey[1]} == "[" ]]
123                 then
124                         if [[ $key == "A" ]]; then sig=$sigRotate        #<向上键>
125                         elif [[ $key == "B" ]]; then sig=$sigDown        #<向下键>
126                         elif [[ $key == "D" ]]; then sig=$sigLeft        #<向左键>
127                         elif [[ $key == "C" ]]; then sig=$sigRight        #<向右键>
128                         fi
129                 elif [[ $key == "W" || $key == "w" ]]; then sig=$sigRotate        #W, w
130                 elif [[ $key == "S" || $key == "s" ]]; then sig=$sigDown        #S, s
131                 elif [[ $key == "A" || $key == "a" ]]; then sig=$sigLeft        #A, a
132                 elif [[ $key == "D" || $key == "d" ]]; then sig=$sigRight        #D, d
133                 elif [[ "[$key]" == "[]" ]]; then sig=$sigAllDown        #空格键
134                 elif [[ $key == "Q" || $key == "q" ]]                        #Q, q
135                 then
136                         MyExit
137                 fi
138  
139                 if [[ $sig != 0 ]]
140                 then
141                         #向另一进程发送消息
142                         kill -$sig $pidDisplayer
143                 fi
144         done
145 }
146  
147 #退出前的恢复
148 function MyExitNoSub()
149 {
150         local y
151  
152         #恢复终端属性
153         stty $sTTY
154         ((y = iTop + iTrayHeight + 4))
155  
156         #显示光标
157         echo -e "\033[?25h\033[${y};0H"
158         exit
159 }
160  
161  
162 function MyExit()
163 {
164         #通知显示进程需要退出
165         kill -$sigExit $pidDisplayer
166  
167         MyExitNoSub
168 }
169  
170  
171 #处理显示和游戏流程的主函数
172 function RunAsDisplayer()
173 {
174         local sigThis
175         InitDraw
176  
177         #挂载各种信号的处理函数
178         trap "sig=$sigRotate;" $sigRotate
179         trap "sig=$sigLeft;" $sigLeft
180         trap "sig=$sigRight;" $sigRight
181         trap "sig=$sigDown;" $sigDown
182         trap "sig=$sigAllDown;" $sigAllDown
183         trap "ShowExit;" $sigExit
184  
185         while :
186         do
187                 #根据当前的速度级iLevel不同,设定相应的循环的次数
188                 for ((i = 0; i < 21 - iLevel; i++))
189                 do
190                         sleep 0.02
191                         sigThis=$sig
192                         sig=0
193  
194                         #根据sig变量判断是否接受到相应的信号
195                         if ((sigThis == sigRotate)); then BoxRotate;        #旋转
196                         elif ((sigThis == sigLeft)); then BoxLeft;        #左移一列
197                         elif ((sigThis == sigRight)); then BoxRight;        #右移一列
198                         elif ((sigThis == sigDown)); then BoxDown;        #下落一行
199                         elif ((sigThis == sigAllDown)); then BoxAllDown;        #下落到底
200                         fi
201                 done
202                 #kill -$sigDown $$
203                 BoxDown        #下落一行
204         done
205 }
206  
207  
208 #BoxMove(y, x), 测试是否可以把移动中的方块移到(x, y)的位置, 返回0则可以, 1不可以
209 function BoxMove()
210 {
211         local j i x y xTest yTest
212         yTest=$1
213         xTest=$2
214         for ((j = 0; j < 8; j += 2))
215         do
216                 ((i = j + 1))
217                 ((y = ${boxCur[$j]} + yTest))
218                 ((x = ${boxCur[$i]} + xTest))
219                 if (( y < 0 || y >= iTrayHeight || x < 0 || x >= iTrayWidth))
220                 then
221                         #撞到墙壁了
222                         return 1
223                 fi
224                 if ((${iMap[y * iTrayWidth + x]} != -1 ))
225                 then
226                         #撞到其他已经存在的方块了
227                         return 1
228                 fi
229         done
230         return 0;
231 }
232  
233  
234 #将当前移动中的方块放到背景方块中去,
235 #并计算新的分数和速度级。(即一次方块落到底部)
236 function Box2Map()
237 {
238         local j i x y xp yp line
239  
240         #将当前移动中的方块放到背景方块中去
241         for ((j = 0; j < 8; j += 2))
242         do
243                 ((i = j + 1))
244                 ((y = ${boxCur[$j]} + boxCurY))
245                 ((x = ${boxCur[$i]} + boxCurX))
246                 ((i = y * iTrayWidth + x))
247                 iMap[$i]=$cBoxCur
248         done
249  
250         #消去可被消去的行
251         line=0
252         for ((j = 0; j < iTrayWidth * iTrayHeight; j += iTrayWidth))
253         do
254                 for ((i = j + iTrayWidth - 1; i >= j; i--))
255                 do
256                         if ((${iMap[$i]} == -1)); then break; fi
257                 done
258                 if ((i >= j)); then continue; fi
259  
260                 ((line++))
261                 for ((i = j - 1; i >= 0; i--))
262                 do
263                         ((x = i + iTrayWidth))
264                         iMap[$x]=${iMap[$i]}
265                 done
266                 for ((i = 0; i < iTrayWidth; i++))
267                 do
268                         iMap[$i]=-1
269                 done
270         done
271  
272         if ((line == 0)); then return; fi
273  
274         #根据消去的行数line计算分数和速度级
275         ((x = iLeft + iTrayWidth * 2 + 7))
276         ((y = iTop + 11))
277         ((iScore += line * 2 - 1))
278         #显示新的分数
279         echo -ne "\033[1m\033[3${cScoreValue}m\033[${y};${x}H${iScore}         "
280         if ((iScore % iScoreEachLevel < line * 2 - 1))
281         then
282                 if ((iLevel < 20))
283                 then
284                         ((iLevel++))
285                         ((y = iTop + 14))
286                         #显示新的速度级
287                         echo -ne "\033[3${cScoreValue}m\033[${y};${x}H${iLevel}        "
288                 fi
289         fi
290         echo -ne "\033[0m"
291  
292  
293         #重新显示背景方块
294         for ((y = 0; y < iTrayHeight; y++))
295         do
296                 ((yp = y + iTrayTop + 1))
297                 ((xp = iTrayLeft + 1))
298                 ((i = y * iTrayWidth))
299                 echo -ne "\033[${yp};${xp}H"
300                 for ((x = 0; x < iTrayWidth; x++))
301                 do
302                         ((j = i + x))
303                         if ((${iMap[$j]} == -1))
304                         then
305                                 echo -ne "  "
306                         else
307                                 echo -ne "\033[1m\033[7m\033[3${iMap[$j]}m\033[4${iMap[$j]}m[]\033[0m"
308                         fi
309                 done
310         done
311 }
312  
313  
314 #下落一行
315 function BoxDown()
316 {
317         local y s
318         ((y = boxCurY + 1))        #新的y坐标
319         if BoxMove $y $boxCurX        #测试是否可以下落一行
320         then
321                 s="`DrawCurBox 0`"        #将旧的方块抹去
322                 ((boxCurY = y))
323                 s="$s`DrawCurBox 1`"        #显示新的下落后方块
324                 echo -ne $s
325         else
326                 #走到这儿, 如果不能下落了
327                 Box2Map                #将当前移动中的方块贴到背景方块中
328                 RandomBox        #产生新的方块
329         fi
330 }
331  
332 #左移一列
333 function BoxLeft()
334 {
335         local x s
336         ((x = boxCurX - 1))
337         if BoxMove $boxCurY $x
338         then
339                 s=`DrawCurBox 0`
340                 ((boxCurX = x))
341                 s=$s`DrawCurBox 1`
342                 echo -ne $s
343         fi
344 }
345  
346 #右移一列
347 function BoxRight()
348 {
349         local x s
350         ((x = boxCurX + 1))
351         if BoxMove $boxCurY $x
352         then
353                 s=`DrawCurBox 0`
354                 ((boxCurX = x))
355                 s=$s`DrawCurBox 1`
356                 echo -ne $s
357         fi
358 }
359  
360  
361 #下落到底
362 function BoxAllDown()
363 {
364         local k j i x y iDown s
365         iDown=$iTrayHeight
366  
367         #计算一共需要下落多少行
368         for ((j = 0; j < 8; j += 2))
369         do
370                 ((i = j + 1))
371                 ((y = ${boxCur[$j]} + boxCurY))
372                 ((x = ${boxCur[$i]} + boxCurX))
373                 for ((k = y + 1; k < iTrayHeight; k++))
374                 do
375                         ((i = k * iTrayWidth + x))
376                         if (( ${iMap[$i]} != -1)); then break; fi
377                 done
378                 ((k -= y + 1))
379                 if (( $iDown > $k )); then iDown=$k; fi
380         done
381  
382         s=`DrawCurBox 0`        #将旧的方块抹去
383         ((boxCurY += iDown))
384         s=$s`DrawCurBox 1`        #显示新的下落后的方块
385         echo -ne $s
386         Box2Map                #将当前移动中的方块贴到背景方块中
387         RandomBox        #产生新的方块
388 }
389  
390  
391 #旋转方块
392 function BoxRotate()
393 {
394         local iCount iTestRotate boxTest j i s
395         iCount=${countBox[$iBoxCurType]}        #当前的方块经旋转可以产生的样式的数目
396  
397         #计算旋转后的新的样式
398         ((iTestRotate = iBoxCurRotate + 1))
399         if ((iTestRotate >= iCount))
400         then
401                 ((iTestRotate = 0))
402         fi
403  
404         #更新到新的样式, 保存老的样式(但不显示)
405         for ((j = 0, i = (${offsetBox[$iBoxCurType]} + $iTestRotate) * 8; j < 8; j++, i++))
406         do
407                 boxTest[$j]=${boxCur[$j]}
408                 boxCur[$j]=${box[$i]}
409         done
410  
411         if BoxMove $boxCurY $boxCurX        #测试旋转后是否有空间放的下
412         then
413                 #抹去旧的方块
414                 for ((j = 0; j < 8; j++))
415                 do
416                         boxCur[$j]=${boxTest[$j]}
417                 done
418                 s=`DrawCurBox 0`
419  
420                 #画上新的方块
421                 for ((j = 0, i = (${offsetBox[$iBoxCurType]} + $iTestRotate) * 8; j < 8; j++, i++))
422                 do
423                         boxCur[$j]=${box[$i]}
424                 done
425                 s=$s`DrawCurBox 1`
426                 echo -ne $s
427                 iBoxCurRotate=$iTestRotate
428         else
429                 #不能旋转,还是继续使用老的样式
430                 for ((j = 0; j < 8; j++))
431                 do
432                         boxCur[$j]=${boxTest[$j]}
433                 done
434         fi
435 }
436  
437  
438 #DrawCurBox(bDraw), 绘制当前移动中的方块, bDraw为1, 画上, bDraw为0, 抹去方块。
439 function DrawCurBox()
440 {
441         local i j t bDraw sBox s
442         bDraw=$1
443  
444         s=""
445         if (( bDraw == 0 ))
446         then
447                 sBox="\040\040"
448         else
449                 sBox="[]"
450                 s=$s"\033[1m\033[7m\033[3${cBoxCur}m\033[4${cBoxCur}m"
451         fi
452  
453         for ((j = 0; j < 8; j += 2))
454         do
455                 ((i = iTrayTop + 1 + ${boxCur[$j]} + boxCurY))
456                 ((t = iTrayLeft + 1 + 2 * (boxCurX + ${boxCur[$j + 1]})))
457                 #\033[y;xH, 光标到(x, y)处
458                 s=$s"\033[${i};${t}H${sBox}"
459         done
460         s=$s"\033[0m"
461         echo -n $s
462 }
463  
464  
465 #更新新的方块
466 function RandomBox()
467 {
468         local i j t
469  
470         #更新当前移动的方块
471         iBoxCurType=${iBoxNewType}
472         iBoxCurRotate=${iBoxNewRotate}
473         cBoxCur=${cBoxNew}
474         for ((j = 0; j < ${#boxNew[@]}; j++))
475         do
476                 boxCur[$j]=${boxNew[$j]}
477         done
478  
479  
480         #显示当前移动的方块
481         if (( ${#boxCur[@]} == 8 ))
482         then
483                 #计算当前方块该从顶端哪一行""出来
484                 for ((j = 0, t = 4; j < 8; j += 2))
485                 do
486                         if ((${boxCur[$j]} < t)); then t=${boxCur[$j]}; fi
487                 done
488                 ((boxCurY = -t))
489                 for ((j = 1, i = -4, t = 20; j < 8; j += 2))
490                 do
491                         if ((${boxCur[$j]} > i)); then i=${boxCur[$j]}; fi
492                         if ((${boxCur[$j]} < t)); then t=${boxCur[$j]}; fi
493                 done
494                 ((boxCurX = (iTrayWidth - 1 - i - t) / 2))
495  
496                 #显示当前移动的方块
497                 echo -ne `DrawCurBox 1`
498  
499                 #如果方块一出来就没处放,Game over!
500                 if ! BoxMove $boxCurY $boxCurX
501                 then
502                         kill -$sigExit ${PPID}
503                         ShowExit
504                 fi
505         fi
506  
507  
508  
509         #清除右边预显示的方块
510         for ((j = 0; j < 4; j++))
511         do
512                 ((i = iTop + 1 + j))
513                 ((t = iLeft + 2 * iTrayWidth + 7))
514                 echo -ne "\033[${i};${t}H        "
515         done
516  
517         #随机产生新的方块
518         ((iBoxNewType = RANDOM % ${#offsetBox[@]}))
519         ((iBoxNewRotate = RANDOM % ${countBox[$iBoxNewType]}))
520         for ((j = 0, i = (${offsetBox[$iBoxNewType]} + $iBoxNewRotate) * 8; j < 8; j++, i++))
521         do
522                 boxNew[$j]=${box[$i]};
523         done
524  
525         ((cBoxNew = ${colorTable[RANDOM % ${#colorTable[@]}]}))
526  
527         #显示右边预显示的方块
528         echo -ne "\033[1m\033[7m\033[3${cBoxNew}m\033[4${cBoxNew}m"
529         for ((j = 0; j < 8; j += 2))
530         do
531                 ((i = iTop + 1 + ${boxNew[$j]}))
532                 ((t = iLeft + 2 * iTrayWidth + 7 + 2 * ${boxNew[$j + 1]}))
533                 echo -ne "\033[${i};${t}H[]"
534         done
535         echo -ne "\033[0m"
536 }
537  
538  
539 #初始绘制
540 function InitDraw()
541 {
542         clear
543         RandomBox        #随机产生方块,这时右边预显示窗口中有方快了
544         RandomBox        #再随机产生方块,右边预显示窗口中的方块被更新,原先的方块将开始下落
545         local i t1 t2 t3
546  
547         #显示边框
548         echo -ne "\033[1m"
549         echo -ne "\033[3${cBorder}m\033[4${cBorder}m"
550  
551         ((t2 = iLeft + 1))
552         ((t3 = iLeft + iTrayWidth * 2 + 3))
553         for ((i = 0; i < iTrayHeight; i++))
554         do
555                 ((t1 = i + iTop + 2))
556                 echo -ne "\033[${t1};${t2}H||"
557                 echo -ne "\033[${t1};${t3}H||"
558         done
559  
560         ((t2 = iTop + iTrayHeight + 2))
561         for ((i = 0; i < iTrayWidth + 2; i++))
562         do
563                 ((t1 = i * 2 + iLeft + 1))
564                 echo -ne "\033[${iTrayTop};${t1}H=="
565                 echo -ne "\033[${t2};${t1}H=="
566         done
567         echo -ne "\033[0m"
568  
569  
570         #显示"Score""Level"字样
571         echo -ne "\033[1m"
572         ((t1 = iLeft + iTrayWidth * 2 + 7))
573         ((t2 = iTop + 10))
574         echo -ne "\033[3${cScore}m\033[${t2};${t1}HScore"
575         ((t2 = iTop + 11))
576         echo -ne "\033[3${cScoreValue}m\033[${t2};${t1}H${iScore}"
577         ((t2 = iTop + 13))
578         echo -ne "\033[3${cScore}m\033[${t2};${t1}HLevel"
579         ((t2 = iTop + 14))
580         echo -ne "\033[3${cScoreValue}m\033[${t2};${t1}H${iLevel}"
581         echo -ne "\033[0m"
582 }
583  
584  
585 #退出时显示GameOVer!
586 function ShowExit()
587 {
588         local y
589         ((y = iTrayHeight + iTrayTop + 3))
590         echo -e "\033[${y};0HGameOver!\033[0m"
591         exit
592 }
593  
594  
595 #显示用法.
596 function Usage
597 {
598         cat << EOF
599 Usage: $APP_NAME
600 Start tetris game.
601  
602   -h, --help              display this help and exit
603       --version           output version information and exit
604 EOF
605 }
606  
607  
608 #游戏主程序在这儿开始.
609 if [[ "$1" == "-h" || "$1" == "--help" ]]; then
610         Usage
611 elif [[ "$1" == "--version" ]]; then
612         echo "$APP_NAME $APP_VERSION"
613 elif [[ "$1" == "--show" ]]; then
614         #当发现具有参数--show时,运行显示函数
615         RunAsDisplayer
616 else
617         bash $0 --show&        #以参数--show将本程序再运行一遍
618         RunAsKeyReceiver $!        #以上一行产生的进程的进程号作为参数
619 fi

 

posted @ 2016-11-15 14:19  winfu  阅读(391)  评论(0编辑  收藏  举报