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

ブラックジャックゲームの概要
ゲームの目的
- プレイヤーとディーラーがカードの合計点数を競い、21に近い方が勝ちます。ただし、21を超えるとバスト(負け)になります。
ゲームのルール
- カードの値
・数字カード(2〜10):そのままの数値。
・絵札(J、Q、K):10点。
・エース(A):1点または11点(手札の合計に応じて有利な方を選択)。 - ゲームの進行
・プレイヤーとディーラーはそれぞれ2枚のカードを引きます。
・プレイヤーはカードを追加で引く(ヒット)か、引かない(スタンド)かを選択できます。
・ディーラーは手札の合計が17以上になるまでカードを引きます。 - 勝敗の判定
・手札の合計が21を超えた場合、バストとなり負け。
・プレイヤーとディーラーの手札の合計を比較し、21に近い方が勝ち。
・合計が同じ場合は引き分け。
カードの例
カード | 値 |
---|---|
2〜10 | 2〜10 |
J(ジャック) | 10 |
Q(クイーン) | 10 |
K(キング) | 10 |
A(エース) | 1または11 |
blackjack.sh
の作成から実行までの手順と解説
ステップ1:スクリプトファイルの作成
ターミナルで以下のコマンドを実行し、新しいスクリプトファイル blackjack.sh
を作成します。
user01@ubuntu:~$ nano blackjack.sh
ステップ2:スクリプトの内容を記述
エディタが開いたら、以下のコードをコピーして貼り付けます。
#!/bin/bash
# デッキの作成
create_deck() {
suits=("Hearts" "Diamonds" "Clubs" "Spades")
ranks=("2" "3" "4" "5" "6" "7" "8" "9" "10" "J" "Q" "K" "A")
deck=()
for suit in "${suits[@]}"; do
for rank in "${ranks[@]}"; do
deck+=("$rank of $suit")
done
done
}
# カードを引く関数
draw_card() {
index=$((RANDOM % ${#deck[@]}))
card="${deck[$index]}"
deck=("${deck[@]:0:$index}" "${deck[@]:$((index + 1))}")
echo "$card"
}
# カードの値を取得する関数
get_card_value() {
rank=$(echo "$1" | awk '{print $1}')
case "$rank" in
"2"|"3"|"4"|"5"|"6"|"7"|"8"|"9"|"10")
echo "$rank"
;;
"J"|"Q"|"K")
echo "10"
;;
"A")
echo "11"
;;
esac
}
# 手札の合計を計算する関数
calculate_total() {
total=0
aces=0
hand=("$@")
for card in "${hand[@]}"; do
value=$(get_card_value "$card")
total=$((total + value))
if [ "$value" -eq 11 ]; then
aces=$((aces + 1))
fi
done
while [ $total -gt 21 ] && [ $aces -gt 0 ]; do
total=$((total - 10))
aces=$((aces - 1))
done
echo "$total"
}
# ゲームの開始
create_deck
player_hand=()
dealer_hand=()
# プレイヤーとディーラーが2枚のカードを引く
player_hand+=("$(draw_card)")
dealer_hand+=("$(draw_card)")
player_hand+=("$(draw_card)")
dealer_hand+=("$(draw_card)")
echo "ブラックジャックへようこそ!"
# プレイヤーのターン
while true; do
echo
echo "あなたの手札: ${player_hand[@]}"
player_total=$(calculate_total "${player_hand[@]}")
echo "あなたの合計: $player_total"
if [ "$player_total" -gt 21 ]; then
echo "バストしました!あなたの負けです。"
exit
fi
read -p "カードを引きますか?(Y/N): " choice
if [[ "$choice" =~ ^[Yy]$ ]]; then
player_hand+=("$(draw_card)")
else
break
fi
done
# ディーラーのターン
echo
echo "ディーラーの手札: ${dealer_hand[@]}"
dealer_total=$(calculate_total "${dealer_hand[@]}")
echo "ディーラーの合計: $dealer_total"
while [ "$dealer_total" -lt 17 ]; do
echo "ディーラーがカードを引きます。"
dealer_hand+=("$(draw_card)")
echo "ディーラーの手札: ${dealer_hand[@]}"
dealer_total=$(calculate_total "${dealer_hand[@]}")
echo "ディーラーの合計: $dealer_total"
done
# 勝敗の判定
echo
if [ "$dealer_total" -gt 21 ]; then
echo "ディーラーがバストしました!あなたの勝ちです。"
elif [ "$player_total" -gt "$dealer_total" ]; then
echo "あなたの勝ちです!"
elif [ "$player_total" -lt "$dealer_total" ]; then
echo "あなたの負けです。"
else
echo "引き分けです。"
fi
ステップ3:スクリプトの内容の解説
1.シェバン行
#!/bin/bash
- スクリプトが Bash シェルで実行されることを指定します。
2.デッキの作成関数 create_deck
create_deck() {
suits=("Hearts" "Diamonds" "Clubs" "Spades")
ranks=("2" "3" "4" "5" "6" "7" "8" "9" "10" "J" "Q" "K" "A")
deck=()
for suit in "${suits[@]}"; do
for rank in "${ranks[@]}"; do
deck+=("$rank of $suit")
done
done
}
- 目的:トランプの全52枚のデッキを作成します。
- 解説:
・suits
:カードのスート(柄)を配列に格納。
・ranks
:カードのランク(数字や絵札)を配列に格納。
・ネストされたfor
ループで全ての組み合わせをdeck
配列に追加。
3.カードを引く関数 draw_card
draw_card() {
index=$((RANDOM % ${#deck[@]}))
card="${deck[$index]}"
deck=("${deck[@]:0:$index}" "${deck[@]:$((index + 1))}")
echo "$card"
}
- 目的:デッキからランダムにカードを引き、そのカードを返します。
- 解説:
・$RANDOM
を使用してデッキのインデックス範囲内でランダムな数を生成。
・選択したカードをdeck
から削除して重複を防止。
・引いたカードを返す。
4.カードの値を取得する関数 get_card_value
get_card_value() {
rank=$(echo "$1" | awk '{print $1}')
case "$rank" in
"2"|"3"|"4"|"5"|"6"|"7"|"8"|"9"|"10")
echo "$rank"
;;
"J"|"Q"|"K")
echo "10"
;;
"A")
echo "11"
;;
esac
}
- 目的:カードのランクからその値を取得します。
- 解説:
・awk
を使用してカード名からランク部分を抽出。
・case
文でランクに応じた値を返す。
5.手札の合計を計算する関数 calculate_total
calculate_total() {
total=0
aces=0
hand=("$@")
for card in "${hand[@]}"; do
value=$(get_card_value "$card")
total=$((total + value))
if [ "$value" -eq 11 ]; then
aces=$((aces + 1))
fi
done
while [ $total -gt 21 ] && [ $aces -gt 0 ]; do
total=$((total - 10))
aces=$((aces - 1))
done
echo "$total"
}
- 目的:手札の合計点数を計算し、必要に応じてエース(A)の値を11から1に調整します。
- 解説:
・手札の各カードの値を合計。
・エースの数をカウント。
・合計が21を超え、エースがある場合、合計から10を引きエースの値を1に変更。
6.ゲームの開始と初期設定
create_deck
player_hand=()
dealer_hand=()
# プレイヤーとディーラーが2枚のカードを引く
player_hand+=("$(draw_card)")
dealer_hand+=("$(draw_card)")
player_hand+=("$(draw_card)")
dealer_hand+=("$(draw_card)")
echo "ブラックジャックへようこそ!"
解説
- デッキを作成。
- プレイヤーとディーラーの手札を初期化。
- それぞれ2枚のカードを引く。
- ゲーム開始のメッセージを表示。
7.プレイヤーのターン
while true; do
echo
echo "あなたの手札: ${player_hand[@]}"
player_total=$(calculate_total "${player_hand[@]}")
echo "あなたの合計: $player_total"
if [ "$player_total" -gt 21 ]; then
echo "バストしました!あなたの負けです。"
exit
fi
read -p "カードを引きますか?(Y/N): " choice
if [[ "$choice" =~ ^[Yy]$ ]]; then
player_hand+=("$(draw_card)")
else
break
fi
done
解説
- 手札と合計を表示。
- 合計が21を超えた場合、バストとしてゲーム終了。
- ユーザーにカードを引くかどうかを尋ね、引く場合はカードを追加。
8.ディーラーのターン
echo
echo "ディーラーの手札: ${dealer_hand[@]}"
dealer_total=$(calculate_total "${dealer_hand[@]}")
echo "ディーラーの合計: $dealer_total"
while [ "$dealer_total" -lt 17 ]; do
echo "ディーラーがカードを引きます。"
dealer_hand+=("$(draw_card)")
echo "ディーラーの手札: ${dealer_hand[@]}"
dealer_total=$(calculate_total "${dealer_hand[@]}")
echo "ディーラーの合計: $dealer_total"
done
解説
- ディーラーの手札と合計を表示。
- 合計が17以上になるまでカードを引く。
9.勝敗の判定
echo
if [ "$dealer_total" -gt 21 ]; then
echo "ディーラーがバストしました!あなたの勝ちです。"
elif [ "$player_total" -gt "$dealer_total" ]; then
echo "あなたの勝ちです!"
elif [ "$player_total" -lt "$dealer_total" ]; then
echo "あなたの負けです。"
else
echo "引き分けです。"
fi
解説
- ディーラーがバストした場合、プレイヤーの勝ち。
- プレイヤーとディーラーの合計を比較し、勝敗を決定。
ステップ4:スクリプトに実行権限を付与
スクリプトを保存してエディタを終了したら、実行権限を付与します。
user01@ubuntu:~$ chmod +x blackjack.sh
ステップ5:スクリプトの実行
スクリプトを実行して、ブラックジャックゲームを開始します。
user01@ubuntu:~$ ./blackjack.sh
実行例
ブラックジャックへようこそ!
あなたの手札: 7 of Hearts 3 of Clubs
あなたの合計: 10
カードを引きますか?(Y/N): y
あなたの手札: 7 of Hearts 3 of Clubs K of Diamonds
あなたの合計: 20
カードを引きますか?(Y/N): n
ディーラーの手札: 5 of Spades 9 of Hearts
ディーラーの合計: 14
ディーラーがカードを引きます。
ディーラーの手札: 5 of Spades 9 of Hearts 8 of Clubs
ディーラーの合計: 22
ディーラーがバストしました!あなたの勝ちです。
解説
- プレイヤーのターン
・手札は「7 of Hearts」と「3 of Clubs」で合計10点。
・カードを引いて「K of Diamonds」を得て、合計20点。
・さらに引くか尋ねられ、引かないことを選択。 - ディーラーのターン
・手札は「5 of Spades」と「9 of Hearts」で合計14点。
・合計が17未満のため、カードを引く。
・「8 of Clubs」を引いて合計22点となり、バスト。 - 結果
・ディーラーがバストしたため、プレイヤーの勝ち。
ここで作成したファイルの削除
ゲームのテストが終了したら、作成した blackjack.sh
ファイルを削除します。
user01@ubuntu:~$ rm blackjack.sh
まとめ
- シェルスクリプトの応用:ブラックジャックゲームを通じて、シェルスクリプトでのゲーム作成方法を学びました。
- 配列の操作:デッキや手札を配列で管理し、カードの操作を効率的に行いました。
- 関数の定義と利用:再利用可能なコードを関数として定義し、コードの可読性と保守性を向上させました。
- ランダム性の導入:
$RANDOM
変数を使用して、ゲームに必要なランダム性を実現しました。 - 条件分岐とループ:
if
文やwhile
ループを使って、ゲームの進行とロジックを制御しました。 - 文字列操作:カード名の解析や表示など、文字列操作を行いました。
シェルスクリプトは、システム管理以外にも多くの用途で活用できます。今回のブラックジャックゲームを基に、ベット機能の追加や複数ラウンドの実装、他のプレイヤーとの対戦など、さらなる機能拡張に挑戦してみてください。