做一个小游戏练习 shell 脚本的语法什么的。入门一般都是俄罗斯方块,不过也都有了,推箱子有用C写过,很简单,网上也有了,就做一个网上还没有人用 shell 写过的吧。模拟 IPAD 上一个“消灭星星”的游戏吧,就是消去几个连在一起的方块得分的游戏,看上去很简单。
正文
就放一个截图吧:
代码
放主程序吧,其它的放附件,方便下载查看。block.zip
这个代码框怎么格式都没了啊,还是 CSDN 的代码框更好,能保留缩进啊。
点击(此处)折叠或打开
- #!/bin/bash
- # block.sh
- # 作者:亚丹
- # 时间:2012-03-16
- # http://seesea.blog.chinaunix.net
- # http://blog.csdn.net/nicenight
- # 功能:模拟 IPAD 的消灭星星游戏
- # 游戏规则:
- # 1. 有两个以上的同色方块就可以消去,同时消去的方块越多,得分越多
- # 2. 过关时,剩余的方块越少,奖励分数越多
- # 3. 当总分低于过关目标分数时,游戏结束
- #
- # 写着写着发现控制和逻辑越来越混了,也罢,就是练习一下语法嘛
-
- #-----------------------------------------------------------
- # 加载通用文件
- source colors.sh
- source array2d.sh
- source util.sh
-
- #-----------------------------------------------------------
- # 全局配置
-
- # 响应的信号
- declare -r SIG_UP=SIGRTMIN+1
- declare -r SIG_DOWN=SIGRTMIN+2
- declare -r SIG_LEFT=SIGRTMIN+3
- declare -r SIG_RIGHT=SIGRTMIN+4
- declare -r SIG_SHOOT=SIGRTMIN+5
- declare -r SIG_PAUSE=SIGRTMIN+6
- declare -r SIG_EXIT=SIGRTMIN+7
-
- # 响应的按键(注意:使用大写配置)
- declare -r KEY_UP="W"
- declare -r KEY_DOWN="S"
- declare -r KEY_LEFT="A"
- declare -r KEY_RIGHT="D"
- declare -r KEY_SHOOT="J"
- declare -r KEY_PAUSE="P"
- declare -r KEY_EXIT="Q"
-
- # 光标效果,设置光标所在方块的特殊显示效果,这里设置为白色背景显示
- declare -r EFFECT_CURSOR=${BWHT}
-
- # 当前位置同色的成片方块特殊效果,这里设置为亮黄色背景显示
- declare -r EFFECT_CONNECTION=${BYEL}
-
- # 游戏区域顶边界和左边界
- declare -r MAP_AREA_TOP=9
- declare -r MAP_AREA_LEFT=20
-
- # 游戏边界显示字符(分横向和纵向两种字符)
- declare -r MAP_BORDER_H="${BHIG} ${NOR}"
- declare -r MAP_BORDER_V="${BHIG} ${NOR}"
-
- # 游戏最高分存放文件
- declare -r FILE_TOP_SCORE=".block_top_score"
-
- #-----------------------------------------------------------
- # 常量
- declare -r BLOCK_WIDTH=3 # 一个元素显示的宽度
- declare -r BLOCK_HEIGHT=3 # 一个元素显示的高度
-
- declare -r BONUS_STAGE=1000 # 过关奖励
- declare -r BONUS_DECREASE=100 # 过关剩余方块扣分基数
-
- #-----------------------------------------------------------
- # 全局变量
- declare pid # 主线程pid
- declare sub_pid # 子线程pid
- declare sign # 信号
- declare stty_save # 保存stty配置
-
- declare score # 当前分数
- declare score_top # 最高分
- declare score_shoot # 当前区域消去可得分数
- declare score_goal # 当前关卡过关分数
- declare stage_round # 当前关卡数
- declare bonus_stage # 当前过关奖励分数
-
- declare width # 屏宽
- declare height # 屏高
- declare limit_cursor_row # 光标可移动范围的行数限制
- declare limit_cursor_col # 光标可移动范围的列数限制
- declare paused # 是否暂停状态标志
- declare exiting # 是否在退出状态
- declare repaint # 是否需要重绘
-
- declare map_area_width # 游戏区域宽
- declare map_area_height # 游戏区域高
-
- declare row_cur # 当前行
- declare col_cur # 当前列
- declare row_old # 旧行
- declare col_old # 旧列
-
- declare -a memory_page1 # 内存页一
- declare -a memory_page2 # 内存页二
-
- declare -a invalid_rect # 需要重绘的脏矩形,格式:row0 col0 row1 col1
-
- declare -a range_row_cur # 光标所示同色方块相连的所有方块 行 位置
- declare -a range_col_cur # 光标所示同色方块相连的所有方块 列 位置
-
- declare -a range_row_old # 光标所示同色方块相连的所有方块 行 位置备份
- declare -a range_col_old # 光标所示同色方块相连的所有方块 列 位置备份
-
- declare player_shoot=bc_click # 函数指针,玩家击键后,调用游戏逻辑的处理函数
-
- #-----------------------------------------------------------
- # 函数定义
-
- # 设备控制相关初始化
- function Init()
- {
- # ----------------------
- # 初始化游戏配置
-
- # 记录主线程pid
- pid=$$
-
- # 保存stty配置
- stty_save=$(stty -g)
-
- # 清屏
- clear
-
- # 获取显示信息
- width=$(tput cols)
- height=$(tput lines)
-
- # 关闭入出回显
- stty -echo
-
- # 关闭光标
- tput civis
-
- # 开启大小写case比较的开关
- shopt -s nocasematch
- }
-
- # 设置光标在地图中的相对位置
- # 参数一:相对行
- # 参数二:相对列
- function Put_cursor_in_map()
- {
- tput cup $(( MAP_AREA_TOP + $1 )) $(( MAP_AREA_LEFT + $2 ))
- }
-
- # 退出清理函数
- function Exit_clear()
- {
- # 清屏
- clear
-
- # 恢复输入回显
- # stty echo
-
- # 恢复大小写case比较的开关
- shopt -u nocasematch
-
- # 恢复stty配置
- stty $stty_save
- }
-
- # 帧函数
- function Frame()
- {
- :
- }
-
- # 刷新
- function Refresh()
- {
- # 显示提示信息
- Show_message
-
- # 刷新地图
- Refresh_map
-
- # 处理显示光标所在位置的连接在一起的同色区域
- Refresh_range
-
- # 显示光标
- Show_cursor
-
- # 处理光标所在块的显示
- # Show_cursor_block
- }
-
- # 刷新地图
- function Refresh_map()
- {
- local i
- local j
- local bit1
- local bit2
-
- # 得到最新显示页的数据
- memory_page1=( ${bc_map[@]} )
-
- # 关闭光标
- # tput civis
-
- # 逐位扫描
- for (( i = ${invalid_rect[0]}; i < ${invalid_rect[2]}; ++i ))
- do
- for (( j = ${invalid_rect[1]}; j < ${invalid_rect[3]}; ++j ))
- do
- bit1=$(array_get memory_page1 BC_COL $i $j)
- bit2=$(array_get memory_page2 BC_COL $i $j)
-
- # echo "refresh $i $j $bit1 $bit2" >> refresh.txt
-
- # 若两个位置数据一样,不需要更新显示
- if (( ! (bit1 ^ bit2) ))
- then
- continue
- fi
-
- # 根据位置更新该位的字符
- Print_block $i $j "${BC_IMAGE[$bit1]}"
-
- # echo "+ OK ${BC_IMAGE[$bit1]}" >> refresh.txt
- done
- done
-
- # 开启光标
- # tput cnorm
-
- # 将当前显示页数据备份到备份页
- memory_page2=( ${memory_page1[@]} )
-
- # 脏矩形置0
- invalid_rect=( 0 0 0 0 )
- }
- #------------------------------------------------------------------
- #------------------------------------------------------------------
- # 刷新
- # 尝试用内存图
- # declare -a memory_map
- # function Refresh2()
- # {
- # local i
- # local j
- # local bit1
- # local bit2
- #
- # # 显示提示信息
- # Show_message
- #
- # # 得到最新显示页的数据
- # memory_page1=( ${bc_map[@]} )
- #
- # # 关闭光标
- # # tput civis
- #
- # # 逐位扫描
- # for (( i = ${invalid_rect[0]}; i < ${invalid_rect[2]}; ++i ))
- # do
- # for (( j = ${invalid_rect[1]}; j < ${invalid_rect[3]}; ++j ))
- # do
- # bit1=$(array_get memory_page1 BC_COL $i $j)
- # bit2=$(array_get memory_page2 BC_COL $i $j)
- #
- # # 若两个位置数据一样,不需要更新显示
- # if (( ! (bit1 ^ bit2) ))
- # then
- # continue
- # fi
- #
- # # 根据位置更新该位的字符
- # Print_block_memory $i $j "\${BC_IMAGE[$bit1]}"
- # done
- #
- # Print_line $i "$(array_get_row memory_map BC_COL $i)"
- # done
- #
- # # 开启光标
- # # tput cnorm
- #
- # # 将当前显示页数据备份到备份页
- # memory_page2=( ${memory_page1[@]} )
- #
- # # 脏矩形置0
- # invalid_rect=( 0 0 0 0 )
- #
- # # 处理显示光标所在位置的连接在一起的同色区域
- # Show_range
- #
- # # 恢复坐标
- # Show_cursor
- #
- # # 处理光标所在块的显示
- # Show_cursor_block
- # }
- #
- # # 在内存图上打印
- # function Print_block_memory()
- # {
- # local i
- # local j
- # local x
- # local y
- #
- # x=$(( BLOCK_HEIGHT * $1 ))
- # y=$(( BLOCK_WIDTH * $2 ))
- #
- # for (( i = 0; i < BLOCK_HEIGHT; ++i ))
- # do
- # for (( j = 0; j < BLOCK_WIDTH; ++j ))
- # do
- # array_set memory_map BC_COL $(( y + $i )) $(( x + $j )) "$3"
- # done
- # done
- #
- # # echo ${memory_map[@]} >> xx.txt
- # }
- #
- # function Print_line()
- # {
- # local i
- # local x
- #
- # x=$(( BLOCK_HEIGHT * $1 ))
- #
- # for (( i = 0; i < BLOCK_HEIGHT; ++i ))
- # do
- # Put_cursor_in_map $(( x + i )) 0
- # echo -ne "${2}"
- # done
- # }
-
- #------------------------------------------------------------------
- #------------------------------------------------------------------
-
- # 显示提示信息
- function Show_title()
- {
- local title_left
- local title_top
-
- title_top=1
- title_left=$(( MAP_AREA_LEFT - 5 ))
-
- tput cup $(( title_top++ )) $title_left
- echo -ne '_|_|_| _| _|_| _|_|_| _| _| _|_|_|'
- tput cup $(( title_top++ )) $title_left
- echo -ne '_| _| _| _| _| _| _| _| _| '
- tput cup $(( title_top++ )) $title_left
- echo -ne '_|_|_| _| _| _| _| _|_| _|_| '
- tput cup $(( title_top++ )) $title_left
- echo -ne '_| _| _| _| _| _| _| _| _|'
- tput cup $(( title_top++ )) $title_left
- echo -ne '_|_|_| _|_|_|_| _|_| _|_|_| _| _| _|_|_| '
-
-
- # tput cup $(( title_top++ )) $title_left
- # echo -ne '@@@@@@@ @@@ @@@@@@ @@@@@@@ @@@ @@@ @@@@@@ '
- # tput cup $(( title_top++ )) $title_left
- # echo -ne '@@@@@@@@ @@@ @@@@@@@@ @@@@@@@@ @@@ @@@ @@@@@@@ '
- # tput cup $(( title_top++ )) $title_left
- # echo -ne '@@! @@@ @@! @@! @@@ !@@ @@! !@@ !@@ '
- # tput cup $(( title_top++ )) $title_left
- # echo -ne '!@ @!@ !@! !@! @!@ !@! !@! @!! !@! '
- # tput cup $(( title_top++ )) $title_left
- # echo -ne '@!@!@!@ @!! @!@ !@! !@! @!@@!@! !!@@!! '
- # tput cup $(( title_top++ )) $title_left
- # echo -ne '!!!@!!!! !!! !@! !!! !!! !!@!!! !!@!!! '
- # tput cup $(( title_top++ )) $title_left
- # echo -ne '!!: !!! !!: !!: !!! :!! !!: :!! !:!'
- # tput cup $(( title_top++ )) $title_left
- # echo -ne ':!: !:! :!: :!: !:! :!: :!: !:! !:! '
- # tput cup $(( title_top++ )) $title_left
- # echo -ne ' :: :::: :: :::: ::::: :: ::: ::: :: ::: :::: :: '
- # tput cup $(( title_top++ )) $title_left
- # echo -ne ':: : :: : :: : : : : : :: :: : : ::: :: : : '
- }
-
- # 显示提示信息
- function Show_message()
- {
- local msg_left
- local msg_top
-
- msg_left=$(( MAP_AREA_LEFT + BLOCK_WIDTH * BC_COL + 5 ))
- msg_top=$((MAP_AREA_TOP - 1 ))
-
- # tput civis
- tput cup $(( msg_top++ )) $msg_left
- echo -n "Score : $score "
- tput cup $(( msg_top++ )) $msg_left
- echo -n "Top Score : $score_top "
- tput cup $(( msg_top++ )) $msg_left
- echo -n "Preview : $score_shoot "
-
- (( msg_top++ ))
- tput cup $(( msg_top++ )) $msg_left
- echo -n "Stage : $stage_round "
- tput cup $(( msg_top++ )) $msg_left
- echo -n "Stage bonus : $bonus_stage "
- tput cup $(( msg_top++ )) $msg_left
- echo -n "Stage goal : $score_goal "
-
- (( msg_top++ ))
- tput cup $(( msg_top++ )) $msg_left
- echo -n "Operation"
- tput cup $(( msg_top++ )) $msg_left
- echo -n "Up : $KEY_UP"
- tput cup $(( msg_top++ )) $msg_left
- echo -n "Down : $KEY_DOWN"
- tput cup $(( msg_top++ )) $msg_left
- echo -n "Left : $KEY_LEFT"
- tput cup $(( msg_top++ )) $msg_left
- echo -n "Right : $KEY_RIGHT"
- tput cup $(( msg_top++ )) $msg_left
- echo -n "Shoot : $KEY_SHOOT"
- tput cup $(( msg_top++ )) $msg_left
- echo -n "Quit : $KEY_EXIT"
-
- # tput cnorm
- }
-
- # 绘制边界
- function Show_border()
- {
- local i
- local border_h
- local border_v
- local r
- local c
- local c2
-
- # Put_cursor_in_map $i -1
- border_h=""
- for (( i = 0; i < map_area_width + 2; ++i ))
- do
- border_h="$border_h$MAP_BORDER_H"
- done
-
- r=$(( MAP_AREA_TOP ))
- c=$(( MAP_AREA_LEFT ))
- echo -ne "${ESC}[${r};${c}H${border_h}"
-
- r=$(( MAP_AREA_TOP + map_area_height + 1 ))
- echo -ne "${ESC}[${r};${c}H${border_h}"
-
- c2=$(( MAP_AREA_LEFT + map_area_width + 1 ))
- for (( r = MAP_AREA_TOP + 1; r < MAP_AREA_TOP + map_area_height + 1; ++r ))
- do
- echo -ne "${ESC}[${r};${c}H${MAP_BORDER_V}${ESC}[${r};${c2}H${MAP_BORDER_V}"
- done
- }
-
- # 方块消失动画
- # 参数一:方块行
- # 参数二:方块列
- # 拟按顺时针方向一块一块地消失
- function Animation_block_disappear()
- {
- local block_row # 当前块所在的初始行(block_top)
- local block_col # 当前块所在的初始列(block_left)
- local char
- local i
- local r # 当前处理行
- local c # 当前处理列
- local top # 旋转后的厚度:顶
- local bottom # 旋转后的厚度:底
- local left # 旋转后的厚度:左边界
- local right # 旋转后的厚度:右边界
- local dir # 方向:0 1 2 3 -> 右 下 左 上(顺时针)
-
- block_row=$(( BLOCK_HEIGHT * $1 ))
- block_col=$(( BLOCK_WIDTH * $2 ))
- char="${BC_IMAGE[0]}"
-
- dir=0
- top=0
- bottom=0
- left=0
- right=0
- c=0
- r=0
- i=$(( BLOCK_HEIGHT * BLOCK_WIDTH ))
-
- while true
- do
- echo "$i $dir $r $c $top $bottom $right $left" >> dir.txt
- Put_cursor_in_map $(( block_row + r )) $(( block_col + c ))
- echo -ne "$char"
-
- # 若所有块处理完毕,就结束循环
- if (( i <= 1 ))
- then
- break
- fi
-
- case $dir in
- 0) # 右行
- if (( c >= (BLOCK_WIDTH - right - 1) ))
- then
- (( dir = (dir + 1) % 4 ))
- (( ++top ))
- continue
- fi
-
- (( ++c ))
- ;;
-
- 1) # 下行
- if (( r >= (BLOCK_HEIGHT - bottom - 1) ))
- then
- (( dir = (dir + 1) % 4 ))
- (( ++right ))
- continue
- fi
-
- (( ++r ))
- ;;
- 2) # 左行
- if (( c <= left ))
- then
- (( dir = (dir + 1) % 4 ))
- (( ++bottom ))
- continue
- fi
-
- (( --c ))
- ;;
-
- 3) # 上行
- if (( r <= top ))
- then
- (( dir = (dir + 1) % 4 ))
- (( ++left ))
- continue
- fi
-
- (( --r ))
- ;;
- esac
-
- (( --i ))
-
- # 动画延时
- sleep 0.01
- done
- }
-
- # 旧区域消失动画
- function Animation_old_range_disappear()
- {
- local i
-
- # 除最后一个块外,以后台形式播放消失动画
-
- for (( i = 0; i < ${#range_row_old[@]}; ++i ))
- do
- Animation_block_disappear ${range_row_old[$i]} ${range_col_old[$i]}
- done
-
- # Animation_block_disappear ${range_row_old[$i]} ${range_col_old[$i]}
- }
-
- # 显示指定位置的一个方块
- # 参数一:方块 行 位置
- # 参数二:方块 列 位置
- # 参数三:方块填充字符
- function Print_block()
- {
- local i
- local block_row
- local block_col
- local block_line=""
-
- block_row=$(( BLOCK_HEIGHT * $1 ))
- block_col=$(( BLOCK_WIDTH * $2 ))
-
- for (( i = 0; i < BLOCK_WIDTH; ++i ))
- do
- block_line="$block_line$3"
- done
-
- for (( i = 0; i < BLOCK_HEIGHT; ++i ))
- do
- Put_cursor_in_map $(( block_row + i )) $block_col
- echo -ne "$block_line"
- done
- }
-
- # 将光标所在位置的方块特殊显示
- function Show_cursor_block()
- {
- local char
-
- # 恢复旧位置
- char="${BC_IMAGE[$(array_get memory_page1 BC_COL $row_old $col_old)]}"
- Print_block $row_old $col_old "$char"
-
- # 显示新位置
- char="${EFFECT_CURSOR}${BC_IMAGE[$(array_get memory_page1 BC_COL $row_cur $col_cur)]}"
- Print_block $row_cur $col_cur "$char"
-
- # 记录新位置为旧位置
- # row_old=$row_cur
- # col_old=$col_cur
- }
-
- # 判断光标是否在旧的特殊显示区域中
- # 只需要判断前后两个位置的值是不是一样即可
- # 是则返回 0
- # 否则返回 1
- function Is_cursor_in_old_range()
- {
- local i
- local old_val
- local new_val
-
- new_val=$(array_get memory_page1 BC_COL $row_cur $col_cur)
- if [ $new_val -eq 0 ]
- then
- return 1
- fi
-
- old_val=$(array_get memory_page1 BC_COL $row_old $col_old)
- if [ $new_val -ne $old_val ]
- then
- return 1
- fi
-
- return 0
-
- # 原函数
- # if [ ${#range_row_old[@]} -eq 0 ]
- # then
- # return 1
- # fi
- #
- # if [ $(array_get memory_page1 BC_COL $row_cur $col_cur) -eq 0 ]
- # then
- # return 1
- # fi
- #
- # for ((i = 0; i < ${#range_row_old[@]}; ++i ))
- # do
- # if [ $row_cur -eq ${range_row_old[$i]} -a $col_cur -eq ${range_col_old[i]} ]
- # then
- # return 0
- # fi
- # done
- #
- # return 1
- }
-
- # 判断光标是否在特殊显示区域中
- # 是则返回 0
- # 否则返回 1
- # function Is_cursor_in_range()
- # {
- # local i
- #
- # if [ ${#range_row_cur[@]} -eq 0 ]
- # then
- # return 1
- # fi
- #
- # if [ $(array_get memory_page1 BC_COL $row_cur $col_cur) -eq 0 ]
- # then
- # return 1
- # fi
- #
- # for ((i = 0; i < ${#range_row_cur[@]}; ++i ))
- # do
- # if [ $row_cur -eq ${range_row_cur[$i]} -a $col_cur -eq ${range_col_cur[i]} ]
- # then
- # return 0
- # fi
- # done
- #
- # return 1
- # }
-
- # 更新区域信息
- function Refresh_range_info()
- {
- # 重新计算
- bc_calcualate_range $row_cur $col_cur
-
- range_row_old=( ${range_row_cur[@]} )
- range_col_old=( ${range_col_cur[@]} )
- range_row_cur=( ${bc_range_row[@]} )
- range_col_cur=( ${bc_range_col[@]} )
- }
-
- # 清除特殊显示光标所示方块相连的同色方块
- function Clear_range()
- {
- local char
- local i
-
- # 若区域数据为空,则不需要处理
- if [ ${#range_row_old[@]} -le 0 ]
- then
- return
- fi
-
- # 恢复旧块
- for (( i = 0; i < ${#range_row_old[@]}; ++i ))
- do
- char="${BC_IMAGE[$(array_get memory_page1 BC_COL ${range_row_old[$i]} ${range_col_old[$i]})]}"
- Print_block ${range_row_old[$i]} ${range_col_old[$i]} "$char"
- done
- }
-
- # 特殊显示光标所示方块相连的同色方块
- function Show_range()
- {
- local char
- local i
-
- # 显示新块
- char="${EFFECT_CONNECTION}${BC_IMAGE[$(array_get memory_page1 BC_COL $row_cur $col_cur)]}"
- for (( i = 0; i < ${#range_row_cur[@]}; ++i ))
- do
- Print_block ${range_row_cur[$i]} ${range_col_cur[$i]} "$char"
- done
- }
-
- # 更新相连区域的显示
- # 参数一:是否强制刷新
- # 1: 强制刷新,不判断光标位置
- # 0: 判断光标位置变动情况来刷新
- function Refresh_range()
- {
- local force=1
-
- # 不传入参数,默认为 false
- if [ -z "$1" ] || [ $1 -eq 0 ]
- then
- force=0
- fi
-
- # 若当前光标移动后还在本区域内
- # 则不需要擦除旧块
- if [ $force -eq 1 ] || ! Is_cursor_in_old_range
- then
- Clear_range
- fi
-
- # 显示
- Show_range
- }
-
- # 键盘输入响应函数
- function Input()
- {
- while true
- do
- read -s -n 1 key
-
- case $key in
- $KEY_UP) Player_up ;;
- $KEY_DOWN) Player_down ;;
- $KEY_LEFT) Player_left ;;
- $KEY_RIGHT) Player_right ;;
- $KEY_SHOOT) Player_shoot ;;
- $KEY_PAUSE) Game_pause_switch ;;
- $KEY_EXIT) Game_exit ;;
- esac
-
- if (( exiting == 1 ))
- then
- break
- fi
- done
- }
-
- # 游戏坐标:
- # +-----> x
- # |
- # |
- # |
- # v
- # y
-
- # 玩家上移
- function Player_up()
- {
- if (( paused == 1 ))
- then
- return
- fi
-
- if (( row_cur <= 0 ))
- then
- return
- fi
-
- row_old=$row_cur
- col_old=$col_cur
- (( --row_cur ))
- Move_cursor
- }
-
- # 玩家下移
- function Player_down()
- {
- if (( paused == 1 ))
- then
- return
- fi
-
- if (( row_cur >= limit_cursor_row ))
- then
- return
- fi
-
- row_old=$row_cur
- col_old=$col_cur
- (( ++row_cur ))
- Move_cursor
- }
-
- # 玩家左移
- function Player_left()
- {
- if (( paused == 1 ))
- then
- return
- fi
-
- if (( col_cur <= 0 ))
- then
- return
- fi
-
- row_old=$row_cur
- col_old=$col_cur
- (( --col_cur ))
- Move_cursor
- }
-
- # 玩家右移
- function Player_right()
- {
- if (( paused == 1 ))
- then
- return
- fi
-
- if (( col_cur >= limit_cursor_col ))
- then
- return
- fi
-
- row_old=$row_cur
- col_old=$col_cur
- (( ++col_cur ))
- Move_cursor
- }
-
- # 光标显示函数
- function Show_cursor()
- {
- # tput cup $(( row_cur * BLOCK_WIDTH )) $(( col_cur * BLOCK_HEIGHT ))
-
- local char
-
- # 显示新位置
- char="${EFFECT_CURSOR}${BC_IMAGE[$(array_get memory_page1 BC_COL $row_cur $col_cur)]}"
- Print_block $row_cur $col_cur "$char"
- }
-
- # 光标移除函数
- function Clear_cursor()
- {
- local char
-
- # 恢复旧位置
- char="${BC_IMAGE[$(array_get memory_page1 BC_COL $row_old $col_old)]}"
- Print_block $row_old $col_old "$char"
- }
-
- # 移动光标操作处理函数
- function Move_cursor()
- {
- # 若光标不在新生成的区域内则需要刷新获得信息
- if ! Is_cursor_in_old_range
- then
- Refresh_range_info
- fi
-
- Clear_cursor
- Refresh_range
- Show_cursor
-
- Refresh_shoot_score
- }
-
- # 玩家开火
- function Player_shoot()
- {
- # 若当前区域只有一个方块,不允许消除
- if [ ${#range_row_cur[@]} -le 1 ]
- then
- return
- fi
-
- # 调用游戏逻辑处理
- $player_shoot $row_cur $col_cur
-
- # 增加分数
- Increase_score
-
- # # 需要重绘的区域为 顶行,最小列,最大行,最大列
- # # 判断作为容错处理(本不应该出现的情况,但可能脚本运行速度慢,并发的情况下出现)
- # # echo "range_row_cur: ${range_row_cur[@]}" >> debug.txt
- # # echo "range_col_cur: ${range_col_cur[@]}" >> debug.txt
- # if [ -z "${range_row_cur[@]}" ] || [ -z "${range_col_cur[@]}" ]
- # then
- # invalid_rect=( 0 0 $BC_ROW $BC_COL )
- # else
- # invalid_rect=( 0 $(min ${range_col_cur[@]}) $(( $(max ${range_row_cur[@]}) + 1 )) $(( $(max ${range_col_cur[@]}) + 1 )) )
- # fi
-
- # 在有空列需要移动的时候,需要重绘的区域为
- # 顶行,最小列,地图最大行,地图最大列
- invalid_rect=( 0 $(min ${range_col_cur[@]}) $BC_ROW $BC_COL )
- # invalid_rect=( 0 0 $BC_ROW $BC_COL )
-
- # 刷新区域信息
- Refresh_range_info
-
- # 插入旧区域消失动画
- Animation_old_range_disappear
-
- Refresh
-
- Refresh_shoot_score
-
- # 检测是否结束当前关卡
- if ! Is_stage_over
- then
- return
- fi
-
- # 清算关卡
- Clear_stage
-
- # 在结束当前关卡的情况下,检测是否游戏结束
- # 若未结束,则开启新关卡
- # 否则,结束游戏
- if ! Is_game_over
- then
- # 新关卡
- New_stage
- else
- # 结束游戏
- Game_over
- fi
- }
-
- # 计算当前消除方块能得到的分数
- function Calcualate_score_shoot()
- {
- local block_number
-
- block_number=${#range_row_cur[@]}
-
- case $block_number in
- 1) score_shoot=0 ;;
- [2-4]) score_shoot=$(( block_number * 20 )) ;;
- *) score_shoot=$(( (block_number - 4 ) * 25 + 4 * 20 )) ;;
- esac
- }
-
- # 刷新预览分数
- function Refresh_shoot_score()
- {
- Calcualate_score_shoot
- Show_message
- }
-
- # 刷新分数,并根据当前分数来确认是否更新最高分
- function Increase_score()
- {
- (( score += score_shoot ))
-
- if (( score > score_top ))
- then
- score_top=$score
- Save_top_score
- fi
- }
-
- # 测试当前轮次是否结束
- function Is_stage_over()
- {
- bc_check_stage_over
-
- return $?
- }
-
- # 测试游戏是否结束
- # 是则返回 0
- # 否则返回 1
- function Is_game_over()
- {
- if (( score < score_goal ))
- then
- return 0
- fi
-
- return 1
- }
-
- # 计算过关分数
- function Calcualate_score_goal()
- {
- # 这个过关分数需要委认真地计算呀,这里就只是简单做着玩,就不那么认真了
-
- # 计算规则:按一个方块 20 分计算,一局可得 20 * BC_ROW * BC_COL 分
- # 目标就是总分的 1/4
- (( score_goal = stage_round * BC_ROW * BC_COL * 20 / 4 ))
- }
-
- # 开启新关卡
- function New_stage()
- {
- while true
- do
- bc_init
-
- # 万一初始化后就是无法操作的地图,得重新生成
- # 否则就可以继续后续操作了
- if ! Is_stage_over
- then
- break
- fi
- done
-
- memory_page1=( ${bc_map[@]} )
- memory_page2=( ${bc_map[@]/*/0} )
-
- Refresh_range_info
-
- (( ++stage_round ))
- Calcualate_score_goal
- Calcualate_score_shoot
-
- bonus_stage=$BONUS_STAGE
-
- invalid_rect=( 0 0 $BC_ROW $BC_COL )
- Refresh
- }
-
- # 清算关卡
- # 过关奖励 1000 分
- # 剩余一个方块扣除 10 分,直至 0 分
- function Clear_stage()
- {
- local r
- local c
-
- # 从上到下,从右到左地进行清算
- for (( c = BC_ROW - 1; --c; c >= 0 ))
- do
- for (( r = BC_COL - 1; --r; r >= 0 ))
- do
- # echo "---> array_get memory_page1 BC_COL $r $c)"
- if [ $(array_get memory_page1 BC_COL $r $c) -eq 0 ]
- then
- continue
- fi
-
- Animation_block_disappear $r $c
- (( bonus_stage -= BONUS_DECREASE ))
- (( bonus_stage = (bonus_stage < 0) ? 0 : bonus_stage ))
-
- Show_message
- sleep 0.1
- done
- done
- }
-
- # 读取最高分数
- function Read_top_score()
- {
- # 若没有最高分数记录则最高分为0
- if [ ! -f "$FILE_TOP_SCORE" ]
- then
- score_top=0
- return
- fi
-
- # 读取文件内容,若不是有效数字则设置最高分为0
- score_top=$(cat "$FILE_TOP_SCORE")
- if [ "${score_top//[[:digit:]]}" != "" ]
- then
- score_top=0
- fi
- }
-
- # 保存最高分数
- function Save_top_score()
- {
- echo $score_top > "$FILE_TOP_SCORE"
- }
-
- # 获取整个游戏宽度(游戏区+信息区)
- function Game_width()
- {
- # 估计信息区最长信息长度
- local estimate=0
-
- echo $(( MAP_AREA_LEFT + BLOCK_WIDTH * BC_COL + 5 + estimate ))
- }
-
- # 获取整个游戏高度(游戏区+信息区)
- function Game_height()
- {
- # 认为信息区高度不超过游戏区高度
- echo $(( MAP_AREA_TOP + BLOCK_HEIGHT * BC_ROW ))
- }
-
- # 于游戏区中央显示信息
- # 参数一:信息内容
- function Game_tip()
- {
- local msg=$1
- # local len=${#msg}
- local top
- local left
- local h_border
-
- hborder="+--$(echo $msg | sed 's/./-/g')--+"
- msg="| $msg |"
-
- top=$(( $(Game_height) / 2 + 2 ))
- left=$(( $(Game_width) / 2 ))
-
- tput cup $(( top++ )) $left
- echo -ne "$hborder"
- tput cup $(( top++ )) $left
- echo -ne "$msg"
- tput cup $(( top++ )) $left
- echo -ne "$hborder"
- }
-
- # 切换游戏暂停状态
- function Game_pause_switch()
- {
- # 不过这个游戏不需要暂停,所以不允许用户操作
- return
-
- (( paused = paused == 1 ? 0 : 1 ))
- }
-
- # 开始游戏
- function Game_start()
- {
- Game_init
- sub_pid=$!
- }
-
- # 游戏结束
- function Game_over()
- {
- # 停止响应用户的游戏输入
- paused=1
-
- # 于游戏区中央显示游戏结束,按退出键返回
- Game_tip "Game Over! Press '$KEY_EXIT' to quit."
- }
-
- # 退出游戏
- function Game_exit()
- {
- exiting=1
- }
-
- # 游戏初始化
- function Game_init()
- {
- row_cur=0 # 当前行
- col_cur=0 # 当前列
- row_old=0 # 旧行
- col_old=0 # 旧列
-
- paused=0 # 设置非暂停状态
- exiting=0 # 设置非退出状态
-
- stage_round=0 # 关卡数初始化
-
- score=0 # 当前游戏分数
- Read_top_score # 读取最高分
-
- limit_cursor_row=$(( BC_ROW - 1 )) # 光标最大限制行数
- limit_cursor_col=$(( BC_COL - 1 )) # 光标最大限制列数
-
- map_area_width=$(( BLOCK_WIDTH * BC_COL )) # 游戏区域宽
- map_area_height=$(( BLOCK_HEIGHT * BC_ROW )) # 游戏区域高
-
- # 如果屏幕大小不够,则提示退出(不考虑信息显示区,因为没有信息提示也一样可以玩)
- if [ $width -lt $(( MAP_AREA_LEFT + map_area_width )) -o $height -lt $(( MAP_AREA_TOP + map_area_height )) ]
- then
- # 停止响应用户的游戏输入
- paused=1
-
- # 提示错误信息,按退出键返回
- echo "Screen size too small! Press '$KEY_EXIT' to quit."
-
- return
- fi
-
- Show_title # 显示游戏标题
- Show_border # 显示边框
- New_stage # 开启新关卡
- }
-
- # 主函数
- function Main()
- {
- Init
- Game_start
- Input
- Exit_clear
- }
-
- #-----------------------------------------------------------
- # 游戏逻辑部分
- # 游戏名:block_clear(消方块)
- # 游戏逻辑函数、变量均以 bc 为前缀
-
- # 游戏 map,拟二维数组
- declare -a bc_map
-
- # map 的行列
- declare -r BC_ROW=8
- declare -r BC_COL=8
-
- # map 中的方块类型种类数量,及数字对应的显示字符
- declare -r BC_TYPE_NUM=6
- declare -ar BC_IMAGE=(" ${NOR}" "${HIG}O${NOR}" "${HIY}#${NOR}" "${HIR}H${NOR}" "${HIM}M${NOR}" "${HIC}@${NOR}" "${HIW}X${NOR}")
- # declare -ar BC_IMAGE=(" ${NOR}" "${BHG} ${NOR}" "${BHIY} ${NOR}" "${BHIR} ${NOR}" "${BHIM} ${NOR}" "${BHIC} ${NOR}" "${BHIW}.${NOR}")
- # 测试用的显示字符declare -ar BC_IMAGE=(" ${NOR}" "${HIG}1${NOR}" "${HIY}2${NOR}" "${HIR}3${NOR}" "${HIM}4${NOR}" "${HIC}5${NOR}" "${HIW}6${NOR}")
-
- # 用于存放相连方块的坐标数组
- declare -a bc_range_row
- declare -a bc_range_col
-
- # bc 初始化
- function bc_init()
- {
- local i
- local j
-
- array_init bc_map $BC_ROW $BC_COL
- for (( i = 0; i < BC_ROW; ++i ))
- do
- for (( j = 0; j < BC_COL; ++j ))
- do
- array_set bc_map BC_COL $i $j $(( $(random $BC_TYPE_NUM) + 1 ))
- done
- done
- }
-
- # bc 帧逻辑
- function bc_frame()
- {
- :
- }
-
- # bc 数字地图输出
- function bc_show_map()
- {
- local map=""
- local i
-
- for (( i = 0; i < BC_ROW; ++i ))
- do
- map="$map\n$(array_get_row bc_map $BC_COL $i)"
- done
-
- # map=$(echo "$map" | tr $(seq -s "" 0 $BC_TYPE_NUM) "$BC_IMAGE")
- echo "${map}"
- }
-
- # 将 bc 数字图渲染为文字图
- function bc_rander_map()
- {
- local map=""
- local i
-
- for (( i = 0; i < BC_ROW; ++i ))
- do
- map="$map\n$(array_get_row bc_map $BC_COL $i)"
- done
-
- # xxx map=$(echo "$map" | tr $(seq -s "" 0 $BC_TYPE_NUM) "${BC_IMAGE[@]}")
- echo "${map}"
- }
-
- # 点击事件响应函数
- # 参数一:行
- # 参数二:列
- function bc_click()
- {
- local i
- local xxstr
- # array_set bc_map $BC_COL $1 $2 0
-
- for (( i = 0; i < ${#bc_range_row[@]}; ++i ))
- do
- array_set bc_map $BC_COL ${bc_range_row[$i]} ${bc_range_col[$i]} 0
- done
-
- # 调整地图
- bc_adjust_map
-
- # 重新计算区域
- # bc_calcualate_range $1 $2
- }
-
- # 调整地图
- # 空格上方的方块下落
- # 空列右方的方块左移
- # 需要调整的区域为根据当前区域计算的 顶行,最小列,顶行,最大列
- function bc_adjust_map()
- {
- local i
- local j
- local k
- local value
- local bottom_zero_row
- local empty_cols
- local flag_moved
-
- # echo "bc_range_col: ${bc_range_col[@]}" >> debug.txt
- # 容错判断(本不应该出现的情况,但可能脚本运行速度慢,并发的情况下出现)
- # if [ -z "${bc_range_col[*]}" ]
- # then
- # return
- # fi
-
- # 对每一列处理下落
- for (( j = $(min ${bc_range_col[@]}); j <= $(max ${bc_range_col[@]}); ++j ))
- do
- # 从底层往上走,并记录最下一个 0 元素的位置
- # 若发现非 0 元素,则移动到最下一个 0 位置上,并更新最下一个 0 位置的指针
- bottom_zero_row=$(( BC_ROW - 1 ))
- for (( i = $bottom_zero_row; i >= 0; --i ))
- do
- value=$(array_get bc_map BC_COL $i $j)
-
- # 若此位置为 0,则不处理
- if [ $value -eq 0 ]
- then
- continue
- fi
-
- # 非 0 元素
- # 判断下方是否有空位置
- # 若无则调整最下非 0 位置指针
- if [ $bottom_zero_row -eq $i ]
- then
- (( --bottom_zero_row ))
- continue
- fi
-
- # 非 0 元素需要下移
- array_set bc_map BC_COL $bottom_zero_row $j $value
- array_set bc_map BC_COL $i $j 0
-
- # 非 0 元素
- (( --bottom_zero_row ))
- done
- done
-
- # 测试是否有空列,若无则返回
- empty_cols=( $(bc_get_empty_col) )
- if [ ${#empty_cols[@]} -eq 0 ]
- then
- return
- fi
-
- # 有空列,需要移动
-
- # 空列最后一个元素设置为最大列,用于简化控制循环
- empty_cols=( ${empty_cols[@]} $BC_COL )
-
- # 得到当前空列
- k=0
- cur_zero_col=${empty_cols[$k]}
- (( ++k ))
-
- flag_moved=0
-
- # 处理每一列
- for (( j = cur_zero_col + 1; j < BC_COL; ++j ))
- do
- # 若 j 大于行于下一个 0 列,说明当前处理的列也是空列
- # 则移动 k,跳过当前列处理
- # 上面给 empty_cols 加上最后一个元素就用于这里简化控制,不需要判断 k 是否到达末尾了
- if [ $j -ge ${empty_cols[$k]} ]
- then
- (( ++k ))
- continue
- fi
-
- # 处理每一行,进行移动
- # 由下自上倒着处理,当遇到 0 时,这一列可以提前结束处理
- for (( i = BC_ROW - 1; i >= 0; --i ))
- do
- # 得到需要移动的数据,若为0,则提前结束
- value=$(array_get bc_map BC_COL $i $j)
- if [ $value -eq 0 ]
- then
- break
- fi
-
- # 移动
- array_set bc_map BC_COL $i $cur_zero_col $value
-
- # 当前处理列本来需要设置为 0 的,在最后的统一处理,这里略过
- done
-
- # 此列处理完后,下一列就做为当前 0 列
- (( ++ cur_zero_col ))
-
- # 标记有移动过
- flag_moved=1
- done
-
- # 若没有移动过,则不需要做置 0 处理
- if [ $flag_moved -ne 1 ]
- then
- return
- fi
-
- # 对当前 0 列之后的列做置 0 处理
- for (( j = cur_zero_col; j < BC_COL; ++j ))
- do
- # 处理每一行
- # 由下自上倒着处理,当遇到 0 时,这一列可以提前结束处理
- for (( i = BC_ROW - 1; i >= 0; --i ))
- do
- # 得到需要移动的数据,若为0,则提前结束
- value=$(array_get bc_map BC_COL $i $j)
- if [ $value -eq 0 ]
- then
- break
- fi
-
- # 移动
- array_set bc_map BC_COL $i $j 0
- done
- done
- }
-
- # 测试是否需要移动操作
- # 返回所有的空列的列号组成的字串,以空格分隔,可以使用 var=( $(function) ) 方式得到结果
- # 判断结果长度来确定是否有空列及是否需要移动
- function bc_get_empty_col()
- {
- # 在此,只需要判断最底列是否有 0 值即可,并不需要真的判断空列
-
- local bottom_row
- local j
- local ret
-
- ret=""
- (( bottom_row = BC_ROW - 1 ))
- for (( j = 0; j < BC_COL; ++j ))
- do
- if [ $(array_get bc_map BC_COL $bottom_row $j) -ne 0 ]
- then
- continue
- fi
-
- ret="$ret $j"
- done
-
- echo "$ret"
- }
-
- # 计算输入位置所示广场相连的方块坐标,结果保存于数组中备用
- # 参数一:当前行
- # 参数二:当前列
- # local_map_temp 为本函数内部临时使用的一个数组
- declare -a local_map_temp
- function bc_calcualate_range()
- {
- local cur_row=$1
- local cur_col=$2
- local queue_tail
- local queue_head
- local i
- local j
- local value=$(array_get bc_map BC_COL $cur_row $cur_col)
-
- # bc_range_row=( )
- # bc_range_col=( )
- #
- # bc_range_row[0]=$cur_row
- # bc_range_col[0]=$cur_col
- # 换成这样
- bc_range_row=( $cur_row )
- bc_range_col=( $cur_col )
- queue_tail=0
- queue_head=0
-
- if [ $value -eq 0 ]
- then
- return
- fi
-
- local_map_temp=( ${bc_map[@]} )
- array_set local_map_temp BC_COL $cur_row $cur_col 0
-
- while true
- do
- if [ $queue_head -gt $queue_tail ]
- then
- break
- fi
-
- cur_row=${bc_range_row[$queue_head]}
- cur_col=${bc_range_col[$queue_head]}
- # array_set local_map_temp BC_COL $cur_row $cur_col $(( BC_TYPE_NUM + 1 ))
- # array_set local_map_temp BC_COL $cur_row $cur_col 0
- (( ++queue_head ))
-
- # 朝上找
- if [ $(( cur_row - 1 )) -ge 0 ] && [ $value -eq $(array_get local_map_temp BC_COL $(( cur_row - 1 )) $cur_col) ]
- then
- (( ++queue_tail ))
- bc_range_row[queue_tail]=$(( cur_row - 1 ))
- bc_range_col[queue_tail]=$cur_col
-
- # 标记访问过
- array_set local_map_temp BC_COL ${bc_range_row[queue_tail]} ${bc_range_col[queue_tail]} 0
- fi
-
- # 朝下找
- if [ $(( cur_row + 1 )) -lt $BC_ROW ] && [ $value -eq $(array_get local_map_temp BC_COL $(( cur_row + 1 )) $cur_col) ]
- then
- (( ++queue_tail ))
- bc_range_row[queue_tail]=$(( cur_row + 1 ))
- bc_range_col[queue_tail]=$cur_col
-
- # 标记访问过
- array_set local_map_temp BC_COL ${bc_range_row[queue_tail]} ${bc_range_col[queue_tail]} 0
- fi
-
- # 朝左找
- if [ $(( cur_col - 1 )) -ge 0 ] && [ $value -eq $(array_get local_map_temp BC_COL $cur_row $(( cur_col - 1 )) ) ]
- then
- (( ++queue_tail ))
- bc_range_row[queue_tail]=$cur_row
- bc_range_col[queue_tail]=$(( cur_col - 1 ))
-
- # 标记访问过
- array_set local_map_temp BC_COL ${bc_range_row[queue_tail]} ${bc_range_col[queue_tail]} 0
- fi
-
- # 朝右找
- if [ $(( cur_col + 1 )) -lt $BC_COL ] && [ $value -eq $(array_get local_map_temp BC_COL $cur_row $(( cur_col + 1 )) ) ]
- then
- (( ++queue_tail ))
- bc_range_row[queue_tail]=$cur_row
- bc_range_col[queue_tail]=$(( cur_col + 1 ))
-
- # 标记访问过
- array_set local_map_temp BC_COL ${bc_range_row[queue_tail]} ${bc_range_col[queue_tail]} 0
- fi
- done
- }
-
- # 判断是否结束一轮
- # 返回 0 说明已结束 (true)
- # 返回 1 说明还未结束 (false)
- function bc_check_stage_over()
- {
- local i
- local j
- local range_row_bak
- local range_col_bak
-
- # 备份以备后续恢复
- range_row_bak=( ${bc_range_row[@]} )
- range_col_bak=( ${bc_range_col[@]} )
-
- for (( i = 0; i < BC_ROW; ++i ))
- do
- for (( j = 0; j < BC_COL; ++j ))
- do
- bc_calcualate_range $i $j
-
- # 若某一个块的连接区域大于等于 2 块,说明还可以进行游戏
- if [ ${#bc_range_row[@]} -ge 2 ]
- then
- bc_range_row=( ${range_row_bak[@]} )
- bc_range_col=( ${range_col_bak[@]} )
-
- return 1
- fi
- done
- done
-
- bc_range_row=( ${range_row_bak[@]} )
- bc_range_col=( ${range_col_bak[@]} )
-
- return 0
- }
-
- #-----------------------------------------------------------
- # 执行
-
- # Main 2>> err.txt
- Main