#! /usr/bin/env python
# -*- coding: utf-8 -*-
#
# Kuku Anakula
# Copyright (C) 2007, Julius B. Lucks, Adrian DelMaestro, Sera L. Young
# Copyright (C) 2012, Alan Aguiar
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see .
#
# Contact information:
# Julius B. Lucks
# Alan Aguiar
import random
import math
import re
EQ_TYPE = 0
MULTIPLES_TYPE = 1
ADD_TYPE = 2
SUB_TYPE = 3
ADDSUB_TYPE = 4
MULT_TYPE = 5
DIV_TYPE = 6
MULTDIV_TYPE = 7
N_TYPE = 8
#**********************************
#**********************************
class ParseError (Exception) :
pass
class OptionError (Exception):
pass
class FileError (Exception):
pass
#**********************************
#**********************************
class QuestionMaker (object) :
def __init__ (self,newseed) :
self.nop = 4
self.addsub = 2
self.operators = ("+","-","x","/")
random.seed(newseed)
#********
def Gen_arg (self,num_range, pcut, curr_level, max_level, opindex, oplist) :
#Generates equation terms
#SHOULD only be called by Make_eq_question
#TAKES the range of allowed numbers
#TAKES the probability of generating a nested statement
#TAKES the current nesting level
#TAKES the maximum nesting level
#TAKES an integer giving the index of the last operator used in the question
#TAKES list of available operators
#RETURNS a tuple with [0] = a string containing the term and [1] = the integer value of the term
rval = random.uniform(0,1)
if ( (rval <= pcut) and (opindex >= self.addsub) and (curr_level <= max_level) ) :
if (curr_level == max_level) :
new_op_list = ["+","-","+","-"] #redirect the reference
else :
new_op_list = oplist
#endif
(arg,ans) = self.Make_eq_question(num_range, pcut, (curr_level + 1),max_level,new_op_list)
arg = "(" + arg + ")"
else :
arg = round(random.uniform(num_range[0],num_range[1]))
ans = int(arg)
arg = str(ans)
#endif
return(arg,ans)
#********
def Make_eq_question (self,num_range, pcut, curr_level, max_level, op_list):
#Generates a question string, along with an integer answer
#TAKES the range numbers allowed in the question
#if the minimum of the range is >0 the answer will also be > 0
#TAKES the probability of generating a nested term
#TAKES an integer >= 1 which gives the current level of nesting
#TAKES an integer >= 0 which gives the maximum nesting level
#TAKES an list of characters representing the allow operators
#RETURNS a tuple with a question string and an integer answer
nflag = True
while ( nflag == True ) :
qstring = ""
opindex = int(math.floor(random.uniform(0,self.nop)))
op = op_list[opindex]
#ensure an integer answer whenever a div op is selected
if (op == "/") :
ans2 = 0
while (ans2 == 0) :
(arg2,ans2) = self.Gen_arg (num_range, pcut, curr_level, max_level, opindex,op_list)
#end while
ans1 = int(round(random.uniform(num_range[0],num_range[1]))*ans2)
arg1 = str(ans1)
else :
(arg1,ans1) = self.Gen_arg (num_range, pcut, curr_level, max_level, opindex,op_list)
(arg2,ans2) = self.Gen_arg (num_range, pcut, curr_level, max_level, opindex,op_list)
#endif
qstring = arg1 + " " + op + " " + arg2
if (op == "+") :
ans = ans1 + ans2
elif (op == "-") :
ans = ans1 - ans2
elif (op == "x") :
ans = ans1 * ans2
else :
ans = ans1 / ans2
#endif
if ( (ans > 0) or (num_range[0] < 0) ):
nflag = False
#end if
#end while
return (qstring,ans)
#end Make_eq_question
#********
def Make_multiples_question (self,num_range,max_ans) :
#Makes a 'find the multiples' type question
#TAKES a upper bound on the multiplier (ie: find the multiples of something in |[2,num_range]|)
#TAKES a an integer giving the maximum number of answers (# of answers in [1,max_ans])
#RETURNS a string with the question in the format "%n" where n is the base multiplier,
#and an list of answers delimited by sim signs
ans_string = ""
n_ans = int(round(random.uniform(1,max_ans)))
base_val = int(round(random.uniform(2,num_range[1])))
ans_list=[]
ans = base_val*int(round(random.uniform(0,num_range[1])))
ans_list.append(ans)
for i in range(2,n_ans) :
ans = base_val*int(round(random.uniform(0,num_range[1])))
ans_list.append(ans)
#end for
if (num_range[0] < 0):
base_val = base_val*((-1) **int(math.floor(random.uniform(0,10))) )
for i in range(0,len(ans_list)) :
ans_list[i] = ans_list[i] * ((-1) **int(math.floor(random.uniform(0,10))) )
#end if
ans_string = str(ans_list[0])
for i in range(1,len(ans_list)-1):
ans_string += "~" + str(ans_list[i])
#end for
qstring = "%" + str(base_val)
return(qstring,ans_string)
#end Make_mult_question
#**********
def Make_random_question(self,bias,num_range,parameters) :
#Makes a question of a random type
#TAKES the cut off for deciding between a standard question and a multiples type question
#TAKES the allowed range of the question arguments
#TAKES a list of parameters [0] = max answers in a 'multiples' type question
#[1] = pcut for a standard equation question, [2] = max_level for a standard equation question
rval = random.uniform(0,1)
eq_params = [parameters[1],parameters[2]]
mult_params = [parameters[0]]
if (rval > bias) :
q = self.Make_question(EQ_TYPE,num_range,eq_params)
else :
q = self.Make_question(MULTIPLES_TYPE,num_range,mult_params)
#endif
return(q)
def Make_question (self, type_flag, num_range, parameters) :
#Makes a question of a specific type
#TAKES an integer that determines the question type
#TAKES the allowable range for numbers in the question
#TAKES an array of question type-specific paramters
# if type = standard equation,
# parameters[0] = a float in [0,1] that gives the probability of generating a nested statement
# parameters[1] = maximum nesting level
# if type_flag = find the multiples question
# parameters[0] = maximum number of multiples in the answer set
#RETURNS a question object
qstring = ""
#no switch-like statement unfortunately
if (type_flag == EQ_TYPE) : #a standard equation type question
if (len(parameters) != 2) :
raise(OptionError)
#endif
pcut = parameters[0]
max_level = parameters[1]
currlevel = 1
(qstring,a) = self.Make_eq_question(num_range,pcut,currlevel,max_level,self.operators)
a = str(int(a))
elif (type_flag == MULTIPLES_TYPE) : #a 'find the multiples' type question
if (len(parameters) != 1) :
raise(OptionError)
#endif
max_ans = parameters[0]
(qstring,a) = self.Make_multiples_question(num_range,max_ans)
elif (type_flag == ADD_TYPE) :
if (len(parameters) != 2) :
raise(OptionError)
#endif
pcut = 0
max_level = 0
currlevel = 1
oplist = ["+","+","+","+"]
(qstring,a) = self.Make_eq_question(num_range,pcut,currlevel,max_level,oplist)
a = str(int(a))
elif (type_flag == SUB_TYPE) :
if (len(parameters) != 2) :
raise(OptionError)
#endif
pcut = 0
max_level = 0
currlevel = 1
oplist = ["-","-","-","-"]
(qstring,a) = self.Make_eq_question(num_range,pcut,currlevel,max_level,oplist)
a = str(int(a))
elif (type_flag == ADDSUB_TYPE) :
if (len(parameters) != 2) :
raise(OptionError)
#endif
pcut = 0
max_level = 0
currlevel = 1
oplist = ["+","-","+","-"]
(qstring,a) = self.Make_eq_question(num_range,pcut,currlevel,max_level,oplist)
a = str(int(a))
elif (type_flag == MULT_TYPE) :
if (len(parameters) != 2) :
raise(OptionError)
#endif
pcut = 0
max_level = 0
currlevel = 1
oplist = ["x","x","x","x"]
(qstring,a) = self.Make_eq_question(num_range,pcut,currlevel,max_level,oplist)
a = str(int(a))
elif (type_flag == DIV_TYPE) :
if (len(parameters) != 2) :
raise(OptionError)
#endif
pcut = 0
max_level = 0
currlevel = 1
oplist = ["/","/","/","/"]
(qstring,a) = self.Make_eq_question(num_range,pcut,currlevel,max_level,oplist)
a = str(int(a))
elif (type_flag == MULTDIV_TYPE) :
if (len(parameters) != 2) :
raise(OptionError)
#endif
pcut = 0
max_level = 0
currlevel = 1
oplist = ["x","/","x","/"]
(qstring,a) = self.Make_eq_question(num_range,pcut,currlevel,max_level,oplist)
a = str(int(a))
else :
raise (OptionError)
new_q = Question(qstring,a)
return(new_q)
#end Make_Question
#**********************************
#**********************************
class QuestionFileIO (object) :
def __init__ (self) :
pass
# self.file_name = file_name
# self.Read_questions()
#********
def Read_questions (self,file_name) :
#Reads questions from a file
#TAKES a file_name
#RETURNS a list of Question objects
try:
f = file(file_name,"r")
except IOError:
raise(FileError)
question_list = []
for line in f.readlines():
if not re.match('#',line):
(q,a) = line[:-1].split("=") #chomp
question_list.append(Question(q,a))
# a = re.sub('\s+','',a)
if (len(question_list) < 1) :
raise ParseError
return (QuestionList(question_list))
#end Read_questions
#********
def Write_questions (self,file_name,question_list) :
#Writes questions to a file
#TAKES a file anme
#TAKES a list of Question objects
#RETURNS null
try:
f = file(file_name,"w+")
except IOError:
raise(FileError)
for i in question_list :
f.write(i.q_string_raw+" = "+i.a_string_raw+"\n")
f.close()
#end Write_questions
#********
#**********************************
#**********************************
class Question (object) :
def __init__ (self, q_raw, a_raw) :
self.q_string_raw = q_raw
self.a_string_raw = a_raw
(self.type,self.q_string,self.a_list) = self.Parse_question(q_raw,a_raw)
self.n_answers = len(self.a_list)
#**********
def Parse_question(self,q_string,a_string) :
#Parses question and answer strings
#TAKES a unmodified question string
#TAKES an unmodified answer string
#RETURNS an int representing the question type, a formatted question string,
#and a *list* of integers representing the answer(s) to the question
# a multiples question
if (q_string[0] == "%") :
try :
a_list = map(int,a_string.split("~"))
qparts = q_string.split("%")
except TypeError() :
raise ParseError
type = MULTIPLES_TYPE
# new_string = "Find the multiples of " + str(qparts[1])
#Modify for smaller string
new_string = "Divis by " + str(qparts[1])
elif re.search('\.jpg',a_string): #numbers/01x.jpg
type = N_TYPE
new_string = q_string
a_list = [re.sub('\s+','',a_string)] #remove white space
else :
try :
a_list = [int(a_string)]
except ValueError() :
raise ParseError
type = EQ_TYPE
new_string = q_string
#end if
return(type,new_string,a_list)
#end Parse_question
#********
#**********************************
#**********************************
class QuestionList(object):
"""Manages what the next question is."""
def __init__(self,question_list):
self.question_list = question_list
self.num_questions = len(question_list)
self.ind = 0
def next(self):
"""returns next question"""
#might want to use a generator here?
#loop back questions
if self.ind == self.num_questions:
self.ind = 0
self.ind += 1
return self.question_list[self.ind-1]
def get_all_answers(self):
"""docstring for get_all_answers"""
# return [q.a_string for q in self.question_list]
a_list = []
for q in self.question_list:
a_list.extend(q.a_list)
return a_list
class QuestionGroup(object):
"""Manages several QuestionList's
Returns the QuestionList that is next in the
series.
"""
def __init__(self, question_lists):
self.question_lists = question_lists
self.length_lists = [len(l.get_all_answers())
for l in self.question_lists]
#1st index for question_listt
#2nd for question within a question_list
self.ind = [0,0]
def next(self):
"""return question list corresponding to next question"""
if self.ind[1] == self.length_lists[self.ind[0]]:
self.ind[0] += 1
self.ind[1] = 0
if self.ind[0] == len(self.question_lists):
self.ind = [0,0]
self.ind[1] += 1
# print 'ind', self.ind
return self.question_lists[self.ind[0]]
def main () :
qfr = QuestionFileIO()
qlist = qfr.Read_questions("allops_p1_0-10.dat")
nq = len(qlist)
print ("%d" % (nq))
for i in range(0,nq) :
print ("%s : %s |%s = %s|" % (i,qlist[i].type,qlist[i].q_string,qlist[i].a_list))
# print ("%s" % (qlist[i].q_string))
#end for
# qm = QuestionMaker(348)
# qfr = QuestionFileIO()
# qlist=[]
# for i in range(0,1000) :
# qlist.append(qm.Make_random_question(0.0,[-10,10],[5,0.9,3]))
# #endfor
# qfr.Write_questions("test.dat",qlist)
# qlist=[]
# for i in range(0,1000) :
# qlist.append(qm.Make_question(MULT_TYPE,[0,10],[0,0]))
# #end for
# qfr.Write_questions("multiplication_p0_0-10.dat",qlist)
# qlist=[]
# for i in range(0,1000) :
# qlist.append(qm.Make_question(MULT_TYPE,[0,100],[0,0]))
# #end for
# qfr.Write_questions("multiplication_p0_0-100.dat",qlist)
# qlist=[]
# for i in range(0,1000) :
# qlist.append(qm.Make_question(MULT_TYPE,[-10,10],[0,0]))
# #end for
# qfr.Write_questions("multiplication_p0_-10-10.dat",qlist)
# qlist=[]
# for i in range(0,1000) :
# qlist.append(qm.Make_question(MULT_TYPE,[-100,100],[0,0]))
# #end for
# qfr.Write_questions("multiplication_p0_-100-100.dat",qlist)
# qlist=[]
# for i in range(0,1000) :
# qlist.append(qm.Make_question(DIV_TYPE,[0,10],[0,0]))
# #end for
# qfr.Write_questions("division_p0_0-10.dat",qlist)
# qlist=[]
# for i in range(0,1000) :
# qlist.append(qm.Make_question(DIV_TYPE,[0,100],[0,0]))
# #end for
# qfr.Write_questions("division_p0_0-100.dat",qlist)
# qlist=[]
# for i in range(0,1000) :
# qlist.append(qm.Make_question(DIV_TYPE,[-10,10],[0,0]))
# #end for
# qfr.Write_questions("division_p0_-10-10.dat",qlist)
# qlist=[]
# for i in range(0,1000) :
# qlist.append(qm.Make_question(DIV_TYPE,[-100,100],[0,0]))
# #end for
# qfr.Write_questions("division_p0_-100-100.dat",qlist)
# qlist=[]
# for i in range(0,1000) :
# qlist.append(qm.Make_question(ADD_TYPE,[0,10],[0,0]))
# #end for
# qfr.Write_questions("addition_p0_0-10.dat",qlist)
# qlist=[]
# for i in range(0,1000) :
# qlist.append(qm.Make_question(ADD_TYPE,[0,100],[0,0]))
# #end for
# qfr.Write_questions("addition_p0_0-100.dat",qlist)
# qlist=[]
# for i in range(0,1000) :
# new_q = qm.Make_question(SUB_TYPE,[0,10],[0,0])
# qlist.append(new_q)
# #end for
# qfr.Write_questions("subtraction_p0_0-10.dat",qlist)
# qlist=[]
# for i in range(0,1000) :
# new_q = qm.Make_question(SUB_TYPE,[0,100],[0,0])
# qlist.append(new_q)
# #end for
# qfr.Write_questions("subtraction_p0_0-100.dat",qlist)
# qlist=[]
# for i in range(0,1000) :
# new_q = qm.Make_question(ADDSUB_TYPE,[0,10],[0,0])
# qlist.append(new_q)
# #end for
# qfr.Write_questions("addsub_p0_0-10.dat",qlist)
# qlist=[]
# for i in range(0,1000) :
# new_q = qm.Make_question(ADDSUB_TYPE,[0,100],[0,0])
# qlist.append(new_q)
# #end for
# qfr.Write_questions("addsub_p0_0-100.dat",qlist)
# qlist=[]
# for i in range(0,1000) :
# new_q = qm.Make_question(ADDSUB_TYPE,[-10,10],[0,0])
# qlist.append(new_q)
# #end for
# qfr.Write_questions("addsub_p0_-10-10.dat",qlist)
# qlist=[]
# for i in range(0,1000) :
# new_q = qm.Make_question(ADDSUB_TYPE,[-1000,100],[0,0])
# qlist.append(new_q)
# #end for
# qfr.Write_questions("addsub_p0_-100-100.dat",qlist)
# qlist=[]
# for i in range(0,1000) :
# qlist.append(qm.Make_question(MULTDIV_TYPE,[0,10],[0,0]))
# #end for
# qfr.Write_questions("multdiv_p0_0-10.dat",qlist)
# qlist=[]
# for i in range(0,1000) :
# qlist.append(qm.Make_question(MULTDIV_TYPE,[0,100],[0,0]))
# #end for
# qfr.Write_questions("multdiv_p0_0-100.dat",qlist)
# qlist=[]
# for i in range(0,1000) :
# qlist.append(qm.Make_question(MULTDIV_TYPE,[-10,10],[0,0]))
# #end for
# qfr.Write_questions("multdiv_p0_-10-10.dat",qlist)
# qlist=[]
# for i in range(0,1000) :
# qlist.append(qm.Make_question(MULTDIV_TYPE,[-100,100],[0,0]))
# #end for
# qfr.Write_questions("multdiv_p0_-100-100.dat",qlist)
# qlist=[]
# for i in range(0,1000) :
# qlist.append(qm.Make_question(EQ_TYPE,[0,10],[0.5,0]))
# #end for
# qfr.Write_questions("allops_p0_0-10.dat",qlist)
# qlist=[]
# for i in range(0,1000) :
# qlist.append(qm.Make_question(EQ_TYPE,[0,100],[0.5,0]))
# #end for
# qfr.Write_questions("allops_p0_0-100.dat",qlist)
# qlist=[]
# for i in range(0,1000) :
# qlist.append(qm.Make_question(EQ_TYPE,[0,10],[0.5,1]))
# #end for
# qfr.Write_questions("allops_p1_0-10.dat",qlist)
# qlist=[]
# for i in range(0,1000) :
# qlist.append(qm.Make_question(EQ_TYPE,[0,100],[0.5,1]))
# #end for
# qfr.Write_questions("allops_p1_0-100.dat",qlist)
# qlist=[]
# for i in range(0,1000) :
# qlist.append(qm.Make_question(MULTIPLES_TYPE,[0,10],[5]))
# #end for
# qfr.Write_questions("multiples_p0_0-10.dat",qlist)
# qlist=[]
# for i in range(0,1000) :
# qlist.append(qm.Make_question(MULTIPLES_TYPE,[-10,10],[5]))
# #end for
# qfr.Write_questions("multiples_p0_-10-10.dat",qlist)
# qlist=[]
# for i in range(0,1000) :
# qlist.append(qm.Make_random_question(0.2,[0,10],[5,0.3,0]))
# #endfor
# qfr.Write_questions("random_p0_0-10.dat",qlist)
# qlist=[]
# for i in range(0,1000) :
# qlist.append(qm.Make_random_question(0.2,[-10,10],[5,0.3,0]))
# #endfor
# qfr.Write_questions("random_p0_-10-10.dat",qlist)
# qlist=[]
# for i in range(0,1000) :
# qlist.append(qm.Make_random_question(0.2,[0,10],[5,0.3,1]))
# #endfor
# qfr.Write_questions("random_p1_0-10.dat",qlist)
# qlist=[]
# for i in range(0,1000) :
# qlist.append(qm.Make_random_question(0.2,[-10,10],[5,0.3,1]))
# #endfor
# qfr.Write_questions("random_p1_-10-10.dat",qlist)
if (__name__ == "__main__") :
main()