Skip to content

Instantly share code, notes, and snippets.

@john-science
Created August 7, 2017 22:53
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save john-science/889ccf1959282c5623c8e699864cfa70 to your computer and use it in GitHub Desktop.
Save john-science/889ccf1959282c5623c8e699864cfa70 to your computer and use it in GitHub Desktop.
The classic Snake game, written in Bash.
#!/bin/bash
#
# Centipede game
#
# v2.0
#
# Author: sol@subnetzero.org
#
# Functions
drawborder() {
# Draw top
tput setf 6
tput cup $FIRSTROW $FIRSTCOL
x=$FIRSTCOL
while [ "$x" -le "$LASTCOL" ];
do
printf %b "$WALLCHAR"
x=$(( $x + 1 ));
done
# Draw sides
x=$FIRSTROW
while [ "$x" -le "$LASTROW" ];
do
tput cup $x $FIRSTCOL; printf %b "$WALLCHAR"
tput cup $x $LASTCOL; printf %b "$WALLCHAR"
x=$(( $x + 1 ));
done
# Draw bottom
tput cup $LASTROW $FIRSTCOL
x=$FIRSTCOL
while [ "$x" -le "$LASTCOL" ];
do
printf %b "$WALLCHAR"
x=$(( $x + 1 ));
done
tput setf 9
}
apple() {
# Pick coordinates within the game area
APPLEX=$[( $RANDOM % ( $[ $AREAMAXX - $AREAMINX ] + 1 ) ) + $AREAMINX ]
APPLEY=$[( $RANDOM % ( $[ $AREAMAXY - $AREAMINY ] + 1 ) ) + $AREAMINY ]
}
drawapple() {
# Check we haven't picked an occupied space
LASTEL=$(( ${#LASTPOSX[@]} - 1 ))
x=0
apple
while [ "$x" -le "$LASTEL" ];
do
if [ "$APPLEX" = "${LASTPOSX[$x]}" ] && [ "$APPLEY" = "${LASTPOSY[$x]}" ];
then
# Invalid coords... in use
x=0
apple
else
x=$(( $x + 1 ))
fi
done
tput setf 4
tput cup $APPLEY $APPLEX
printf %b "$APPLECHAR"
tput setf 9
}
growsnake() {
# Pad out the arrays with oldest position 3 times to make snake bigger
LASTPOSX=( ${LASTPOSX[0]} ${LASTPOSX[0]} ${LASTPOSX[0]} ${LASTPOSX[@]} )
LASTPOSY=( ${LASTPOSY[0]} ${LASTPOSY[0]} ${LASTPOSY[0]} ${LASTPOSY[@]} )
RET=1
while [ "$RET" -eq "1" ];
do
apple
RET=$?
done
drawapple
}
move() {
case "$DIRECTION" in
u) POSY=$(( $POSY - 1 ));;
d) POSY=$(( $POSY + 1 ));;
l) POSX=$(( $POSX - 1 ));;
r) POSX=$(( $POSX + 1 ));;
esac
# Collision detection
( sleep $DELAY && kill -ALRM $$ ) &
if [ "$POSX" -le "$FIRSTCOL" ] || [ "$POSX" -ge "$LASTCOL" ] ; then
tput cup $(( $LASTROW + 1 )) 0
stty echo
echo " GAME OVER! You hit a wall!"
gameover
elif [ "$POSY" -le "$FIRSTROW" ] || [ "$POSY" -ge "$LASTROW" ] ; then
tput cup $(( $LASTROW + 1 )) 0
stty echo
echo " GAME OVER! You hit a wall!"
gameover
fi
# Get Last Element of Array ref
LASTEL=$(( ${#LASTPOSX[@]} - 1 ))
#tput cup $ROWS 0
#printf "LASTEL: $LASTEL"
x=1 # set starting element to 1 as pos 0 should be undrawn further down (end of tail)
while [ "$x" -le "$LASTEL" ];
do
if [ "$POSX" = "${LASTPOSX[$x]}" ] && [ "$POSY" = "${LASTPOSY[$x]}" ];
then
tput cup $(( $LASTROW + 1 )) 0
echo " GAME OVER! YOU ATE YOURSELF!"
gameover
fi
x=$(( $x + 1 ))
done
# clear the oldest position on screen
tput cup ${LASTPOSY[0]} ${LASTPOSX[0]}
printf " "
# truncate position history by 1 (get rid of oldest)
LASTPOSX=( `echo "${LASTPOSX[@]}" | cut -d " " -f 2-` $POSX )
LASTPOSY=( `echo "${LASTPOSY[@]}" | cut -d " " -f 2-` $POSY )
tput cup 1 10
#echo "LASTPOSX array ${LASTPOSX[@]} LASTPOSY array ${LASTPOSY[@]}"
tput cup 2 10
echo "SIZE=${#LASTPOSX[@]}"
# update position history (add last to highest val)
LASTPOSX[$LASTEL]=$POSX
LASTPOSY[$LASTEL]=$POSY
# plot new position
tput setf 2
tput cup $POSY $POSX
printf %b "$SNAKECHAR"
tput setf 9
# Check if we hit an apple
if [ "$POSX" -eq "$APPLEX" ] && [ "$POSY" -eq "$APPLEY" ]; then
growsnake
updatescore 10
fi
}
updatescore() {
SCORE=$(( $SCORE + $1 ))
tput cup 2 30
printf "SCORE: $SCORE"
}
randomchar() {
[ $# -eq 0 ] && return 1
n=$(( ($RANDOM % $#) + 1 ))
eval DIRECTION=\${$n}
}
gameover() {
tput cvvis
stty echo
sleep $DELAY
trap exit ALRM
tput cup $ROWS 0
exit
}
###########################END OF FUNCS##########################
# Prettier characters but not supported
# by all termtypes/locales
#SNAKECHAR="\0256" # Character to use for snake
#WALLCHAR="\0244" # Character to use for wall
#APPLECHAR="\0362" # Character to use for apples
#
# Normal boring ASCII Chars
SNAKECHAR="@" # Character to use for snake
WALLCHAR="X" # Character to use for wall
APPLECHAR="o" # Character to use for apples
#
SNAKESIZE=3 # Initial Size of array aka snake
DELAY=0.2 # Timer delay for move function
FIRSTROW=3 # First row of game area
FIRSTCOL=1 # First col of game area
LASTCOL=40 # Last col of game area
LASTROW=20 # Last row of game area
AREAMAXX=$(( $LASTCOL - 1 )) # Furthest right play area X
AREAMINX=$(( $FIRSTCOL + 1 )) # Furthest left play area X
AREAMAXY=$(( $LASTROW - 1 )) # Lowest play area Y
AREAMINY=$(( $FIRSTROW + 1)) # Highest play area Y
ROWS=`tput lines` # Rows in terminal
ORIGINX=$(( $LASTCOL / 2 )) # Start point X - use bc as it will round
ORIGINY=$(( $LASTROW / 2 )) # Start point Y - use bc as it will round
POSX=$ORIGINX # Set POSX to start pos
POSY=$ORIGINY # Set POSY to start pos
# Pad out arrays
ZEROES=`echo |awk '{printf("%0"'"$SNAKESIZE"'"d\n",$1)}' | sed 's/0/0 /g'`
LASTPOSX=( $ZEROES ) # Pad with zeroes to start with
LASTPOSY=( $ZEROES ) # Pad with zeroes to start with
SCORE=0 # Starting score
clear
echo "
Keys:
W - UP
S - DOWN
A - LEFT
D - RIGHT
X - QUIT
If characters do not display properly, consider changing
SNAKECHAR, APPLECHAR and WALLCHAR variables in script.
Characters supported depend upon your terminal setup.
Press Return to continue
"
stty -echo
tput civis
read RTN
tput setb 0
tput bold
clear
drawborder
updatescore 0
# Draw the first apple on the screen
# (has collision detection to ensure we don't draw
# over snake)
drawapple
sleep 1
trap move ALRM
# Pick a random direction to start moving in
DIRECTIONS=( u d l r )
randomchar "${DIRECTIONS[@]}"
sleep 1
move
while :
do
read -s -n 1 key
case "$key" in
w) DIRECTION="u";;
s) DIRECTION="d";;
a) DIRECTION="l";;
d) DIRECTION="r";;
x) tput cup $COLS 0
echo "Quitting..."
tput cvvis
stty echo
tput reset
printf "Bye Bye!\n"
trap exit ALRM
sleep $DELAY
exit 0
;;
esac
done
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment