shell版俄罗斯方块最终源码 分类: shell版俄罗斯方块 2015-07-28 17:06 66人阅读 评论(0) 收藏

#!/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 "33[?25h33[${y};0H"
	exit
}

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

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


#绘制方块函数
DrawCurBox()
{
	local i x y bErase sBox
	bErase=$1
	if (( ${bErase} == 0 ))                          #根据参数不同,选择擦除方块或绘制方块
	then
		sBox="4040"
	else
		sBox="[]"
		echo -ne "33[1m33[3${iBoxCurColor}m33[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 "33[${y};${x}H${sBox}"
	done
	echo -ne "33[0m"
}

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

	pidDisplayer=$1
	aKey=(0 0 0)

	cESC=`echo -ne "33"`
	cSpace=`echo -ne "40"`

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

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

	echo -ne "33[?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 "33[1m33[3${cBorder}m33[4${cBorder}m"

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

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

	echo -ne "33[1m"
	((y = marginLeft + mapWidth *2 + 7))
	((x1 = marginTop + 10))

	echo -ne "33[3${cScore}m33[${x1};${y}HScore"
	((x1 = marginTop + 11))
	echo -ne "33[3${cScoreValue}m33[${x1};${y}H${iScore}"
	((x1 = marginTop + 13))
	echo -ne "33[3${cScore}m33[${x1};${y}HLevel"
	((x1 = marginTop + 14))
	echo -ne "33[3${cScoreValue}m33[${x1};${y}H${iLevel}"
	echo -ne "33[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 "33[${y};${x}H4040"
		done
	fi

   #随机生成下一方块
    (( iBoxNextType = RANDOM % iSumType))
	(( iBoxNextStyle = RANDOM % ${boxStyle[$iBoxNextType]} ))
	(( iBoxNextColor = RANDOM % ${iSumColor} + 1 ))

	boxNext=( `eval 'echo ${box'$iBoxNextType'_'$iBoxNextStyle'[@]}'` )

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

    #重新绘制界面
	for (( i = 0; i < mapHeight ; i++))
	do
        #棋盘相对于屏幕的坐标
		((y = i + mapTop + 1))
		((x = mapLeft + 1))
        #移动光标
		echo -ne "33[${y};${x}H"
		
		for (( j = 0; j < mapWidth ; j++))
		do
			((tmp = i * mapWidth + j))
			if ((${map[$tmp]} == -1))                                     #说明是空格
			then
				echo -ne "  "
			else
				echo -ne "33[1m33[3${map[$tmp]}m33[4${map[$tmp]}m[]33[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




原文地址:https://www.cnblogs.com/zclzqbx/p/4687029.html