シェルスクリプト:マスターマインドゲーム

 シェルスクリプトは、システム管理やタスクの自動化だけでなく、簡単なゲームやツールを作成するための強力なツールです。ここでは、シェルスクリプトを使って「マスターマインドゲーム」を作成し、その手順と詳細な解説を行います。このゲームを通じて、シェルスクリプトの基本的な構文、配列の操作、関数の定義、ループ、条件分岐、入力のバリデーションなど、さまざまなスクリプトのテクニックを学ぶことができます。

マスターマインドゲームの概要

  • 目的:コンピューターがランダムに選んだ4桁の数字を、ユーザーが推測して当てるゲームです。
  • ルール
    ・コンピューターは1から6までの数字を使って、重複しない4桁の数字を生成します。
    ・ユーザーは4桁の数字を入力して推測します。
    ・各試行ごとに、以下のフィードバックが与えられます:
     ・ヒット:数字と位置の両方が正しい数。
     ・ブロー:数字は正しいが、位置が間違っている数。
    ・ユーザーはフィードバックをもとに、制限回数内に正解を当てることを目指します。

フィードバックの例

ユーザーの推測ヒットブローフィードバックの意味
1234121つの数字が位置も数字も正しく、2つの数字が位置は間違いだが数字は正しい
135603正しい数字が3つあるが、全て位置が間違っている
2465202つの数字が位置も数字も正しい

スクリプトの作成から実行までの手順と解説

ステップ1:スクリプトファイルの作成

ターミナルで以下のコマンドを実行し、新しいスクリプトファイル mastermind.sh を作成します。

user01@ubuntu:~$ nano mastermind.sh

ステップ2:スクリプトの内容を記述

エディタが開いたら、以下のコードをコピーして貼り付けます。

#!/bin/bash

# 1から6の数字を使って、重複なしの4桁の数字を生成
generate_secret() {
    numbers=(1 2 3 4 5 6)
    secret=()
    for i in {1..4}; do
        index=$((RANDOM % ${#numbers[@]}))
        secret+=("${numbers[$index]}")
        unset 'numbers[$index]'
        numbers=("${numbers[@]}")
    done
}

# ヒットとブローを計算
calculate_hit_blow() {
    local guess=("$@")
    hit=0
    blow=0
    for i in {0..3}; do
        if [ "${guess[$i]}" -eq "${secret[$i]}" ]; then
            hit=$((hit + 1))
        elif [[ " ${secret[@]} " =~ " ${guess[$i]} " ]]; then
            blow=$((blow + 1))
        fi
    done
}

# ゲームの開始
generate_secret
attempts=0
max_attempts=10

echo "マスターマインドゲームへようこそ!1から6の数字を使った重複なしの4桁の数字を当ててください。"
echo "あなたには合計 $max_attempts 回の試行があります。"

# メインゲームループ
while [ $attempts -lt $max_attempts ]; do
    attempts=$((attempts + 1))
    while true; do
        read -p "推測を入力してください(例:1234): " input
        # 入力のバリデーション
        if [[ ! "$input" =~ ^[1-6]{4}$ ]]; then
            echo "エラー: 1から6の数字を使った4桁の数字を入力してください。"
            continue
        fi
        # 重複チェック
        unique_check=$(echo "$input" | grep -o . | sort | uniq -d)
        if [ -n "$unique_check" ]; then
            echo "エラー: 数字は重複しないようにしてください。"
            continue
        fi
        break
    done

    # ヒットとブローの計算
    guess=($(echo "$input" | grep -o .))
    calculate_hit_blow "${guess[@]}"

    if [ $hit -eq 4 ]; then
        echo "おめでとうございます!正解を当てました。"
        break
    else
        echo "ヒット: $hit, ブロー: $blow"
        echo "残り試行回数: $((max_attempts - attempts))"
    fi
done

if [ $hit -ne 4 ]; then
    echo "ゲームオーバー。正解は ${secret[@]} でした。"
fi

ステップ3:スクリプトの内容の解説

1.シェバン行

#!/bin/bash
  • スクリプトが Bash シェルで実行されることを指定します。

2.秘密の数字を生成する関数

generate_secret() {
    numbers=(1 2 3 4 5 6)
    secret=()
    for i in {1..4}; do
        index=$((RANDOM % ${#numbers[@]}))
        secret+=("${numbers[$index]}")
        unset 'numbers[$index]'
        numbers=("${numbers[@]}")
    done
}

目的:1から6の数字を使って、重複しない4桁の秘密の数字を生成します。

解説

  • numbers 配列に1から6の数字を格納します。
  • ループを4回繰り返し、各回で以下を行います。
    RANDOM % ${#numbers[@]}numbers 配列の範囲内でランダムなインデックスを生成します。
    secret 配列に選ばれた数字を追加します。
    unset コマンドで選ばれた数字を numbers 配列から削除し、重複を避けます。

3.ヒットとブローを計算する関数

calculate_hit_blow() {
    local guess=("$@")
    hit=0
    blow=0
    for i in {0..3}; do
        if [ "${guess[$i]}" -eq "${secret[$i]}" ]; then
            hit=$((hit + 1))
        elif [[ " ${secret[@]} " =~ " ${guess[$i]} " ]]; then
            blow=$((blow + 1))
        fi
    done
}

目的:ユーザーの推測と秘密の数字を比較し、ヒットとブローの数を計算します。

解説

  • guess 配列にユーザーの入力を格納します。
  • 0から3までのループで各桁を比較します。
    ・同じ位置で数字が一致すれば hit をカウントアップ。
    ・数字が秘密の数字に含まれていれば(位置は異なる)、blow をカウントアップ。

4.ゲームの開始と変数の初期化

generate_secret
attempts=0
max_attempts=10

echo "マスターマインドゲームへようこそ!1から6の数字を使った重複なしの4桁の数字を当ててください。"
echo "あなたには合計 $max_attempts 回の試行があります。"

解説

  • generate_secret 関数を呼び出して、秘密の数字を生成します。
  • attempts は現在の試行回数を示し、初期値は0です。
  • max_attempts はユーザーが推測できる最大回数です。
  • ゲームのルールと試行回数をユーザーに表示します。

5.メインゲームループ

while [ $attempts -lt $max_attempts ]; do
    attempts=$((attempts + 1))
    while true; do
        read -p "推測を入力してください(例:1234): " input
        # 入力のバリデーション
        if [[ ! "$input" =~ ^[1-6]{4}$ ]]; then
            echo "エラー: 1から6の数字を使った4桁の数字を入力してください。"
            continue
        fi
        # 重複チェック
        unique_check=$(echo "$input" | grep -o . | sort | uniq -d)
        if [ -n "$unique_check" ]; then
            echo "エラー: 数字は重複しないようにしてください。"
            continue
        fi
        break
    done

    # ヒットとブローの計算
    guess=($(echo "$input" | grep -o .))
    calculate_hit_blow "${guess[@]}"

    if [ $hit -eq 4 ]; then
        echo "おめでとうございます!正解を当てました。"
        break
    else
        echo "ヒット: $hit, ブロー: $blow"
        echo "残り試行回数: $((max_attempts - attempts))"
    fi
done

解説

試行回数が最大試行回数に達するまでループを続けます。

  • ユーザー入力のバリデーション
    ・正規表現 ^[1-6]{4}$ を使用して、1から6の数字4桁であることを確認します。
    grepuniq を使って、数字が重複していないかをチェックします。
  • ヒットとブローの計算
    ・ユーザーの入力を1文字ずつ分割して guess 配列に格納します。
    calculate_hit_blow 関数を呼び出して、ヒットとブローを計算します。
  • 結果の表示
    ・ヒットが4であれば、ユーザーの勝利としてゲームを終了します。
    ・そうでなければ、ヒットとブローの数、および残りの試行回数を表示します。

6.ゲームオーバーの判定

if [ $hit -ne 4 ]; then
    echo "ゲームオーバー。正解は ${secret[@]} でした。"
fi

解説

最大試行回数に達してもヒットが4にならなかった場合、ゲームオーバーとして正解を表示します。

ステップ4:スクリプトに実行権限を付与

スクリプトを保存してエディタを閉じたら、実行権限を付与します。

user01@ubuntu:~$ chmod +x mastermind.sh

ステップ5:スクリプトの実行

スクリプトを実行して、マスターマインドゲームを開始します。

user01@ubuntu:~$ ./mastermind.sh

実行例

マスターマインドゲームへようこそ!1から6の数字を使った重複なしの4桁の数字を当ててください。
あなたには合計 10 回の試行があります。
推測を入力してください(例:1234): 1234
ヒット: 1, ブロー: 2
残り試行回数: 9
推測を入力してください(例:1234): 1456
ヒット: 2, ブロー: 1
残り試行回数: 8
推測を入力してください(例:1234): 1546
おめでとうございます!正解を当てました。

解説

  • ユーザーの推測:ユーザーは重複しない1から6の数字4桁を入力します。
  • フィードバック:ヒットとブローの数が表示されます。
  • 推測の繰り返し:フィードバックをもとに、ユーザーは次の推測を行います。
  • ゲームの終了:ヒットが4になるとゲームに勝利します。最大試行回数に達するとゲームオーバーです。

ここで作成したファイルの削除

演習が終了したら、作成した mastermind.sh ファイルを削除します。

user01@ubuntu:~$ rm mastermind.sh

まとめ

  • シェルスクリプトの応用:マスターマインドゲームを通じて、シェルスクリプトでのゲーム作成方法を学びました。
  • 配列の操作:配列を使って秘密の数字やユーザーの推測を管理し、効率的なデータ処理を行いました。
  • 関数の定義と活用:再利用可能なコードを関数として定義し、コードの可読性と保守性を向上させました。
  • 入力のバリデーション:正規表現やコマンドを使って、ユーザーの入力が正しい形式であるかを確認しました。
  • ループと条件分岐while ループや if 文を使って、ゲームの進行と終了条件を制御しました。
  • 文字列操作とコマンドの活用grepsortuniq などのコマンドを使って、文字列の解析や重複チェックを行いました。

 シェルスクリプトは、システム管理以外にも多くの用途で活用できます。今回のマスターマインドゲームを基に、数字の範囲や桁数を変更したり、難易度を調整したり、GUI を導入したりして、さらなる機能拡張に挑戦してみてください。