#!/bin/bash stty -echo trap 'end 130' EXIT INT; trap init_term WINCH skip(){ if (( ${n:=1} > ${1:-1} )); then :; else ((n++)); false; fi; } init_term(){ IFS='[;' read -sp $'\e[999;999H\e[6n' -d R -rs _ LINES COLS ((ROWS=LINES-1)); ((snakeX=COLS/2)); ((snakeY=ROWS/2)); CENTER="$snakeY;$snakeX" printf '\e[?1049h\e[2J\e[?25l\e[?7l' rand_block } end(){ stty echo; printf '\e[?1049l\e[?25h\e[?7h'; exit "${1:-0}"; } game_over(){ local i until (( i > $1 )); do for _ in 1 7; do printf '\e[%sH\e[3D\e[3%dmGAME OVER\e[m' "$CENTER" "$_" sleep .1 done; ((++i)) done; end "$2" } read_keys(){ local i case $1 in 20) i=.7;; 19) i=.6;; 18) i=.5;; 17) i=.4;; 16) i=.3;; 15) i=.2;; 14) i=.1;; 13) i=.09;; 12) i=.08;; 11) i=.075;; 10) i=.07;; 9) i=.065;; 8) i=.06;; 7) i=.055;; 6) i=.05;; 5) i=.045;; 4) i=.04;; 3) i=.035;; 2) i=.03;; 1) i=.025;; esac while read -rsn1 -t "$i"; do [[ $REPLY == $'\e' ]]&& read -rsn2 -t "$i"; KEY="${REPLY^^}" done } status_bar(){ printf '\e[999H\e[2K\e[1;32mSNAKE\e[m x:%d y:%d len:%d spe:%d' "$1" "$2" "$3" "$4" } rand_block(){ ((blockX=SRANDOM%COLS+1)); ((blockY=SRANDOM%ROWS+1)); block="$blockY;$blockX" printf '\e[%sH\e[41m \e[m' "$block" } snake_direction(){ case ${KEY:-$1} in Q) game_over 2;; H|A|\[D) (( dirX == 1 ))||{ dirX=-1 dirY=0; };; J|S|\[B) (( dirY == -1 ))||{ dirX=0 dirY=1; };; K|W|\[A) (( dirY == 1 ))||{ dirX=0 dirY=-1; };; L|D|\[C) (( dirX == -1 ))||{ dirX=1 dirY=0; };; esac; ((snakeX+=dirX)); ((snakeY+=dirY)) } draw_snake(){ snake+=("$2;$1"); printf '\e[%sH\e[42m \e[m' "${snake[-1]}"; } erase_snake(){ printf '\e[%sH \e[m' "${snake[0]}"; snake=("${snake[@]:1}"); } consume_block(){ [[ $1 == "$block" ]]&&{ ((delay--)); ((len++)); rand_block; }; } self_collision(){ [[ $1 == "${snake[-1]}" ]]&& game_over 4 1; } wall_collision(){ (( $1 < 1|| $1 > COLS|| $2 < 1|| $2 > ROWS ))&& game_over 4 1; } len=3 delay=20 init_term for((;;)){ status_bar "$snakeX" "$snakeY" "$len" "$delay" read_keys "$delay" snake_direction L; draw_snake "$snakeX" "$snakeY" wall_collision "$snakeX" "$snakeY" skip "$len"&&{ erase_snake ((snakeBodyCount=${#snake[@]}-1)) for _ in "${snake[@]::$snakeBodyCount}"; do consume_block "$_" self_collision "$_" done } }