shell实现俄罗斯方块脚本
程序员文章站
2022-07-05 08:17:00
本文实例为大家分享了shell实现俄罗斯方块的具体代码,供大家参考,具体内容如下
draw 是画出图形界面,keytest是获取键盘,tetris是整个游戏...
本文实例为大家分享了shell实现俄罗斯方块的具体代码,供大家参考,具体内容如下
draw 是画出图形界面,keytest是获取键盘,tetris是整个游戏
tetris.sh
#!/bin/bash app_name="${0##*[\\/]}" app_version="1.0" #颜色定义 isumcolor=7 #颜色总数 cred=1 #红色 cgreen=2 #绿色 cyellow=3 #黄色 cblue=4 #蓝色 cfuchsia=5 #紫红色 ccyan=6 #青色(蓝绿色) cwhite=7 #白色 #位置与大小 marginleft=3 #边框左边距 margintop=2 #边框上边距 ((mapleft=marginleft+2)) #棋盘左边距 ((maptop=$margintop+1)) #棋盘上边距 mapwidth=10 #棋盘宽度 mapheight=15 #棋盘高度 #颜色设置 cborder=$cgreen cscore=$cfuchsia cscorevalue=$ccyan #控制信号 #游戏使用两个进程,一个用于接收输入,一个用于游戏流程和显示界面; #当前者接收到上下左右等按键时,通过向后者发送signal的方式通知后者。 sigrotate=25 #向上键 sigleft=26 sigright=27 sigdown=28 sigalldown=29 #空格键 sigexit=30 #方块定义,7大类19种样式 #前8位为方块坐标,后2位为方块刚出现的时候的位置 box0_0=(0 0 0 1 1 0 1 1 0 4) box1_0=(0 1 1 1 2 1 3 1 0 3) box1_1=(1 0 1 1 1 2 1 3 -1 3) box2_0=(0 0 1 0 1 1 2 1 0 4) box2_1=(0 1 0 2 1 0 1 1 0 3) box3_0=(0 1 1 0 1 1 2 0 0 4) box3_1=(0 0 0 1 1 1 1 2 0 4) box4_0=(0 2 1 0 1 1 1 2 0 3) box4_1=(0 1 1 1 2 1 2 2 0 3) box4_2=(1 0 1 1 1 2 2 0 -1 3) box4_3=(0 0 0 1 1 1 2 1 0 4) box5_0=(0 0 1 0 1 1 1 2 0 3) box5_1=(0 1 0 2 1 1 2 1 0 3) box5_2=(1 0 1 1 1 2 2 2 -1 3) box5_3=(0 1 1 1 2 0 2 1 0 4) box6_0=(0 1 1 0 1 1 1 2 0 3) box6_1=(0 1 1 1 1 2 2 1 0 3) box6_2=(1 0 1 1 1 2 2 1 -1 3) box6_3=(0 1 1 0 1 1 2 1 0 4) isumtype=7 #方块类型总数 boxstyle=(1 2 2 2 4 4 4) #各种方块旋转后可能的样式数目 iscoreeachlevel=50 #提升一个级别需要的分数 #运行时数据 sig=0 #接收到的signal iscore=0 #总分 ilevel=0 #速度级 boxnext=() #下一个方块 iboxnextcolor=0 #下一个方块的颜色 iboxnexttype=0 #下一个方块的种类 iboxnextstyle=0 #下一个方块的样式 boxcur=() #当前方块的位置定义 iboxcurcolor=0 #当前方块的颜色 iboxcurtype=0 #当前方块的种类 iboxcurstyle=0 #当前方块的样式 boxcurx=-1 #当前方块的x坐标位置 boxcury=-1 #当前方块的y坐标位置 map=() #棋盘图表 #初始化所有背景方块为-1, 表示没有方块 for ((i = 0; i < mapheight * mapwidth; i++)) do map[$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 quit 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 } #退出前的恢复 myexitnosub() { local y #恢复终端属性 stty $stty ((y = margintop + mapheight + 4)) #显示光标 echo -e "\033[?25h\033[${y};0h" exit } myexit() { #通知显示进程需要退出 kill -$sigexit $piddisplayer myexitnosub } #处理显示和游戏流程的主函数 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 } #绘制当前方块,传第一个参数,0表示擦除当前方块,1表示绘制当前方块 drawcurbox() { local i x y berase sbox berase=$1 if (( berase == 0 )) then sbox="\040\040" #用两个空格擦除 else sbox="[]" echo -ne "\033[1m\033[3${iboxcurcolor}m\033[4${iboxcurcolor}m" fi for ((i = 0; i < 8; i += 2)) do ((y = maptop + 1 + ${boxcur[$i]} + boxcury)) ((x = mapleft + 1 + 2 * (boxcurx + ${boxcur[$i + 1]}))) echo -ne "\033[${y};${x}h${sbox}" done echo -ne "\033[0m" } #移动方块 #boxmove(y, x), 测试是否可以把移动中的方块移到(y, x)的位置, 返回0则可以, 1不可以 boxmove() { local i x y xpos ypos ypos=$1 xpos=$2 for ((i = 0; i < 8; i += 2)) do #方块相对于棋盘坐标 ((y = ypos + ${boxcur[$i]})) ((x = xpos + ${boxcur[$i + 1]})) if (( y < 0 || y >= mapheight || x < 0 || x >= mapwidth)) then #撞到墙壁了 return 1 fi if (( ${map[y * mapwidth + x]} != -1 )) then #撞到其他已经存在的方块了 return 1 fi done return 0; } #将方块贴到棋盘上 box2map() { local i j x y line #将当前移动中的方块贴到棋盘对应的区域 for ((i = 0; i < 8; i += 2)) do #计算方块相对于棋盘的坐标 ((y = ${boxcur[$i]} + boxcury)) ((x = ${boxcur[$i + 1]} + boxcurx)) map[y*mapwidth+x]=$iboxcurcolor #将方块颜色赋给地图 done line=0 for ((i = 0; i < mapheight; i++)) do for ((j = 0; j < mapwidth; j++)) do #如果棋盘上有空隙,跳出循环 [[ ${map[i*mapwidth+j]} -eq -1 ]] && break done [ $j -lt $mapwidth ] && continue #说明当前行可消去,可消去行数加一 (( line++ )) #第i行可被消除,将0行至第i-1行全部下移一行,从第i-1行开始移动 for ((j = i*mapwidth-1; j >= 0; j--)) do ((x = j + mapwidth)) map[$x]=${map[$j]} done #因为下移一行,第0行置空 for ((i = 0; i < mapwidth; i++)) do map[$i]=-1 done done [ $line -eq 0 ] && return #根据消去的行数line计算分数和速度级 ((x = marginleft + mapwidth * 2 + 7)) ((y = margintop + 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 = margintop + 14)) #显示新的速度级 echo -ne "\033[3${cscorevalue}m\033[${y};${x}h${ilevel} " fi fi echo -ne "\033[0m" #重新显示背景方块 for ((i = 0; i < mapheight; i++)) do #棋盘相对于屏幕的坐标 ((y = i + maptop + 1)) ((x = mapleft + 1)) echo -ne "\033[${y};${x}h" for ((j = 0; j < mapwidth; j++)) do ((tmp = i * mapwidth + j)) if ((${map[$tmp]} == -1)) then echo -ne " " else echo -ne "\033[1m\033[3${map[$tmp]}m\033[4${map[$tmp]}m[]\033[0m" fi done done } #左移一格 boxleft() { local x ((x = boxcurx - 1)) if boxmove $boxcury $x then drawcurbox 0 ((boxcurx = x)) drawcurbox 1 fi } #右移一格 boxright() { local x ((x = boxcurx + 1)) if boxmove $boxcury $x then drawcurbox 0 ((boxcurx = x)) drawcurbox 1 fi } #向下移一格 boxdown() { local y ((y = boxcury + 1)) #新的y坐标 if boxmove $y $boxcurx #测试是否可以下落一行 then drawcurbox 0 #将旧的方块抹去 ((boxcury = y)) drawcurbox 1 #显示新的下落后方块 else #走到这儿, 如果不能下落了 box2map #将当前移动中的方块贴到背景方块中 createbox #产生新的方块 fi } #下落到底 boxalldown() { local y idown #计算能够下落的行数 idown=0 (( y = boxcury + 1 )) while boxmove $y $boxcurx do (( y++ )) (( idown++ )) done drawcurbox 0 #将旧的方块抹去 ((boxcury += idown)) drawcurbox 1 #显示新的下落后的方块 box2map #将当前移动中的方块贴到背景方块中 createbox #产生新的方块 } #翻转 boxrotate() { [ ${boxstyle[$iboxcurtype]} -eq 1 ] && return ((rotatestyle = (iboxcurstyle + 1) % ${boxstyle[$iboxcurtype]})) #将当前方块保存到boxtmp boxtmp=( `eval 'echo ${boxcur[@]}'` ) boxcur=( `eval 'echo ${box'$iboxcurtype'_'$rotatestyle'[@]}'` ) if boxmove $boxcury $boxcurx #测试旋转后是否有空间放的下 then #抹去旧的方块 boxcur=( `eval 'echo ${boxtmp[@]}'` ) drawcurbox 0 boxcur=( `eval 'echo ${box'$iboxcurtype'_'$rotatestyle'[@]}'` ) drawcurbox 1 iboxcurstyle=$rotatestyle else #不能旋转,还是继续使用老的样式 boxcur=( `eval 'echo ${boxtmp[@]}'` ) fi } #准备下一个方块 preparenextbox() { local i x y #清除右边预显示的方块 if (( ${#boxnext[@]} != 0 )); then for ((i = 0; i < 8; i += 2)) do ((y = margintop + 1 + ${boxnext[$i]})) ((x = marginleft + 2 * mapwidth + 7 + 2 * ${boxnext[$i + 1]})) echo -ne "\033[${y};${x}h\040\040" done fi #随机生成预显式方块 (( iboxnexttype = random % isumtype )) (( iboxnextstyle = random % ${boxstyle[$iboxnexttype]} )) (( iboxnextcolor = random % $isumcolor + 1 )) boxnext=( `eval 'echo ${box'$iboxnexttype'_'$iboxnextstyle'[@]}'` ) #显示右边预显示的方块 echo -ne "\033[1m\033[3${iboxnextcolor}m\033[4${iboxnextcolor}m" for ((i = 0; i < 8; i += 2)) do ((y = margintop + 1 + ${boxnext[$i]})) ((x = marginleft + 2 * mapwidth + 7 + 2 * ${boxnext[$i + 1]})) echo -ne "\033[${y};${x}h[]" done echo -ne "\033[0m" } #显示新方块 createbox() { if (( ${#boxcur[@]} == 0 )); then #当前方块不存在 (( iboxcurtype = random % isumtype )) (( iboxcurstyle = random % ${boxstyle[$iboxcurtype]} )) (( iboxcurcolor = random % $isumcolor + 1 )) else #当前方块已存在, 将下一个方块赋给当前方块 iboxcurtype=$iboxnexttype; iboxcurstyle=$iboxnextstyle; iboxcurcolor=$iboxnextcolor fi #当前方块数组 boxcur=( `eval 'echo ${box'$iboxcurtype'_'$iboxcurstyle'[@]}'` ) #初始化方块起始坐标 boxcury=boxcur[8]; boxcurx=boxcur[9]; drawcurbox 1 #绘制当前方块 if ! boxmove $boxcury $boxcurx then kill -$sigexit $ppid showexit fi preparenextbox } #绘制边框 drawborder() { clear local i y x1 x2 #显示边框 echo -ne "\033[1m\033[3${cborder}m\033[4${cborder}m" ((x1 = marginleft + 1)) #左边框x坐标 ((x2 = x1 + 2 + mapwidth * 2)) #右边框x坐标 for ((i = 0; i < mapheight; i++)) do ((y = i + margintop + 2)) echo -ne "\033[${y};${x1}h||" #绘制左边框 echo -ne "\033[${y};${x2}h||" #绘制右边框 done ((x1 = margintop + mapheight + 2)) for ((i = 0; i < mapwidth + 2; i++)) do ((y = i * 2 + marginleft + 1)) echo -ne "\033[${maptop};${y}h==" #绘制上边框 echo -ne "\033[${x1};${y}h==" #绘制下边框 done echo -ne "\033[0m" #显示"score"和"level"字样 echo -ne "\033[1m" ((y = marginleft + mapwidth * 2 + 7)) ((x1 = margintop + 10)) echo -ne "\033[3${cscore}m\033[${x1};${y}hscore" ((x1 = margintop + 11)) echo -ne "\033[3${cscorevalue}m\033[${x1};${y}h${iscore}" ((x1 = margintop + 13)) echo -ne "\033[3${cscore}m\033[${x1};${y}hlevel" ((x1 = margintop + 14)) echo -ne "\033[3${cscorevalue}m\033[${x1};${y}h${ilevel}" echo -ne "\033[0m" } initdraw() { clear #清屏 drawborder #绘制边框 createbox #创建方块 } #退出时显示gameover! showexit() { local y ((y = mapheight + maptop + 3)) echo -e "\033[${y};1hgameover!\033[0m" exit } #游戏主程序在这儿开始. if [[ "$1" == "--version" ]]; then echo "$app_name $app_version" elif [[ "$1" == "--show" ]]; then #当发现具有参数--show时,运行显示函数 runasdisplayer else bash $0 --show& #以参数--show将本程序再运行一遍 runaskeyreceiver $! #以上一行产生的进程的进程号作为参数 fi
keytest.sh
#!/bin/bash getkey() { akey=(0 0 0) #定义一个数组来保存3个按键 cesc=`echo -ne "\033"` cspace=`echo -ne "\040"` while : do read -s -n 1 key #读取一个字符,将读取到的字符保存在key中 #echo $key #echo xxx akey[0]=${akey[1]} #第一个按键 akey[1]=${akey[2]} #第二个按键 akey[2]=$key #第三个按键 if [[ $key == $cesc && ${akey[1]} == $cesc ]] then myexit elif [[ ${akey[0]} == $cesc && ${akey[1]} == "[" ]] then if [[ $key == "a" ]]; then echo keyup elif [[ $key == "b" ]]; then echo keydown elif [[ $key == "d" ]]; then echo keyleft elif [[ $key == "c" ]]; then echo keyright fi fi done } getkey
draw.sh
#!/bin/bash #位置与大小 marginleft=8 #边框左边距 margintop=6 #边框上边距 ((mapleft=marginleft+2)) #棋盘左边距 ((maptop=$margintop+1)) #棋盘上边距 mapwidth=10 #棋盘宽度 mapheight=15 #棋盘高度 #方块定义,7大类19种样式 #前8位为方块坐标,后2位为方块刚出现的时候的位置 box0_0=(0 0 0 1 1 0 1 1 0 4) box1_0=(0 1 1 1 2 1 3 1 0 3) box1_1=(1 0 1 1 1 2 1 3 -1 3) box2_0=(0 0 1 0 1 1 2 1 0 4) box2_1=(0 1 0 2 1 0 1 1 0 3) box3_0=(0 1 1 0 1 1 2 0 0 4) box3_1=(0 0 0 1 1 1 1 2 0 4) box4_0=(0 2 1 0 1 1 1 2 0 3) box4_1=(0 1 1 1 2 1 2 2 0 3) box4_2=(1 0 1 1 1 2 2 0 -1 3) box4_3=(0 0 0 1 1 1 2 1 0 4) box5_0=(0 0 1 0 1 1 1 2 0 3) box5_1=(0 1 0 2 1 1 2 1 0 3) box5_2=(1 0 1 1 1 2 2 2 -1 3) box5_3=(0 1 1 1 2 0 2 1 0 4) box6_0=(0 1 1 0 1 1 1 2 0 3) box6_1=(0 1 1 1 1 2 2 1 0 3) box6_2=(1 0 1 1 1 2 2 1 -1 3) box6_3=(0 1 1 0 1 1 2 1 0 4) #绘制边框 drawborder() { clear local i y x1 x2 #显示边框 echo -ne "\033[1m\033[32m\033[42m" ((x1 = marginleft + 1)) #左边框x坐标 ((x2 = x1 + 2 + mapwidth * 2)) #右边框x坐标 for ((i = 0; i < mapheight; i++)) do ((y = i + margintop + 2)) echo -ne "\033[${y};${x1}h||" #绘制左边框 echo -ne "\033[${y};${x2}h||" #绘制右边框 done ((x1 = margintop + mapheight + 2)) for ((i = 0; i < mapwidth + 2; i++)) do ((y = i * 2 + marginleft + 1)) echo -ne "\033[${maptop};${y}h==" #绘制上边框 echo -ne "\033[${x1};${y}h==" #绘制下边框 done echo -ne "\033[0m" } drawbox() { local i x y xpos ypos ypos=${box0_0[8]} xpos=${box0_0[9]} echo -ne "\033[1m\033[35m\033[45m" for ((i = 0; i < 8; i += 2)) do (( y = maptop + 1 + ${box0_0[$i]} + ypos )) (( x = mapleft + 1 + 2 * (${box0_0[$i + 1]} + xpos) )) echo -ne "\033[${y};${x}h[]" done echo -ne "\033[0m" } initdraw() { clear #清屏 drawborder #绘制边框 drawbox while : do sleep 1 done } initdraw
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。