#!/bin/sh
# shell snake game
# license LOL
clear
echo "use q/a/o/p to move up/down/left/right or run with "vi" arg for vi keys"
echo press ENTER
read -t 2 crap
TP=$(tput cm 123 123)
tPRE=${TP%%124*}
tEND=${TP##*124}
TMP=${TP#*124}
tMID=${TMP%124*}
RED=$(tput AF 5)
GRE=$(tput AF 2)
OOO=$(tput op)
COLS=$(tput co)
ROWS=$(tput li)
SCORE=0
ESCORE=0
MX=$(($COLS-2))
[ $MX -gt 60 ] && MX=60
MY=$((ROWS-2))
[ $MY -gt 20 ] && MY=20
PX=$(($MX/2))
PY=$(($MY/2))
SQ="$PY,$PX"
EX=2
EY=2
L_KEY="o"
R_KEY="p"
U_KEY="q"
D_KEY="a"
if [ "$1" = "vi" ];then
L_KEY="h"
R_KEY="l"
U_KEY="k"
D_KEY="j"
shift
fi
if [ "$1" = "1" ];then
AI=0
else
AI=1
fi
SEED=$(date +%s)
trap 'die "Script interrupted by user!"' INT
trap 'die "Exit for some reason"' EXIT
tput vi
clear
die() {
tput vs
print_at2 $MY 0 " "
echo $1
stty sane </dev/tty
exit
}
rand() {
SEED=$(((1103515245 * $SEED + 12345) % 2147483648));
RANDO=$((SEED % $1));
}
find_free() {
while true;do
rand $((MY-3))
N_Y=$(($RANDO+2))
rand $((MX-3))
N_X=$(($RANDO+2))
N_ALL="$N_Y,$N_X"
OK=1
for combo in $1;do
if [ $combo = $N_ALL ];then
OK=0
break
fi
done
[ $OK -eq 1 ] && break
done
}
gen_apple() {
find_free "$SQ $EQ"
A_Y=$N_Y
A_X=$N_X
print_at2 $A_Y $A_X "*"
}
check_wall() {
[ $1 -le 1 -o $1 -ge $MY ] && return 1
[ $2 -le 1 -o $2 -ge $MX ] && return 1
return 0
}
move_head() {
DEAD=0
OPX=$PX
OPY=$PY
case "$dir" in
d) PY=$(($PY+1));;
u) PY=$(($PY-1));;
l) PX=$((PX-1));;
r) PX=$((PX+1));;
*) return;;
esac
print_at2 $((MY+1)) 40 "Y:$PY X:$PX "
check_wall $PY $PX
if [ $? -ne 0 ];then
DEAD=1
HIT="the wall"
fi
NHEAD="$PY,$PX"
for combo in $SQ;do
if [ "$combo" = "$NHEAD" ];then
DEAD=1
HIT="yourself"
break
fi
done
if [ $AI -ne 0 ];then
for combo in $EQ;do
if [ "$combo" = "$NHEAD" ];then
DEAD=1
HIT="the enemy"
break
fi
done
fi
SQ="$NHEAD $SQ"
}
draw_snake() {
TAIL=${SQ##* }
Y=${TAIL%,*}
X=${TAIL#*,}
if [ $A_Y -eq $PY -a $A_X -eq $PX ]; then
# we ate an apple
SCORE=$(($SCORE+1))
gen_apple
print_at2 $((MY+1)) 0 "SCORE: $SCORE : $ESCORE"
else
# delete tail
SQ=${SQ% *}
print_at2 $Y $X " "
fi
C=O
nseg=0
echo $GRE
for combo in $SQ;do
Y=${combo%,*}
X=${combo#*,}
print_at2 $Y $X $C
C=o
nseg=$(($nseg+1))
# we really only need to draw the 1st 2 segs
[ $nseg -ge 2 ] && break
done
echo $OOO
}
distXY() {
DIST=$((($A_Y-$TY)*($A_Y-$TY) + ($A_X-$TX)*($A_X-$TX)))
}
ai_dir() {
case $edir in
d) can="d l r";;
u) can="u l r";;
l) can="l u d";;
r) can="r u d";;
x) can="u d r l";;
esac
TY=$EY
TX=$EX
distXY
dmin=$DIST
ndir="u"
for nd in $can;do
case $nd in
d) IY=1; IX=0;;
u) IY=-1; IX=0;;
l) IY=0; IX=-1;;
r) IY=0; IX=1;;
esac
TY=$(($EY+IY));
TX=$(($EX+$IX));
check_wall $TY $TX
if [ $? -ne 0 ];then
continue
else
bombo="$TY,$TX"
bok=1
ZQ=${EQ#* }
ZQ="$SQ $ZQ"
#test not to hit player or self
for combo in $ZQ;do
if [ $combo = $bombo ];then
bok=0
break
fi
done
[ $bok -eq 0 ] && continue
fi
distXY
if [ $DIST -lt $dmin ];then
ndir=$nd
dmin=$DIST
break
fi
done
edir=$ndir
}
move_head2() {
EDEAD=0
OPX=$EX
OPY=$EY
case "$edir" in
d) EY=$(($EY+1));;
u) EY=$(($EY-1));;
l) EX=$(($EX-1));;
r) EX=$(($EX+1));;
*) return;;
esac
check_wall $EY $EX
if [ $? -ne 0 ];then
EDEAD=1
fi
NHEAD="$EY,$EX"
for combo in $EQ;do
if [ "$combo" = "$NHEAD" ];then
EDEAD=2
break
fi
done
for combo in $SQ;do
if [ "$combo" = "$NHEAD" ];then
EDEAD=3
break
fi
done
EQ="$NHEAD $EQ"
}
draw_snake2() {
if [ $EDEAD -ne 0 ];then
#delete dead enemy
for combo in $EQ;do
Y=${combo%,*}
X=${combo#*,}
print_at2 $Y $X " "
done
#respan enemy
ESCORE=0
print_at2 $((MY+1)) 0 "SCORE: $SCORE : $ESCORE"
find_free "$SQ $A_Y,$A_X"
edir="x"
EY=$N_Y
EX=$N_X
EQ="$EY,$EX"
return
fi
TAIL=${EQ##* }
Y=${TAIL%,*}
X=${TAIL#*,}
if [ $A_Y -eq $EY -a $A_X -eq $EX ]; then
# we ate an apple
ESCORE=$(($ESCORE+1))
gen_apple
print_at2 $((MY+1)) 0 "SCORE: $SCORE : $ESCORE"
else
# delete tail
EQ=${EQ% *}
print_at2 $Y $X " "
fi
C=O
nseg=0
echo $RED
for combo in $EQ;do
Y=${combo%,*}
X=${combo#*,}
print_at2 $Y $X $C
C=o
nseg=$(($nseg+1))
# we really only need to draw the 1st 2 segs
[ $nseg -ge 2 ] && break
done
echo $OOO
}
print_at2() {
ZZ=$tPRE$1$tMID$2$tEND
echo -n "$ZZ$3"
}
for i in $(jot $MX);do print_at2 0 $i -;print_at2 $MY $i -;done
for i in $(jot $MY);do print_at2 $i 0 "|";print_at2 $i $MX "|";done
print_at2 1 1 +
print_at2 1 $MX +
print_at2 $MY 1 +
print_at2 $MY $MX +
print_at2 $(($MY+2)) 10 "Left=$L_KEY Right=$R_KEY Up=$U_KEY Down=$D_KEY"
gen_apple
dir=x
stty -icanon -echo;dd ibs=1 obs=1 cbs=1 conv=unblock 2>/dev/null | \
while true;do
read -t 0 inp
sleep 0.1
if [ "$inp" = "" ];then
NOOP=1
#echo -n "."
else
case "$inp" in
$L_KEY) [ $dir != "r" ] && dir=l;;
$D_KEY) [ $dir != "u" ] && dir=d;;
$U_KEY) [ $dir != "d" ] && dir=u;;
$R_KEY) [ $dir != "l" ] && dir=r;;
esac
#echo read $inp
inp=""
fi
move_head
draw_snake
[ $DEAD -eq 1 ] && die "You hit $HIT !"
if [ $dir != "x" -a $AI -eq 1 ];then
ai_dir
move_head2
draw_snake2
fi
done