zoukankan      html  css  js  c++  java
  • 【转】shell脚本写的俄罗斯方块游戏

    亲测一个很好玩的shell脚本写的俄罗斯方块游戏,脚本来自互联网

    先来讲一下思维流程

    一、方块的表示

         由于shell不能定义二维数组,所以只能用一维数组表示方块,俄罗斯方块主要可以分为7类,每一类方块都是由四类小方块构成,表示方法如下。

            Box=(x1,y1,x2,y2,x3,y3,x4,y4,x,y)

         xi、yi是各个小方块在俄罗斯方块表示区域中的坐标,最后的两个,x、y是在方块出现时,该表示区域相对于棋盘的坐标,7类方块的表示如下:

        

     

    二、相关函数定义

    1、两个主要函数

    RunAsDisplayer()、RunAsKeyReceiver(),这两个函数当中各有一个死循环,并且这两个函数运行在不同的进程当中,所以这两个函数在游戏期间始终在运行,其中,显示进程作为后台进程,命令接收进程作为前台进程。前台进程在接收到命令后,由kill指令将命令发送给显示进程,显示进程执行相关操作。前台进程发送给显示进程的主要命令有:翻转、左移、右移、下移、直接下落到底、退出。当前台进程收到退出指令后,关闭自己之前,会先关闭显示进程,同样,显示进程关闭之前,也会关闭命令接收进程。

    2、创建方块函数

    CreateBox()   #创建方块,如果是第一次创建则随机生成,否则读取预生成的方块

    PrepareNextBox()  #生成下一个方块,并预显示

    3、绘制函数

    DrawBorder()    #绘制边框及成绩等

    DrawCurBox()    #绘制当前方块

    4、指令处理函数

    BoxRotate()   #接收上方向键,用于旋转方块

    BoxRight()    #右移

    BoxLeft()     #左移

    BoxDown()   #下移,下移时要判断,如果到底了,要写入背景,并创建新方块

    BoxAllDown() #直接下移到最底部

    5、移动可行性判断函数

    BoxMove()   #该函数包含两个表示目的地址的参数,如果可以移动,则返回0,否则返回1

    6、写入背景函数

    Box2Map()   #该函数用于把到底的方块写入到背景当中,并消去可以消去的行

    7、退出函数

    MyExitNoSub()    #用于恢复终端并退出指令接收进程

    MyExit()   #关闭两个进程,调用其他两个退出函数实现

    ShowExit()  #关闭显示进程

    三、系统流程

     

    2.代码如下:

    #!/bin/bash

    # Tetris Game
    # 10.21.2003 xhchen<[email]xhchen@winbond.com.tw[/email]>

    #APP declaration
    APP_NAME="${0##*[\/]}"
    APP_VERSION="1.0"


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

    #位置和大小
    iLeft=3
    iTop=2
    ((iTrayLeft = iLeft + 2))
    ((iTrayTop = iTop + 1))
    ((iTrayWidth = 10))
    ((iTrayHeight = 15))

    #颜色设置
    cBorder=$cGreen
    cScore=$cFuchsia
    cScoreValue=$cCyan

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

    #七中不同的方块的定义
    #通过旋转,每种方块的显示的样式可能有几种
    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 0 1 1 1 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 11 15)

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

    #运行时数据
    sig=0 #接收到的signal
    iScore=0 #总分
    iLevel=0 #速度级
    boxNew=() #新下落的方块的位置定义
    cBoxNew=0 #新下落的方块的颜色
    iBoxNewType=0 #新下落的方块的种类
    iBoxNewRotate=0 #新下落的方块的旋转角度
    boxCur=() #当前方块的位置定义
    cBoxCur=0 #当前方块的颜色
    iBoxCurType=0 #当前方块的种类
    iBoxCurRotate=0 #当前方块的旋转角度
    boxCurX=-1 #当前方块的x坐标位置
    boxCurY=-1 #当前方块的y坐标位置
    iMap=() #背景方块图表

    #初始化所有背景方块为-1, 表示没有方块
    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 "33"`
    cSpace=`echo -ne "40"`

    #保存终端属性。在read -s读取终端键时,终端的属性会被暂时改变。
    #如果在read -s时程序被不幸杀掉,可能会导致终端混乱,
    #需要在程序退出时恢复终端属性。
    sTTY=`stty -g`

    #捕捉退出信号
    trap "MyExit;" INT TERM
    trap "MyExitNoSub;" $sigExit

    #隐藏光标
    echo -ne "33[?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 "33[?25h33[${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
    #撞到墙壁了
    return 1
    fi
    if ((${iMap[y * iTrayWidth + x]} != -1 ))
    then
    #撞到其他已经存在的方块了
    return 1
    fi
    done
    return 0;
    }


    #将当前移动中的方块放到背景方块中去,
    #并计算新的分数和速度级。(即一次方块落到底部)
    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 = ${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; i--))
    do
    if ((${iMap[$i]} == -1)); then break; fi
    done
    if ((i >= j)); then continue; fi

    ((line++))
    for ((i = j - 1; i >= 0; i--))
    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 "33[1m33[3${cScoreValue}m33[${y};${x}H${iScore} "
    if ((iScore % iScoreEachLevel < line * 2 - 1))
    then
    if ((iLevel < 20))
    then
    ((iLevel++))
    ((y = iTop + 14))
    #显示新的速度级
    echo -ne "33[3${cScoreValue}m33[${y};${x}H${iLevel} "
    fi
    fi
    echo -ne "33[0m"


    #重新显示背景方块
    for ((y = 0; y < iTrayHeight; y++))
    do
    ((yp = y + iTrayTop + 1))
    ((xp = iTrayLeft + 1))
    ((i = y * iTrayWidth))
    echo -ne "33[${yp};${xp}H"
    for ((x = 0; x < iTrayWidth; x++))
    do
    ((j = i + x))
    if ((${iMap[$j]} == -1))
    then
    echo -ne " "
    else
    echo -ne "33[1m33[7m33[3${iMap[$j]}m33[4${iMap[$j]}m[]33[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` #将旧的方块抹去
    ((boxCurY += iDown))
    s=$s`DrawCurBox 1` #显示新的下落后的方块
    echo -ne $s
    Box2Map #将当前移动中的方块贴到背景方块中
    RandomBox #产生新的方块
    }


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

    #计算旋转后的新的样式
    ((iTestRotate = iBoxCurRotate + 1))
    if ((iTestRotate >= iCount))
    then
    ((iTestRotate = 0))
    fi

    #更新到新的样式, 保存老的样式(但不显示)
    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="4040"
    else
    sBox="[]"
    s=$s"33[1m33[7m33[3${cBoxCur}m33[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]})))
    #33[y;xH, 光标到(x, y)处
    s=$s"33[${i};${t}H${sBox}"
    done
    s=$s"33[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 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 "33[${i};${t}H "
    done

    #随机产生新的方块
    ((iBoxNewType = RANDOM % ${#offsetBox[@]}))
    ((iBoxNewRotate = 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 "33[1m33[7m33[3${cBoxNew}m33[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 "33[${i};${t}H[]"
    done
    echo -ne "33[0m"
    }


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

    #显示边框
    echo -ne "33[1m"
    echo -ne "33[3${cBorder}m33[4${cBorder}m"

    ((t2 = iLeft + 1))
    ((t3 = iLeft + iTrayWidth * 2 + 3))
    for ((i = 0; i < iTrayHeight; i++))
    do
    ((t1 = i + iTop + 2))
    echo -ne "33[${t1};${t2}H||"
    echo -ne "33[${t1};${t3}H||"
    done

    ((t2 = iTop + iTrayHeight + 2))
    for ((i = 0; i < iTrayWidth + 2; i++))
    do
    ((t1 = i * 2 + iLeft + 1))
    echo -ne "33[${iTrayTop};${t1}H=="
    echo -ne "33[${t2};${t1}H=="
    done
    echo -ne "33[0m"


    #显示"Score"和"Level"字样
    echo -ne "33[1m"
    ((t1 = iLeft + iTrayWidth * 2 + 7))
    ((t2 = iTop + 10))
    echo -ne "33[3${cScore}m33[${t2};${t1}HScore"
    ((t2 = iTop + 11))
    echo -ne "33[3${cScoreValue}m33[${t2};${t1}H${iScore}"
    ((t2 = iTop + 13))
    echo -ne "33[3${cScore}m33[${t2};${t1}HLevel"
    ((t2 = iTop + 14))
    echo -ne "33[3${cScoreValue}m33[${t2};${t1}H${iLevel}"
    echo -ne "33[0m"
    }


    #退出时显示GameOVer!
    function ShowExit()
    {
    local y
    ((y = iTrayHeight + iTrayTop + 3))
    echo -e "33[${y};0HGameOver!33[0m"
    exit
    }


    #显示用法.
    function Usage
    {
    cat << EOF
    Usage: $APP_NAME
    Start tetris game.

    -h, --help display this help and exit
    --version output version information and exit
    EOF
    }


    #游戏主程序在这儿开始.
    if [[ "$1" == "-h" || "$1" == "--help" ]]; then
    Usage
    elif [[ "$1" == "--version" ]]; then
    echo "$APP_NAME $APP_VERSION"
    elif [[ "$1" == "--show" ]]; then
    #当发现具有参数--show时,运行显示函数
    RunAsDisplayer
    else
    bash $0 --show& #以参数--show将本程序再运行一遍
    RunAsKeyReceiver $! #以上一行产生的进程的进程号作为参数
    fi

  • 相关阅读:
    Sum Root to Leaf Numbers
    Sum Root to Leaf Numbers
    Sort Colors
    Partition List
    Binary Tree Inorder Traversal
    Binary Tree Postorder Traversal
    Remove Duplicates from Sorted List II
    Remove Duplicates from Sorted List
    Search a 2D Matrix
    leetcode221
  • 原文地址:https://www.cnblogs.com/chengjian-physique/p/8605829.html
Copyright © 2011-2022 走看看