0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

wikipediaのAPIを使ってゲーム作ってみた#4

Posted at

はじめに

こんにちは!前回は、文中にあるワードをクリックしたら、そのワードの記事を生成する機能を追加しました。

前の記事からかなり時間が経過してしまいましたね・・・。自分でコードを見返したら「これなんで動いてんの?」状態になっちゃってました笑

Qiitaに自分である程度解説書いておいてよかった~

というわけで、今回は、「スタートとなるワード」と「ゴールとなるワード」を取得し、ゴールに辿りついたら「ゴール!」みたいなのを表示させるようにしましょう。

前回の記事はこちらです👇

作業開始

1.スタートとなるワードを取得する

wikipediaに収納されている記事のタイトルをランダムに取得し、それをスタートにしましょう。
では、その関数を作ります。

views.py
def select_word():
    import requests
    #wikipediaからランダムに記事を受け取るAPI
    S = requests.Session()
    URL = "https://ja.wikipedia.org/w/api.php"
    PARAMS = {
        "action": "query",
        "format": "json",
        "list": "random",
        "rnlimit": "1",
        "rnnamespace": "0",
    }

    #要求したデータを受け取る
    R = S.get(url=URL, params=PARAMS)
    DATA = R.json()

    #データをランダムに受け取る
    RANDOMS = DATA["query"]["random"]
    selected_word = RANDOMS[0]["title"]
           
    return selected_word

この関数を使えばwikipediaからランダムな記事のタイトルを取得できます。
WikipediaのAPIについては#1👇で詳しく解説しているので割愛します。

select_first_word()で生成したselected_wordを使って、スタートのページを表示させることができます。

2.ゴールとなるワードを取得する

こちらも、スタートとなるワードを取得した時と全く同じようにすればいいのですが、ゴールをwikipediaにあるすべての記事を対象にしてしまうと、めちゃくちゃマイナーな用語の記事が出てきて、ゲームが難しすぎるので、タイトル画面でモード選択できるようにします。
views.py
#タイトル画面
@csrf_exempt
def title(request):
    #モード選択用
    if 'checkbox' in request.POST:
        #チェックボックスのデータを受け取る
        checkedbox = request.POST.get('checkbox')

        #モードに応じて目標の文字を受け取る
        if checkedbox == "1":           
            #別モード
            #今回は実装しない
        elif checkedbox == "2":
            #wikiモード
            goal_word = select_word()
        else:
            print("エラーです")

        # セッションにgoal_wordを保存
        request.session['goal_word'] = goal_word

    return render(request, 'main/title.html')


titlse.js
// スタートボタン
function submitForm(event) {
    event.preventDefault(); // デフォルトのフォーム送信をキャンセル

    var form = document.getElementById("start");
    var formData = new FormData(form);

    // チェックボックスの値を取得
    var checkbox = document.querySelector('input[name="checkbox"]:checked');
    var checkboxValue = checkbox ? checkbox.value : null

    //チェックボックスの値が取得されなかったら
    if (checkboxValue === null) {
        console.error('チェックボックスが選択されていません');
        alert('ゲームモードを選択してください');
        return;
    }

    //formDataにチェックボックスの値を追加
    formData.append('checkbox', checkboxValue);

    // フォームデータを送信
    fetch(form.action, {
        method: form.method,
        body: formData,
    }).then(function(response) {
        // ページ遷移
        window.location.href = "/maingame/";
        console.log("ページ遷移・・・完了")
    }).catch(function(error) {
        console.error('エラー:', error);
    });
}

// 排他的モード選択
function toggleCheckboxes(clickedCheckbox) {
    var checkboxes = document.getElementsByName("checkbox");
    checkboxes.forEach(function(checkbox) {
        if (checkbox !== clickedCheckbox) {
            checkbox.checked = false;
        }
    });
}

titlse.html
{% extends "main/base.html" %}
{% load static %}

{% block header %}
    <link rel="stylesheet" type="text/css" href="{% static 'css/title.css' %}">
    <script src="{% static 'js/title.js' %}"></script>
{% endblock %}

{% block content %}
    <div class="title_panel">
        <form id="start" action="" method="POST">
            <div  class="start_button btn-shine">
                <a href="#" onclick="submitForm(event)">ゲーム開始</a>
            </div>
            
            <label>
                <input type="checkbox" id="countrymode" name="checkbox"  value="1" onclick="toggleCheckboxes(this)">別モード
            </label>
            <label>
                <input type="checkbox" id="wikimode" name="checkbox" value="2" onclick="toggleCheckboxes(this)">wikiモード
            </label>
        </form>
    </div>
{% endblock %}

まず、javascriptとhtmlの方で、モード選択の情報を受け取るチェックボックスを作成します。

title.js
    //htmlから「start」のIDのformを受け取る
    var form = document.getElementById("start");
    //formのデータをformDataとして宣言する
    var formData = new FormData(form);

    // チェックされたチェックボックスの値を追加する。
    // 今回はhtmlでそれぞれのvalueに1や2を設定しているので、それを登録する。
    var checkbox = document.querySelector('input[name="checkbox"]:checked');
    var checkboxValue = checkbox ? checkbox.value : null

    //チェックボックスの値が取得されなかったらエラーメッセージを表示する
    if (checkboxValue === null) {
        console.error('チェックボックスが選択されていません');
        alert('ゲームモードを選択してください');
        return;
    }

    //formDataに値を追加
    formData.append('checkbox', checkboxValue);

この時、チェックボックスが複数選択できてしまうと、モード選択ができないので、1つ選択したら、それ以外の選択を無効化する関数を作ります。

title.js
// 排他的モード選択
function toggleCheckboxes(clickedCheckbox) {
    //htmlでname属性がcheckboxと定義されている要素を取得する。
    var checkboxes = document.getElementsByName("checkbox");
    //各チェックボックスを調査し、clickedCheckbox(クリックしたチェックボックス)でなければ、そのチェックボックスをfalseにする。
    checkboxes.forEach(function(checkbox) {
        if (checkbox !== clickedCheckbox) {
            checkbox.checked = false;
        }
    });
}

javascriptはこんな感じです。
次はpython側の処理です。
受け取ったモードデータに基づいてゴールのワードを設定します。

チェックされたチェックボックスの値に応じて、ゴールに設定するワードを決めます。
今回は、wikipedia内にあるランダムなワードを取得します。

views.py
    #モード選択用
    if 'checkbox' in request.POST:
        #チェックボックスのデータを受け取る
        checkedbox = request.POST.get('checkbox')

        #モードに応じて目標の文字を受け取る
        if checkedbox == "1":           
            #別モード
            #今回は実装しない
        elif checkedbox == "2":
            #wikiモード
            goal_word = select_word()
        else:
            print("エラーです")

さて、ここで決定したゴールのワードをメインのhtmlで表示したいので、メイン画面を定義しているmain()に送ります。
ただ、main()でtitle()を呼び出すわけにはいかないので、間接的にデータを送らなければなりません。
これを実現するには、request.sessionというDjangoフレームワーク内でセッション管理を行うためのオブジェクトを利用して、無理やり送ります。
もっとスマートなやり方を知っている方は是非教えてください・・・。

views.py
# セッションにgoal_wordを保存
request.session['goal_word'] = goal_word

これでmain()でtitle()で定義したゴールのワードを受け取ることができます。
#3で作成したmain()にゴールのワードを受け取って、htmlに渡せるようにしましょう。

views.py
#メインの画面
@csrf_exempt
def main(request):         
    #ワードクリック用
    if 'word' in request.POST:
        #ここは変化なし
    #スタートゲーム用
    else:
        # セッションからgoal_wordを取得
        goal_word = request.session.get('goal_word')#←追加

        #省略

    #main.htmlに情報を返す
    return render(request, 'main/main.html', {
        'page_text': page_text, 
        'page_title': page_title,
        'res': res,
        'goal_word':goal_word,#←追加
        })
main.html
{% extends "main/base.html" %}
{% load static %}

{% block header %}
    <title>Result</title>
    <script src="{% static 'js/main.js' %}"></script>
{% endblock %}

{% block content %}
    <!-- ここにゴールのワードを表示させる -->
    <div id="goal-word" data-goal-word="{{ goal_word }}">
        {{ goal_word }}
    </div>

    <div class="main-text" id="main-text">
        <h1>{{ page_title }}</h1>
        <p>{{ res|safe }}</p>
    </div>

{% endblock %}

これで無事、メインの画面にゴールのワードを表示させられるようになりました。

3.ゴールの処理

最後に、ゴールのワードにたどり着いたら、何らかのアクションが発生するようにしておきましょう。
今回は、表示されている記事のタイトルとゴールのワードが一致した時に「ゴール!」と画面上に表示させましょう。

まず、一致の判断をjavascriptでさせるためにjavascriptでゴールのワードを受け取ります。

{{ goal_word }}の中にある、正しいゴールのワードを取得するには、htmlでdata-goal-word{{ goal_word }}の中のデータを入れることで、受け取ることができるようになります。

main.js
// IDがgoal-wordで、data-goal-wordのデータを受け取る
let goalWord = document.getElementById('goal-word').getAttribute('data-goal-word');

ここでデータを入れたgoalWordと、ページを作るときにviews.pyから受け取るpage_titleを比較して、一致したらhtmlに「ゴール!」と書き込みます。

main.js
    //省略
        
    .then(data => {
            let goalWord = document.getElementById('goal-word').getAttribute('data-goal-word');
            // 受け取ったデータを使用してHTMLを再構築する
            let html = '';
            html += `
                    <h1>${data.page_title}</h1>
                    <p>${data.res}</p>
                `;
                
            //下のif文を追加
            //ゴールにたどり着いたとき
            if (goalWord === data.page_title) {
                html += "<p>正しい</p>";
            }

            
            document.getElementById('main-text').innerHTML = html;
        })

すごくシンプルですが、一応ゴールした時に何らかのアクションを起こすことができました。

おわりに

以上、「スタートとなるワード」と「ゴールとなるワード」を取得し、ゴールに辿りついたら「ゴール!」と表示させる所まで作成しました。

一応、ゲームとして遊べるようにはなりましたね!

ちょっと時間を空けてしまったので、説明が難しかったな・・・。
もし分かりにくいところや、改善点があれば、是非教えてください!!

次回は、まだモードが一個のままなので、新しいモードを追加していきたいと思います。

Github:https://github.com/ITTON2001/WikiWord

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

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?