Bootstrap

Shell_18-贪吃蛇跑马灯版

一、走马灯版本贪吃蛇

在这里插入图片描述

  • 代码:
#!/bin/bash

# 基础知识

# 前景色(文字颜色): echo -e "\e[${num}m有颜色的文字"
#   39默认前景色   30黑色  31红色         32绿色   33黄色     34蓝色          35品红   36青色   37浅灰
#       90深灰色   91红灯  92浅绿色  93淡黄色   94浅蓝   95浅洋红色   96浅青色  97白色

# 背景色: echo -e "\e[$(num)m有背景颜色的文字"
#   49默认背景颜色     40黑色       41红色          42绿色   43黄色         44蓝色         45品红       46青色      47浅灰
#        100深灰色    101红灯       102浅绿色       103淡黄色  104浅蓝    105浅洋红色     106浅青色      107白色

# 属性:
#   \e[0m             关闭所有属性
#   \e[1m             设置高亮度
#   \e[4m             下划线
#   \e[5m             闪烁
#   \e[7m             反显
#   \e[8m             消隐
#   \e[30m -- \e[37m 设置前景色
#   \e[40m -- \e[47m 设置背景色
#   \e[nA             光标上移n行
#   \e[nB             光标下移n行
#   \e[nC             光标右移n行
#   \e[nD             光标左移n行
#   \e[y;xH           设置光标位置
#   \e[2J             清屏
#   \e[K              清除从光标到行尾的内容
#   \e[s              保存光标位置
#   \e[u              恢复光标位置
#   \e[?25l           隐藏光标
#   \e[?25h           显示光标

# 属性示例:
#   1    粗体高亮                    echo -e "\e[1mBold"
#   2    变暗                        echo -e "\e[2mDim"
#   4    下划线                        echo -e "\e[4mUnderlined"
#   5    闪烁                          echo -e "\e[5mBlink"
#   7    反转 (反转前景色和背景色)      echo -e "\e[7minverted"
#   8    隐藏(对密码有用)             echo -e "\e[8mHidden"

# 结合:
#   echo -e "\e[41;32m文字\e[0m"                      红色背景绿色字并清除属性
#   echo -e "\e[1;3H*\e[0m"                           第一行第三列输出一个*
#   echo -e "\e[3;3H\e[44;31m*\e[0m"                  第三行第三列输出一个蓝色背景红色字
#   echo -e "\e[5;31;42m文字\e[0m"                    闪烁、红色背景、绿色的文字并清除属性
#   echo -e "\e[3;3H\e[5;31;42m*************\e[0m"    第三行第三列输出一排闪烁的星

# 键盘控制 w s a d
# 退出游戏 q

# 地图大小 长宽可以自由改动
declare -i MAP_WIDTH=60
declare -i MAP_HIGHT=20

# 地图起始位置(左上角的开始绘制的位置, 可以自由改动)
declare -i MAP_X=20
declare -i MAP_Y=5

# 定义 蛇头的起始位置x,y 及 蛇初始化长度4
declare -i X=30
declare -i Y=30
declare -i LENGTH=4
# 舌头蛇身的坐标点
declare -a X_ARR=()
declare -a Y_ARR=()

# 食物的坐标
declare -i RANDOM_X=0
declare -i RANDOM_Y=0

# 跑马灯的开始颜色
declare -i COLOR_INDEX=15

# 初始化 蛇 出生移动方向
DIR='d'
score=0

# 游戏结束恢复环境
function end_of_game() {
    kill -9 $!
    echo -e "\e[0m"
    echo -e "\e[?25h"
    exit 1
}

# 生成边界
function init_map() {
    # 两横
    for i in $(seq ${MAP_X} $((${MAP_X} + ${MAP_WIDTH}))); do
        echo -e "\e[${MAP_Y};${i}H@\e[0m"
        echo -e "\e[$((${MAP_Y} + ${MAP_HIGHT}));${i}H@\e[0m"
    done
    # 两竖
    for j in $(seq ${MAP_Y} $((${MAP_Y} + ${MAP_HIGHT}))); do
        echo -e "\e[${j};${MAP_X}H@\e[0m"
        echo -e "\e[${j};$((${MAP_X} + ${MAP_WIDTH}))H@\e[0m"
    done
}

# 跑马灯效果
function init_map_lamp() {
    while true :; do
        let COLOR_INDEX=15

        for i in $(seq ${MAP_X} $((${MAP_X} + ${MAP_WIDTH}))); do
            let COLOR_INDEX++
            sleep 0.003
            echo -e "\e[38;5;${COLOR_INDEX}m\e[${MAP_Y};${i}H@\e[0m"
        done

        for j in $(seq ${MAP_Y} $((${MAP_Y} + ${MAP_HIGHT}))); do
            let COLOR_INDEX++
            sleep 0.003
            echo -e "\e[38;5;${COLOR_INDEX}m\e[${j};$((${MAP_X} + ${MAP_WIDTH}))H@\e[0m"
        done

        for ((k = $((${MAP_X} + ${MAP_WIDTH})); k >= ${MAP_X}; k--)); do
            let COLOR_INDEX--
            sleep 0.003
            echo -e "\e[38;5;${COLOR_INDEX}m\e[$((${MAP_Y} + ${MAP_HIGHT}));${k}H@\e[0m"
        done

        for ((m = $((${MAP_Y} + ${MAP_HIGHT})); m >= ${MAP_Y}; m--)); do
            let COLOR_INDEX--
            sleep 0.003
            echo -e "\e[38;5;${COLOR_INDEX}m\e[${m};${MAP_X}H@\e[0m"
        done
    done
}

# 初始化蛇
function draw_snake() {
    clear

    # 生成蛇身
    for ((i = 1; i < ${LENGTH}; i++)); do
        echo -e "\e[${Y_ARR[$i]};${X_ARR[$i]}H*\e[0m"
    done

    # 生成蛇头
    echo -e "\e[${Y_ARR[$i]};${X_ARR[$i]}H$\e[0m"

    # 随机生成食物
    echo -e "\e[5;32m\e[${RANDOM_Y};${RANDOM_X}H#\e[0m"

    # 生成地图
    init_map

    # 输出蛇头、食物的起位置 打印分数
    echo -e "\e[$(($MAP_Y + $MAP_HIGHT + 2));${MAP_X}H\e[32mSnake:${X},${Y}\e[0m\e[0m"
    echo -e "\e[$(($MAP_Y + $MAP_HIGHT + 3));${MAP_X}H\e[32mFood :${RANDOM_X},${RANDOM_Y}\e[0m\e[0m"
    echo -e "\e[$(($MAP_Y + $MAP_HIGHT + 4));${MAP_X}H\e[33mScore:${score}\e[0m\e[0m"
}

# 生成食物
function gen_food() {
    let flag=1
    while [ $flag -eq 1 ]; do
        # 0-58 + 21
        let RANDOM_X=$(($RANDOM % $((${MAP_WIDTH} - 1)) + $((${MAP_X} + 1))))
        # 0-18 + 6
        let RANDOM_Y=$(($RANDOM % $((${MAP_HIGHT} - 1)) + $((${MAP_Y} + 1))))
        # 生成与蛇身+蛇头 坐标不相等的 食物坐标
        for ((i = 0; i < ${LENGTH}; i++)); do
            # 如果相等 退出当前循环,重新 while循环, i又从1开始
            if [[ ${X_ARR[$i]} -eq ${RANDOM_X} && ${Y_ARR[$i]} -eq ${RANDOM_Y} ]]; then
                break
            fi
        done
        # 只有生成的食物坐标与蛇头+蛇身坐标完全不相等时
        if [ $i -eq ${LENGTH} ]; then
            flag=0
        fi
    done
}

function init() {
    clear
    # 清除所有属性
    echo -e "\e[0m"
    # 隐藏光标
    echo -e "\e[?25l"

    # 生成蛇的初始化坐标
    for i in $(seq 5); do
        # 在地图的中点取五个点初始化蛇的位置
        let pos_x=$((${MAP_WIDTH} / 2 - 2 + ${i}))+${MAP_X}
        let pos_y=$(((${MAP_Y} + ${MAP_HIGHT}) / 2))+${MAP_Y}
        X_ARR+=($pos_x)
        Y_ARR+=($pos_y)
    done
    X=$pos_x
    Y=$pos_y

    # 生成食物
    gen_food
}

function move() {
    case $DIR in
    "w") Y=$Y-1 ;; # 往上移动
    "s") Y=$Y+1 ;; # 往下移动
    "a") X=$X-1 ;; # 往左移动
    "q") end_of_game ;;
    *) X=$X+1 ;; # 默认右移
    esac

    # 如果蛇头碰到食物, 长度+1, 并重新生成食物
    if [[ $X -eq ${RANDOM_X} && $Y -eq ${RANDOM_Y} ]]; then
        ((LENGTH++))
        let score+=10
        X_ARR[${LENGTH}]=$X
        Y_ARR[${LENGTH}]=$Y
        gen_food
        return
    fi

    # 越界退出
    if [[ $X -le $MAP_X || $Y -le $MAP_Y || $X -ge $(($MAP_X + $MAP_WIDTH)) || $Y -ge $(($MAP_Y + $MAP_HIGHT)) ]]; then
        end_of_game
    fi

    # 蛇头碰到蛇身
    for ((i = 2; i <= ${LENGTH}; i++)); do
        if [[ $X -eq ${X_ARR[$i]} && $Y -eq ${Y_ARR[$i]} ]]; then
            end_of_game
        fi
    done

    # 向前蠕动一下
    for ((i = 0; i <= ${LENGTH}; i++)); do
        X_ARR[$i]=${X_ARR[$i + 1]}
        Y_ARR[$i]=${Y_ARR[$i + 1]}
    done

    # 更新蛇头到数组
    X_ARR[${LENGTH}]=$X
    Y_ARR[${LENGTH}]=$Y
}

function main() {
    init
    draw_snake

    # 以子进程的方式开启跑马灯效果
    {
        init_map_lamp
    } &

    while :; do
        OLD_DIR=$DIR
        # 如果没有wasd输入, 则保持原方向不变, 否则调转方向
        if ! read -n 1 -t 0.5 -s DIR; then
            DIR=$OLD_DIR
        fi
        sleep 0.0001
        move
        draw_snake
    done
}

main
;