前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >shell写个贪吃蛇游戏

shell写个贪吃蛇游戏

作者头像
PedroQin
发布2020-03-11 13:25:58
1.1K0
发布2020-03-11 13:25:58
举报
文章被收录于专栏:WriteSimpleDemoWriteSimpleDemo

之前看到各位大神们各种语言完成贪吃蛇游戏,不禁想试试shell实现

github

https://github.com/pedroqin/shell_script

实现功能

可通过修改参数实现

  1. 难度调整(调速)
  2. 是否随分数增加加大难度(调速)
  3. 暂停及恢复
  4. 调整游戏界面大小
  5. 穿墙模式
  6. 贪吃蛇外观修改
  7. 游戏重开

实现思路

  1. 为防止整体刷新时闪屏情况发生,采用局部刷新思路,即:只刷新贪吃蛇涉及元素和鸡蛋(贪吃蛇的食物)
  2. 贪吃蛇身体的所有元素的 x,y坐标组成一个数组,贪吃蛇头部为数组第一个元素,然后依次描绘每个坐标即可完成贪吃蛇的打印
  3. 贪吃蛇头部的坐标变化根据目前的方向决定,如,向左时 x减去1,向下时 y加1
  4. 在贪吃蛇前进时,只有数组第一个元素更新为新的坐标,第二个元素获取第一个元素的坐标,第三个元素获取第二个元素的坐标...以此类推,计算前进后每个元素坐标,依次打印每个元素,并在打印完成后,清空前进之前最后一个元素,以达到贪吃蛇前进的效果
  5. 当贪吃蛇吃到鸡蛋时(头部坐标等于鸡蛋坐标),重新绘制上一步最后一个元素,以达到贪吃蛇长度增加的效果
  6. 由于只有头部为新坐标,所以只需检测头部坐标位置即可判定贪吃蛇是否撞墙或撞到自己

方法重点

  1. shell 数组操作,如赋值,获取数组长度
  2. trap 捕获 SIGTERM信号
  3. kill---$PID
  4. tput 进行画面元素的描绘与指针的隐藏
  5. 脚本内部函数后台运行时信号传递(通过flag文件实现)

代码

代码语言:javascript
复制
#!/bin/bash
###############################################
# Filename    :   snake.sh
# Author      :   PedroQin
# Email       :   constmyheart@163.com
# Date        :   2020-02-28 10:47:19
# Description :   
# Version     :   1.0.0
###############################################

# user-defined
begin_direction=left
length=50
height=15
if_go_through_the_wall=1
# snake speed
flash_time=0.40
min_flash_time=0.15
# if the snake speed up per 5 score
more_difficult=1
# when more_difficult=1, current speed = flash_time - score/5 * rising_rate > min_flash_time
rising_rate=0.03
# define the snake
snake_head="+"
snake="@"
egg="@"

##########################
# trap the kill singal
trap 'clean_env' SIGTERM
# some flag
flag_file=/tmp/snake_game.log
finish_flag=/tmp/snake_game_fail.log

max_blocks=`echo "$length*$height"|bc`
border="#"
space=" "
score=0

# prepare
begin_y=`echo "$height/2"|bc`
begin_x=`echo "$length/2"|bc`
help_msg="
Press 'q' to quit
Press 'c' to retry
Press 'p' to pause and any other key to resume
"
function display_info()
{
    display_str="$1
$help_msg"

    line_num=`echo "$display_str"|wc -l`
    for i in `seq 1 $line_num`;do
        str_line=`echo "$display_str"|sed -n "$i"p`
        str_len=`expr length "$str_line"`
        str_x=`echo "$begin_x - $str_len / 2 "|bc`
        str_y=`echo "$begin_y - $line_num / 2 + $i "|bc`
        tput cup $str_y $str_x
        echo -n "$str_line"
    done
}

function clean_env()
{
    rm $flag_file 2>/dev/null
    # unhide cursor
    tput cnorm
    # reposit cursor
    tput cup $[height+1] 0
    echo
    echo "finished."
    exit
}

function lay_egg()
{
    has_egg=0
    while [ "$has_egg" -eq 0 ]; do
        egg_x=`echo "$RANDOM%$length + 1" |bc`
        egg_y=`echo "$RANDOM%$height + 1" |bc`
        has_egg=1
        # the egg can't in the snake
        for i in ${snakes[@]};do
            [ "$i" == "$egg_x,$egg_y" ] && has_egg=0
        done
    done
    tput cup $egg_y $egg_x
    echo -n "$egg"

}

function finish()
{
    touch $finish_flag
    display_info "$1" 
}

function game()
{
    snakes=( "$[begin_x-1],$begin_y" "$begin_x,$begin_y" "$[begin_x+1],$begin_y")
    lay_egg
    previous_direction="$begin_direction"
    while ((1)) ;do
        # calculate per loop
        pre_snake=${snakes[0]}
        x0=${snakes[0]%%,*}
        y0=${snakes[0]##*,}
        direction=`cat $flag_file 2>/dev/null`
        direction="${direction:-$previous_direction}"
        [ -e $flag_file ] && rm $flag_file
        case ${direction:-$begin_direction} in 
            up)
            y0=$[ ${snakes[0]##*,} - 1 ]
            if [ $y0 -le 0 ] ;then
                if [ $if_go_through_the_wall -eq 0 ] ;then
                    finish "Your Score: $score"  && return
                else
                    let y0=$y0+$height
                fi
            fi
            ;;

            left)
            x0=$[ ${snakes[0]%%,*} - 1 ]
            if [ $x0 -le 0 ] ;then
                if [ $if_go_through_the_wall -eq 0 ] ;then
                    finish "Your Score: $score"  && return
                else
                    let x0=$x0+$length
                fi
            fi
            ;;

            down)
            y0=$[ ${snakes[0]##*,} + 1 ]
            if [ $y0 -gt $height ] ;then
                if [ $if_go_through_the_wall -eq 0 ] ;then
                    finish "Your Score: $score"  && return
                else
                    let y0=$y0-$height
                fi
            fi
            ;;

            right)
            x0=$[ ${snakes[0]%%,*} + 1 ]
            if [ $x0 -gt $length ] ;then
                if [ $if_go_through_the_wall -eq 0 ] ;then
                    finish "Your Score: $score"  && return
                else
                    let x0=$x0-$length
                fi
            fi
            ;;

            pause)
            while [ ! -f "$flag_file" ];do
                sleep $flash_time
            done
            continue
            ;;

            *)
            continue
            ;;

        esac
        previous_direction="$direction"
        # clear the last one and draw the first one,like snake crawl
        tput cup ${snakes[$[${#snakes[@]}-1]]##*,} ${snakes[$[${#snakes[@]}-1]]%%,*}
        echo -n "$space"
        # calculate the snakes
        snakes[0]="$x0,$y0"
        for i in `seq 1 $[ ${#snakes[@]} - 1]`;do
            # snake eat itself
            [ "${snakes[0]}" == "${snakes[$i]}" ] && finish "Your Score: $score" && return
            transition_value=${snakes[$i]}
            snakes[$i]=$pre_snake
            pre_snake=$transition_value
        done
        # eat egg and lay egg
        if [ "${snakes[0]}" == "$egg_x,$egg_y" ] ;then
            snakes[${#snakes[@]}]=$pre_snake
            [ "${#snakes[@]}" -eq "$max_blocks" ] && finish "Congratulations, You WIN !" && return
            lay_egg
            let score++ 
            # when more_difficult=1, current speed = flash_time - score/5* rising_rate > min_flash_time
            if [ $more_difficult -ne 0 ] ;then
                speed_tmp=`printf "%.2f" $(echo "$flash_time - $score / 5 * $rising_rate"|bc)`
                [[ $speed_tmp > $min_flash_time ]] &&  cur_speed=$speed_tmp || cur_speed=$min_flash_time
            fi
        fi
        # display the snake
        tput cup ${snakes[0]##*,} ${snakes[0]%%,*}
        echo -n "$snake_head"
        for i in `seq 1 $[${#snakes[@]}-1]`;do
            tput cup ${snakes[$i]##*,} ${snakes[$i]%%,*}
            echo -n "$snake"
        done
        sleep $cur_speed
    done
}

function input()
{
    input_direction=$begin_direction
    while (( 1 ));do
        read -sn 1 dir
        [ -f $flag_file -a ! -f $finish_flag ] && continue
        case $dir in 
            W|w)
            [ "$input_direction" == up -o "$input_direction" == down ] && continue
            input_direction=up
            echo "$input_direction" > $flag_file
            ;;

            A|a)
            [ "$input_direction" == left -o "$input_direction" == right ] && continue
            input_direction=left
            echo "$input_direction" > $flag_file
            ;;

            S|s)
            [ "$input_direction" == up -o "$input_direction" == down ] && continue
            input_direction=down
            echo "$input_direction" > $flag_file
            ;;

            D|d)
            [ "$input_direction" == left -o "$input_direction" == right ] && continue
            input_direction=right
            echo "$input_direction" > $flag_file
            ;;

            # pause
            P|p)
            echo "pause" > $flag_file
            ;;

            # exit
            Q|q)
            kill -- -$$
            ;;

            # retry
            C|c)
            [ ! -f $finish_flag ] && continue
            input_direction=$begin_direction
            begin &
            ;;

            # press any key to exit the pause
            *)
            echo "$dir" > $flag_file
            ;;

        esac
    done
}

function begin()
{
    [ -e $flag_file ] && rm $flag_file
    [ -e $finish_flag ] && rm $finish_flag
    cur_speed=$flash_time
    top_border=$border
    second_border=$border
    for ((i=0;i < length;i++));do
        top_border="$top_border$border"
        second_border="$second_border$space"
    done
    top_border="$top_border$border"
    second_border="$second_border$border"
    # hide cursor
    tput civis
    clear
    # draw border
    echo $top_border
    for ((i=0;i < height;i++));do
        echo "$second_border"
    done
    echo $top_border
    game
} 

begin&
input

后续思考

鸡蛋的坐标是通过获取随机值实现,并增加判断以防止鸡蛋坐标在蛇坐标数组内,在实际应用时,当游戏界面足够大,蛇的长度已经足够长时,所剩的空白很少甚至1个时,可能会出现连续取多个鸡蛋坐标都在蛇的坐标数组内,无法及时产生下一个鸡蛋导致卡顿。

解决思路:可更改判定赢得胜利的标准,原为 蛇长度==游戏界面内总元素数,改为 蛇长度==游戏界面内总元素数*80%。即:在出现上诉情况前结束游戏,没有出现issue就是没有issue,掩耳盗铃。。。

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2020-02-28,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 WriteSimpleDemo 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • github
  • 实现功能
  • 实现思路
  • 方法重点
  • 代码
  • 后续思考
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档