#!/bin/sh ################################################################# # # (#) yamisweep (CC-BY-NC-SA) Jan 2021 by H.Zuleger HZNET # # yet another mine sweeper (now written in bourne shell) # # A replica to c't 2/2021 p150 with some add-on's # # Bugs: The 'lot' command usually used on BSD systems # to distribute the mines at random positions, # potentially creates duplicates. # This could end up in a smaller number of mines. # # 2021/01/09 Fixed some Mac OS/X problems # ################################################################# PATH=/bin:/usr/bin # set default vars debug=0 term=0 rows=10 # default # of rows cols=10 # default # of cols mines=8 # default number of mines in percent auto=0 # auto mode # set default water and mine chars water="~" mine="@" ################################################################# ## parse options prog=`basename $0` usage() { test $# -gt 0 && echo "$@" 1<&2 echo "usage: $prog [-a] [-t] [-d [-d]] {[-r rows][-c cols]|[-s size]} [-m mines(%)]" 1<&2 exit $# } ## parse options while test $# -gt 0 do case $1 in -h) usage ;; -t) term=1 bold=`tput bold` blink=`tput blink` dim=`tput dim` rev=`tput rev` norm=`tput sgr0` red=`tput setaf 1` blue=`tput setaf 4` green=`tput setaf 2` black=`tput setaf 0` ;; -a) auto=`expr $auto + 1` ;; -d) debug=`expr $debug + 1` ;; -s) rows="$2" cols="$2" shift ;; -r) rows="$2" shift ;; -c) cols="$2" shift ;; -m) mines="$2" shift ;; -*) usage "Illegal option $1" ;; *) break ;; esac shift done # calculate size of playground pgsize=`expr $rows \* $cols` # size of play ground # define border string dstr=" 1 2 3 4 5 6 7 8 9 1" cstr="123456789o123456789o123456789o123456789o123456789o123456789o123456789o123456789o123456789o123456789o" rule="----------------------------------------------------------------------------------------------------" cstr=`echo "$cstr" | cut -c 1-$cols | sed "s/\(.\)/\1 /g"` dstr=`echo "$dstr" | cut -c 1-$cols | sed "s/\(.\)/\1 /g"` rule=`echo "$rule" | cut -c 1-$cols | sed "s/\(.\)/\1 /g"` test "$debug" -gt 2 && echo ":$cstr:" 1>&2 ################################################################# # printpg # print playground with border # show mines if 'uncovered* is greater 0 # print 'mesg' or newline after the playground printpg() { minec="$mine" replc="$water" test "$2" -gt 0 && replc="$mine" test "$term" -gt 0 && tput clear echo "\t$dstr" echo "\t$cstr" echo "\t$rule" echo "$3" | tr "$minec" "$replc" | sed -e "s/ 1 / ${green}1$norm /g" -e "s/1 /${green}1$norm /" -e "s/ 1/ ${green}1$norm/" \ -e "s/ 2 / ${blue}2$norm /g" -e "s/2 /${blue}2$norm /" -e "s/ 2/ ${blue}2$norm/" \ -e "s/ \([3-8]\) / ${red}\1$norm /g" -e "s/\([3-8]\) /${red}\1$norm /" \ -e "s/ \([3-8]\)/ ${red}\1$norm/" | nl # This shorter version is not working on Mac OS/X # sed -e "s/\b1\b/${green}1$norm/g" -e "s/\b2\b/${blue}2$norm/g" -e "s/\b\([3-8]\)\b/${red}\1$norm/g" | echo "$1" } ################################################################# # count # count the number of in count() { echo "$2" | tr -cd "$1" | wc -c } ################################################################# # distribute mines on the playground mines=`expr $mines \* $pgsize / 100` test $mines -lt 1 && mines=1 n=`expr $pgsize - 1` test "$debug" -gt 1 && echo "Number of mines: $mines" 1>&2 if test -x /bin/shuf -o -x /usr/bin/shuf then set -- `shuf -i 0-$n | head -$mines | sort -n` else # BSD Unixes prefer jot set -- `jot -r $pgsize 0 $n | head -$mines | sort -n` fi test "$debug" -gt 1 && echo "Mines: $*" 1>&2 ################################################################# # create playground case `seq -s"x" 1` in # determine which seq version is running 1x) start=1 ;; # Mac OS *) start=0 ;; # Linux esac pg="" for r in `seq 1 $rows` do line=`seq -s "$water" $start $cols | tr -s "[0-9]" " " | sed -e "s/^ //"` while test $# -gt 0 do pos=$1 Row=`expr $pos / $cols + 1` Col=`expr $pos % $cols + 1` if test $r != $Row # not at the right row ? then break fi test "$debug" -gt 1 && echo "$pos ==> $Row $Col" 1>&2 line=`echo $line | sed "s/[$water$mine]/$mine/$Col"` shift done pg="$line\n$pg" done test "$debug" -gt 2 && echo $pg | od -c ################################################################# # count__mines # enumerate the number of mines at pos x,y surrounding playground count_mines() { Row="$1" Col="$2" pg="$3" # look around (just one step in every direction) fromcol=`expr $Col - 1`; test $fromcol -lt 1 && fromcol=1 tocol=`expr $Col + 1` testenv=`echo "$pg" | nl -s: | grep -C1 " $Row:" | cut -d":" -f2- | cut -d" " -f$fromcol-$tocol` test $debug -gt 2 && echo "$testenv" 1>&2 # count surrounding mines cmines=`count $mine "$testenv"` test "$debug" -gt 2 && echo Mines found: $cmines 1>&2 echo $cmines } ################################################################# # run the game ################################################################# mesg="" cnt=`count "$water" "$pg"` test "$debug" -gt 1 && echo "Number of $water:" $cnt while test $cnt -ne 0 do printpg "$mesg" $debug "$pg" # print playground with border mesg="" # get coordinates from user and test for correctness echo $cnt "attempts left: " read irow icol case $irow in [0-9]*) test $irow -lt 1 -o $irow -gt $rows && mesg="Illegal value $irow" && continue ;; d) debug=$icol; continue # set debug switch ;; a) auto=$icol; continue # set auto level ;; q) endmesg="Quit? What a pity!"; break ;; *) mesg="Not a row number: $irow"; continue esac case $icol in [0-9]*) if test $icol -lt 1 -o $icol -gt $cols then mesg="Illegal value $icol" continue fi ;; d) debug=`expr \( $debug + 1 \) % 2`; continue # toggle debug switch ;; a) auto=`expr \( $auto + 1 \) % 2`; continue # toggle auto switch ;; *) mesg="Not a column number: $icol"; continue ;; esac ## Test for mine hit curr=`echo "$pg" | head -$irow | tail -1 | cut -d" " -f$icol` test $debug -gt 1 && echo "$irow $icol ==> $curr" 1>&2 if test "$curr" = "$mine" then endmesg="${bold}!!! ${red}${blink}BOOM${norm}${black} ${bold}!!!!${norm}" break fi # count surrounding mines cmines=`count_mines $irow $icol "$pg"` # replace current position with number of mines found test $debug -gt 2 && echo sed "${irow}s/[0-9$water$mine]/$cmines/$icol" 1>&2 pg=`echo "$pg" | sed "${irow}s/[0-9$water$mine]/$cmines/$icol"` # test for auto mode if test "$auto" -gt 0 -a $cmines -eq 0 then for nr in `seq $rows | grep -C1 "^$irow$"` do for nc in `seq $cols | grep -C1 "^$icol$"` do test $irow -eq $nr -a $icol -eq $nc && continue test $debug -gt 1 && echo "automode $nr $nc" 1>&2 cmines=`count_mines $nr $nc "$pg"` pg=`echo "$pg" | sed "${nr}s/[0-9$water$mine]/$cmines/$nc"` done done fi # how many tries left ? cnt=`count $water "$pg"` done test $cnt -eq 0 && endmesg="${rev}${blue}!${green}Congratulations${blue}!${black}${norm}" printpg "$endmesg" 1 "$pg"