プログラミング言語を学ぶには、実際に何かを作ってみるというのが有効です。それも、どういうものなのかを自分がわかっているものを作ってみると、「自分の理解を(プログラミング言語で)表現する」という練習になります。
ということで、簡単なゲーム、Hit and Blowを作ってみます。
Hit and Blow
4桁の数字を当てるゲームです。
元々はマスターマインドと言って、4色のピンを用意するらしいです。ピンの代わりに数字を使うことが多いんじゃないかと。
Hit and Blowで検索すると、オンラインで楽しめるサイトもあるようです。
で、ルールは
- 桁(位置)も数字もあっていれば「ヒット」
- 数字はあっているが桁が違う場合は「ブロー」
- 予想された数字を判定して、「xヒットyブロー」と、ヒットとブローの数をそれぞれ回答する
- 4ヒットとなれば終了
です。
じゃぁこれをプログラミング…というわけにはいかなくて。
- ブラウザで遊ぶの?スマホアプリ?
- 2人いないと遊べないの?
など、ルール以外にも決めないといけないことがあります。それをうやむやにしたままにすると、「いや、そうじゃないよ」って1からやりなおさないといけなくなることもあるので、大事なことです。
今回は
- パソコンで遊ぶ。ブラウザではなくて、ターミナルに表示して入力する
- パソコンが決めた数値を、プレーヤー(人間)が当てる
という仕様にしましょう。
作り方を少し考える
この状態でプログラムを書き始めるのはよくないでしょう。どんな構成にするかを考えるのが先です。
構成というか、「何をやらないといけないか」ですね。
- 当ててもらう数字を決める
- 入力された数字を判定して、「X hit Y blow」と表示する
- 4 hitだったら終了する
この3つをやる必要があります、ざっくりですが。
当ててもらう数字を決める
「乱数で…」というわけにはいかないって、気が付きますか?
数字ってダブってはいけないのです。”2022″とか”1192″とか。元々が「4色のピン」ですからね。
あと、見落としがちなのが”123″みたいな3桁の数字。3桁ですけど、”0123″で4つの数字とも言えますよね。
こういった、明示されていない仕様?制限事項?を踏まえて、数字を決める方法を考えていきます。
- 乱数で決めた数字が条件にあっているかチェックして、合うまで繰り返す
- 一桁ずつ乱数で決めていって、決めるときにそれまでの数字とダブっていないかチェックする。ダブっていたら決め直す
というのが思いつきます。
スピードとかメモリの使用量とかで方法を決めるのですが、今回はお勉強です。エイヤって決めてしまいましょう。1番で。
判定する
まずは終わりの条件。4 ヒットって、数字が当たった、つまり、当ててもらう数字と入力された数字が一致したということですね。数字そのものを比較すればよいので、ヒットがいくつか数える必要はないと。
次に簡単そうなのは、ヒット。桁(位置)と数字が一致している個数を数えればいいので、当ててもらう数字と入力された数字の特定の桁が同じかどうかを見ていけばいい。
最後はブロー。入力された数字のある桁の数字(表現が難しい)が、当ててもらう数字に含まれているかを見ていくのですが、桁が同じだった場合はヒットになる(ブローにならない)ので、注意しなければなりません。これは、「ヒットになる」分を後から差っ引くことにすればよさそうです。
Pythonで表現する
「やらないといけないこと」をPythonでやっていきます。
長くなってきたら分ければいいよね?ということで、main.py に書いていくことにします。hit-and-blow.pyでもいいんですけど。
当ててもらう数字を決める
乱数が必要です。ググって見つかった記事を見るのもいいですが、公式ドキュメントを見る習慣を身につけるべきでしょう。それでわからなければ、他の記事を見ればいいのです。
ちょうどいい関数がありました。「これで0から9999まで」ではないのです。数字がダブってはいけないので「123から9876まで」でいいのです。「どうせダブりのチェックをするのだからシンプルに」という考え方もアリですが、取り得る数値(条件に当てはまる数値)という視点で考えれば、123~9876になります。
次に、各桁の数字を取り出す方法を考えましょう。一番右の桁は、10で割った時の「あまり」ですね。
右から2番目の桁は、「10で割った商」を10で割った時の「あまり」。
右から3番目の桁は、「100で割った商」を10で割った時の「あまり」。
右から4番目(一番左)は、「1000で割った商」です。
各桁の数字を取り出せたら、同じものがあるかどうかを判断して、「あったから不適切」「なかったから適切」と判断すればよいでしょう。
random.randint()で得られた数字が妥当かどうかを判断するということで、関数名はis_number_correct、戻り値はTrueかFalseとしましょう。判断すべき数字は引数で受け取ります。
判定する
ヒットがいくつか、ブローがいくつか、判定しないとゲームになりません。
判定とその結果を表示する処理を分けるとすると、ヒットとブローの数をそれぞれ返さなければならないので、関数にすると面倒そうです。ここでは、判定結果をその場で表示することにして、ゲーム終了(4 hit)だったらTrue、そうでなければFalseを返す関数にしましょう。
乱数で決めた数字とプレーヤーが入力した数字を渡して
- 両者が一致していればメッセージを出してTrueを返す
- ヒットとブローの数を数えて表示し、Falseを返す
という処理を行います。judgeという関数名にしましょう。
ヒットの数を数える部分とブローの数を数える部分は、別の関数にしてみましょうか。
count_hitはヒットの数を数えて、それを返します。
count_blowはブローの数を数えて、それを返します。hitとしてカウントすべきものも数えてしまうので、ヒットの数も渡すようにしましょう。
judgeはこれらを呼び出せばいいので、
つなげる
必要な処理はできたので、これらを繋げて遊べるようにします。
数字を決めるのは一番最初だけで、あとは、判定でTrue(ゲーム終了)になるまで、入力と判定の繰り返し。
で、最終的なプログラムはこちら。
Visual Studio Codeでターミナルを開いて動かしている様子。
もっときれいなプログラムに
今回は、「プログラミング始めた人ってこんな感じ?」と想像しながら作ってみました。
ですので、count_blow()で同じような処理が続いていたり、数字を桁毎にばらす処理がis_number_correct()とcount_hit()とcount_blow()にあったりと、まぁ、よろしくないプログラムになっています。
自分が書きたいと思ったプログラムが書けるようになったら、次のステップとして良いプログラムの書き方を勉強するのも良いでしょう。