#!/bin/bash
APP_NAME="${0##*[\\/]}"
APP_VERSION="1.0"
#顏色定義
iSumColor=7 #顏色總數(shù)
cRed=1 #紅色
cGreen=2 #綠色
cYellow=3 #黃色
cBlue=4 #藍(lán)色
cFuchsia=5 #紫紅色
cCyan=6 #青色(藍(lán)綠色)
cWhite=7 #白色
#位置與大小
marginLeft=3 #邊框左邊距
marginTop=2 #邊框上邊距
((mapLeft=marginLeft+2)) #棋盤左邊距
((mapTop=$marginTop+1)) #棋盤上邊距
mapWidth=10 #棋盤寬度
mapHeight=15 #棋盤高度
#顏色設(shè)置
cBorder=$cGreen
cScore=$cFuchsia
cScoreValue=$cCyan
#控制信號
#游戲使用兩個進(jìn)程,一個用于接收輸入,一個用于游戲流程和顯示界面;
#當(dāng)前者接收到上下左右等按鍵時,通過向后者發(fā)送signal的方式通知后者。
sigRotate=25 #向上鍵
sigLeft=26
sigRight=27
sigDown=28
sigAllDown=29 #空格鍵
sigExit=30
#方塊定義,7大類19種樣式
#前8位為方塊坐標(biāo),后2位為方塊剛出現(xiàn)的時候的位置
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 #方塊類型總數(shù)
boxStyle=(1 2 2 2 4 4 4) #各種方塊旋轉(zhuǎn)后可能的樣式數(shù)目
iScoreEachLevel=50 #提升一個級別需要的分?jǐn)?shù)
#運(yùn)行時數(shù)據(jù)
sig=0 #接收到的signal
iScore=0 #總分
iLevel=0 #速度級
boxNext=() #下一個方塊
iboxNextColor=0 #下一個方塊的顏色
iboxNextType=0 #下一個方塊的種類
iboxNextStyle=0 #下一個方塊的樣式
boxCur=() #當(dāng)前方塊的位置定義
iBoxCurColor=0 #當(dāng)前方塊的顏色
iBoxCurType=0 #當(dāng)前方塊的種類
iBoxCurStyle=0 #當(dāng)前方塊的樣式
boxCurX=-1 #當(dāng)前方塊的x坐標(biāo)位置
boxCurY=-1 #當(dāng)前方塊的y坐標(biāo)位置
map=() #棋盤圖表
#初始化所有背景方塊為-1, 表示沒有方塊
for ((i = 0; i mapHeight * mapWidth; i++))
do
map[$i]=-1
done
#接收輸入的進(jìn)程的主函數(shù)
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時程序被不幸殺掉,可能會導(dǎo)致終端混亂,
#需要在程序退出時恢復(fù)終端屬性。
sTTY=`stty -g`
#捕捉退出信號
trap "MyExit;" INT QUIT
trap "MyExitNoSub;" $sigExit
#隱藏光標(biāo)
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
#向另一進(jìn)程發(fā)送消息
kill -$sig $pidDisplayer
fi
done
}
#退出前的恢復(fù)
MyExitNoSub()
{
local y
#恢復(fù)終端屬性
stty $sTTY
((y = marginTop + mapHeight + 4))
#顯示光標(biāo)
echo -e "\033[?25h\033[${y};0H"
exit
}
MyExit()
{
#通知顯示進(jìn)程需要退出
kill -$sigExit $pidDisplayer
MyExitNoSub
}
#處理顯示和游戲流程的主函數(shù)
RunAsDisplayer()
{
local sigThis
InitDraw
#掛載各種信號的處理函數(shù)
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
#根據(jù)當(dāng)前的速度級iLevel不同,設(shè)定相應(yīng)的循環(huán)的次數(shù)
for ((i = 0; i 21 - iLevel; i++))
do
sleep 0.02
sigThis=$sig
sig=0
#根據(jù)sig變量判斷是否接受到相應(yīng)的信號
if ((sigThis == sigRotate)); then BoxRotate; #旋轉(zhuǎn)
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
}
#繪制當(dāng)前方塊,傳第一個參數(shù),0表示擦除當(dāng)前方塊,1表示繪制當(dāng)前方塊
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
#方塊相對于棋盤坐標(biāo)
((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
#撞到其他已經(jīng)存在的方塊了
return 1
fi
done
return 0;
}
#將方塊貼到棋盤上
Box2Map()
{
local i j x y line
#將當(dāng)前移動中的方塊貼到棋盤對應(yīng)的區(qū)域
for ((i = 0; i 8; i += 2))
do
#計(jì)算方塊相對于棋盤的坐標(biāo)
((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
#如果棋盤上有空隙,跳出循環(huán)
[[ ${map[i*mapWidth+j]} -eq -1 ]] break
done
[ $j -lt $mapWidth ] continue
#說明當(dāng)前行可消去,可消去行數(shù)加一
(( line++ ))
#第i行可被消除,將0行至第i-1行全部下移一行,從第i-1行開始移動
for ((j = i*mapWidth-1; j >= 0; j--))
do
((x = j + mapWidth))
map[$x]=${map[$j]}
done
#因?yàn)橄乱埔恍?,?行置空
for ((i = 0; i mapWidth; i++))
do
map[$i]=-1
done
done
[ $line -eq 0 ] return
#根據(jù)消去的行數(shù)line計(jì)算分?jǐn)?shù)和速度級
((x = marginLeft + mapWidth * 2 + 7))
((y = marginTop + 11))
((iScore += line * 2 - 1))
#顯示新的分?jǐn)?shù)
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
#棋盤相對于屏幕的坐標(biāo)
((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坐標(biāo)
if BoxMove $y $boxCurX #測試是否可以下落一行
then
DrawCurBox 0 #將舊的方塊抹去
((boxCurY = y))
DrawCurBox 1 #顯示新的下落后方塊
else
#走到這兒, 如果不能下落了
Box2Map #將當(dāng)前移動中的方塊貼到背景方塊中
CreateBox #產(chǎn)生新的方塊
fi
}
#下落到底
BoxAllDown()
{
local y iDown
#計(jì)算能夠下落的行數(shù)
iDown=0
(( y = boxCurY + 1 ))
while BoxMove $y $boxCurX
do
(( y++ ))
(( iDown++ ))
done
DrawCurBox 0 #將舊的方塊抹去
((boxCurY += iDown))
DrawCurBox 1 #顯示新的下落后的方塊
Box2Map #將當(dāng)前移動中的方塊貼到背景方塊中
CreateBox #產(chǎn)生新的方塊
}
#翻轉(zhuǎn)
BoxRotate()
{
[ ${boxStyle[$iBoxCurType]} -eq 1 ] return
((rotateStyle = (iBoxCurStyle + 1) % ${boxStyle[$iBoxCurType]}))
#將當(dāng)前方塊保存到boxTmp
boxTmp=( `eval 'echo ${boxCur[@]}'` )
boxCur=( `eval 'echo ${box'$iBoxCurType'_'$rotateStyle'[@]}'` )
if BoxMove $boxCurY $boxCurX #測試旋轉(zhuǎn)后是否有空間放的下
then
#抹去舊的方塊
boxCur=( `eval 'echo ${boxTmp[@]}'` )
DrawCurBox 0
boxCur=( `eval 'echo ${box'$iBoxCurType'_'$rotateStyle'[@]}'` )
DrawCurBox 1
iBoxCurStyle=$rotateStyle
else
#不能旋轉(zhuǎn),還是繼續(xù)使用老的樣式
boxCur=( `eval 'echo ${boxTmp[@]}'` )
fi
}
#準(zhǔn)備下一個方塊
PrepareNextBox()
{
local i x y
#清除右邊預(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
#隨機(jī)生成預(yù)顯式方塊
(( iBoxNextType = RANDOM % iSumType ))
(( iBoxNextStyle = RANDOM % ${boxStyle[$iBoxNextType]} ))
(( iBoxNextColor = RANDOM % $iSumColor + 1 ))
boxNext=( `eval 'echo ${box'$iBoxNextType'_'$iBoxNextStyle'[@]}'` )
#顯示右邊預(yù)顯示的方塊
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
#當(dāng)前方塊不存在
(( iBoxCurType = RANDOM % iSumType ))
(( iBoxCurStyle = RANDOM % ${boxStyle[$iBoxCurType]} ))
(( iBoxCurColor = RANDOM % $iSumColor + 1 ))
else
#當(dāng)前方塊已存在, 將下一個方塊賦給當(dāng)前方塊
iBoxCurType=$iBoxNextType;
iBoxCurStyle=$iBoxNextStyle;
iBoxCurColor=$iBoxNextColor
fi
#當(dāng)前方塊數(shù)組
boxCur=( `eval 'echo ${box'$iBoxCurType'_'$iBoxCurStyle'[@]}'` )
#初始化方塊起始坐標(biāo)
boxCurY=boxCur[8];
boxCurX=boxCur[9];
DrawCurBox 1 #繪制當(dāng)前方塊
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坐標(biāo)
((x2 = x1 + 2 + mapWidth * 2)) #右邊框x坐標(biāo)
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 #創(chuàng)建方塊
}
#退出時顯示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
#當(dāng)發(fā)現(xiàn)具有參數(shù)--show時,運(yùn)行顯示函數(shù)
RunAsDisplayer
else
bash $0 --show #以參數(shù)--show將本程序再運(yùn)行一遍
RunAsKeyReceiver $! #以上一行產(chǎn)生的進(jìn)程的進(jìn)程號作為參數(shù)
fi
#!/bin/bash
GetKey()
{
aKey=(0 0 0) #定義一個數(shù)組來保存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
#!/bin/bash
#位置與大小
marginLeft=8 #邊框左邊距
marginTop=6 #邊框上邊距
((mapLeft=marginLeft+2)) #棋盤左邊距
((mapTop=$marginTop+1)) #棋盤上邊距
mapWidth=10 #棋盤寬度
mapHeight=15 #棋盤高度
#方塊定義,7大類19種樣式
#前8位為方塊坐標(biāo),后2位為方塊剛出現(xiàn)的時候的位置
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坐標(biāo)
((x2 = x1 + 2 + mapWidth * 2)) #右邊框x坐標(biāo)
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