Bash でダービー
〜 Practice of scripting language Bash (1) 〜
2024-11-11 作成 福島
TOP > shell > bash-derby
[ TIPS | TOYS | OTAKU | LINK | MOVIE | CGI | AvTitle | ConfuTerm | HIST | AnSt | Asob | Shell ]

Bash だけで*1動きのあるスクリプト*2を作成します。
一種のゲームのような動作をしますが、勝ち負けは気にしません。

競走馬「@」を用意し、直線の走路を走らせます。
いずれかが一着でゴールしたら終了です*3
一着にならなかった馬は、ゴール通過のチャンスがありません。
なので、一般的な競馬とはかなり異なります。

*1外部呼び出しの seq コマンドを使っているので、正確には「だけ」になっていません。
 インデックス処理なので本当は for((…)) を使うべきだけど、シェルらしさを優先させました。

*2Bash の場合はプログラミングが可能なので「スクリプト」=「プログラム」です。
JCLSQL のようにプログラミングをしない「スクリプト言語」も存在します。

*3処理は同着を許容しているので、複数の一着がありえます。


1. スクリプトファイルの作成

derby.sh
#!/usr/bin/bash
# derby.sh
# Usage:
#   bash ./derby.sh

# 競走馬の名前を定義する (表示用)
# ' ' は全角の空白文字。表示をそろえるために必要
Horses=(
    'ディープインパクト'
    'オルフェーヴル  '
    'オグリキャップ  '
    'ウオッカ     '
    'サイレンススズカ '
)

# 競走馬それぞれの歩数を用意する
GallopSteps=(0 0 0 0 0)

# 競走馬の頭数を数える。覚えにくいが Bash ではこう記述する
HorseCnt=${#Horses[@]}

# コースの長さを定義する
CourseLength=40

# 画面クリアの機能を定義する*4
CLEAR="echo -n -e \e[2J\e[1;1H"

# どれか 1 頭でもゴールするまで繰り返す
finish=false
while [ $finish == false ]
do
    # つまづく馬を一頭だけ適当に決める*5
    stumble=$(($RANDOM % $HorseCnt))

    # 全競走馬の歩数を一歩進める
    for i in `seq 0 $(($HorseCnt - 1))`
    do
        if [ $i == $stumble ]; then
            # つまづく馬は進ませない
            :   # Bash は if ブロック内に命令がないとエラーになるので : を置く  
        else
            # 馬を一歩進ませる
            GallopSteps[$i]=$((${GallopSteps[$i]} + 1))
        fi

        # コースの最後に到達した馬がいたら終了フラグを立てる
        if [ ${GallopSteps[$i]} -ge $CourseLength ]; then
            finish=true
        fi
    done

    # 画面をクリア (初期化) する
    $CLEAR

    # 全競走馬と歩数を表示する
    for i in `seq 0 $(($HorseCnt - 1))`
    do
        # 馬の名前を表示する
        echo -n ${Horses[$i]}

        # 歩数とスタートラインを表示する
        dist=${GallopSteps[$i]}
        printf '%2d!' $dist

        # スタートラインから現在位置まで空白文字を表示する
        for j in `seq 0 $(($dist - 1))` ; do
            echo -n ' '
        done

        # 馬の現在位置にマークを表示する
        echo -n '@'

        # 現在位置からゴールラインまで空白文字を表示する
        for j in `seq $dist $(($CourseLength - 1))` ; do
            echo -n ' '
        done

        # ゴールラインを表示する
        echo '|'
    done

    # 実行を少し休む
    sleep 0.5
done
*4「エスケープシーケンス」という特殊文字列を定義しています。
 様々な種類があり、ここでは画面をクリアする指示を定義しています。
*5 $RANDOM は Bash に用意されている特殊な変数で、読み込むたびに異なる数値を返します。(0~32767)
 ここでは、競走馬の数 (= 5) で割ったあまりを求めることにより、0~4 のどれかを得ています。
2. 実行
ファイルに保存してから実行していますが、上記スクリプトを直接タイプしても実行可能です。

$ bash ./derby.sh
(デモなので繰り返し表示しています。実際は 1 回で終了します)