コマンドラインで動くようになったので、Web化してブラウザで遊べるようにしましょう。
Web化するときの問題点
Webの通信はステートレスです。Hit and Blowでは、「当てるべき数字」が問題になります。コンピュータが作った数字を当てるために何度も入力するので、当たるまで、その数字を覚えておかなければなりません。「当てる数字はxxxx」という状態(ステート)を保持しておく必要があります。
また、ゲームを進める上では、「どの数字だと 何 hit 何 blowか」という判定結果も表示したいです。履歴ですね。履歴ですから、今までプレーヤーが入力した数字とその判定結果を記録しておく必要があります。一方、ゲーム開始時点では、履歴はないはずですから、どこかのタイミングで履歴をクリアする必要があります。
セッション
リクエストをまたいで情報を伝えていくときに使う方法の1つが、セッションです。Hit and Blowでは、コンピュータが決めた数字をずっと伝えていく必要がありますし、プレーヤーが入力した数字とその判定結果も伝えていく必要があります。ですから、これらをセッションに格納します。
画面
Webの場合、基本的には、アクセスした結果を画面に表示します。Hit and Blowでは、プレーヤーが数字を入力(アクセス)しますから、受け取って判定して、その結果を表示させればよいでしょう。
最初(トップページ)にアクセスしたとき(ゲーム開始時点)はHTTP Method GETでの、数字を入力したときはHTTP Method POSTでのアクセスになりますので、ゲーム開始かプレー中かは、HTTP Methodで区別できます。
数字を当てた時はゲーム終了ですので、数字の入力欄は不要です。プレー中とは別の表示内容にします。トップページへのリンクを設けておけば続けてプレーできますし、そのリンクをクリックしたときはGETでのアクセスになるので、ゲーム開始だと判断できます。
Flaskを使う
FlaskはWebアプリケーションフレームワークです。Webでのお作法的なところはフレームワークに任せられるので、簡単に作れるようになります。安全に作れるかどうかは、使い方次第ですが。
ということで、Flaskを使ってWeb化したものが、こちら。
少しプログラムを見てみましょう。
ゲーム開始
ゲーム開始はトップページにHTTP Method GETでアクセスするので、
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
@app.route("/", methods = ["GET"])
def main():
session["target"] = target_num()
session["history"] = []
return render_template("index.html")
@app.route()
と付ければ、Webアクセスしたときの処理を行う関数になります。フレームワークって偉大。コンピュータに数字を作ってもらって、履歴(list型)は空っぽにしておきます。
画面の情報(HTML)を返す必要があるので、templatesディレクトリにあるindex.htmlを返すようにします。
プレー中(判定処理)
プレーヤーが入力した数字がPOSTで送られてくるので、
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
@app.route("/", methods=["POST"])
def play():
t: str = "0000" + (request.form.get("number") or "" )
p: str = t[len(t)-4:]
j = judge(session["target"], p)
h =session["history"]
h.append(j)
session["history"] = h
if j[1] == 4:
return render_template("win.html")
return render_template("index.html")
リクエスト先のpathが同じでも、methodによって処理する関数を分けられます。
数字の入力はhtmlのformで行われるので、request.form.get()
で取り出します。数字が入力されないまま送信される(ゲームとしては一手損する)こともあり得て、その場合、None
が返されます。空文字だったら型が同じで扱いやすいのに…。
ということで、(request.formget() or "")
です。None
はTrue
/False
の判定をすればFalse
になるので、or
の後ろにある""
が返されることになります。これで、常にstr型になります。
あとは、今までのinput_number()
と同じ処理を行って、4桁(4文字)の数字にします。数字かどうかのチェックはしていないので、4文字ですね。チェックはすべきでしょう。今回は手抜きです。
判定処理のjudge()
も変更しています。
今までは、数字が当たった時のメッセージをこの関数の中で表示していました。が、ゲーム終了の画面を用意することにしました。この関数で終了画面を返すのは、ちょっと欲張りすぎでしょう。judgeの名にふさわしく、hitとblowの数を返したいです。履歴のことを考えて、プレーヤーが入力した数字もいっしょに返しましょう。戻り値の型をtupleにして、プレーヤーの数字、hit数、blow数をまとめて返します。
judge()の戻り値にあるhitの数が4であれば、数字を当てられたということです。ゲーム終了ですので終了画面であるwin.htmlを返します。そうでなければ次の数字をプレーヤーに入力してもらうため、入力欄のあるindex.htmlを返します。
遊んでみる
プログラムを直接実行したときの処理も書いてあります。
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
if __name__ == "__main__":
app.run(debug=True)
ので、Visual Studio Codeでターミナルを開いて、
python3 main.py
と入力します。ブラウザで http://127.0.0.1:5000/ を開きましょう。数字を入力する欄のある画面が開くはずです。


数字を入力してJudgeボタンを押すと、判定結果が表示されます。


頑張って数字を当てましょう。