シェルスクリプト:ヤッツィーゲーム

 ヤッツィーは、5つのサイコロを使って特定の組み合わせを作り、高得点を目指すダイスゲームです。ここでは、シェルスクリプトでヤッツィーゲームを作成し、その手順と詳細な解説を行います。このゲームを通じて、シェルスクリプトの配列操作、関数の定義、条件分岐、ループ、ユーザー入力の処理などの知識を深めることができます。

ヤッツィーゲームの概要

ゲームの目的

  • サイコロを振って特定の役を作り、可能な限り高い得点を獲得することを目指します。

ゲームのルール

ゲームの進行

  • プレイヤーは5つのサイコロを3回まで振ることができます。
  • 各ターンで、保持するサイコロと再度振るサイコロを選択できます。
  • 振り終わった後、得点表の未使用の項目に得点を記録します。

得点表の項目

項目説明最大得点
エース(1の目)1の目の合計5
ツー(2の目)2の目の合計10
スリー(3の目)3の目の合計15
フォー(4の目)4の目の合計20
ファイブ(5の目)5の目の合計25
シックス(6の目)6の目の合計30
3カード同じ目が3つ以上で、サイコロの合計30
4カード同じ目が4つ以上で、サイコロの合計30
フルハウス同じ目が3つと2つで、25点25
スモールストレート4つの連続した目で、30点30
ラージストレート5つの連続した目で、40点40
ヤッツィー同じ目が5つで、50点50
チャンスサイコロの合計30

勝利条件

  • 全ての項目に得点を記入した後、合計得点が最も高いプレイヤーが勝利します。
  • このシェルスクリプトでは、プレイヤーが最高得点を目指すシングルプレイとなります。

yahtzee.shの作成から実行までの手順と解説

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

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

user01@ubuntu:~$ nano yahtzee.sh

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

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

#!/bin/bash

# サイコロを振る関数
roll_dice() {
    dice=()
    for ((i=0; i<5; i++)); do
        dice+=($((RANDOM % 6 + 1)))
    done
}

# サイコロを再度振る関数
reroll_dice() {
    local indices=("$@")
    for index in "${indices[@]}"; do
        dice[$index]=$((RANDOM % 6 + 1))
    done
}

# サイコロの表示
display_dice() {
    echo "現在のサイコロ: ${dice[@]}"
}

# 得点を計算する関数
calculate_score() {
    local category=$1
    local counts=()
    for ((i=1; i<=6; i++)); do
        counts[$i]=0
    done
    for die in "${dice[@]}"; do
        ((counts[$die]++))
    done

    case $category in
        "1") echo $((counts[1] * 1)) ;;
        "2") echo $((counts[2] * 2)) ;;
        "3") echo $((counts[3] * 3)) ;;
        "4") echo $((counts[4] * 4)) ;;
        "5") echo $((counts[5] * 5)) ;;
        "6") echo $((counts[6] * 6)) ;;
        "3kind")
            for count in "${counts[@]}"; do
                if [ "$count" -ge 3 ]; then
                    echo $(( ${dice[0]} + ${dice[1]} + ${dice[2]} + ${dice[3]} + ${dice[4]} ))
                    return
                fi
            done
            echo 0
            ;;
        "4kind")
            for count in "${counts[@]}"; do
                if [ "$count" -ge 4 ]; then
                    echo $(( ${dice[0]} + ${dice[1]} + ${dice[2]} + ${dice[3]} + ${dice[4]} ))
                    return
                fi
            done
            echo 0
            ;;
        "fullhouse")
            if [[ " ${counts[@]} " =~ " 2 " ]] && [[ " ${counts[@]} " =~ " 3 " ]]; then
                echo 25
            else
                echo 0
            fi
            ;;
        "smallstraight")
            straights=("1234" "2345" "3456")
            dice_str=$(printf "%s\n" "${dice[@]}" | sort -n | uniq | tr -d '\n')
            for straight in "${straights[@]}"; do
                if [[ "$dice_str" == *"$straight"* ]]; then
                    echo 30
                    return
                fi
            done
            echo 0
            ;;
        "largestraight")
            straights=("12345" "23456")
            dice_str=$(printf "%s\n" "${dice[@]}" | sort -n | uniq | tr -d '\n')
            for straight in "${straights[@]}"; do
                if [[ "$dice_str" == "$straight" ]]; then
                    echo 40
                    return
                fi
            done
            echo 0
            ;;
        "yahtzee")
            for count in "${counts[@]}"; do
                if [ "$count" -eq 5 ]; then
                    echo 50
                    return
                fi
            done
            echo 0
            ;;
        "chance")
            echo $(( ${dice[0]} + ${dice[1]} + ${dice[2]} + ${dice[3]} + ${dice[4]} ))
            ;;
        *)
            echo 0
            ;;
    esac
}

# 得点表の初期化
declare -A scorecard
categories=("1" "2" "3" "4" "5" "6" "3kind" "4kind" "fullhouse" "smallstraight" "largestraight" "yahtzee" "chance")
for category in "${categories[@]}"; do
    scorecard[$category]=""
done

# ゲーム開始
echo "=== ヤッツィーゲームへようこそ! ==="

for turn in {1..13}; do
    echo "ターン $turn/13"
    roll_dice
    display_dice

    for roll in {1..2}; do
        read -p "再度振りたいサイコロの番号をスペースで区切って入力(例: 0 2 4)、振らない場合はnを入力: " input
        if [ "$input" == "n" ]; then
            break
        else
            indices=($input)
            reroll_dice "${indices[@]}"
            display_dice
        fi
    done

    echo "得点表:"
    for category in "${categories[@]}"; do
        if [ -z "${scorecard[$category]}" ]; then
            echo "$category: 未使用"
        else
            echo "$category: ${scorecard[$category]}"
        fi
    done

    while true; do
        read -p "得点を記入するカテゴリーを選んでください: " chosen_category
        if [ -z "${scorecard[$chosen_category]}" ]; then
            score=$(calculate_score "$chosen_category")
            scorecard[$chosen_category]=$score
            echo "カテゴリー '$chosen_category' に $score 点を記入しました。"
            break
        else
            echo "そのカテゴリーは既に使用されています。別のカテゴリーを選んでください。"
        fi
    done
done

# 合計得点の計算
total_score=0
for score in "${scorecard[@]}"; do
    total_score=$((total_score + score))
done

echo "ゲーム終了!あなたの合計得点は $total_score 点です!お疲れ様でした!"

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

1.シェバン行

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

2.サイコロを振る関数 roll_dice

roll_dice() {
    dice=()
    for ((i=0; i<5; i++)); do
        dice+=($((RANDOM % 6 + 1)))
    done
}
  • 目的:5つのサイコロを振り、結果をdice配列に格納します。
  • 解説RANDOM % 6 + 1:1から6までのランダムな数字を生成します。

3.サイコロを再度振る関数 reroll_dice

reroll_dice() {
    local indices=("$@")
    for index in "${indices[@]}"; do
        dice[$index]=$((RANDOM % 6 + 1))
    done
}
  • 目的:指定したインデックスのサイコロを再度振ります。
  • 解説:ユーザーが入力したサイコロの位置を受け取り、そのサイコロを新しい値に更新します。

4.サイコロの表示関数 display_dice

display_dice() {
    echo "現在のサイコロ: ${dice[@]}"
}
  • 目的:現在のサイコロの目を表示します。

5.得点を計算する関数 calculate_score

calculate_score() {
    # カテゴリーに応じて得点を計算し、返します。
}
  • 目的:指定したカテゴリーに基づいて、現在のサイコロから得点を計算します。
  • 解説
    ・サイコロの目の出現回数をcounts配列に格納します。
    case文でカテゴリーごとに計算ロジックを実装します。

6.得点表の初期化

declare -A scorecard
categories=("1" "2" "3" "4" "5" "6" "3kind" "4kind" "fullhouse" "smallstraight" "largestraight" "yahtzee" "chance")
for category in "${categories[@]}"; do
    scorecard[$category]=""
done
  • 目的:得点表を連想配列scorecardで管理し、初期化します。
  • 解説:各カテゴリーをキーとし、得点を値として格納します。

7.ゲームのメインループ

for turn in {1..13}; do
    # 各ターンの処理を行います。
done

解説

  • ゲームは13ターンで構成されます。
  • 各ターンでサイコロを振り、最大2回まで再度振ることができます。
  • 最後に得点を得点表に記入します。

8.サイコロの再度振りの処理

for roll in {1..2}; do
    read -p "再度振りたいサイコロの番号を... "
    # ユーザーの入力に応じてサイコロを再度振ります。
done

解説

  • ユーザーが再度振りたいサイコロの番号を入力します。
  • nを入力すると、再度振りをスキップします。

9.得点の記入

while true; do
    read -p "得点を記入するカテゴリーを選んでください: " chosen_category
    if [ -z "${scorecard[$chosen_category]}" ]; then
        score=$(calculate_score "$chosen_category")
        scorecard[$chosen_category]=$score
        echo "カテゴリー '$chosen_category' に $score 点を記入しました。"
        break
    else
        echo "そのカテゴリーは既に使用されています。別のカテゴリーを選んでください。"
    fi
done

解説

  • ユーザーが得点を記入したいカテゴリーを選択します。
  • 既に使用済みのカテゴリーは選択できません。

10.合計得点の計算と表示

total_score=0
for score in "${scorecard[@]}"; do
    total_score=$((total_score + score))
done

echo "ゲーム終了!あなたの合計得点は $total_score 点です!お疲れ様でした!"
  • 目的:ゲーム終了後に合計得点を計算し、表示します。

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

スクリプトを保存してエディタを終了したら、以下のコマンドで実行権限を付与します。

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

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

スクリプトを実行して、ヤッツィーゲームを開始します。

user01@ubuntu:~$ ./yahtzee.sh

実行例

=== ヤッツィーゲームへようこそ! ===
ターン 1/13
現在のサイコロ: 2 5 3 6 1
再度振りたいサイコロの番号をスペースで区切って入力(例: 0 2 4)、振らない場合はnを入力: 1 3
現在のサイコロ: 2 4 3 2 1
再度振りたいサイコロの番号をスペースで区切って入力(例: 0 2 4)、振らない場合はnを入力: n
得点表:
1: 未使用
2: 未使用
3: 未使用
4: 未使用
5: 未使用
6: 未使用
3kind: 未使用
4kind: 未使用
fullhouse: 未使用
smallstraight: 未使用
largestraight: 未使用
yahtzee: 未使用
chance: 未使用
得点を記入するカテゴリーを選んでください: 2
カテゴリー '2' に 4 点を記入しました。
...
(同様にターンが進行します)

解説

  • ユーザーはサイコロを振り、再度振りたいサイコロを選択します。
  • 得点を記入するカテゴリーを選び、得点表を埋めていきます。
  • 13ターン終了後、合計得点が表示されます。

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

ゲームのテストが終了したら、作成したyahtzee.shファイルを削除します。

user01@ubuntu:~$ rm yahtzee.sh

まとめ

  • シェルスクリプトの応用:ヤッツィーゲームを通じて、シェルスクリプトでのゲーム開発手法を学びました。
  • 配列の操作:サイコロや得点表を配列で管理し、データの操作を効率的に行いました。
  • 関数の活用:機能ごとに関数を定義し、コードの再利用性と可読性を向上させました。
  • 条件分岐とループif文やforループを駆使して、ゲームのロジックを制御しました。
  • ユーザー入力の処理readコマンドでユーザーからの入力を取得し、バリデーションを行いました。
  • ゲームロジックの実装:ヤッツィーの複雑な得点計算やルールをシェルスクリプトで実装しました。

 シェルスクリプトはシステム管理だけでなく、さまざまな用途で活用できます。今回のヤッツィーゲームを基に、コンピュータとの対戦モードを追加したり、スコアの保存機能を実装したりして、さらなる機能拡張に挑戦してみてください。