Commit 72411f6d authored by mrk022's avatar mrk022
Browse files

commented and cleaned up code

parent 6d69bce5
# base tree code from http://knuth.luther.edu/~leekent/CS2Plus/chap6/chap6.html '''
# changed this code to allow for variable functions, and change infix expressions to postfix AICogSci Final Project
# added variable node class to consider when both nodes have variables in them
base tree code from http://knuth.luther.edu/~leekent/CS2Plus/chap6/chap6.html
Added:
- EquationTree class to generate a specialized tree
- Branch and Equals Node classes to make printing more legible
- canEval, getInverse and inorder methods to most of the nodes to allow further functionality
- VariableNode class was added to account for nodes with variables
- add_to_tree and generate_tree methods used to create and change equation tree class'''
import queue import queue
...@@ -73,7 +80,6 @@ class EquationTree: ...@@ -73,7 +80,6 @@ class EquationTree:
q.enqueue(currentNode.right) q.enqueue(currentNode.right)
print(q.dequeue().value, end =' ') print(q.dequeue().value, end =' ')
class Branch: class Branch:
def __init__(self): def __init__(self):
self.value = "\n" self.value = "\n"
...@@ -93,19 +99,6 @@ class TimesNode: ...@@ -93,19 +99,6 @@ class TimesNode:
def eval(self): def eval(self):
'''if check_if_variables(str(self.left.eval()), str(self.right.eval())) == "both_vars":
v1 = str(self.left.eval()).split("x")[0]
v2 = str(self.right.eval()).split("x")[0]
evalStr = v1 + "*" + v2
return eval(evalStr)
else:
return self.left.eval() * self.right.eval()'''
#if isinstance(self.left, VariableNode) and isinstance(self.right,VariableNode):
# return self.left.eval()*self.right.eval()
#elif isinstance(self.left,VariableNode) or isinstance(self.right,VariableNode):
# print("can't combine variables of differing types")
#need some sort of combined node here i think
#else:
return self.left.eval() * self.right.eval() return self.left.eval() * self.right.eval()
def inorder(self): def inorder(self):
...@@ -129,9 +122,6 @@ class PlusNode: ...@@ -129,9 +122,6 @@ class PlusNode:
return MinusNode(None,None) return MinusNode(None,None)
def eval(self): def eval(self):
#print("left value ", self.left)
#print(isinstance(self.left, NumNode))
#print("right value ", self.right.value())
return self.left.eval() + self.right.eval() return self.left.eval() + self.right.eval()
...@@ -187,44 +177,11 @@ class VariableNode: ...@@ -187,44 +177,11 @@ class VariableNode:
return int(self.value.split("x")[0]) return int(self.value.split("x")[0])
def canEval(self): def canEval(self):
return False #not an operator return False
def inorder(self): def inorder(self):
return str(self.eval()) return str(self.eval())
def check_if_variables(x, y):
if 'x' in x and 'x' in y:
return "both_vars"
elif 'x' in x or 'x' in y:
return "one_var"
else:
return "both_const"
def E(q):
if q.isEmpty():
raise ValueError("Invalid Prefix Expression")
token = q.dequeue()
if token == "+":
return PlusNode(E(q),E(q))
if token == "*":
return TimesNode(E(q),E(q))
if token == "-":
return MinusNode(E(q), E(q))
#try:
if "x" in token:
output = VariableNode(token)
# make a different class for variable node
else:
output = NumNode(int(token))
#except:
# print("Not a valid operation, need variables of like terms")
# output = NumNode(float(0))
return output
OPERATORS = set(['+', '-', '*', '/', '(', ')']) OPERATORS = set(['+', '-', '*', '/', '(', ')'])
PRIORITY = {'+':1, '-':1, '*':2, '/':2} PRIORITY = {'+':1, '-':1, '*':2, '/':2}
...@@ -251,8 +208,6 @@ def infix_to_prefix(formula): ...@@ -251,8 +208,6 @@ def infix_to_prefix(formula):
b = exp_stack.pop() b = exp_stack.pop()
exp_stack.append( op+" "+b+" "+a ) exp_stack.append( op+" "+b+" "+a )
op_stack.append(ch) op_stack.append(ch)
# leftover
while op_stack: while op_stack:
op = op_stack.pop() op = op_stack.pop()
a = exp_stack.pop() a = exp_stack.pop()
...@@ -312,67 +267,4 @@ def generate_tree(equation): ...@@ -312,67 +267,4 @@ def generate_tree(equation):
add_to_tree(q2, t.root, False) # add expression tree to right side of equation tree add_to_tree(q2, t.root, False) # add expression tree to right side of equation tree
t.printTree() t.printTree()
return t return t
\ No newline at end of file
def eval_tree(expression):
'''x = NumNode(5)
y = NumNode(4)
p = PlusNode(x,y)
t = TimesNode(p, NumNode(6))
root = PlusNode(t, NumNode(3))
print(root.eval())
print(root.inorder())'''
#x = input("Please enter a prefix expression: ")
x = infix_to_prefix(expression)
lst = x.split()
q = queue.Queue()
for token in lst:
q.enqueue(token)
root = E(q)
result = root.eval()
print(root.eval())
print(root.inorder())
return result
#print(infix_to_prefix("3 + 4 - 6"))
def split_equation(equation):
expressions = equation.split("=")
return expressions
def main():
#exp = split_equation("4x * 3x + 2")
#x = eval_tree(exp[0])
'''eqTree = EquationTree()
x1 = VariableNode("4x")
x2 = NumNode(3)
x3 = NumNode(2)
x4 = NumNode(7)
t = PlusNode(x1,x2)
t2 = PlusNode(x3,x4)
eqTree.root.left = t
eqTree.root.right = t2
eqTree.printTree()'''
equationTree = generate_tree("4x + 3 = 2 + 7")
print(equationTree.root.left.canEval())
print(equationTree.root.right.canEval())
newNode = NumNode(equationTree.root.right.eval())
equationTree.updateNode(equationTree.root, False, newNode)
equationTree.printTree()
equationTree.get_expression()
'''
print("old node should be 2, is ", oldNum.value)
print("moved node should be 7, is ", moveNode.value)
print("new op is ", newOp.value)
'''
if __name__ == "__main__":
main()
\ No newline at end of file
...@@ -5,12 +5,19 @@ import tree ...@@ -5,12 +5,19 @@ import tree
import queue import queue
class TutorState: class TutorState:
''' Class to store the state that the tutor thinks the equation is in
Tracks this by generating an equation tree from the tree.py file, and moving nodes accordingly
'''
def __init__(self,equation): def __init__(self,equation):
'''Initializes tree with an equation, and generates equation tree from this equation.'''
self.equation = equation self.equation = equation
self.expressions = equation.split("=") self.expressions = equation.split("=")
self.tree = tree.generate_tree(equation) self.tree = tree.generate_tree(equation)
def move_values(self, left, constants): def move_values(self, left, constants):
''' If the user is moving either variables or constants, the tutor will call this
function to rearrange nodes in the tree accordingly
'''
moveNode = None moveNode = None
newOp = None newOp = None
q = queue.Queue() q = queue.Queue()
...@@ -26,11 +33,10 @@ class TutorState: ...@@ -26,11 +33,10 @@ class TutorState:
else: # if moving variables else: # if moving variables
nodeType = tree.VariableNode nodeType = tree.VariableNode
emptyNode = tree.NumNode(0) emptyNode = tree.NumNode(0)
print("node is ", node)
if isinstance(node, tree.PlusNode) or isinstance(node, tree.MinusNode): if isinstance(node, tree.PlusNode) or isinstance(node, tree.MinusNode):
# if plus or minus node # if plus or minus node
if node.canEval() == False: if node.canEval() == False:
# if can't evaluate children, need to move one # if can't evaluate children, need to move one to the other side
newOp = node.getInverse() newOp = node.getInverse()
if isinstance(node.left, nodeType): if isinstance(node.left, nodeType):
moveNode = node.left moveNode = node.left
...@@ -38,10 +44,9 @@ class TutorState: ...@@ -38,10 +44,9 @@ class TutorState:
elif isinstance(node.right, nodeType): elif isinstance(node.right, nodeType):
moveNode = node.right moveNode = node.right
node.right = emptyNode node.right = emptyNode
moved = False moved = False # trackes if a node has been moved yet
while not q.isEmpty() and not moved: while not q.isEmpty() and not moved:
currentNode = q.front() currentNode = q.front()
print("currentNode is ", currentNode.value)
if currentNode.left != None: if currentNode.left != None:
if isinstance(currentNode.left, nodeType): if isinstance(currentNode.left, nodeType):
oldNum = currentNode.left oldNum = currentNode.left
...@@ -59,10 +64,8 @@ class TutorState: ...@@ -59,10 +64,8 @@ class TutorState:
moved = True moved = True
q.enqueue(currentNode.right) q.enqueue(currentNode.right)
if isinstance(currentNode, nodeType) and not moved: if isinstance(currentNode, nodeType) and not moved:
# case where there is no operator and want to shift node over
oldNum =currentNode oldNum =currentNode
print("old node should be 2, is ", oldNum.value)
print("moved node should be 7, is ", moveNode.value)
print("new op is ", newOp.value)
if left: if left:
self.tree.root.left = newOp self.tree.root.left = newOp
self.tree.root.left.left = oldNum self.tree.root.left.left = oldNum
...@@ -73,40 +76,58 @@ class TutorState: ...@@ -73,40 +76,58 @@ class TutorState:
self.tree.root.right.right = moveNode self.tree.root.right.right = moveNode
moved=True moved=True
q.dequeue() q.dequeue()
# will need to change this to just combine variables 4x + 0x if left: self.combine_like_terms(False)
if left: self.combine_like_terms(False) else: self.combine_like_terms(True)
else : self.combine_like_terms(True)
return self.tree.get_expression() return self.tree.get_expression()
def combine_like_terms(self, left): def combine_like_terms(self, left):
''' Combines like terms on a designated side of the tree by simplifying like terms.
'''
self.tree.get_expression() self.tree.get_expression()
print("COMBINING LIKE TERMS")
if left: if left:
print("evaluating left side")
self.tree.evaluate(self.tree.root, True) self.tree.evaluate(self.tree.root, True)
else: else:
print("evaluating right side")
self.tree.evaluate(self.tree.root, False) self.tree.evaluate(self.tree.root, False)
return self.tree.get_expression() return self.tree.get_expression()
def compare_Equations(self, equation1, equation2):
''' Compares two equations to see if both sides are equivalent, regardless of their order
Assumes equations have operators that do not require a specific order
'''
equation1 = "".join(equation1.split())
equation2 = "".join(equation2.split())
eq1 = equation1.split('=')
eq2 = equation2.split('=')
eq1list = []
eq2list = []
for side in eq1:
eq1list.append(sorted(re.split('[-|+|*|/]',side)))
for side in eq2:
eq2list.append(sorted(re.split('[-|+|*|/]',side)))
#print(eq1list,eq2list)
return eq1list==eq2list
class UserState(ccm.Model): #ccm.ProductionSystem
#moveConstants = "0" # move constants is 0 when need to move them, 1 when move variables, 2 when neither
ready = False
class UserState(ccm.Model):
''' Class for the user state to track where the user is in their solution
'''
def __init__(self, equation): def __init__(self, equation):
''' Initializes the model itself, saves the equation, initial sides of the equation
and initial count of variable types.
'''
ccm.Model.__init__(self) ccm.Model.__init__(self)
self.equation = equation self.equation = equation
self.oldEquation = None self.oldEquation = None
self.sides = self.getLeftandRightVals() self.sides = self.getLeftandRightVals()
self.constantCount = self.getConstantCount() self.constantCount = self.getConstantCount()
self.state = None self.state = None
self.end = False
def get_input(self): def get_input(self):
''' Gets input from the user by showing them the current state and asking for the next one.
Takes their input and saves all needed information to process it accordingly.
'''
outputString = "Equation is currently: " + str(self.equation) + "\nWhat is your next step? " outputString = "Equation is currently: " + str(self.equation) + "\nWhat is your next step? "
user_step = input(outputString) #figure out what user step means user_step = input(outputString)
# call some get_state() function comparing user step to what state should be
self.oldEquation = self.equation self.oldEquation = self.equation
self.equation = user_step self.equation = user_step
oldSides = self.sides oldSides = self.sides
...@@ -114,14 +135,11 @@ class UserState(ccm.Model): #ccm.ProductionSystem ...@@ -114,14 +135,11 @@ class UserState(ccm.Model): #ccm.ProductionSystem
oldConstantCount = self.constantCount oldConstantCount = self.constantCount
self.constantCount = self.getConstantCount() self.constantCount = self.getConstantCount()
self.state = self.get_state(oldConstantCount) self.state = self.get_state(oldConstantCount)
#print("state is ", self.state)
#return oldConstantCount
def getLeftandRightVals(self): def getLeftandRightVals(self):
''' Splits equation to get left and right sides of equation
'''
expressions = self.equation.split("=") expressions = self.equation.split("=")
leftVars = None
rightVars = None
try: try:
leftVars = re.split("[+-]", expressions[0]) leftVars = re.split("[+-]", expressions[0])
rightVars = re.split("[+-]", expressions[1]) rightVars = re.split("[+-]", expressions[1])
...@@ -132,6 +150,8 @@ class UserState(ccm.Model): #ccm.ProductionSystem ...@@ -132,6 +150,8 @@ class UserState(ccm.Model): #ccm.ProductionSystem
return [leftVars, rightVars] return [leftVars, rightVars]
def getConstantCount(self): def getConstantCount(self):
''' Counts number of variables of each type on each side of the equation
'''
constantsOnLeft = 0 constantsOnLeft = 0
variablesOnLeft = 0 variablesOnLeft = 0
constantsOnRight = 0 constantsOnRight = 0
...@@ -149,63 +169,46 @@ class UserState(ccm.Model): #ccm.ProductionSystem ...@@ -149,63 +169,46 @@ class UserState(ccm.Model): #ccm.ProductionSystem
return [variablesOnLeft, constantsOnLeft, variablesOnRight, constantsOnRight] return [variablesOnLeft, constantsOnLeft, variablesOnRight, constantsOnRight]
def isConstant(self, value): def isConstant(self, value):
''' Given a value, checks if it is a constant.
'''
if not value.isdigit(): if not value.isdigit():
return False return False
return True return True
def get_state(self, oldConstantCount): def get_state(self, oldConstantCount):
# based on number of variables constants on each side of the equation, the agent guesses which state the user is moving to ''' Based on number of variables and constants on each side of the equation
print("old: ", oldConstantCount) the agent guesses which state the user is moving to
print("new: ", self.constantCount) '''
if oldConstantCount[0] != self.constantCount[0] and oldConstantCount[2] != self.constantCount[2]: if oldConstantCount[0] != self.constantCount[0] and oldConstantCount[2] != self.constantCount[2]:
# if not same number of variables as before for both sides then moved variables # if not same number of variables as before for both sides then moved variables
print("variables were moved") if oldConstantCount[0] < self.constantCount[0]: self.state = "move_variables 1"
self.state = "move_variables 1" else: self.state = "move_variables 0"
elif oldConstantCount[0] != self.constantCount[0] or oldConstantCount[2] != self.constantCount[2]: elif oldConstantCount[0] != self.constantCount[0] or oldConstantCount[2] != self.constantCount[2]:
print("variables were added")
if oldConstantCount[0] > self.constantCount[0]: self.state = "add_variables 1" if oldConstantCount[0] > self.constantCount[0]: self.state = "add_variables 1"
else: self.state = "add_variables 0" else: self.state = "add_variables 0"
elif oldConstantCount[1] != self.constantCount[1] and oldConstantCount[3] != self.constantCount[3]: elif oldConstantCount[1] != self.constantCount[1] and oldConstantCount[3] != self.constantCount[3]:
print("constants were moved")
if oldConstantCount[1] < self.constantCount[1]: #moving constants to left if oldConstantCount[1] < self.constantCount[1]: #moving constants to left
self.state = "move_constants 1" self.state = "move_constants 1"
else: else:
self.state = "move_constants 0" self.state = "move_constants 0"
elif oldConstantCount[1] != self.constantCount[1] or oldConstantCount[3] != self.constantCount[3]: elif oldConstantCount[1] != self.constantCount[1] or oldConstantCount[3] != self.constantCount[3]:
print("constants were added") if oldConstantCount[1] > self.constantCount[1]: self.state = "add_constants 1"
if oldConstantCount[1] > self.constantCount[1]: else: self.state = "add_constants 0"
self.state = "add_constants 1" else: print("nothing triggered")
else:
self.state = "add_constants 0"
else:
print("nothing triggered")
# need to do this for division and simplifying fraction
return self.state return self.state
'''def moveConstants(self):
print("Constants moved to one side of equation")
self.state = "move_constants"
def moveVariables(self):
print("Variables moved to one side of equation")
self.state = "move_variables"''' # dont think i need these functions anymore
# look at how to structure UserState () to not run every function
class IntelligentTutor(ACTR): class IntelligentTutor(ACTR):
''' RBES for the tutor itself. Tracks states
'''
goal = Buffer() goal = Buffer()
user = UserState("4x+7=5x+2") user = UserState("4x+7=5x+2")
tutor = TutorState("4x + 7 = 5x + 2") tutor = TutorState("4x + 7 = 5x + 2")
#tutor.move_constants_right()
#tutor.combine_like_terms(False)
#tutor.combine_like_terms(True)
def init(): def init():
user.get_input() user.get_input()
print("USER STATE IS ", user.state) # need to get user input to translate to state print("USER STATE IS ", user.state) # need to get user input to translate to state
goal.set(user.state) #get_state(cc) goal.set(user.state)
def moved_constants(goal="move_constants ?left"): #true if moving constants left def moved_constants(goal="move_constants ?left"): #true if moving constants left
tutorEq = tutor.move_values(bool(int(left)), True) tutorEq = tutor.move_values(bool(int(left)), True)
...@@ -228,7 +231,7 @@ class IntelligentTutor(ACTR): ...@@ -228,7 +231,7 @@ class IntelligentTutor(ACTR):
goal.set("check_state " + tutorEq) goal.set("check_state " + tutorEq)
def check_state(goal = "check_state ?tutorEq"): def check_state(goal = "check_state ?tutorEq"):
if tutorEq == user.equation: if tutor.compare_Equations(tutorEq, user.equation):
user.get_input() user.get_input()
goal.set(user.state) goal.set(user.state)
else: else:
...@@ -236,7 +239,6 @@ class IntelligentTutor(ACTR): ...@@ -236,7 +239,6 @@ class IntelligentTutor(ACTR):
def incorrect_state(goal = "invalid_state ?tutorEqn"): def incorrect_state(goal = "invalid_state ?tutorEqn"):
print("You have entered an invalid state. We anticipated the correct step to be: ", tutorEqn, "\nPlease continue solving the problem with the corrected equation.") print("You have entered an invalid state. We anticipated the correct step to be: ", tutorEqn, "\nPlease continue solving the problem with the corrected equation.")
# need to print what correct state would have been
user.equation = tutorEqn user.equation = tutorEqn
user.get_input() user.get_input()
goal.set(user.state) goal.set(user.state)
...@@ -248,12 +250,11 @@ class IntelligentTutor(ACTR): ...@@ -248,12 +250,11 @@ class IntelligentTutor(ACTR):
class EmptyEnvironment(ccm.Model): class EmptyEnvironment(ccm.Model):
pass pass
def main(): def main():
env_name = EmptyEnvironment() env_name = EmptyEnvironment()
agent_name = IntelligentTutor() agent_name = IntelligentTutor()
env_name.agent = agent_name env_name.agent = agent_name
ccm.log_everything(env_name) #ccm.log_everything(env_name) # log statement, uncomment if you want to log information
env_name.run() env_name.run()
main() main()
\ No newline at end of file
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment