シェルスクリプト:ニムゲーム
シェルスクリプトは、システム管理だけでなく、ゲームの作成にも活用できる強力なツールです。ここでは、シェルスクリプトを使って「ニムゲーム」を作成し、その手順と詳細な解説を行います。この演習を通じて、シェルスクリプトの基本的な構文、配列の操作、条件分岐、ループ、ユーザー入力の処理などの知識を総合的に学ぶことができます。
ニムゲームの概要
ゲームの目的
- 複数の石(マッチ棒)からなる山から交互に石を取り、最後の石を取らないようにします。
ゲームのルール
- ゲームの進行
・ゲームには複数の山があり、それぞれに一定数の石があります。
・プレイヤーとコンピュータは交互にターンを行います。
・自分のターンで、プレイヤーは一つの山を選び、そこから1個以上の石を取ります。
・一度に複数の山から石を取ることはできません。 - 勝利条件
・最後の石を取ったプレイヤーが負けとなります。
ゲームの流れ
- 初期設定として、3つの山にそれぞれ3、5、7個の石があります。
- プレイヤーとコンピュータが交互に石を取っていきます。
- 戦略的に石を取ることで、相手に最後の石を取らせるようにします。
nim_game.sh
の作成から実行までの手順と解説
ステップ1:スクリプトファイルの作成
ターミナルで以下のコマンドを実行し、新しいスクリプトファイル nim_game.sh
を作成します。
user01@ubuntu:~$ nano nim_game.sh
ステップ2:スクリプトの内容を記述
エディタが開いたら、以下のコードをコピーして貼り付けます。
#!/bin/bash
# 山の初期設定
piles=(3 5 7)
# ゲーム開始のメッセージ
echo "=== ニムゲームへようこそ! ==="
echo "最後の石を取らないようにしましょう。"
# 現在の山の状態を表示する関数
display_piles() {
echo "現在の山の状態:"
for i in "${!piles[@]}"; do
echo "山$((i + 1)): ${piles[$i]} 個の石"
done
}
# プレイヤーのターン
player_turn() {
while true; do
display_piles
read -p "どの山から石を取りますか?(1〜3): " pile_choice
if ! [[ "$pile_choice" =~ ^[1-3]$ ]]; then
echo "無効な山の番号です。もう一度入力してください。"
continue
fi
pile_index=$((pile_choice - 1))
if [ "${piles[$pile_index]}" -eq 0 ]; then
echo "選択した山には石がありません。別の山を選んでください。"
continue
fi
read -p "いくつの石を取りますか?(1〜${piles[$pile_index]}): " remove_count
if ! [[ "$remove_count" =~ ^[1-9][0-9]*$ ]]; then
echo "正の整数を入力してください。"
continue
fi
if [ "$remove_count" -lt 1 ] || [ "$remove_count" -gt "${piles[$pile_index]}" ]; then
echo "無効な石の数です。もう一度入力してください。"
continue
fi
piles[$pile_index]=$((piles[$pile_index] - remove_count))
break
done
}
# コンピュータのターン
computer_turn() {
echo "コンピュータのターンです。"
# 単純な戦略:最初に石が残っている山を見つけて1個取る
for i in "${!piles[@]}"; do
if [ "${piles[$i]}" -gt 0 ]; then
echo "コンピュータは山$((i + 1))から1個の石を取った。"
piles[$i]=$((piles[$i] - 1))
break
fi
done
}
# ゲーム終了の判定
is_game_over() {
total=0
for pile in "${piles[@]}"; do
total=$((total + pile))
done
if [ "$total" -eq 0 ]; then
return 0 # ゲームオーバー
else
return 1 # ゲーム続行
fi
}
# メインループ
while true; do
# プレイヤーのターン
player_turn
if is_game_over; then
echo "あなたが最後の石を取りました。あなたの負けです。"
exit
fi
# コンピュータのターン
computer_turn
if is_game_over; then
echo "コンピュータが最後の石を取りました。あなたの勝ちです!"
exit
fi
done
ステップ3:スクリプトの内容の解説
1.シェバン行
#!/bin/bash
- スクリプトが Bash シェルで実行されることを指定します。
2.山の初期設定
piles=(3 5 7)
- 目的:3つの山にそれぞれ3、5、7個の石を配置します。
3.ゲーム開始のメッセージ
echo "=== ニムゲームへようこそ! ==="
echo "最後の石を取らないようにしましょう。"
- ゲーム開始の挨拶とルールの簡単な説明を表示します。
4.山の状態を表示する関数 display_piles
display_piles() {
echo "現在の山の状態:"
for i in "${!piles[@]}"; do
echo "山$((i + 1)): ${piles[$i]} 個の石"
done
}
- 目的:現在の各山の石の数を表示します。
- 解説:
・for
ループで各山を巡回し、山の番号と石の数を表示します。
5.プレイヤーのターン関数 player_turn
player_turn() {
while true; do
display_piles
read -p "どの山から石を取りますか?(1〜3): " pile_choice
if ! [[ "$pile_choice" =~ ^[1-3]$ ]]; then
echo "無効な山の番号です。もう一度入力してください。"
continue
fi
pile_index=$((pile_choice - 1))
if [ "${piles[$pile_index]}" -eq 0 ]; then
echo "選択した山には石がありません。別の山を選んでください。"
continue
fi
read -p "いくつの石を取りますか?(1〜${piles[$pile_index]}): " remove_count
if ! [[ "$remove_count" =~ ^[1-9][0-9]*$ ]]; then
echo "正の整数を入力してください。"
continue
fi
if [ "$remove_count" -lt 1 ] || [ "$remove_count" -gt "${piles[$pile_index]}" ]; then
echo "無効な石の数です。もう一度入力してください。"
continue
fi
piles[$pile_index]=$((piles[$pile_index] - remove_count))
break
done
}
- 目的:プレイヤーが石を取る処理を行います。
- 解説:
・山の番号と石の数を入力してもらい、入力のバリデーションを行います。
・入力が正しければ、指定された山から石を減らします。
6.コンピュータのターン関数 computer_turn
computer_turn() {
echo "コンピュータのターンです。"
# 単純な戦略:最初に石が残っている山を見つけて1個取る
for i in "${!piles[@]}"; do
if [ "${piles[$i]}" -gt 0 ]; then
echo "コンピュータは山$((i + 1))から1個の石を取った。"
piles[$i]=$((piles[$i] - 1))
break
fi
done
}
- 目的:コンピュータが石を取る処理を行います。
- 解説:
・単純な戦略として、最初に見つけた石が残っている山から1個の石を取ります。
7.ゲーム終了の判定関数 is_game_over
is_game_over() {
total=0
for pile in "${piles[@]}"; do
total=$((total + pile))
done
if [ "$total" -eq 0 ]; then
return 0 # ゲームオーバー
else
return 1 # ゲーム続行
fi
}
- 目的:全ての山から石がなくなったかをチェックします。
- 解説:
・すべての山の石の数を合計し、合計が0ならゲームオーバーとします。
8.メインループ
while true; do
# プレイヤーのターン
player_turn
if is_game_over; then
echo "あなたが最後の石を取りました。あなたの負けです。"
exit
fi
# コンピュータのターン
computer_turn
if is_game_over; then
echo "コンピュータが最後の石を取りました。あなたの勝ちです!"
exit
fi
done
解説
- プレイヤーとコンピュータのターンを交互に行います。
- 各ターンの後にゲームオーバーのチェックを行い、ゲーム終了時には勝敗を表示してプログラムを終了します。
ステップ4:スクリプトに実行権限を付与
スクリプトを保存してエディタを終了したら、実行権限を付与します。
user01@ubuntu:~$ chmod +x nim_game.sh
ステップ5:スクリプトの実行
スクリプトを実行して、ニムゲームを開始します。
user01@ubuntu:~$ ./nim_game.sh
実行例
=== ニムゲームへようこそ! ===
最後の石を取らないようにしましょう。
現在の山の状態:
山1: 3 個の石
山2: 5 個の石
山3: 7 個の石
どの山から石を取りますか?(1〜3): 3
いくつの石を取りますか?(1〜7): 2
コンピュータのターンです。
コンピュータは山1から1個の石を取った。
現在の山の状態:
山1: 2 個の石
山2: 5 個の石
山3: 5 個の石
どの山から石を取りますか?(1〜3): 2
いくつの石を取りますか?(1〜5): 3
コンピュータのターンです。
コンピュータは山1から1個の石を取った。
現在の山の状態:
山1: 1 個の石
山2: 2 個の石
山3: 5 個の石
どの山から石を取りますか?(1〜3): 3
いくつの石を取りますか?(1〜5): 5
コンピュータのターンです。
コンピュータは山1から1個の石を取った。
コンピュータが最後の石を取りました。あなたの勝ちです!
解説
- プレイヤーは山と取る石の数を入力して、石を取ります。
- コンピュータは単純な戦略で石を取ります。
- 最後の石をコンピュータが取ったので、プレイヤーの勝ちとなります。
ここで作成したファイルの削除
ゲームのテストが終了したら、作成した nim_game.sh
ファイルを削除します。
user01@ubuntu:~$ rm nim_game.sh
まとめ
- シェルスクリプトの応用:ニムゲームを通じて、シェルスクリプトでのゲーム作成方法を学びました。
- 配列の操作:山の状態を配列で管理し、効率的にデータを扱いました。
- 条件分岐とループ:
if
文やwhile
ループを使って、ユーザー入力のバリデーションやゲームの進行を制御しました。 - ユーザー入力の処理:
read
コマンドを使用してユーザーの入力を受け取り、正しい形式かをチェックしました。 - 関数の定義と利用:再利用可能なコードを関数として定義し、コードの可読性と保守性を向上させました。
シェルスクリプトは、システム管理以外にも多くの用途で活用できます。今回のニムゲームを基に、コンピュータの戦略を高度化したり、山の数や石の数を変更できるようにしたり、2人のプレイヤーで対戦できるようにしたりして、さらなる機能拡張に挑戦してみてください。