LinuxSir.cn,穿越时空的Linuxsir!

 找回密码
 注册
搜索
热搜: shell linux mysql
查看: 1938|回复: 4

比教好玩的东西---shell中的俄罗斯方块

[复制链接]
发表于 2005-5-3 01:13:44 | 显示全部楼层 |阅读模式
刚整理硬盘是发现这么一个东西,挺好玩的,给大家分享,
(C)好像是在网上的,不过不知道是哪里,所以就不写了,如作者有疑问请联系斑猪删贴或联系本人yuri@centech.com.cn

  1. #!/bin/bash


  2. #颜色定义
  3. cRed=1
  4. cGreen=2
  5. cYellow=3
  6. cBlue=4
  7. cFuchsia=5
  8. cCyan=6
  9. cWhite=7
  10. colorTable=($cRed $cGreen $cYellow $cBlue $cFuchsia $cCyan $cWhite)

  11. #位置和大小
  12. iLeft=3
  13. iTop=2
  14. ((iTrayLeft = iLeft + 2))
  15. ((iTrayTop = iTop + 1))
  16. ((iTrayWidth = 10))
  17. ((iTrayHeight = 15))

  18. #颜色设置
  19. cBorder=$cGreen
  20. cScore=$cFuchsia
  21. cScoreValue=$cCyan

  22. #控制信号
  23. #改游戏使用两个进程,一个用于接收输入,一个用于游戏流程和显示界面;
  24. #当前者接收到上下左右等按键时,通过向后者发送signal的方式通知后者。
  25. sigRotate=25
  26. sigLeft=26
  27. sigRight=27
  28. sigDown=28
  29. sigAllDown=29
  30. sigExit=30

  31. #七中不同的方块的定义
  32. #通过旋转,每种方块的显示的样式可能有几种
  33. box0=(0 0 0 1 1 0 1 1)
  34. box1=(0 2 1 2 2 2 3 2 1 0 1 1 1 2 1 3)
  35. box2=(0 0 0 1 1 1 1 2 0 1 1 0 1 1 2 0)
  36. box3=(0 1 0 2 1 0 1 1 0 0 1 0 1 1 2 1)
  37. 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)
  38. 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)
  39. 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)
  40. #所有其中方块的定义都放到box变量中
  41. box=(${box0[@]} ${box1[@]} ${box2[@]} ${box3[@]} ${box4[@]} ${box5[@]} ${box6[@]})
  42. #各种方块旋转后可能的样式数目
  43. countBox=(1 2 2 2 4 4 4)
  44. #各种方块再box数组中的偏移
  45. offsetBox=(0 1 3 5 7 11 15)

  46. #每提高一个速度级需要积累的分数
  47. iScoreEachLevel=50   #be greater than 7

  48. #运行时数据
  49. sig=0      #接收到的signal
  50. iScore=0   #总分
  51. iLevel=0   #速度级
  52. boxNew=()   #新下落的方块的位置定义
  53. cBoxNew=0   #新下落的方块的颜色
  54. iBoxNewType=0   #新下落的方块的种类
  55. iBoxNewRotate=0   #新下落的方块的旋转角度
  56. boxCur=()   #当前方块的位置定义
  57. cBoxCur=0   #当前方块的颜色
  58. iBoxCurType=0   #当前方块的种类
  59. iBoxCurRotate=0   #当前方块的旋转角度
  60. boxCurX=-1   #当前方块的x坐标位置
  61. boxCurY=-1   #当前方块的y坐标位置
  62. iMap=()      #背景方块图表

  63. #初始化所有背景方块为-1, 表示没有方块
  64. for ((i = 0; i < iTrayHeight * iTrayWidth; i++)); do iMap[$i]=-1; done


  65. #接收输入的进程的主函数
  66. function RunAsKeyReceiver()
  67. {
  68.    local pidDisplayer key aKey sig cESC sTTY

  69.    pidDisplayer=$1
  70.    aKey=(0 0 0)

  71.    cESC=`echo -ne "\33"`
  72.    cSpace=`echo -ne "\40"`

  73.    #保存终端属性。在read -s读取终端键时,终端的属性会被暂时改变。
  74.    #如果在read -s时程序被不幸杀掉,可能会导致终端混乱,
  75.    #需要在程序退出时恢复终端属性。
  76.    sTTY=`stty -g`
  77.    
  78.    #捕捉退出信号
  79.    trap "MyExit;" INT TERM
  80.    trap "MyExitNoSub;" $sigExit
  81.    
  82.    #隐藏光标
  83.    echo -ne "\33[?25l"

  84.    
  85.    while [[ 1 ]]
  86.    do
  87.       #读取输入。注-s不回显,-n读到一个字符立即返回
  88.       read -s -n 1 key
  89.       
  90.       aKey[0]=${aKey[1]}
  91.       aKey[1]=${aKey[2]}
  92.       aKey[2]=$key
  93.       sig=0

  94.       #判断输入了何种键
  95.       if [[ $key == $cESC && ${aKey[1]} == $cESC ]]
  96.       then
  97.          #ESC键
  98.          MyExit
  99.       elif [[ ${aKey[0]} == $cESC && ${aKey[1]} == "[" ]]
  100.       then
  101.          if [[ $key == "A" ]]; then sig=$sigRotate   #<向上键>
  102.          elif [[ $key == "B" ]]; then sig=$sigDown   #<向下键>
  103.          elif [[ $key == "D" ]]; then sig=$sigLeft   #<向左键>
  104.          elif [[ $key == "C" ]]; then sig=$sigRight   #<向右键>
  105.          fi
  106.       elif [[ $key == "W" || $key == "w" ]]; then sig=$sigRotate   #W, w
  107.       elif [[ $key == "S" || $key == "s" ]]; then sig=$sigDown   #S, s
  108.       elif [[ $key == "A" || $key == "a" ]]; then sig=$sigLeft   #A, a
  109.       elif [[ $key == "D" || $key == "d" ]]; then sig=$sigRight   #D, d
  110.       elif [[ "[$key]" == "[]" ]]; then sig=$sigAllDown   #空格键
  111.       elif [[ $key == "Q" || $key == "q" ]]         #Q, q
  112.       then
  113.          MyExit
  114.       fi

  115.       if [[ $sig != 0 ]]
  116.       then
  117.          #向另一进程发送消息
  118.          kill -$sig $pidDisplayer
  119.       fi
  120.    done
  121. }

  122. #退出前的恢复
  123. function MyExitNoSub()
  124. {
  125.    local y
  126.    
  127.    #恢复终端属性
  128.    stty $sTTY
  129.    ((y = iTop + iTrayHeight + 4))

  130.    #显示光标
  131.    echo -e "\33[?25h\33[${y};0H"
  132.    exit
  133. }


  134. function MyExit()
  135. {
  136.    #通知显示进程需要退出
  137.    kill -$sigExit $pidDisplayer
  138.    
  139.    MyExitNoSub
  140. }


  141. #处理显示和游戏流程的主函数
  142. function RunAsDisplayer()
  143. {
  144.    local sigThis
  145.    InitDraw

  146.    #挂载各种信号的处理函数
  147.    trap "sig=$sigRotate;" $sigRotate
  148.    trap "sig=$sigLeft;" $sigLeft
  149.    trap "sig=$sigRight;" $sigRight
  150.    trap "sig=$sigDown;" $sigDown
  151.    trap "sig=$sigAllDown;" $sigAllDown
  152.    trap "ShowExit;" $sigExit

  153.    while [[ 1 ]]
  154.    do
  155.       #根据当前的速度级iLevel不同,设定相应的循环的次数
  156.       for ((i = 0; i < 21 - iLevel; i++))
  157.       do
  158.          usleep 20000
  159.          sigThis=$sig
  160.          sig=0

  161.          #根据sig变量判断是否接受到相应的信号
  162.          if ((sigThis == sigRotate)); then BoxRotate;   #旋转
  163.          elif ((sigThis == sigLeft)); then BoxLeft;   #左移一列
  164.          elif ((sigThis == sigRight)); then BoxRight;   #右移一列
  165.          elif ((sigThis == sigDown)); then BoxDown;   #下落一行
  166.          elif ((sigThis == sigAllDown)); then BoxAllDown;   #下落到底
  167.          fi
  168.       done
  169.       #kill -$sigDown $$
  170.       BoxDown   #下落一行
  171.    done
  172. }


  173. #BoxMove(y, x), 测试是否可以把移动中的方块移到(x, y)的位置, 返回0则可以, 1不可以
  174. function BoxMove()
  175. {
  176.    local j i x y xTest yTest
  177.    yTest=$1
  178.    xTest=$2
  179.    for ((j = 0; j < 8; j += 2))
  180.    do
  181.       ((i = j + 1))
  182.       ((y = ${boxCur[$j]} + yTest))
  183.       ((x = ${boxCur[$i]} + xTest))
  184.       if (( y < 0 || y >= iTrayHeight || x < 0 || x >= iTrayWidth))
  185.       then
  186.          #撞到墙壁了
  187.          return 1
  188.       fi
  189.       if ((${iMap[y * iTrayWidth + x]} != -1 ))
  190.       then
  191.          #撞到其他已经存在的方块了
  192.          return 1
  193.       fi
  194.    done
  195.    return 0;
  196. }


  197. #将当前移动中的方块放到背景方块中去,
  198. #并计算新的分数和速度级。(即一次方块落到底部)
  199. function Box2Map()
  200. {
  201.    local j i x y xp yp line

  202.    #将当前移动中的方块放到背景方块中去
  203.    for ((j = 0; j < 8; j += 2))
  204.    do
  205.       ((i = j + 1))
  206.       ((y = ${boxCur[$j]} + boxCurY))
  207.       ((x = ${boxCur[$i]} + boxCurX))
  208.       ((i = y * iTrayWidth + x))
  209.       iMap[$i]=$cBoxCur
  210.    done
  211.    
  212.    #消去可被消去的行
  213.    line=0
  214.    for ((j = 0; j < iTrayWidth * iTrayHeight; j += iTrayWidth))
  215.    do
  216.       for ((i = j + iTrayWidth - 1; i >= j; i--))
  217.       do
  218.          if ((${iMap[$i]} == -1)); then break; fi
  219.       done
  220.       if ((i >= j)); then continue; fi
  221.    
  222.       ((line++))   
  223.       for ((i = j - 1; i >= 0; i--))
  224.       do
  225.          ((x = i + iTrayWidth))
  226.          iMap[$x]=${iMap[$i]}
  227.       done
  228.       for ((i = 0; i < iTrayWidth; i++))
  229.       do
  230.          iMap[$i]=-1
  231.       done
  232.    done
  233.    
  234.    if ((line == 0)); then return; fi

  235.    #根据消去的行数line计算分数和速度级
  236.    ((x = iLeft + iTrayWidth * 2 + 7))
  237.    ((y = iTop + 11))
  238.    ((iScore += line * 2 - 1))
  239.    #显示新的分数
  240.    echo -ne "\33[1m\33[3${cScoreValue}m\33[${y};${x}H${iScore}         "
  241.    if ((iScore % iScoreEachLevel < line * 2 - 1))
  242.    then
  243.       if ((iLevel < 20))
  244.       then
  245.          ((iLevel++))
  246.          ((y = iTop + 14))
  247.          #显示新的速度级
  248.          echo -ne "\33[3${cScoreValue}m\33[${y};${x}H${iLevel}        "
  249.       fi
  250.    fi
  251.    echo -ne "\33[0m"


  252.    #重新显示背景方块
  253.    for ((y = 0; y < iTrayHeight; y++))
  254.    do
  255.       ((yp = y + iTrayTop + 1))
  256.       ((xp = iTrayLeft + 1))
  257.       ((i = y * iTrayWidth))
  258.       echo -ne "\33[${yp};${xp}H"
  259.       for ((x = 0; x < iTrayWidth; x++))
  260.       do
  261.          ((j = i + x))
  262.          if ((${iMap[$j]} == -1))
  263.          then
  264.             echo -ne "  "
  265.          else
  266.             echo -ne "\33[1m\33[7m\33[3${iMap[$j]}m\33[4${iMap[$j]}m[]\33[0m"
  267.          fi
  268.       done
  269.    done
  270. }


  271. #下落一行
  272. function BoxDown()
  273. {
  274.    local y s
  275.    ((y = boxCurY + 1))   #新的y坐标
  276.    if BoxMove $y $boxCurX   #测试是否可以下落一行
  277.    then
  278.       s="`DrawCurBox 0`"   #将旧的方块抹去
  279.       ((boxCurY = y))
  280.       s="$s`DrawCurBox 1`"   #显示新的下落后方块
  281.       echo -ne $s
  282.    else
  283.       #走到这儿, 如果不能下落了
  284.       Box2Map      #将当前移动中的方块贴到背景方块中
  285.       RandomBox   #产生新的方块
  286.    fi
  287. }

  288. #左移一列
  289. function BoxLeft()
  290. {
  291.    local x s
  292.    ((x = boxCurX - 1))
  293.    if BoxMove $boxCurY $x
  294.    then
  295.       s=`DrawCurBox 0`
  296.       ((boxCurX = x))
  297.       s=$s`DrawCurBox 1`
  298.       echo -ne $s
  299.    fi
  300. }

  301. #右移一列
  302. function BoxRight()
  303. {
  304.    local x s
  305.    ((x = boxCurX + 1))
  306.    if BoxMove $boxCurY $x
  307.    then
  308.       s=`DrawCurBox 0`
  309.       ((boxCurX = x))
  310.       s=$s`DrawCurBox 1`
  311.       echo -ne $s
  312.    fi
  313. }


  314. #下落到底
  315. function BoxAllDown()
  316. {
  317.    local k j i x y iDown s
  318.    iDown=$iTrayHeight

  319.    #计算一共需要下落多少行
  320.    for ((j = 0; j < 8; j += 2))
  321.    do
  322.       ((i = j + 1))
  323.       ((y = ${boxCur[$j]} + boxCurY))
  324.       ((x = ${boxCur[$i]} + boxCurX))
  325.       for ((k = y + 1; k < iTrayHeight; k++))
  326.       do
  327.          ((i = k * iTrayWidth + x))
  328.          if (( ${iMap[$i]} != -1)); then break; fi
  329.       done
  330.       ((k -= y + 1))
  331.       if (( $iDown > $k )); then iDown=$k; fi
  332.    done
  333.    
  334.    s=`DrawCurBox 0`   #将旧的方块抹去
  335.    ((boxCurY += iDown))   
  336.    s=$s`DrawCurBox 1`   #显示新的下落后的方块
  337.    echo -ne $s
  338.    Box2Map      #将当前移动中的方块贴到背景方块中
  339.    RandomBox   #产生新的方块
  340. }


  341. #旋转方块
  342. function BoxRotate()
  343. {
  344.    local iCount iTestRotate boxTest j i s
  345.    iCount=${countBox[$iBoxCurType]}   #当前的方块经旋转可以产生的样式的数目

  346.    #计算旋转后的新的样式
  347.    ((iTestRotate = iBoxCurRotate + 1))
  348.    if ((iTestRotate >= iCount))
  349.    then
  350.       ((iTestRotate = 0))
  351.    fi

  352.    #更新到新的样式, 保存老的样式(但不显示)
  353.    for ((j = 0, i = (${offsetBox[$iBoxCurType]} + $iTestRotate) * 8; j < 8; j++, i++))
  354.    do
  355.       boxTest[$j]=${boxCur[$j]}
  356.       boxCur[$j]=${box[$i]}
  357.    done

  358.    if BoxMove $boxCurY $boxCurX   #测试旋转后是否有空间放的下
  359.    then
  360.       #抹去旧的方块
  361.       for ((j = 0; j < 8; j++))
  362.       do
  363.          boxCur[$j]=${boxTest[$j]}
  364.       done
  365.       s=`DrawCurBox 0`

  366.       #画上新的方块
  367.       for ((j = 0, i = (${offsetBox[$iBoxCurType]} + $iTestRotate) * 8; j < 8; j++, i++))
  368.       do
  369.          boxCur[$j]=${box[$i]}
  370.       done
  371.       s=$s`DrawCurBox 1`
  372.       echo -ne $s
  373.       iBoxCurRotate=$iTestRotate
  374.    else
  375.       #不能旋转,还是继续使用老的样式
  376.       for ((j = 0; j < 8; j++))
  377.       do
  378.          boxCur[$j]=${boxTest[$j]}
  379.       done
  380.    fi
  381. }


  382. #DrawCurBox(bDraw), 绘制当前移动中的方块, bDraw为1, 画上, bDraw为0, 抹去方块。
  383. function DrawCurBox()
  384. {
  385.    local i j t bDraw sBox s
  386.    bDraw=$1

  387.    s=""
  388.    if (( bDraw == 0 ))
  389.    then
  390.       sBox="\40\40"
  391.    else
  392.       sBox="[]"
  393.       s=$s"\33[1m\33[7m\33[3${cBoxCur}m\33[4${cBoxCur}m"      
  394.    fi
  395.    
  396.    for ((j = 0; j < 8; j += 2))
  397.    do
  398.       ((i = iTrayTop + 1 + ${boxCur[$j]} + boxCurY))
  399.       ((t = iTrayLeft + 1 + 2 * (boxCurX + ${boxCur[$j + 1]})))
  400.       #\33[y;xH, 光标到(x, y)处
  401.       s=$s"\33[${i};${t}H${sBox}"
  402.    done
  403.    s=$s"\33[0m"
  404.    echo -n $s
  405. }


  406. #更新新的方块
  407. function RandomBox()
  408. {
  409.    local i j t

  410.    #更新当前移动的方块
  411.    iBoxCurType=${iBoxNewType}
  412.    iBoxCurRotate=${iBoxNewRotate}
  413.    cBoxCur=${cBoxNew}
  414.    for ((j = 0; j < ${#boxNew[@]}; j++))
  415.    do
  416.       boxCur[$j]=${boxNew[$j]}
  417.    done
  418.    

  419.    #显示当前移动的方块
  420.    if (( ${#boxCur[@]} == 8 ))
  421.    then
  422.       #计算当前方块该从顶端哪一行"冒"出来
  423.       for ((j = 0, t = 4; j < 8; j += 2))
  424.       do
  425.          if ((${boxCur[$j]} < t)); then t=${boxCur[$j]}; fi
  426.       done
  427.       ((boxCurY = -t))
  428.       for ((j = 1, i = -4, t = 20; j < 8; j += 2))
  429.       do
  430.          if ((${boxCur[$j]} > i)); then i=${boxCur[$j]}; fi
  431.          if ((${boxCur[$j]} < t)); then t=${boxCur[$j]}; fi
  432.       done
  433.       ((boxCurX = (iTrayWidth - 1 - i - t) / 2))

  434.       #显示当前移动的方块
  435.       echo -ne `DrawCurBox 1`

  436.       #如果方块一出来就没处放,Game over!
  437.       if ! BoxMove $boxCurY $boxCurX
  438.       then
  439.          kill -$sigExit ${PPID}
  440.          ShowExit
  441.       fi
  442.    fi
  443.    
  444.    

  445.    #清除右边预显示的方块
  446.    for ((j = 0; j < 4; j++))
  447.    do
  448.       ((i = iTop + 1 + j))
  449.       ((t = iLeft + 2 * iTrayWidth + 7))
  450.       echo -ne "\33[${i};${t}H        "
  451.    done

  452.    #随机产生新的方块
  453.    ((iBoxNewType = RANDOM % ${#offsetBox[@]}))
  454.    ((iBoxNewRotate = RANDOM % ${countBox[$iBoxNewType]}))
  455.    for ((j = 0, i = (${offsetBox[$iBoxNewType]} + $iBoxNewRotate) * 8; j < 8; j++, i++))
  456.    do
  457.       boxNew[$j]=${box[$i]};
  458.    done

  459.    ((cBoxNew = ${colorTable[RANDOM % ${#colorTable[@]}]}))
  460.    
  461.    #显示右边预显示的方块
  462.    echo -ne "\33[1m\33[7m\33[3${cBoxNew}m\33[4${cBoxNew}m"
  463.    for ((j = 0; j < 8; j += 2))
  464.    do
  465.       ((i = iTop + 1 + ${boxNew[$j]}))
  466.       ((t = iLeft + 2 * iTrayWidth + 7 + 2 * ${boxNew[$j + 1]}))
  467.       echo -ne "\33[${i};${t}H[]"
  468.    done
  469.    echo -ne "\33[0m"
  470. }


  471. #初始绘制
  472. function InitDraw()
  473. {
  474.    clear
  475.    RandomBox   #随机产生方块,这时右边预显示窗口中有方快了
  476.    RandomBox   #再随机产生方块,右边预显示窗口中的方块被更新,原先的方块将开始下落
  477.    local i t1 t2 t3

  478.    #显示边框
  479.    echo -ne "\33[1m"
  480.    echo -ne "\33[3${cBorder}m\33[4${cBorder}m"
  481.    
  482.    ((t2 = iLeft + 1))
  483.    ((t3 = iLeft + iTrayWidth * 2 + 3))
  484.    for ((i = 0; i < iTrayHeight; i++))
  485.    do
  486.       ((t1 = i + iTop + 2))
  487.       echo -ne "\33[${t1};${t2}H||"
  488.       echo -ne "\33[${t1};${t3}H||"
  489.    done
  490.    
  491.    ((t2 = iTop + iTrayHeight + 2))
  492.    for ((i = 0; i < iTrayWidth + 2; i++))
  493.    do
  494.       ((t1 = i * 2 + iLeft + 1))
  495.       echo -ne "\33[${iTrayTop};${t1}H=="
  496.       echo -ne "\33[${t2};${t1}H=="
  497.    done
  498.    echo -ne "\33[0m"

  499.    
  500.    #显示"Score"和"Level"字样
  501.    echo -ne "\33[1m"
  502.    ((t1 = iLeft + iTrayWidth * 2 + 7))
  503.    ((t2 = iTop + 10))
  504.    echo -ne "\33[3${cScore}m\33[${t2};${t1}HScore"
  505.    ((t2 = iTop + 11))
  506.    echo -ne "\33[3${cScoreValue}m\33[${t2};${t1}H${iScore}"
  507.    ((t2 = iTop + 13))
  508.    echo -ne "\33[3${cScore}m\33[${t2};${t1}HLevel"
  509.    ((t2 = iTop + 14))
  510.    echo -ne "\33[3${cScoreValue}m\33[${t2};${t1}H${iLevel}"
  511.    echo -ne "\33[0m"
  512. }


  513. #退出时显示GameOVer!
  514. function ShowExit()
  515. {
  516.    local y
  517.    ((y = iTrayHeight + iTrayTop + 3))
  518.    echo -e "\33[${y};0HGameOver!\33[0m"
  519.    exit
  520. }



  521. #游戏主程序在这儿开始.
  522. if [[ $1 != "--show" ]]
  523. then
  524.    sh $0 --show&   #以参数--show将本程序再运行一遍
  525.    RunAsKeyReceiver $!   #以上一行产生的进程的进程号作为参数
  526.    exit
  527. else
  528.    #当发现具有参数--show时,运行显示函数
  529.    RunAsDisplayer   
  530.    exit
  531. fi
复制代码



版主注:此程序出自CHINAUNIX,由飞灰橙所作。
如下是链接:http://chinaunix.net/jh/24/184858.html
 楼主| 发表于 2005-5-3 01:17:23 | 显示全部楼层
版竹加精!!!!!
^@^
回复 支持 反对

使用道具 举报

发表于 2005-5-3 01:27:53 | 显示全部楼层
这是shell版的俄罗斯方块,作者:飞灰橙
出自: http://chinaunix.net/jh/24/184858.html
回复 支持 反对

使用道具 举报

发表于 2005-5-6 13:27:43 | 显示全部楼层
"usleep 20000"
SHOULD BE
sleep 0.02
???
回复 支持 反对

使用道具 举报

发表于 2005-5-6 16:10:12 | 显示全部楼层
多谢yuri-master兄:),加精了
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 注册

本版积分规则

快速回复 返回顶部 返回列表