Bash Tic-Tac-Toe Remix

October 2019

Overview

Created a single or multiplayer game in bash shell language
Re-imagined Tic-Tac-Toe for more strategic gameplay
Developed a script for a semi-competitive computer opponent
Tic-tac-toe Game

Screenshot of terminal running the game

Why Use Shell Scripting?

Before I made this game I had been teaching myself how to use the terminal in Linux and found myself deep in the world of shell scripting. I was really enjoying the things I was learning, however, beyond scripting menial tasks and custom live linux usb drives I didn't really know of a way to code something that applied and challenged my knowledge of bash. So I thought, "Why not make a game?" And that's what I did.

Tic-Tac-Toe Remix Explanation

I started by just making a terminal based tic-tac-toe game. But when I came to the point of wanting to program a computer player I realized that tic-tac-toe can be perfected. Basically it would be too easy to make an unbeatable computer. So I decided to change the game.

This new game is played and won like traditional tic-tac-toe with one twist. Each filled spot can be overwritten once. The way this is illustrated in this game is with different colored symbols. When you first mark in a spot the X or O is green. This means that it can still be overwritten. Once a green symbol is overwritten the new symbol is red, meaning that square is locked. This single rule adds much more complexity to the game.

Tic-tac-toe Game Tutorial 1

Usually to prevent losing O-player would choose spot 2

Tic-tac-toe Game Tutorial 2

But in this game X-player could just overwrite the green O in spot 2 to win

Tic-tac-toe Game Tutorial 3

So instead O should choose the middle spot (4) and since the O will be red they player-X can't overwrite it and win this turn

BASH Tic-Tac-Toe Code

Download
  				
#!/bin/bash

# Tic Tac Toe Program
# Creator: Willem Van Dam
# Date Started: 10/4/2019

# Purpose: To play tic-tac-toe of course!



# Init Cases
main () {
case $1 in
	-[Hh]*) clear; echo -e "Tic Tac Toe Game created by Willem Van Dam 
	\n\n-h : help message\n\n-m : multiplayer\n\n-s : solo game vs computer\n\n";;
	-[Mm]*)	initGame 1;;
	-[Ss]*)	initGame 0;;
	*) clear; read -p "Tic Tac Toe Game created by Willem Van Dam

What game mode would you like to play?
Solo (vs. CPU)
Multiplayer
Choice: " mode
success=0
while test $success -eq 0
do
if [[ $mode = [Ss]* ]]
then
initGame 0
success=1
elif [[ $mode = [Mm]* ]]
then
initGame 1
success=1
else
read -p "Sorry that is not an option.
Please choose Solo (s) or Multiplayer (m)
Choice: " mode
fi
done
;;
esac
}

initGame () {
#$1=solo (0) or multi(1)
cpu=$1
cap=3
redlimit=(0 $cap $cap)
refboard=(0 1 2 3 4 5 6 7 8) 
dgrey='\033[0m'
green='\033[0;32m'
red='\033[1;31m'
yellow='\033[1;33m'
nocolor='\033[0m'
formboard=("|" "|" "\n-----------\n" "|" "|" "\n-----------\n" "|" "|" "\n")

xo=0
winboard=(0 0 0 0 0 0 0 0)
config=("l1" "l2" "l3" "l4" "l5" "l6" "l7" "l8")
l1=(0 1 2)
l2=(3 4 5)
l3=(6 7 8)
l4=(0 3 6)
l5=(1 4 7)
l6=(2 5 8)
l7=(0 4 8)
l8=(2 4 6)

for i in {0..8}
do
iroboard[$i]="$dgrey"
refboard[$i]="$i"
done 
#turn=1
#choice=0
#markBoard "X" 
#choice=1
#markBoard "X"
#choice=0
#markBoard "X"
#choice=1
#markBoard "X"
#choice=8
#markBoard "X"
win=0
gameturn=0
turn=1 #1=player1 2=player2 or CPU
while test $win -eq 0
do
	clear
	#for j in {0..7}
	#do
	#echo "${winboard[$j]}"
	#done

	dispBoard
	#echo "$gameturn"
	
	if test $turn -eq 1 #P1 turn
	then
	echo -e "Player 1, choose a spot. You are Xs.\n"
	read -p "Choice: " choice
	markBoard "X"
	turn=2

	else #P2 or CPU turn
	if test $1 -eq 0 #CPU turn
	then
	botdecide
	markBoard "O"
	
	else #P2 turn
	echo -e "Player 2, choose a spot. You are Os.\n"
	read -p "Choice: " choice
	markBoard "O"
	fi
	turn=1
	fi
	gameturn=$(($gameturn + 1))

done
clear
dispBoard
if test $win -eq 3
then
echo -e "${yellow}Well... It looks like you tied...${nocolor}"
elif (( cpu == 0 && win == 2 ))
then
echo -e "${red}The CPU has crushed your dreams of winning\nYOU LOSE!!!${nocolor}"
else
echo -e "${green}Congratulations Player ${win}. You won!!!${nocolor}"
fi
read -p "Would you like to play again? (y/n)
" replay
if [[ $replay = [Nn]* ]]
then
quit=1
fi
}

dispBoard () {
echo -e "\n"
for d in {0..8}
do
echo -en " ${iroboard[$d]}${refboard[$d]}$nocolor ${formboard[$d]}"
done
echo -e "\n"
}

markBoard () {
mark=$1
change=1
if [[ "$mark" = "X" ]]
then
xo=1
else
xo=2
fi
while test $change -eq 1
do
if [[ "${iroboard[$choice]}" = "$dgrey" ]]
then
iroboard[$choice]="$green"
refboard[$choice]=$mark
change=0; iro=0;
elif [[ "${iroboard[$choice]}" = "$green" ]]
then
if test ${redlimit[$turn]} -gt 0 
then
iroboard[$choice]="$red"
refboard[$choice]=$mark
redlimit[$turn]=$((${redlimit[$turn]} - 1))
change=0; iro=3
else
read -p "Sorry you have run out of red caps.
Please choose a blank spot: " choice
fi
else
read -p "Sorry that spot is locked.
Please choose a different spot: " choice
fi
done
lcount=0

for l in ${config[@]}
do
	temp="${l}[@]"
	tcount=0
	for t in "${!temp}"
	do
		if test $choice -eq ${t}
		then
		calcwin $lcount $tcount
		split ${winboard[$lcount]}
		if (( ones%3 == xo && tens%3 == xo && hundreds%3 == xo ))
		then
		win=$turn
		return
		fi
		fi
		tcount=$(($tcount + 1))
	done
	lcount=$(($lcount + 1))
done
fullcheck
}

fullcheck () {
filled=0
for tie in ${refboard[@]}
do
if [[ "$tie" = "X" || "$tie" = "O" ]]
then
filled=$(($filled + 1))
fi
done
if test $filled -eq 9
then
win=3
return
fi
}

calcwin () {
lx=$1
li=$2
case $li in
	0)winboard[$lx]=$(( ${winboard[$lx]} % 100 + ($xo + $iro) * 100 ));;
	1)winboard[$lx]=$((${winboard[$lx]} % 10 + (${winboard[$lx]} - ${winboard[$lx]} % 100 ) + ($xo + $iro) * 10 ));;
	2)winboard[$lx]=$(((${winboard[$lx]} - ${winboard[$lx]} % 10) + $xo + $iro));;
	*) echo "Error!!!"; exit;;
esac
}
	
split () {
	full=$1
	ones=$(($full % 10))
	tens=$((($full % 100 - $ones)/10))
	hundreds=$((($full - $tens - $ones)/100))
}

botdecide () {
decision=0
#echo "hey"
#goforthewin
goforthewin
if test $decision -ne 0
then
return
fi
#stop death
stopdeath
if test $decision -ne 0
then
return
fi

#NOGO
nogo=(0 0 0 0 0 0 0 0 0)
#nogolock
#centeraftertheyout include take c if open

#randomnogo
#random
random
if test $decision -ne 0
then
return
fi
#echo "gg" 
}

goforthewin () {
#echo "winnnnnn"
wincount=0
for lx in ${winboard[@]}
do
split $lx
#echo "$hundreds:$tens:$ones"
aname=${config[$wincount]}
if (( ones%3 == 2 && tens%3 == 2 && hundreds != 4 && ( hundreds == 0 || (hundreds == 1 && redlimit[2] > 0) ) ))
then
#echo "ahahund"
aloc=$aname[0]
choice=${!aloc}
decision=1
break
elif (( ones%3 == 2 && hundreds%3 == 2 && tens != 4 && ( tens == 0 || (tens == 1 && redlimit[2] > 0) ) ))
then
#echo "ahaten"
aloc=$aname[1]
choice=${!aloc}
decision=1
break
elif (( hundreds%3 == 2 && tens%3 == 2 && ones != 4 && ( ones == 0 || (ones == 1 && redlimit[2] > 0) ) ))
then
#echo "ahaone"
aloc=$aname[2]
choice=${!aloc}
decision=1
break
fi
	wincount=$(($wincount + 1))
	done 
}

stopdeath () {
wincount=0
for lx in ${winboard[@]}
do
aname=${config[$wincount]}
if test ${redlimit[2]} -eq 0
then
case $lx in
	011 | 014 | 041) rando=0;;
	104 | 401 | 104) rando=1;;
	110 | 410 | 140) rando=2;;
	*) ;;
esac
else
decision=1
case $lx in
	140 | 142 | 104 | 124) rando=0
	break
	;;
	410 | 412 | 014 | 214) rando=1
	break
	;;
	041 | 241 | 401 | 421) rando=2
	break
	;;
	110 | 112) rando=$((RANDOM % 2))
	break
	;;
	011 | 211) rando=$((RANDOM%2+1))
	break
	;;
	101 | 121)rando=1
	while test $rando -eq 1
	do
	rando=$((RANDOM%3))
	done
	break
	;;
	*) decision=0;;
esac
fi
wincount=$(($wincount + 1))
done
if test $decision -eq 1
then
aloc=$aname[$rando]
choice=${!aloc}
fi
}

random () {
echo "rando"
change=0
while test $change == 0
do
rando=$(( RANDOM % 9 ))
if { [ "${iroboard[$rando]}" == "$green" ] && [ ${redlimit[2]} -gt 0 ];} || [ "${iroboard[$rando]}" != "$red" ]
then
choice=$rando
change=1
decision=1
fi
done
}
#start execute
quit=0
while test $quit -eq 0
do
main $1
done
clear
exit