LoginSignup
0
0

More than 1 year has passed since last update.

DjangoとDocker練習OA16o3o_2o0 Tic-Tac-Toeのサーバーからクライアントへ送る通信メッセージを取り決めしよう!

Last updated at Posted at 2022-08-21

サンプルを見る

📖 この記事のゴール

目標

〇×ゲーム(Tic tac toe)の通信対戦をしたい

いきなり作るのは難しいので その前に、サーバーからクライアントへ送るメッセージを取り決めておく

情報

この記事は Lesson 1. から順に全部やってこないと ソースが足りず実行できないので注意されたい

What is This is
Lesson 1. 📖 DjangoとDockerでゲーム対局サーバーを作ろう!

この記事のアーキテクチャ:

What is This is
OS Windows10
Container Docker
Database Postgresql, (Redis)
Program Language Python 3
Web framework Django
Auth allauth
Frontend Vuetify
Data format JSON
Others (Socket), Web socket
Editor Visual Studio Code (以下 VSCode と表記)

以下、参考にした元記事は 📖Django Channels and WebSockets だ。
わたしの記事は単に やってみた ぐらいの位置づけだ

ディレクトリ構成を抜粋すると 以下のようになっている

    ├── 📂 src1                            # あなたのDjangoサーバー開発用ディレクトリー。任意の名前
    │   ├── 📂 apps1
    │   │   ├── 📂 accounts_vol1o0    # アプリケーション
    │   │   ├── 📂 portal_v1                # アプリケーション
    │   │   ├── 📂 practice_vol1o0              # アプリケーション
    │   │   ├── 📂 tic_tac_toe_vol1o0           # アプリケーション
    │   │   └── 📂 tic_tac_toe_vol2o0           # アプリケーション
    │   │       ├── 📂 migrations
    │   │       │   └── 📄 __init__.py
    │   │       ├── 📂 static
    │   │       │   └── 📂 tic_tac_toe_vol2o0
    │   │       │       └── 📂 think
    │   │       │           ├── 📂 concepts
    │   │       │           │   └── 📄 ver1o0.js
    │   │       │           ├── 📂 engine
    │   │       │           │   └── 📄 ver1o0.js
    │   │       │           ├── 📂 judge_ctrl
    │   │       │           │   └── 📄 ver1o0.js
    │   │       │           ├── 📂 position
    │   │       │           │   └── 📄 ver1o0.js
    │   │       │           ├── 📂 things
    │   │       │           │   └── 📄 ver1o0.js
    │   │       │           └── 📂 user_ctrl
    │   │       │               └── 📄 ver1o0.js
    │   │       ├── 📂 templates
    │   │       │   └── 📂 tic_tac_toe_vol2o0
    │   │       │       └── 📂 think
    │   │       │           └── 📂 engine_manual
    │   │       │               └── 📄 ver1o0.html
    │   │       ├── 📂 views
    │   │       │   └── 📂 think
    │   │       │       └── 📂 engine_manual
    │   │       │           └── 📂 ver1o0
    │   │       │               ├── 📄 __init__.py
    │   │       │               └── 📄 v_render.py
    │   │       ├── 📄 __init__.py
    │   │       ├── 📄 admin.py
    │   │       ├── 📄 apps.py
    │   │       └── 📄 tests.py
    │   ├── 📂 data
    │   ├── 📂 project1                  # プロジェクト
    │   │   ├── 📄 __init__.py
    │   │   ├── 📄 asgi.py
    │   │   ├── 📄 settings_secrets_example.txt
    │   │   ├── 📄 settings.py
    │   │   ├── 📄 urls_accounts_vol1o0.py
    │   │   ├── 📄 urls_practice.py
    │   │   ├── 📄 urls_tic_tac_toe_v1.py
    │   │   ├── 📄 urls_tic_tac_toe_v2.py
    │   │   ├── 📄 urls.py
    │   │   ├── 📄 ws_urls_tic_tac_toe_v1.py
    │   │   └── 📄 wsgi.py
    │   ├── 📂 project2                  # プロジェクト
    │   ├── 🐳 docker-compose-project2.yml
    │   ├── 🐳 docker-compose.yml
    │   ├── 🐳 Dockerfile
    │   ├── 📄 manage.py
    │   └── 📄 requirements.txt
    ├── 📂 src1_meta
    │   ├── 📂 data
    │   │   └── 📄 urls.csv
    │   └── 📂 scripts
    │       └── 📂 auto_generators
    │           └── 📄 urls.py
    ├── 📂 src2_local                      # Djangoとは関係ないもの
    │    ├── 📂 sockapp1
    │    └── 📂 websockapp1
    └── 📄 .gitignore

手順

Step. Dockerコンテナの起動

👇 (していなければ) Docker コンテナを起動しておいてほしい

# docker-compose.yml ファイルを置いてあるディレクトリーへ移動してほしい
cd src1

# Docker コンテナ起動
docker-compose up

Step [OA16o3o_2o0g1o_1o0] メッセージ作成 - msg/s2c_json_gen/messages/end/ver1o0/init.py ファイル

Separated from OA16o3o_2o0g1o0

👇 以下のファイルを新規作成してほしい

    └── 📂 src1
        └── 📂 apps1
            └── 📂 tic_tac_toe_vol2o0    # アプリケーション
                └── 📂 views
                    └── 📂 msg
                        └── 📂 s2c_json_gen
                            └── 📂 messages
                                └── 📂 end
                                    └── 📂 ver1o0
👉                                      └── 📄 __init__.py
# BOF OA16o3o_2o0g1o_1o0

class EndS2cMessage:
    def __init__(self, args):
        """設定"""
        self._winner = args["player1"]

    def asDict(self):
        """Dict形式で取得"""
        return {
            'type': 'send_message',  # type属性は必須
            'event': "S2C_End", # Server to client
            'winner': self._winner,
        }

# EOF OA16o3o_2o0g1o_1o0

Step [OA16o3o_2o0g1o_2o0] メッセージ作成 - msg/s2c_json_gen/messages/moved/ver1o0/init.py ファイル

Separated from OA16o3o_2o0g1o0

👇 以下のファイルを新規作成してほしい

    └── 📂 src1
        └── 📂 apps1
            └── 📂 tic_tac_toe_vol2o0    # アプリケーション
                └── 📂 views
                    └── 📂 msg
                        └── 📂 s2c_json_gen
                            └── 📂 messages
                                ├── 📂 end
                                │   └── 📂 ver1o0
                                │       └── 📄 __init__.py
                                └── 📂 moved
                                    └── 📂 ver1o0
👉                                      └── 📄 __init__.py
# BOF OA16o3o_2o0g1o_2o0

class MovedS2cMessage:
    def __init__(self, args):
        """設定"""
        self._sq = args["sq1"]
        self._pieceMoved = args["piece1"]

    def asDict(self):
        """Dict形式で取得"""
        return {
            'type': 'send_message',  # type属性は必須
            'event': 'S2C_Moved', # Server to client
            'sq': self._sq,
            'piece': self._pieceMoved,
        }

# EOF OA16o3o_2o0g1o_2o0

Step [OA16o3o_2o0g1o_3o0] メッセージ作成 - msg/s2c_json_gen/messages/start/ver1o0/init.py ファイル

Separated from OA16o3o_2o0g1o0

👇 以下のファイルを新規作成してほしい

    └── 📂 src1
        └── 📂 apps1
            └── 📂 tic_tac_toe_vol2o0    # アプリケーション
                └── 📂 views
                    └── 📂 msg
                        └── 📂 s2c_json_gen
                            └── 📂 messages
                                ├── 📂 end
                                │   └── 📂 ver1o0
                                │       └── 📄 __init__.py
                                ├── 📂 moved
                                │   └── 📂 ver1o0
                                │       └── 📄 __init__.py
                                └── 📂 start
                                    └── 📂 ver1o0
👉                                      └── 📄 __init__.py
# BOF OA16o3o_2o0g1o_3o0

class StartS2cMessage:
    def __init__(self, args):
        """設定"""
        pass

    def asDict(self):
        """Dict形式で取得"""
        return {
            'type': 'send_message',  # type属性は必須
            'event': "S2C_Start", # Server to client
        }

# EOF OA16o3o_2o0g1o_3o0

Step [OA16o3o_2o0g1o0]

  1. Separated to OA16o3o_2o0g1o_1o0
  2. Separated to OA16o3o_2o0g1o_2o0
  3. Separated to OA16o3o_2o0g1o_3o0
  4. Removed

Step [OA16o3o_2o0g2o0] 画面作成 - msg/s2c_json_gen/v1o0.html ファイル

👇 以下のファイルを新規作成してほしい

    └── 📂 src1
        └── 📂 apps1
            └── 📂 tic_tac_toe_vol2o0    # アプリケーション
                ├── 📂 templates
                │   └── 📂 tic_tac_toe_vol2o0    # アプリケーションと同名
                │       └── 📂 msg
                │           └── 📂 s2c_json_gen
👉              │               └── 📄 ver1o0.html
                └── 📂 views
                    └── 📂 msg
                        └── 📂 s2c_json_gen
                            └── 📂 commands
                                └── 📂 ver1o0
                                    └── 📄 __init__.py
<!-- BOF OA16o3o_2o0g2o0 -->
{% load static %} {# 👈あとで static "URL" を使うので load static します #}
<!DOCTYPE html>
<html>
    <head>
        <link rel="shortcut icon" type="image/png" href="{% static 'favicon.ico' %}" />
        <link href="https://fonts.googleapis.com/css?family=Roboto:100,300,400,500,700,900" rel="stylesheet" />
        <link href="https://cdn.jsdelivr.net/npm/@mdi/font@6.x/css/materialdesignicons.min.css" rel="stylesheet" />
        <link href="https://cdn.jsdelivr.net/npm/vuetify@2.x/dist/vuetify.min.css" rel="stylesheet" />
        <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no, minimal-ui" />
        <title>Tic Tac Toe</title>
        <style>
            /* 等幅 */
            .v-textarea textarea {
                font-family: monospace, monospace;
            }
        </style>
    </head>
    <body>
        <div id="app">
            <v-app>
                <v-main>
                    <v-container fluid>
                        <h1>Tic Tac Toe - S2C Json Generator</h1>
                        <form method="POST" action="">
                            <!--                    =
                                                    1
                            1. 宛先を間違えないように
                               `http://example.com/tic-tac-toe/vol2.0/ver1.0/`
                                                                             =
                            -->

                            {% csrf_token %}
                            <!--
                               ==========
                               1
                            1. form要素の中に csrf_token を入れてください
                            -->

                            <!-- 入力 -->
                            <!-- リスト -->
                            <!-- `po_` は POST送信するパラメーター名の目印 -->
                            <v-select name="po_messageType" v-model="c2sMessageTypeListbox.value" :items="c2sMessageTypeItems" label="サーバーからクライアントへ送られてくるメッセージの種類一覧"></v-select>
                            <v-select name="po_sq1" v-model="sqListbox.value" :items="sqItems" label="マス番号一覧"></v-select>
                            <v-select name="po_player1" v-model="playerListbox.value" :items="playerItems" label="プレイヤー一覧"></v-select>
                            <v-select name="po_piece1" v-model="pieceListbox.value" :items="pieceItems" label="駒一覧"></v-select>

                            <!-- ボタン -->
                            <v-btn type="submit" block elevation="2" v-on:click="pullVu()"> Pull </v-btn>

                            <!-- 出力 -->
                            <v-textarea required v-model="outputTextbox.value" label="Output" rows="10"/>
                        </v-form>
                    </v-container>
                </v-main>
            </v-app>
        </div>

        <script src="{% static 'tic_tac_toe_vol2o0/think/things/ver1o0.js' %}"></script>
        <script src="{% static 'tic_tac_toe_vol2o0/think/concepts/ver1o0.js' %}"></script>
        <script src="{% static 'tic_tac_toe_vol2o0/think/position/ver1o0.js' %}"></script>
        <script src="{% static 'tic_tac_toe_vol2o0/think/user_ctrl/ver1o0.js' %}"></script>
        <script src="{% static 'tic_tac_toe_vol2o0/think/judge_ctrl/ver1o0.js' %}"></script>
        <script src="{% static 'tic_tac_toe_vol2o0/think/engine/ver1o0.js' %}"></script>
        <!--            =================================================
                        1
        1. src1/apps1/tic_tac_toe_vol2o0/static/tic_tac_toe_vol2o0/think/engine/ver1o0.js
                                         ================================================
        -->

        <script src="https://cdn.jsdelivr.net/npm/vue@2.x/dist/vue.js"></script>
        <script src="https://cdn.jsdelivr.net/npm/vuetify@2.x/dist/vuetify.js"></script>
        <script>
            const vue1 = new Vue({
                el: "#app",
                vuetify: new Vuetify(),
                data: {
                    // 出力
                    outputTextbox: {
                        // `dj_` は Djangoでレンダーするパラメーター名の目印
                        value: JSON.stringify(JSON.parse('{{ dj_output_json|escapejs }}'), null, '    '),
                    },
                    // メッセージの種類リストボックス
                    c2sMessageTypeListbox: {
                        value: "S2C_End",
                    },
                    // マス番号リストボックス
                    sqListbox: {
                        value: "",
                    },
                    // プレイヤーリストボックス
                    playerListbox: {
                        value: "",
                    },
                    // 駒リストボックス
                    pieceListbox: {
                        value: "",
                    },
                    // メッセージの種類一覧
                    c2sMessageTypeItems: ["S2C_End", "S2C_Moved", "S2C_Start"],
                    // マス番号の一覧
                    sqItems: ["", 0, 1, 2, 3, 4, 5, 6, 7, 8],
                    // プレイヤーの一覧
                    playerItems: ["", "X", "O"],
                    // 駒の一覧
                    pieceItems: ["", "X", "O"],
                },
                methods: {
                    // 関数名の末尾の Vu は vue1 のメソッドであることを表す目印
                    /**
                     * po_input 欄のコマンドを入力します
                     */
                    pullVu() {
                        // ボタン押下後、ページ再読み込み前にやりたいことがあれば、ここに書いてください
                    },
                },
            });
        </script>
    </body>
</html>
<!-- EOF OA16o3o_2o0g2o0 -->

Step [OA16o3o_2o0g3o0] ビュー作成 - msg/s2c_json_gen/v1o0/v_render.py ファイル

👇 以下のファイルを新規作成してほしい

    └── 📂 src1
        └── 📂 apps1
            └── 📂 tic_tac_toe_vol2o0    # アプリケーション
                ├── 📂 templates
                │   └── 📂 tic_tac_toe_vol2o0    # アプリケーションと同名
                │       └── 📂 msg
                │           └── 📂 s2c_json_gen
                │               └── 📄 ver1o0.html
                └── 📂 views
                    └── 📂 msg
                        └── 📂 s2c_json_gen
                            ├── 📂 commands
                            │   └── 📂 ver1o0
                            │       └── 📄 __init__.py
                            └── 📂 ver1o0
👉                              └── 📄 v_render.py
# BOF OA16o3o_2o0g3o0

import json
from django.shortcuts import render

# OA16o3o_2o0g1o_1o0 〇×ゲーム2.0巻 S2cメッセージ End 1.0版
from apps1.tic_tac_toe_vol2o0.views.msg.s2c_json_gen.messages.end.ver1o0 import EndS2cMessage
from apps1.tic_tac_toe_vol2o0.views.msg.s2c_json_gen.messages.moved.ver1o0 import MovedS2cMessage
from apps1.tic_tac_toe_vol2o0.views.msg.s2c_json_gen.messages.start.ver1o0 import StartS2cMessage
#          ------------------                                       ------        ---------------
#          11                                                       12            2
#    ---------------------------------------------------------------------
#    10
# 10, 12. ディレクトリー
# 11. アプリケーション
# 2. `12.` に含まれる __init__.py にさらに含まれるクラス


def render_main(request, template_path):
    """OA16o3o_2o0g3o0 S2C JSON ジェネレーター - 描画

    Parameters
    ----------
    template_path : str
        Template path
    """

    if request.method == "POST":
        # 送信後

        messageType = request.POST.get("po_messageType")
        args = {
            "player1": request.POST.get("po_player1"),
            "sq1": request.POST.get("po_sq1"),
            "piece1": request.POST.get("po_piece1"),
        }
        # print(
        #     f'[render_main] messageType:{messageType} player1:{args["player1"]} sq1:{args["sq1"]} piece1:{args["piece1"]}')
        # TODO バリデーションチェックしたい

        message_object_dict = {
            "S2C_End": EndS2cMessage(args),
            "S2C_Moved": MovedS2cMessage(args),
            "S2C_Start": StartS2cMessage(args),
        }

        if messageType in message_object_dict:
            doc = message_object_dict.get(messageType).asDict()
            dj_output_json = json.dumps(doc)
        else:
            # 空っぽのJSON文字列
            dj_output_json = "{}"

    else:
        # 空っぽのJSON文字列
        dj_output_json = "{}"

    context = {
        # `dj_` は Djangoでレンダーするパラメーター名の目印
        "dj_output_json": dj_output_json,
    }

    return render(request, template_path, context)

# EOF OA16o3o_2o0g3o0

Step [OA16o3o_2o0g4o0] ビュー作成 - msg/s2c_json_gen/v1o0 フォルダー

👇 以下のファイルを新規作成してほしい

    └── 📂 src1
        └── 📂 apps1
            └── 📂 tic_tac_toe_vol2o0    # アプリケーション
                ├── 📂 templates
                │   └── 📂 tic_tac_toe_vol2o0    # アプリケーションと同名
                │       └── 📂 msg
                │           └── 📂 s2c_json_gen
                │               └── 📄 ver1o0.html
                └── 📂 views
                    └── 📂 msg
                        └── 📂 s2c_json_gen
                            ├── 📂 commands
                            │   └── 📂 ver1o0
                            │       └── 📄 __init__.py
                            └── 📂 ver1o0
👉                              ├── 📄 __init__.py
                                └── 📄 v_render.py
# BOF OA16o3o_2o0g4o0

class S2cJsonGenView():
    """OA16o3o_2o0g4o0 S2C Json ジェネレーター ビュー"""

    template_path = "tic_tac_toe_vol2o0/msg/s2c_json_gen/ver1o0.html"
    #                               ^two
    #                -----------------------------------------------
    #                1
    # 1. src1/apps1/tic_tac_toe_vol2o0/templates/tic_tac_toe_vol2o0/msg/c2s_json_gen/ver1o0.html
    #                                            -----------------------------------------------

    @staticmethod
    def render(request):
        """描画"""

        # 以下のファイルはあとで作ります
        from .v_render import render_main
        #    ---------        -----------
        #    1                2
        # 1. `src1/apps1/tic_tac_toe_vol2o0/views/msg/c2s_json_gen/v1o0/v_render.py`
        #                                                               --------
        # 2. `1.` に含まれる関数

        return render_main(
            request,
            S2cJsonGenView.template_path)

# EOF OA16o3o_2o0g4o0

Step [OA16o3o_2o0g5o0]

Merged to OA16o3o_2o0g5o1o0

Step [OA16o3o_2o0g5o1o0] ルート編集 - urls.csv ファイル

👇 以下の既存ファイルの末尾に追記してほしい

    ├── 📂 src1
    │   └── 📂 apps1
    │       └── 📂 tic_tac_toe_vol2o0    # アプリケーション
    │           ├── 📂 templates
    │           │   └── 📂 tic_tac_toe_vol2o0    # アプリケーションと同名
    │           │       └── 📂 msg
    │           │           └── 📂 s2c_json_gen
    │           │               └── 📄 ver1o0.html
    │           └── 📂 views
    │               └── 📂 msg
    │                   └── 📂 s2c_json_gen
    │                       ├── 📂 commands
    │                       │   └── 📂 ver1o0
    │                       │       └── 📄 __init__.py
    │                       └── 📂 ver1o0
    │                           ├── 📄 __init__.py
    │                           └── 📄 v_render.py
    └── 📂 src1_meta
        └── 📂 data
👉          └── 📄 urls.csv
...略... file,path,name,comment,module,class,alias,method
...略...


../src1/project1/urls_tic_tac_toe_vol2o0_autogen.py,tic-tac-toe/vol2.0/s2c-json-gen/ver1.0/,,"OA16o3o_2o0g5o1o0 〇×ゲーム2.0巻 S2C JSON ジェネレーター1.0版",apps1.tic_tac_toe_vol2o0.views.msg.s2c_json_gen.ver1o0,S2cJsonGenView,S2cJsonGenViewV1o0,render

Step [OA16o3o_2o0g5o2o0] ルート編集 - コマンド打鍵

👇 以下のコマンドを打鍵してほしい

cd ../src1_meta
python -m scripts.auto_generators.urls
cd ../src1
docker-compose restart
  • ディレクトリーは、がんばって移動してほしい
  • スクリプトについて See also: O3o2o_1o0g2o0
  • 設定ファイルを変更したら、サーバーの再起動が必要

Step. Web画面へアクセス

📖 http://localhost:8000/tic-tac-toe/vol2.0/s2c-json-gen/ver1.0/

次の記事

📖 OA16o3o0 Webブラウザ越しに2人対戦できる〇×ゲームを作ろう! Vuetify編

参考にした記事

Python

📖 3 Ways to Implement Python Switch Case Statement

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0