I've developed this a bit more. I've added division, square and cube questions and refactored the code a bit.

import math, random, re
#Custom classes
############################################
class Question:
def __init__(self, equation, answer):
self.equation = equation
self.answer = answer
#Functions
############################################
def getHi(path):
try:
with open(path, 'r') as file:
return int(file.readline().strip())
except:
print('''---Error: Unable to access "{}" and load 'hi' value.---
---Using default value of 10.---\n'''.format(path))
return 10
def incrementHi(path, newHi):
try:
with open(path, 'w') as file:
file.write(str(newHi))
except:
print('---Error: Unable to access "{}" and increase number range.---\n'.format(path))
def findBiFactorisations(n):
factors = []
for i in range(2, n + 1):
for j in range(2, n + 1):
if i * j == n:
factors.append((i, j))
return factors
def isComposite(n):
for i in range(2, int(math.sqrt(n)) + 1):
if n % i == 0:
return True
return False
def findNonPrimes(lo, hi):
nonPrimes = []
for i in range(lo, hi + 1):
if isComposite(i):
nonPrimes.append(i)
return nonPrimes
def makeAddQuestion():
a = random.randint(lo, hi)
b = random.randint(lo, hi)
return Question('{} + {} ='.format(a, b), int(a + b))
def makeSubQuestion():
a = random.randint(lo, hi)
b = random.randint(lo, a)
return Question('{} - {} ='.format(a, b), int(a - b))
def makeTimesQuestion():
a = random.randint(lo, hi)
b = random.randint(lo, hi)
return Question('{} * {} ='.format(a, b), int(a * b))
def makeDivQuestion():
a = random.choice(nonPrimes)
b, answer = random.choice(findBiFactorisations(a))
return Question('{} / {} ='.format(a, b), int(a / b))
def makeSquareQuestion():
a = random.randrange(lo, hi)
return Question('{} ^ 2 ='.format(a), int(a ** 2))
def makeCubeQuestion():
a = random.randrange(lo, hi)
return Question('{} ^ 3 ='.format(a), int(a ** 3))
#Constants
############################################
hiFile = r'hi.txt'
lo = 1
hi = getHi(hiFile) # must be at least 4
nonPrimes = findNonPrimes(lo, hi)
nQuestions = 250
passingMark = 98 # integer percent
questionMakers = [makeAddQuestion, makeSubQuestion, makeTimesQuestion,
makeDivQuestion, makeSquareQuestion, makeCubeQuestion]
#Start
#############################################
print('''Using numbers in range {}-{}. {} questions. Passing mark is {}%.
Press ctrl+c to exit early.
---------------'''.format(lo, hi, nQuestions, passingMark))
nCorrect = 0
for i in range(nQuestions):
if i == nQuestions - 1:
print('\n---Last question---')
elif i > 0 and (nQuestions - i) % 25 == 0:
print('\n---{} questions remaining---'.format(nQuestions - i))
question = random.choice(questionMakers)()
print('\n{}\n'.format(question.equation))
userIn = input().strip()
if re.match(r'^[+-]?\d+$', userIn) and int(userIn) == question.answer:
nCorrect += 1
else:
print('\nWRONG. Answer is {}.'.format(question.answer))
score = round(100 * nCorrect / nQuestions, 2)
if score % 1 == 0:
score = int(score)
print('''
---------------
Score: {}%'''.format(score))
if score >= passingMark:
print('\nLesson passed. Number range will be increased by 1 next lesson.\n')
incrementHi(hiFile, hi + 1)
else:
print('\nLesson failed. Lesson will be repeated.\n')
print('Press enter to exit.')
input()