Shell脚本实现俄罗斯方块

#!/bin/bash

#Pargram  tetris game

#History Walker 2015-07-27 version:first

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

#颜色定义
iSumColor=7    #颜色总数
cRed=1        #红色
cGreen=2
cYellow=3
cBlue=4
cFuchSia=5
cCyan=6
cWhite=7

#位置定义
marginLeft=10
marginTop=10
((mapLeft=marginLeft+2))
((mapTop=marginTop+1))
mapWidth=10
mapHeight=15

#信号定义
sigRotate=25
sigLeft=26
sigRight=27
sigDown=28
sigAllDown=29
sigExit=30

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

#方块类型
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
iScore=0
iLevel=0

boxNext=()  #下一个方块
iboxNextColor=0
iboxNextType=0
iboxNextStyle=0

boxCur=()
iBoxCurColor=0
iBoxCurType=0
iBoxCurStyle=0

#显示进程PID
pidDisplayer=0

#这两个是相对于边框的坐标
boxCurX=-1
boxCurY=-1
#背景数组,用颜色表示,没有方块则为-1
map=()

for (( i = 0; i < mapHeight * mapWidth ; i++ ))
do
	map[$i]=-1
done

MyExitNoSub()
{
	local y
	stty $sTTY               #恢复终端
	((y = marginTop + mapHeight + 10 ))

	echo -e "\033[?25h\033[${y};0H"
	exit
}

MyExit()
{
	kill -$sigExit $pidDisplayer                  #关闭显示进程
	MyExitNoSub
}

 #显示退出
ShowExit()
{
    local y
    (( y = marginTop + mapHeight + 3 ))
    echo -e "\033[${y};1HGameOver!\033[0m"
    exit
}


#绘制方块函数
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"
}

#接收命令
RunAsKeyReceiver()
{
	local Key aKey sig cESC sTTY

	pidDisplayer=$1
	aKey=(0 0 0)

	cESC=`echo -ne "\033"`
	cSpace=`echo -ne "\040"`

	sTTY=`stty -g`                                   #保存终端

	trap "MyExit;" INT QUIT
	trap "MyExitNoSub;" $sigExit

	echo -ne "\033[?25l"

	while :
	do                                               #始终在等待信号
		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
			MyExit
		elif [[ ${aKey[0]} == $cESC && ${aKey[1]} == "[" ]]
		then
			if [[ $Key == "A" ]]; then sig=$sigRotate              #判断上下左右输入
			elif [[ $Key == "B" ]]; then sig=$sigDown
			elif [[ $Key == "C" ]]; then sig=$sigRight
			elif [[ $Key == "D" ]]; then sig=$sigLeft
			fi
		elif [[ $Key == "W" || $Key == "w" ]]; then sig=$sigRotate
		elif [[ $Key == "S" || $Key == "s" ]]; then sig=$sigDown
		elif [[ $Key == "A" || $Key == "a" ]]; then sig=$sigLeft
		elif [[ $Key == "D" || $Key == "d" ]]; then sig=$sigRight
		elif [[ [$Key] == "[]" ]]; then sig=$sigAllDown
		elif [[ $Key == "Q" || $Key == "q" ]]	
		then
			MyExit
		fi

		if [[ $sig != 0 ]]
		then
			kill -$sig $pidDisplayer
		fi
	done
}

#绘制边界
DrawBorder()
{
	clear
	local i y x1 x2

	echo -ne "\033[1m\033[3${cBorder}m\033[4${cBorder}m"

	((x1 = marginLeft + 1))
	((x2 = x1 + 2 + mapWidth * 2))

	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))
	((upBorder = marginTop +1))
	for ((i =0 ;i < mapWidth + 2;i++))
	do
		((y = i * 2 + marginLeft + 1))
		echo -ne "\033[${upBorder};${y}H=="
		echo -ne "\033[${x1};${y}H=="
	done
	echo -ne "\033[0m"

	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"
}

#用于判断是否可以移动
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
}

#准备下一方块
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
		MyExit
#	ShowExit
	fi

#同时开始准备下一方块
	PrepareNextBox
}

#初始化
InitDraw()
{
	clear
	DrawBorder
	CreateBox
}

#将方块写入背景当中
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行全部下移一行,移动行的下限(0)可以进一步简化
		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

	(( x = marginLeft + mapWidth * 2 + 7))
	(( y = marginTop + 11 ))
	(( iScore += line * 2 ))
    #显示新的分数
	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
}

#直接下落到底
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=( `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
}


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 ))
	if BoxMove $y $boxCurX                     #如果可移动则移动,不能则写入背景当中
	then
		DrawCurBox 0
		(( boxCurY = y ))
		DrawCurBox 1
	else
		Box2Map                               #写入背景当中,并创建下一方块
		CreateBox
	fi
}


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
#本循环用于接收信号,for循环中有个睡眠时间,for循环之后有个BoxDown函数
#for循环睡眠时间越长,自动下落延迟越长,所以for循环的次数决定了下落速度
		for ((i = 0; i < 21 - iLevel; i++))
		do
			sleep 0.02
			sigThis=$sig
			sig=0

			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
		BoxDown
	done
}

#help
usage()
{
	echo "tetris.sh [option]"
	echo "option:"
	echo "   --version:for version information"
	echo "   --help:for help information"
	echo "no option to run game"	
}

#游戏主程序,以上是函数定义
if [[ "$1" == "--version" ]]; then
	echo "$APP_NAME $APP_VERSION"
elif [[ "$1" == "--help" || "$1" == "--h" ]];then
	usage
elif [[ "$1" == "--show" || "$1" == "--v" ]]; then
	RunAsDisplayer                              #只运行显示进程
else
	bash $0 --show&                             #启动显示进程,并放入后台开始执行
	RunAsKeyReceiver $!                         #获取最后一个后台进程
fi

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
使用Shell脚本俄罗斯方块并不是很方便,因为Shell脚本主要用于自动化任务和系统管理,不是最适合编游戏的语言。如果您想学习如何用Shell脚本俄罗斯方块,我可以给您提供一些指导。 首先,您需要了解Shell脚本的基本语法和命令。然后,您可以使用Shell脚本中的ASCII图形字符来绘制游戏界面,并使用循环和条件语句来控制方块的移动和旋转。还需要使用随机数生成器来随机生成方块。 下面是一个简单的示例代码,该代码使用Shell脚本实现俄罗斯方块的基本功能: ``` #!/bin/bash # 游戏区域大小 ROWS=20 COLS=10 # 初始化游戏区域 for ((i=0; i<$ROWS; i++)); do for ((j=0; j<$COLS; j++)); do board[$i,$j]='.' done done # 定义方块的形状和颜色 shapes=('O' 'I' 'S' 'Z' 'L' 'J' 'T') colors=('31' '32' '33' '34' '35' '36' '37') # 随机生成方块 shape=${shapes[$((RANDOM % ${#shapes[@]}))]} color=${colors[$((RANDOM % ${#colors[@]}))]} # 方块初始位置 row=0 col=$((($COLS-4)/2)) # 绘制游戏界面 function draw_board() { clear for ((i=0; i<$ROWS; i++)); do for ((j=0; j<$COLS; j++)); do echo -ne "\033[${i};${j}H\033[${colors[@]}m${board[$i,$j]}\033[0m" done done } # 绘制方块 function draw_shape() { for ((i=0; i<4; i++)); do for ((j=0; j<4; j++)); do if [[ ${shape:i*4+j:i*4+j+1} == '*' ]]; then echo -ne "\033[$((${row}+${i}));$((${col}+${j}))H\033[${color}m*\033[0m" fi done done } # 判断方块是否可以向下移动 function can_move_down() { for ((i=0; i<4; i++)); do for ((j=0; j<4; j++)); do if [[ ${shape:i*4+j:i*4+j+1} == '*' ]]; then if [[ $((row+i)) -ge $ROWS || ${board[$((row+i)),$((col+j))]} != '.' ]]; then return 1 fi fi done done return 0 } # 将方块固定在游戏区域中 function fix_shape() { for ((i=0; i<4; i++)); do for ((j=0; j<4; j++)); do if [[ ${shape:i*4+j:i*4+j+1} == '*' ]]; then board[$((row+i)),$((col+j))]=${shape} fi done done } # 消除已经填满的行 function clear_lines() { for ((i=$ROWS-1; i>=0; i--)); do if [[ "${board[$i,$(seq -s , 0 $(($COLS-1)))]}" == *"."* ]]; then continue fi for ((j=i; j>0; j--)); do board[$j]="${board[$((j-1))]}" done for ((j=0; j<$COLS; j++)); do board[0,$j]="." done ((i++)) done } # 主循环 while true; do draw_board draw_shape # 方块可以向下移动则向下移动,否则固定在游戏区域中并生成新方块 if can_move_down; then ((row++)) else fix_shape clear_lines shape=${shapes[$((RANDOM % ${#shapes[@]}))]} color=${colors[$((RANDOM % ${#colors[@]}))]} row=0 col=$((($COLS-4)/2)) fi # 按键控制方块移动和旋转(待实现) done ``` 以上代码只是一个简单的示例,还有很多细节需要根据自己的需求和想象力进行调整和实现
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值