はじめに
なぜじゃんけんゲームなのか
- 研修でJavaを習ったときにじゃんけんゲームを作ったけれど、シェルスクリプトで作ってみたらどうなるのだろうという興味
- 単純にいろいろな要素(if文やキー入力など)が入っているので勉強になりそうだった
前提 - 実務で約5か月ほどLinuxとシェルスクリプトを触った(とはいえずっとやっていたわけではない)
- 大学は文系卒、専門は美術と哲学(これで特定されそう)
以上のような状態なのであまり高度な技術で作れるわけではありませんが、間違っている点や改善できる点などあればぜひ教えてください。
環境
OS:Windows上のVirtual Boxに構築したCentOS7
bash:バージョン4.2.46
実際にじゃんけんゲームを作成する
####基本設計
- 前口上でこのプログラムが何なのかを説明
- ユーザーによるキー入力
- コンピュータの手を決定
- 勝敗を判定
- 結果を出力
おおまかな処理の流れは上記の予定で作成を始める
####詳細設計
- echoコマンドで説明を出力
- readコマンドでユーザーのキー入力を待つ
- コンピュータの手を決定する
- ランダムな数字をひとつ決定(
$RANDOM
を利用)し、3で割ったあまりの数字をファイル出力 - catコマンドでファイルを読みだして変数に格納
- 格納した変数を
グー
、チョキ
、パー
に変換し、別の変数に格納
- ランダムな数字をひとつ決定(
- echoコマンドでコンピュータの手を表示する
- 勝敗の判定を行う
####実装
まずはおまじない
#!/bin/bash
#以下雑談(どうしても話したかった、絶対私以外にもこう思った人いるはず)
はじめてシェルスクリプトを触ったときは#
がコメントだとは知らず(研修で触ったJavaはコメントが//
だった)、「ほーん、先頭にはこういうのをつけるのか」程度の認識でした。
しかし、シェルのコメントが#
だと知ってからは「なんでわざわざ先頭にコメントつけてんだろ、でもなんか意味深な文字列だよなあ」と思い、実際に調べてみると、どうやらコイツは「シバン」というらしく、インタプリタの指定を行うという役割を担っているそう。
詳しいことはさっぱり分からなかったが、ここを変えるとファイルが実行できたりできなかったりするので、とても重要な役割だということは分かった。
#雑談おしまい
(2018/11/18追記)
コメント欄よりご丁寧な解説をいただきました。
ありがとうございます!
echoコマンドで説明を出力
echo "コンピュータとじゃんけんをしましょう、出す手を入力してください"
ここは特に何もありません、ただただechoでこれはじゃんけんゲームだよって言っただけです。
readでユーザーのキー入力を待つ
echo "グー・チョキ・パーのどれかを入力してください"
echo -n "あなたの手 : "
read input
ここではinput
という変数にユーザーの手を格納しています。
read
でキー入力待ちのプロンプトを実装しています。
コンピュータの手の元ネタとなる数字を決定
echo $(($RANDOM % 3)) > ./com_tmp`
ここではまず、ランダムな数字を一つ選び、選ばれた数字を3で割って、あまりの数(0-2)をcom_tmp
というファイルに出力しています。
これは後続の手順でコンピュータの手にグー
、チョキ
、パー
を振る元ネタの数字となります。
コンピュータの手を演算できる形に変換
if [ $com_hand = 0 ]; then
com_hand_dec="グー"
elif [ $com_hand = 1 ]; then
com_hand_dec="チョキ"
elif [ $com_hand = 2 ]; then
com_hand_dec="パー"
else
echo "コンピュータの手が正しく処理されませんでした、ゲームを終了します"
exit 1;
fi
ここでは先ほど用意した0から2の数字を判定し、新たな変数を作成して、そこに数字によってグー
、チョキ
、パー
の文字列を割り振り、格納します。
後続の手順で勝敗判定を行う際に文字列同士の演算を行うためです。
申し訳程度のエラー処理も入っています。シェルを実行したディレクトリが書き込み禁止だったりした場合はここでエラーになります。
#そもそもそんなディレクトリでじゃんけんゲームをするシチュエーションは想像できませんが・・・
コンピュータの手を出力
echo "コンピュータの手 : "$com_hand_dec
フェアにするために判定の前にユーザーにコンピュータの手を知らせます。
勝敗の判定
if [ $com_hand_dec = $input ]; then
echo "あいこです、もう一度遊びましょう"
exit 0;
fi
if [ $com_hand_dec = "グー" ]; then
if [ $input = "パー" ]; then
echo "おめでとうございます!あなたの勝ちです!"
exit 0;
else
echo "残念、あなたの負けです。外出は控えましょう。"
exit 0;
fi
fi
if [ $com_hand_dec = "チョキ" ]; then
if [ $input = "グー" ]; then
echo "おめでとうございます!あなたの勝ちです!"
exit 0;
else
echo "残念、あなたの負けです。壺を買ったりしないようにしましょう。"
exit 0;
fi
fi
if [ $com_hand_dec = "パー" ]; then
if [ $input = "チョキ" ]; then
echo "おめでとうございます!あなたの勝ちです!"
exit 0;
else
echo "残念、あなたの負けです。転ばないように注意しましょう。"
exit 0;
fi
fi
ここでは勝敗の判定を行います。
コンピュータの手がグーだった場合、チョキだった場合、パーだった場合のそれぞれでユーザーの手を確認し、判定します。
判定後は勝敗結果のメッセージを出力し、正常終了します。
作り終わった、そう思ったけれど
そう、勘のいい方々ならお気づきかもしれませんが、この仕様は非常にまずいです。
コンピュータの手はエラー処理も入っているため、大丈夫(だと思っている)ですが、ユーザーの手の部分で明らかな設計ミスがあります。
ユーザーが予期しない手を入力した際にその入力を弾く、あるいはエラーにする処理がどこにもありません。
これではあまりにもお粗末なので(もともとお粗末かもですが)、ユーザーの手の正常性チェックを入れたいと思います。
キー入力を関数化する
function key_input () {
echo "グー・チョキ・パーのどれかを入力してください"
#キー入力
echo -n "あなたの手 : "
read input
}
関数化したキー入力を呼び出す
key_input
これでキー入力を関数化することができました。
この関数にユーザーの入力をチェックする処理を入れていきます。
入力値の判定後に正常に入力されていなかった場合はもう一度自分自身を呼び出します。
つまり、再帰呼び出しの処理を行います。
再帰呼び出しの処理を関数に追加
function key_input () {
echo "グー・チョキ・パーのどれかを入力してください"
#キー入力
echo -n "あなたの手 : "
read input
case $input in
"グー")
return 0;
;;
"チョキ")
return 0;
;;
"パー")
return 0;
;;
*)
echo "入力規則を満たしていません、不正はいけませんよ?"
key_input
;;
esac
}
これでキー入力の確認ができるようになりました。
それでは実行してみましょう。
#試験
正常な値を入力した場合
[user@localhost ~]$ ./game.sh
コンピュータとじゃんけんをしましょう、出す手を入力してください
グー・チョキ・パーのどれかを入力してください
あなたの手 : グー
コンピュータの手 : グー
あいこです、もう一度遊びましょう
[user@localhost ~]$ ./game.sh
コンピュータとじゃんけんをしましょう、出す手を入力してください
グー・チョキ・パーのどれかを入力してください
あなたの手 : チョキ
コンピュータの手 : パー
おめでとうございます!あなたの勝ちです!
きちんと勝敗の判定ができています!Javaのときはこれに3日もかかかりました(笑)
異常な値を入力した場合
[user@localhost ~]$ ./game.sh
コンピュータとじゃんけんをしましょう、出す手を入力してください
グー・チョキ・パーのどれかを入力してください
あなたの手 : 異常な値
入力規則を満たしていません、不正はいけませんよ?
グー・チョキ・パーのどれかを入力してください
あなたの手 : いじょうなあたい
入力規則を満たしていません、不正はいけませんよ?
グー・チョキ・パーのどれかを入力してください
あなたの手 : イジョウナアタイ
入力規則を満たしていません、不正はいけませんよ?
グー・チョキ・パーのどれかを入力してください
あなたの手 : ijounaatai
入力規則を満たしていません、不正はいけませんよ?
グー・チョキ・パーのどれかを入力してください
あなたの手 :
しっかりと異常な値の判定ができています。
異常だった場合にももう一度自分を呼び出すことができていますね。
これで正常に機能したと言えるでしょう。
#リリース
こんなのをGitHubで公開したところでだれか使うかも分かりませんが、せっかくなのでリリースとして出来上がった資材をGitHub上で公開したいと思います。
「こんな機能付けたらいいんじゃない?」とか「ここは別のやり方のほうがスマートじゃない?」とかありましたら是非コメントやプルリクください。お待ちしております。
[GitHub
mercurius51/any/tree/master/janken]
(https://github.com/mercurius51/any/blob/master/janken)
(2018/11/17追記)
コメント欄よりご指摘いただいた内容をリポジトリにマージいたしました。
ご指摘ありがとうございました。
https://github.com/mercurius51/any/commit/9f9c454c78fb60ceae33aaded8ce8773ad55adbb
#反省
・仕様策定の段階でエラー処理などに目を向けておらず、適当に実装してしまったので完成後に設計の甘さが露見してしまった。
・もう少しスマートな処理ができる部分がある気がするが、エイヤーで作ってしまった。
#今後の課題
・スマートに処理できる部分は最適化した処理を行いたい。
・あいこだった場合はもう一度自動でゲームを実行する処理を入れたい。