Skip to content

Instantly share code, notes, and snippets.

@programus
Last active August 29, 2015 14:04
Show Gist options
  • Save programus/41e774ac052248619b39 to your computer and use it in GitHub Desktop.
Save programus/41e774ac052248619b39 to your computer and use it in GitHub Desktop.
mine sweeper shell version
#!/bin/bash
MINE="@"
BOOM="X"
BLANK=" "
NEW="."
MARK="M"
OPEN_FLAG=1
MARK_FLAG=2
function printBanner()
{
echo "****************************"
echo "* MINE SWEEPER *"
echo "****************************"
}
function getIndex()
{
if [[ -n $3 ]]
then
local w=$3
else
local w=8
fi
local x=$1
local y=$2
echo $((x+y*w))
}
function generateMines()
{
if [[ -n $* ]]
then
local ex=$(echo $*|tr -d ' ')
else
local ex=xx
fi
local pool=($(seq -w 0 77|sed '/[89]/d'|grep -v $ex))
for i in $(seq 0 9)
do
let local pick=${RANDOM}%${#pool[@]}
local mine[$i]=${pool[$pick]}
pool=(${pool[@]:0:$pick} ${pool[@]:$((pick+1))})
done
echo ${mine[@]}
}
function split2char()
{
echo $*|sed 's/./& /g'
}
function isValidPosition()
{
[[ $# -eq 2 ]] && echo $*|grep '^[0-7] [0-7]$' > /dev/null
}
function readPosition()
{
read -p ':' input
if echo $input|grep ^[^0-9] > /dev/null
then
local cmd=$(echo $input|cut -c1)
local pos=$(split2char ${input:1})
else
local pos=$(split2char $input)
fi
isValidPosition $pos && echo $pos $cmd
}
function generateField()
{
local field=($(yes 00|head -64))
for m in $*
do
local pos=$(split2char $m)
local i=$(getIndex $pos)
local field[$i]=$(echo "${MINE}0")
local x=${m:0:1}
local y=${m:1:2}
for dx in -1 0 1
do
for dy in -1 0 1
do
local xx=$((x+dx))
local yy=$((y+dy))
if isValidPosition $xx $yy
then
local index=$(getIndex $xx $yy)
local n=${field[$index]:0:1}
if [[ $n != $MINE ]]
then
((n++))
fi
field[$index]=$(echo "$n${field[$index]:1:2}")
fi
done
done
done
echo ${field[@]}
}
function markOpen()
{
local field=($3)
local x=$1
local y=$2
local force=$4
local i=$(getIndex $x $y)
local mask=${field[$i]:1:2}
local n=${field[$i]:0:1}
if [[ -n $force ]] || ([[ $mask != ${OPEN_FLAG} ]] && [[ $mask != ${MARK_FLAG} ]])
then
field[$i]=$(echo "$n${OPEN_FLAG}")
if [[ $n = "0" ]] || [[ -n $force ]]
then
for dx in -1 0 1
do
local xx=$((x+dx))
if [[ xx -ge 0 ]] && [[ xx -le 7 ]]
then
for dy in -1 $([[ dx -ne 0 ]] && echo 0) 1
do
local yy=$((y+dy))
if [[ yy -ge 0 ]] && [[ yy -le 7 ]]
then
field=($(markOpen $xx $yy "${field[*]}"))
fi
done
fi
done
fi
fi
if [[ ${field[$i]:0:1} = $MINE ]] && [[ $mask != ${MARK_FLAG} ]]
then
field[$i]=$(echo "$BOOM${field[$i]:1:2}")
echo ${field[@]}|sed 's/\([^ ]\)[^ ]/\11/g'
return 1
else
echo ${field[@]}
fi
}
function markFlag()
{
local field=($3)
local i=$(getIndex $1 $2)
local mask=${field[$i]:1:2}
if [[ $mask -eq ${MARK_FLAG} ]]
then
field[$i]=$(echo "${field[$i]:0:1}0")
elif [[ $mask -ne ${OPEN_FLAG} ]]
then
field[$i]=$(echo "${field[$i]:0:1}${MARK_FLAG}")
fi
echo ${field[@]}
}
function showField()
{
local field=($*)
echo " $(seq 0 7|tr '\n' ' ')"
echo " +$(yes '-'|head -15|tr -d '\n')+"
for y in $(seq 0 7)
do
for x in $(seq 0 7)
do
local i=$(getIndex $x $y)
local mask=${field[$i]:1:2}
local n=${field[$i]:0:1}
if [[ $mask -eq ${OPEN_FLAG} ]]
then
local line[$x]=$(echo $n|tr '0' "$BLANK")
elif [[ $mask -eq ${MARK_FLAG} ]]
then
local line[$x]=$MARK
else
local line[$x]=$NEW
fi
done
local ln=$(echo "${line[*]}"|sed \
-e "s/${BOOM}/\\\e[41;37;1m${BOOM}\\\e[0m/g" \
-e "s/${MARK}/\\\e[43;34;2m${MARK}\\\e[0m/g" \
-e "s/${MINE}/\\\e[43;31;2m${MINE}\\\e[0m/g" \
-e "s/\<1\>/\\\e[34;1m1\\\e[0m/g" \
-e "s/\<2\>/\\\e[32;1m2\\\e[0m/g" \
-e "s/\<3\>/\\\e[31;1m3\\\e[0m/g" \
-e "s/\<4\>/\\\e[34;2m3\\\e[0m/g" \
)
echo -e "$y |$ln|"
done
echo " +$(yes '-'|head -15|tr -d '\n')+"
}
function game()
{
local field=
local step=0
local mine=10
trap 'echo;echo "You lose because you aborted!";exit' INT
echo "There are $mine mines. "
echo "Sweep them all!"
while [[ 1 = 1 ]]
do
((step++))
showField $field
printf "[%2d]" $step
until pos=($(readPosition))
do
echo "Input format wrong!"
echo "Please input a valid position in format x y or m x y for marking."
printf "[%2d]" $step
done
echo $(yes "="|head -23|tr -d '\n')
if [[ ! -n $field ]]
then
field=$(generateField $(generateMines ${pos[@]}))
fi
if [[ ${pos[@]:2:3} = "m" ]]
then
field=$(markFlag ${pos[@]:0:2} "$field")
else
if ! field=$(markOpen ${pos[@]:0:2} "$field" ${pos[@]:2:3}) || echo $field|grep "$BOOM" > /dev/null
then
showField $field
echo "Oops, you exploded a mine!"
echo
break
fi
local remain=$(echo $field|tr ' ' '\n'|sed '/1$/d'|wc -l)
if [[ $remain -eq 10 ]]
then
showField $field
echo "Wow, you sweeped all mine after $step steps!"
echo
break
fi
fi
done
trap - INT
}
printBanner
until [[ $exit ]]
do
game $1
echo "-----"
select sel in "Play again?" "Exit"
do
case $sel in
Exit)
exit=1
break;;
*)
echo "====="
break;;
esac
done
done
@programus
Copy link
Author

Great idea. Added color. :-)

@limingjie
Copy link

Just found that gists can be cloned! Thx!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment