设为首页 收藏本站
查看: 827|回复: 0

shell写的俄罗斯方块游戏

[复制链接]

尚未签到

发表于 2018-8-29 13:41:29 | 显示全部楼层 |阅读模式
#!/bin/bash  

  
# Tetris Game
  
# 10.21.2003 xhchen
  

  
#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 "\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
  
                        #撞到墙壁了
  
                        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 "\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`        #将旧的方块抹去
  
        ((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="\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 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[@]}))
  
        ((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 "\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"
  
}
  

  

  
#初始绘制
  
function InitDraw()
  
{
  
        clear
  
        RandomBox        #随机产生方块,这时右边预显示窗口中有方快了
  
        RandomBox        #再随机产生方块,右边预显示窗口中的方块被更新,原先的方块将开始下落
  
        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++))
  
        do
  
                ((t1 = i * 2 + iLeft + 1))
  
                echo -ne "\033[${iTrayTop};${t1}H=="
  
                echo -ne "\033[${t2};${t1}H=="
  
        done
  
        echo -ne "\033[0m"
  

  

  
        #显示"Score"和"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"
  
}
  

  

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

  

  
#显示用法.
  
function Usage
  
{
  
        cat

运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-558231-1-1.html 上篇帖子: 关于shell脚本编程一些有用资源的小结 下篇帖子: 4、Bash基础及配置、标准I/O、管道及shell编程基础;
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

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