【Python】英単語バラバラクイズを作成:漢字シャッフルクイズからの組み替え その1

  • URLをコピーしました!
目次

英単語バラバラクイズ

前回、漢字シャッフルクイズを関数化して、実行形式ファイルに変換しました。

結構大きな改変だったわけですが、その大きな改変をやったことで、大きなメリットが生まれました。

それは似通ったクイズを作る時、プログラムを流用できるというメリットです。

前回のプログラムのmain関数を見てみましょう。

def main():
    question_json = os.path.join(default_dir, 'question.json')
    fonts_json = os.path.join(default_dir, 'fonts.json')
    settings_json = os.path.join(default_dir, 'settings.json')
    question_log = os.path.join(default_dir, 'question_log.txt')
    
    timenow = datetime.datetime.now().strftime("%Y%m%d%H%M%S")
    
    question_dir = os.path.join(default_dir, 'question')
    
    output_dir = os.path.join(question_dir, timenow)
    os.mkdir(output_dir)
    
    output_original_name = timenow + '.png'
    output_original_path = os.path.join(output_dir, output_original_name)
    
    output_banner_name = 'banner_' + timenow + '.png'
    output_banner_path = os.path.join(output_dir, output_banner_name)
    
    output_question_name = 'question_' + timenow + '.png'
    output_question_path = os.path.join(output_dir, output_question_name)
    
    question, prev_question = question_choice(question_json, question_log)
    font_name, font_path = font_choice(fonts_json, default_dir)
    log_write(question_log, timenow, question, font_name)
    fig_make(output_original_path, font_path, question)
    banner_make(output_banner_path, font_path)
    img_num, crop_list, size = divide_image(divide_num, output_original_path, output_dir)
    shuffle_image(img_num, output_dir, crop_list, size, output_question_path)
    add_banner(output_question_path, output_banner_path)
    applytotwitter(settings_json, output_question_path, prev_question)

実はこのmain関数の中身はさらに四つに分類することができます。

1. 問題や設定を読み込んでいる部分

    question_json = os.path.join(default_dir, 'question.json')
    fonts_json = os.path.join(default_dir, 'fonts.json')
    settings_json = os.path.join(default_dir, 'settings.json')
    question_log = os.path.join(default_dir, 'question_log.txt')

2. 出入力のファイル名やフォルダ名を設定している部分

timenow = datetime.datetime.now().strftime("%Y%m%d%H%M%S")
    
    question_dir = os.path.join(default_dir, 'question')
    
    output_dir = os.path.join(question_dir, timenow)
    os.mkdir(output_dir)
    
    output_original_name = timenow + '.png'
    output_original_path = os.path.join(output_dir, output_original_name)
    
    output_banner_name = 'banner_' + timenow + '.png'
    output_banner_path = os.path.join(output_dir, output_banner_name)
    
    output_question_name = 'question_' + timenow + '.png'
    output_question_path = os.path.join(output_dir, output_question_name)

3. 問題を作成する準備やログの書き出し、ツイートする部分

    question, prev_question = question_choice(question_json, question_log)
    font_name, font_path = font_choice(fonts_json, default_dir)
    log_write(question_log, timenow, question, font_name)




    applytotwitter(settings_json, output_question_path, prev_question)

4. 問題の画像を作成する部分

    fig_make(output_original_path, font_path, question)
    banner_make(output_banner_path, font_path)
    img_num, crop_list, size = divide_image(divide_num, output_original_path, output_dir)
    shuffle_image(img_num, output_dir, crop_list, size, output_question_path)
    add_banner(output_question_path, output_banner_path)

こうみると「1. 問題や設定を読み込んでいる部分」、「2. 出入力のファイル名やフォルダ名を設定している部分」、「3. 問題を作成する準備やログの書き出し、ツイートする部分」は他のクイズになっても多少の修正だけで使うことができます。

つまり「4. 問題の画像を作成する部分」だけ変えれば、違う種類のクイズになるというわけです。

(もちろんquestion.json内の問題は変更する必要はあります)

ということで早速新しいクイズ、英単語バラバラクイズを作ってみました。

ブログラム全体

どういうクイズかというと、こんな感じで英単語がバラバラに配置されるので、その元の単語を当てるというゲームです。

ということで作成したプログラムがこちら。

default_dir = './'

import random
import json
from matplotlib import pyplot as plt
import matplotlib.font_manager as fm
import datetime
import os
import random
import tweepy
import csv

def question_choice(question_json, question_log):
    with open(question_log, 'r') as f_in:
        reader = csv.reader(f_in)
        for row in reader:
            prev_question = row[1]
    
    with open(question_json, 'r') as f_in:
        question_list = json.load(f_in)

    question = question_list[random.choice(list(question_list))]
    
    return question, prev_question

def font_choice(fonts_json, default_dir):
    with open(fonts_json, 'r') as f_in:
        fonts_list = json.load(f_in)
    
    font_name = random.choice(list(fonts_list))
    font_path = os.path.join(default_dir, fonts_list[font_name])
    
    return font_name, font_path

def log_write(question_log, timenow, question, font_name):
    with open(question_log, 'a') as f_in:
        row = f'{timenow},{question},{font_name}\n'
        f_in.write(row)
        
def fig_make(output_question_path, font_path, question):
    fp = fm.FontProperties(fname=font_path)
    
    fig = plt.figure()
    plt.clf()
    
    position = []
    while True:
        x = round(random.random(), 1)
        y = round(random.random(), 1)
        if not [x,y] in position:
            position.append([x,y])
        if len(position) == len(question):
            break
            
    color_list = ['r','g', 'b', 'c', 'm', 'y', 'k']
    
    for pos, word in zip(position, question):
        
        plt.text(pos[0], pos[1], word, fontsize=25, color=random.choice(color_list), fontproperties=fp)
        
    plt.gca().spines['right'].set_visible(False)
    plt.gca().spines['top'].set_visible(False)
    plt.gca().spines['bottom'].set_visible(False)
    plt.gca().spines['left'].set_visible(False)
    plt.tick_params(labelbottom=False, labelleft=False, labelright=False, labeltop=False, bottom=False, left=False, right=False, top=False)

    plt.xlabel('3PySci https://3pysci.com', fontsize=30, fontproperties=fp)
    plt.savefig(output_question_path, facecolor="white", pad_inches = 0)
    
def applytotwitter(settings_json, output_question_path, prev_question):
    
    text = f'前回の答えは「{prev_question}」でした。\nこのバラバラになった英単語はなんでしょう?😆\n#分かったらRT\n#脳トレ\n#クイズ\n#パズル\n#アハ体験\n#Python\n#プログラミング'
    
    with open(settings_json, 'r') as f_in:
        settings = json.load(f_in)

    auth = tweepy.OAuthHandler(settings['consumer_key'], settings['consumer_secret'])
    auth.set_access_token(settings['access_token'], settings['access_token_secret'])

    api = tweepy.API(auth, wait_on_rate_limit = True)

    api.update_status_with_media(status = text, filename = output_question_path)

def main():
    question_json = os.path.join(default_dir, 'question.json')
    fonts_json = os.path.join(default_dir, 'fonts.json')
    settings_json = os.path.join(default_dir, 'settings.json')
    question_log = os.path.join(default_dir, 'question_log.txt')
    
    timenow = datetime.datetime.now().strftime("%Y%m%d%H%M%S")
    
    question_dir = os.path.join(default_dir, 'question')
    
    output_dir = os.path.join(question_dir, timenow)
    os.mkdir(output_dir)
    
    output_question_name = 'question_' + timenow + '.png'
    output_question_path = os.path.join(output_dir, output_question_name)
    
    question, prev_question = question_choice(question_json, question_log)
    font_name, font_path = font_choice(fonts_json, default_dir)
    log_write(question_log, timenow, question, font_name)
    fig_make(output_question_path, font_path, question)
    applytotwitter(settings_json, output_question_path, prev_question)

if __name__ == '__main__':
    main()

前回の漢字シャッフルクイズのプログラムの関数と比較してみると、こんな感じです。

question_choiceそのまま移植
font_choiceそのまま移植
log_writeそのまま移植
fig_make内容を大幅に変更
banner_make削除
divide_image削除
shuffle_image削除
add_banner削除
applytotwitter内容を多少変更

「divide_image」、「shuffle_image」は漢字シャッフルクイズに特有なものなので今回は使用しません。

また「banner_make」、「add_banner」は今回は後からバナーを付ける必要がないので、今回は使用しないというわけです。

ということでクイズ画像を作るメインとなる「fig_make関数」だけ入れ替えてしまえば、新しいクイズのプログラムになるということです。

fig_make関数

ということで今回新たに書き換えてたfig_make関数はこんな感じです。

def fig_make(output_question_path, font_path, question):
    fp = fm.FontProperties(fname=font_path)
    
    fig = plt.figure()
    plt.clf()
    
    position = []
    while True:
        x = round(random.random(), 1)
        y = round(random.random(), 1)
        if not [x,y] in position:
            position.append([x,y])
        if len(position) == len(question):
            break
            
    color_list = ['r','g', 'b', 'c', 'm', 'y', 'k']
    
    for pos, word in zip(position, question):
        
        plt.text(pos[0], pos[1], word, fontsize=25, color=random.choice(color_list), fontproperties=fp)
        
    plt.gca().spines['right'].set_visible(False)
    plt.gca().spines['top'].set_visible(False)
    plt.gca().spines['bottom'].set_visible(False)
    plt.gca().spines['left'].set_visible(False)
    plt.tick_params(labelbottom=False, labelleft=False, labelright=False, labeltop=False, bottom=False, left=False, right=False, top=False)

    plt.xlabel('3PySci https://3pysci.com', fontsize=30, fontproperties=fp)
    plt.savefig(output_question_path, facecolor="white", pad_inches = 0)

今回のキモとなる場所はこちら。

    position = []
    while True:
        x = round(random.random(), 1)
        y = round(random.random(), 1)
        if not [x,y] in position:
            position.append([x,y])
        if len(position) == len(question):
            break

英単語をバラバラにした後、画面上のあっちこっちにばら撒くために、その座標をランダムに取得しています。

        x = round(random.random(), 1)
        y = round(random.random(), 1)

ただし今回はfor文ではなく、while文を使っています。

理由としては、random.random()は0から1までの小数をランダムに取得できますが、その後round関数を使って小数第一位までにしています。

つまり選択できるのは0、0.1、0.2、0.3、0.4、0.5、0.6、0.7、0.8、0.9、1の11個。

そうなると文字数分だけ座標を取得した場合、全く同じ座標を示してしまう文字が出てくる可能性があります。

そこでランダムに「x, y」を取得したのち、これまで出てこなかった組み合わせのものだけリストに追加するということにしています。

        if not [x,y] in position:
            position.append([x,y])

こうすることで文字が被らなくなるというわけです。

しかしwhile文だとずっと続いてしまうため、単語の文字数分の座標が集まったら、breakでwhile文を抜けています。

        if len(position) == len(question):
            break

あとは文字の色をその都度ランダムに変えていること、

    color_list = ['r','g', 'b', 'c', 'm', 'y', 'k']
    
    for pos, word in zip(position, question):
        
        plt.text(pos[0], pos[1], word, fontsize=25, color=random.choice(color_list), fontproperties=fp)

宣伝のバナーをX軸ラベルを使って表示しているため、枠線とメモリ線を消していること、

    plt.gca().spines['right'].set_visible(False)
    plt.gca().spines['top'].set_visible(False)
    plt.gca().spines['bottom'].set_visible(False)
    plt.gca().spines['left'].set_visible(False)
    plt.tick_params(labelbottom=False, labelleft=False, labelright=False, labeltop=False, bottom=False, left=False, right=False, top=False)

    plt.xlabel('3PySci https://3pysci.com', fontsize=30, fontproperties=fp)

というのが大きな変更点になるでしょうか。

JSONファイルの変更点

JSONファイルで変更するのは基本的には「question.json」だけです。

ただ、形式的には同じで「”問題番号”:”問題”」です。

{
    "1":"question",
    "2":"preliminary",
    "3":"beautiful",
    "4":"technology",
    "5":"science"
}

今回は問題が英語に変更されているという点が違うだけです。

あとは全体的に細かい修正を加えれば、新しいクイズプログラムの出来上がり。

全く0から作るよりはかなり簡単にクイズプログラムが作れました。

ということで調子に乗って、次回ももう一つ新しいクイズプログラムを作成してみましょう。

ではでは今回はこんな感じで。

よかったらシェアしてね!
  • URLをコピーしました!

コメント

コメントする

目次