Python(ランダム問題生成(改2))

前回まで作成していたランダム生成問題ですが、最初にexit入力で終了できるように設計しようとしていたにもかかわらず、exit入力時の処理を記載し忘れていたので処理を追加しました。
また、+-と浮動小数点の入力検査を再帰を利用して簡潔にまとめました。
難易度レベル2,3は通常の人が計算するには難しいことと%や//演算子の計算で正確な数字でなくても0,1などの計算結果になったり、/で分母に巨大な数の**演算が入ると正確な数でなくても極値0で浮動小数点のround処理で0.0になったりする問題があり、計算問題としてどうなのって事から、debug表示で式の要素が表示され、数合わせ問題みたいに変更しました。

import random
from random import randint as rand
from time import time

class Question:
    def __init__(self):
        self._list=[]
        self._operators=[]
        self._maxNum=9
        self._length=3

g_q=[]
for i in range(4):
    g_q.append(Question())
    if i==0:
        g_q[i]._operators=["+", "-", "*"]
    elif i==1:
        g_q[i]._operators=["+", "-", "*","/"]
        g_q[i]._maxNum = 19
        g_q[i]._length = 5
    elif i==2:
        g_q[i]._operators=["+", "-", "*","/","**"]
        g_q[i]._length = 7
        g_q[i]._maxNum = 99
    else:
        g_q[i]._operators=["+", "-", "*","/","**","//","%"]
        g_q[i]._length = 7
        g_q[i]._maxNum = 999    

def input_difficulty():
    """入力を求める関数"""
    print("0:簡単 1:普通 2:難しい 3:激難")
    return input("0~3の範囲で難易度を入力してください:")

def get_difficulty_int(function):
    """<高階関数>入力を求める関数の値を判断する関数
       @param function 入力処理を行う関数
       @return s 認識可能なstring"""
    while True:
        s = function()        
        if s.lower() == "exit":
            break
        if s.isdigit():
            n=int(s)
            if n < 0 or n > 3:
                print(f"「{n}」は0~3の数ではありません\n正しい入力値をお願いします")            
            else:
                return int(s)
        else:
            print(f"「{s}」は正の数値ではありません\n正しい入力値をお願いします")
    return "exit"

def input_num():
    """入力を求める関数"""
    return input("問題の出題数を入力してください:")

def get_int(function):
    """<高階関数>入力を求める関数の値を判断する関数
       @param function 入力処理を行う関数
       @return tmpNum 正しい入力値の数"""
    while True:
        s = function()
        if s.isdigit():
            if int(s)>0:
                return int(s)
            else:
                print("0より大きな正の整数を入力してください")
        else:
            print(f"「{s}」は正の半角整数ではありません\n正しい入力値をお願いします")

def get_num(function):
    """<高階関数>入力を求める関数の値を判断する関数
       @param function 入力処理を行う関数
       @return         正しい入力値の数値"""
    while True:
        s = function()
        try:
            validate_num(s)
            return eval(s)
        except:
            print(f"「{s}」は無効な入力値です\n正しい入力値をお願いします")            
 
def validate_num(s):
    """<検査関数>有効な数値かどうか検査する関数
       @param s 文字列
       @return  有効な数値の文字列"""
    def err():
        raise ValueError
    
    if s == "":
        err()
    elif s[0] == "-":
        validate_num(s[1:])
    elif s[0] == "+":
        validate_num(s[1:])
    elif s.count(".") == 1:
        [a,b] = s.split(".")
        if not a.isdigit() or not b.isdigit():
            err()
        return s
    elif not s.isdigit():
        err()
    return s    

def RandPos(posNum):
    """シークエンス内のある位置をランダムに取得する関数
       @param  posNum シークエンスの要素数
       @retval シークエンス内のある位置をあらわす数"""
    return rand(0,posNum-1)

def RandNum(maxNum):
    """2~maxNumまでの範囲の数をランダムに取得する関数
       @param maxNum 範囲の最大値
       @retval ランダムな整数"""
    return rand(2,maxNum)

def GetRandNum():
    """RandNum(maxNum)を用いて数をランダムに取得する関数
       @retval ランダムな整数"""
    return RandNum(9)
    
def Make99Question(difficultyNum):
    """九九問題を生成する関数
       @retval タプル(作成した問題のリスト,隠す場所の整数)"""    
    while True:
        try:
            question = g_q[difficultyNum]
            question._list.clear()
            results =[]
            is_pow = False
            for i in range(question._length):                
                if i % 2 == 0:
                    if not is_pow:
                        question._list.append(str(RandNum(question._maxNum)))
                        results.append(int(question._list[i]))
                    else:
                        question._list.append(str(rand(1,4)))
                        results.append(int(question._list[i]))                                          
                else:
                    operator,is_pow = check_pow(question,is_pow)
                    question._list.append(operator)
            question._list.append("".join(question._list))
            n = len(question._list)
            if difficultyNum>1:
                print("Debug:",question._list)            
            results.append(eval(question._list[n-1]))            
            pos = RandPos(len(results)) # マジックナンバーを使わすシーケンスの長さで汎用性向上
            return (results,pos,question)
        except:
            print("再計算")
    
def check_pow(question,is_pow):
    """計算式のべき乗が連続していないか確認し、正しく計算を行う演算子を返す関数
       @param question  難易度に応じた問題クラスのリスト要素
       @param is_pow    計算式の一つ前の演算子にべき乗があるかどうか
       @return operator str型の演算子
               is_pow   べき乗があるかどうかの真偽値"""
    while True:
        operator = random.choice(question._operators)        
        if operator == "**":
            if is_pow == True:
                print("べき乗制限")
                continue
            is_pow = True
            return (operator,is_pow)
        else:
            if is_pow == True:
                is_pow = False
            return (operator,is_pow)

def Input99Question(results,pos,question):
    """九九の問題を表示し入力値を判断する関数
       @param results 九九問題のリスト
       @param pos     リストで隠す場所の要素番号
       @retval s     入力値の整数"""
    tmps = results.copy()
    tmps[pos] = '?'
    
    q_list = question._list.copy()
    q_list.pop()
    n=len(q_list)
    q_list[0] = tmps[0]
    q_list[2] = tmps[1]
    if n > 3:
        q_list[4]=tmps[2]
    if n > 5:
        q_list[6] = tmps[3]

    def GetAnswer():
        """入力値を返す関数
      @retval 入力値の文字列"""
        for i in range(len(q_list)):
            print(q_list[i],end=" ")        
        s = input(f"= {tmps[len(tmps)-1]}: ")
        return s

    return get_num(GetAnswer)
    
def Execution99Question(difficultyNum):
    """九九の問題を処理する関数
       @retval result 回答に対する真偽値(0:false 1:true)"""
    results,pos,question = Make99Question(difficultyNum)
    ans = Input99Question(results,pos,question)
    solution = results[pos]
    result = (ans==solution)
    print({False:"不正解…",True:"正解!"}[result])    # Dictionaryで結果を表示
    return result

def Do99Game(difficultyNum):
    """九九の問題を実行する関数
       @param difficultyNum 難易度レベル数"""
    if difficultyNum == "exit":
        print("end")
        return False    
    num = get_int(input_num)    
    startTime = time()
    successNum = 0
    for i in range(num):
        print(f"\n{i+1}問目")
        successNum += Execution99Question(difficultyNum)
    endTime = time()
    deltaTime = round(endTime - startTime, 2)
    print(f"\n正解した数:{successNum} / {num}")
    print(f"あなたの正答率は{int(100*successNum/num)}%でした")
    print(f"回答時間は{deltaTime}秒でした")
    print(f"1問あたりの回答時間は{round(deltaTime/num, 2)}秒でした")


"""実行処理"""
Do99Game(get_difficulty_int(input_difficulty))

ひとまず、こちらのプログラムは一段落とさせてもらいます。

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

This site uses Akismet to reduce spam. Learn how your comment data is processed.

Python

前の記事

Python(Pygameで描画)
Python

次の記事

Python(後置記法の数式処理)