
シェルスクリプト:バトルシップゲーム
シェルスクリプトは、システム管理やタスクの自動化だけでなく、ゲームの作成にも活用できる強力なツールです。本章では、シェルスクリプトを使って「バトルシップゲーム」を作成し、その手順と詳細な解説を行います。この演習を通じて、シェルスクリプトの基本的な構文、配列の操作、関数の定義と利用、条件分岐、ループなどの知識を総合的に学ぶことができます。

バトルシップゲームの概要
ゲームの目的
- 敵の艦隊を見つけて全て撃沈すること。
ゲームのルール
- ボードの設定
・5×5のグリッドを使用します。
・敵の艦隊はランダムに配置された3隻の船から構成されます。
・巡洋艦(C):サイズ1
・潜水艦(S):サイズ1
・駆逐艦(D):サイズ1 - ゲームの進行
・プレイヤーは座標を指定して砲撃します(例:A1, B3)。
・命中した場合は「命中」、外れた場合は「外れ」と表示されます。
・全ての船を撃沈するとゲームクリアです。
ボードの例
1 | 2 | 3 | 4 | 5 | |
---|---|---|---|---|---|
A | |||||
B | |||||
C | |||||
D | |||||
E |
battleship.sh
の作成から実行までの手順と解説
ステップ1:スクリプトファイルの作成
ターミナルで以下のコマンドを実行し、新しいスクリプトファイル battleship.sh
を作成します。
user01@ubuntu:~$ nano battleship.sh
ステップ2:スクリプトの内容を記述
エディタが開いたら、以下のコードをコピーして貼り付けます。
#!/bin/bash
# ボードの初期化
board=()
for ((i=0; i<25; i++)); do
board[$i]="~"
done
# 船の配置
place_ships() {
ships=("C" "S" "D")
for ship in "${ships[@]}"; do
while true; do
position=$((RANDOM % 25))
if [ "${board[$position]}" == "~" ]; then
board[$position]=$ship
break
fi
done
done
}
# ボードの表示(プレイヤー用)
display_board() {
echo " 1 2 3 4 5"
rows=("A" "B" "C" "D" "E")
for i in {0..4}; do
echo -n "${rows[$i]} "
for j in {0..4}; do
index=$((i * 5 + j))
if [ "${hits[$index]}" == "hit" ]; then
echo -n "X "
elif [ "${hits[$index]}" == "miss" ]; then
echo -n "O "
else
echo -n "~ "
fi
done
echo
done
}
# 座標をインデックスに変換
coordinate_to_index() {
declare -A row_map=( ["A"]=0 ["B"]=1 ["C"]=2 ["D"]=3 ["E"]=4 )
row_letter=${1^^}
col_number=$2
row=${row_map[$row_letter]}
col=$((col_number - 1))
index=$((row * 5 + col))
echo $index
}
# ゲームの開始
place_ships
hits=()
echo "バトルシップゲームへようこそ!"
# メインゲームループ
while true; do
display_board
read -p "砲撃する座標を入力してください(例:A1): " input
row_input=${input:0:1}
col_input=${input:1}
if [[ ! "$row_input" =~ [A-Ea-e] ]] || [[ ! "$col_input" =~ [1-5] ]]; then
echo "エラー: A1からE5の範囲で入力してください。"
continue
fi
index=$(coordinate_to_index "$row_input" "$col_input")
if [ "${hits[$index]}" ]; then
echo "既に砲撃した座標です。別の座標を選んでください。"
continue
fi
if [ "${board[$index]}" != "~" ]; then
echo "命中!"
hits[$index]="hit"
board[$index]="X"
# 全ての船が撃沈されたかチェック
remaining_ships=0
for cell in "${board[@]}"; do
if [[ "$cell" == "C" || "$cell" == "S" || "$cell" == "D" ]]; then
remaining_ships=$((remaining_ships + 1))
fi
done
if [ "$remaining_ships" -eq 0 ]; then
echo "全ての船を撃沈しました!あなたの勝ちです。"
break
fi
else
echo "外れ!"
hits[$index]="miss"
fi
done
ステップ3:スクリプトの内容の解説
1.シェバン行
#!/bin/bash
- スクリプトが Bash シェルで実行されることを指定します。
2.ボードの初期化
board=()
for ((i=0; i<25; i++)); do
board[$i]="~"
done
- 目的:5×5のボードを表す配列
board
を初期化します。 - 解説:
・~
は海を表します。
・インデックス0から24までの配列要素を~
に設定します。
3.船の配置関数 place_ships
place_ships() {
ships=("C" "S" "D")
for ship in "${ships[@]}"; do
while true; do
position=$((RANDOM % 25))
if [ "${board[$position]}" == "~" ]; then
board[$position]=$ship
break
fi
done
done
}
- 目的:ランダムに船をボード上に配置します。
- 解説:
・船は「C」(巡洋艦)、「S」(潜水艦)、「D」(駆逐艦)の3種類。
・while
ループで空いているマス(~
)を見つけて船を配置します。
4.ボードの表示関数 display_board
display_board() {
echo " 1 2 3 4 5"
rows=("A" "B" "C" "D" "E")
for i in {0..4}; do
echo -n "${rows[$i]} "
for j in {0..4}; do
index=$((i * 5 + j))
if [ "${hits[$index]}" == "hit" ]; then
echo -n "X "
elif [ "${hits[$index]}" == "miss" ]; then
echo -n "O "
else
echo -n "~ "
fi
done
echo
done
}
- 目的:プレイヤーに見えるボードを表示します。
- 解説:
・命中箇所はX
、外れた箇所はO
、未攻撃箇所は~
で表示します。
・行はAからE、列は1から5でラベル付けします。
5.座標をインデックスに変換する関数 coordinate_to_index
coordinate_to_index() {
declare -A row_map=( ["A"]=0 ["B"]=1 ["C"]=2 ["D"]=3 ["E"]=4 )
row_letter=${1^^}
col_number=$2
row=${row_map[$row_letter]}
col=$((col_number - 1))
index=$((row * 5 + col))
echo $index
}
- 目的:ユーザーが入力した座標を配列のインデックスに変換します。
- 解説:
・row_map
連想配列で行の文字を数値にマッピングします。
・行と列からインデックスを計算します。
6.ゲームの開始と変数の初期化
place_ships
hits=()
echo "バトルシップゲームへようこそ!"
解説
place_ships
関数を呼び出して船を配置します。- 攻撃結果を記録する
hits
配列を初期化します。 - ゲーム開始のメッセージを表示します。
7.メインゲームループ
while true; do
display_board
read -p "砲撃する座標を入力してください(例:A1): " input
row_input=${input:0:1}
col_input=${input:1}
if [[ ! "$row_input" =~ [A-Ea-e] ]] || [[ ! "$col_input" =~ [1-5] ]]; then
echo "エラー: A1からE5の範囲で入力してください。"
continue
fi
index=$(coordinate_to_index "$row_input" "$col_input")
if [ "${hits[$index]}" ]; then
echo "既に砲撃した座標です。別の座標を選んでください。"
continue
fi
if [ "${board[$index]}" != "~" ]; then
echo "命中!"
hits[$index]="hit"
board[$index]="X"
# 全ての船が撃沈されたかチェック
remaining_ships=0
for cell in "${board[@]}"; do
if [[ "$cell" == "C" || "$cell" == "S" || "$cell" == "D" ]]; then
remaining_ships=$((remaining_ships + 1))
fi
done
if [ "$remaining_ships" -eq 0 ]; then
echo "全ての船を撃沈しました!あなたの勝ちです。"
break
fi
else
echo "外れ!"
hits[$index]="miss"
fi
done
解説
- ユーザー入力の処理
・座標を読み取り、行と列に分割します。
・正規表現で入力のバリデーションを行います。 - 攻撃結果の判定
・既に攻撃した座標かどうかをチェックします。
・命中か外れかを判定し、hits
配列に結果を記録します。 - 勝利条件のチェック
・ボード上に残っている船がないかを確認します。
・全ての船が撃沈された場合、ゲームを終了します。
ステップ4:スクリプトに実行権限を付与
スクリプトを保存してエディタを終了したら、実行権限を付与します。
user01@ubuntu:~$ chmod +x battleship.sh
ステップ5:スクリプトの実行
スクリプトを実行して、バトルシップゲームを開始します。
user01@ubuntu:~$ ./battleship.sh
実行例
バトルシップゲームへようこそ!
1 2 3 4 5
A ~ ~ ~ ~ ~
B ~ ~ ~ ~ ~
C ~ ~ ~ ~ ~
D ~ ~ ~ ~ ~
E ~ ~ ~ ~ ~
砲撃する座標を入力してください(例:A1): B2
外れ!
1 2 3 4 5
A ~ ~ ~ ~ ~
B ~ O ~ ~ ~
C ~ ~ ~ ~ ~
D ~ ~ ~ ~ ~
E ~ ~ ~ ~ ~
砲撃する座標を入力してください(例:A1): D4
命中!
1 2 3 4 5
A ~ ~ ~ ~ ~
B ~ O ~ ~ ~
C ~ ~ ~ ~ ~
D ~ ~ ~ X ~
E ~ ~ ~ ~ ~
砲撃する座標を入力してください(例:A1): A3
外れ!
(中略)
全ての船を撃沈しました!あなたの勝ちです。
解説
- ユーザーは砲撃したい座標を入力します。
- 攻撃結果が「命中」または「外れ」として表示されます。
- ボードが更新され、命中箇所は
X
、外れた箇所はO
で表示されます。 - 全ての船を撃沈するとゲームクリアとなります。
ここで作成したファイルの削除
ゲームのテストが終了したら、作成した battleship.sh
ファイルを削除します。
user01@ubuntu:~$ rm battleship.sh
まとめ
- シェルスクリプトの活用:シェルスクリプトを使って、バトルシップゲームを作成しました。これにより、スクリプトの基本的な構文や制御構造を学ぶことができます。
- 配列の操作:ボードや攻撃結果を配列で管理し、効率的にデータを扱いました。
- 関数の定義と利用:再利用可能なコードを関数として定義し、コードの可読性と保守性を向上させました。
- ランダム性の導入:
$RANDOM
変数を使用して、船の配置にランダム性を持たせました。 - 条件分岐とループ:
if
文やwhile
ループを使って、ゲームの進行とロジックを制御しました。 - 文字列操作:ユーザーの入力を解析し、座標をインデックスに変換するなどの文字列操作を行いました。
シェルスクリプトは、システム管理以外にも多くの用途で活用できます。今回のバトルシップゲームを基に、ボードサイズの拡大や船の種類・サイズの増加、攻撃回数の制限など、さらなる機能拡張に挑戦してみてください。