shellスクリプトでタイピングゲーム書いてみた


端末から1文字ずつ読み込むにはreadではなくsttyコマンドを使います。
これとddコマンドを組み合わせています。

また、tputコマンドでカーソルを移動させています。

``` sh
#!/bin/sh
#
# Reference Web sites:
#   http://d.hatena.ne.jp/anmino/20091029/1256806944
#   http://www.geocities.jp/abandonment_cat/cygwin/bshell/read.html
#   http://www.ibm.com/developerworks/jp/aix/library/au-learningtput/
#

declare -i x_position=`expr \`tput lines\` / 3`
declare -i y_position=`expr \`tput  cols\` / 3`

function string_generator() {

    string[1]="Behold, Laputa's thunder."
    string[2]="Stay hungry, stay foolish!"
    string[3]="Innovation distinguishes between a leader and a follower"
    string[4]="Your time is limited, so don't waste it living someone else's life."
    string[5]="Life is like riding a bicycle. To keep your balance you must keep moving."
    string[6]="Everything should be made as simple as possible, but not simpler."
    string[7]="We never surrender"
    string[8]="Life is not fair; get used to it."
    string[9]="You can always become better."
    string[10]="Don't find fault, find a remedy"

    echo ${string[$(((RANDOM%10) + 1))]}

}

function set_timer() {
    if   [ `uname` == "Darwin" ] ; then
        echo `printf '%.3f' \`date '+%s'\``
    elif [ `uname` == "Linux" ] ; then
        echo `printf '%.3f' \`date '+%s.%N'\``
    fi
}

##############
###  main  ###
##############

###  set invisible cursor  ###
tput civis

###  set terminal in raw mode  ###
stty raw -echo

###  # of characters you type  ###
characters=0

###  # of typing  ###
typing=0

###  start timer  ###
start=`set_timer`

for itry in {1..10}; do

    ###  clear display  ###
    clear

    ###  set typing word  ###
    string="`string_generator`"

    ###  increase total # of characters  ###
    characters=`expr ${characters} + ${#string}`

    ###  print information  ###
    tput cup ${x_position} ${y_position}
    echo "Type following sentence (type '|' to quit)" 

    ###  stop if # of characters of ${string} is zero  ###
    while [ ${#string} -gt 0 ] ; do

        ###  set length of the string  ###
        declare -i string_length=${#string}

        ###  print the remaining string  ###
        tput cup `expr ${x_position} + 5` `expr ${y_position} + ${string_length}` ; printf " "
        tput cup `expr ${x_position} + 5` ${y_position}
        echo "${string}"

        flag=0

        while [ "${flag}" == 0 ] ; do

            ###  increase total # of typing  ###
            let typing=${typing}+1

            ###  real time input => ${char}  ###
            tput cup `expr ${x_position} + 5` ${y_position}
            tput cnorm
            char=`dd bs=1 count=1 2>/dev/null`
            tput civis

            ###  if ${char} matches the first character of ${string}...  ###
            if [ "${char}" == "${string:0:1}" ] ; then
              flag=1
              break 1
            fi

            ###  "|" is defined as an escape key  ###
            if [ "${char}" == "|" ] ; then
              clear
              stty -raw echo
              tput cnorm
              exit
            fi

            ###  beep for wrong typing  ###
            echo '\a'

        done

        ###  if the first character of ${string} is blank...  ###
        if [ "${string:0:1}" == " " ] ; then
            string=`echo ${string} | sed -e 's|^\s||'`
        ###  cut the first character of ${string}...  ###
        else
            string=`echo ${string} | sed -e 's|^.||'`
        fi

    done

done

###  stop timer (in mill-seconds)  ###
stop=`set_timer`

###  unset terminal in raw mode  ###
stty -raw echo

###  set visible cursor  ###
tput cnorm

###  print the result  ###
clear
tput cup ${x_position} ${y_position}
echo "     --- Your Score ---"

time=`echo "scale=3;${stop}-${start}" | bc`

tput cup `expr ${x_position} + 2` ${y_position}
if   [ `uname` == "Darwin" ] ; then
  echo "total time        : "${time} "[sec]"
elif [ `uname` == "Linux" ] ; then
  echo "total time        : "${time} "[msec]"
fi

tput cup `expr ${x_position} + 4` ${y_position}
echo "total characaters : "${characters} "[characters]"

tput cup `expr ${x_position} + 6` ${y_position}
echo "# of typing       : "${typing} "[typing]"

tput cup `expr ${x_position} + 8` ${y_position}
echo "accuracy          : "`echo "scale=3;${characters}/${typing}*100" |bc` "[%]"

tput cup `expr ${x_position} + 10` ${y_position}
echo "typing speed      : "`echo "scale=3;${time}/${characters}" | bc` "[sec/character]"

tput cup `expr ${x_position} + 12` ${y_position}
echo "hit return key"
read *

clear
```

[gistにもアップしてあります](https://gist.github.com/YukiYamashina/f97c0b84c42dbe3597c3)。