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
# added variable node class to consider when both nodes have variables in them
'''
AICogSci Final Project
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
......@@ -73,7 +80,6 @@ class EquationTree:
q.enqueue(currentNode.right)
print(q.dequeue().value, end =' ')
class Branch:
def __init__(self):
self.value = "\n"
......@@ -93,19 +99,6 @@ class TimesNode:
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()
def inorder(self):
......@@ -129,9 +122,6 @@ class PlusNode:
return MinusNode(None,None)
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()
......@@ -187,44 +177,11 @@ class VariableNode:
return int(self.value.split("x")[0])
def canEval(self):
return False #not an operator
return False
def inorder(self):
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(['+', '-', '*', '/', '(', ')'])
PRIORITY = {'+':1, '-':1, '*':2, '/':2}
......@@ -251,8 +208,6 @@ def infix_to_prefix(formula):
b = exp_stack.pop()
exp_stack.append( op+" "+b+" "+a )
op_stack.append(ch)
# leftover
while op_stack:
op = op_stack.pop()
a = exp_stack.pop()
......@@ -312,67 +267,4 @@ def generate_tree(equation):
add_to_tree(q2, t.root, False) # add expression tree to right side of equation tree
t.printTree()
return t
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
return t
\ No newline at end of file
......@@ -5,12 +5,19 @@ import tree
import queue
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):
'''Initializes tree with an equation, and generates equation tree from this equation.'''
self.equation = equation
self.expressions = equation.split("=")
self.tree = tree.generate_tree(equation)
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
newOp = None
q = queue.Queue()
......@@ -26,11 +33,10 @@ class TutorState:
else: # if moving variables
nodeType = tree.VariableNode
emptyNode = tree.NumNode(0)
print("node is ", node)
if isinstance(node, tree.PlusNode) or isinstance(node, tree.MinusNode):
# if plus or minus node
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()
if isinstance(node.left, nodeType):
moveNode = node.left
......@@ -38,10 +44,9 @@ class TutorState:
elif isinstance(node.right, nodeType):
moveNode = node.right
node.right = emptyNode
moved = False
moved = False # trackes if a node has been moved yet
while not q.isEmpty() and not moved:
currentNode = q.front()
print("currentNode is ", currentNode.value)
if currentNode.left != None:
if isinstance(currentNode.left, nodeType):
oldNum = currentNode.left
......@@ -59,10 +64,8 @@ class TutorState:
moved = True
q.enqueue(currentNode.right)
if isinstance(currentNode, nodeType) and not moved:
# case where there is no operator and want to shift node over
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:
self.tree.root.left = newOp
self.tree.root.left.left = oldNum
......@@ -73,40 +76,58 @@ class TutorState:
self.tree.root.right.right = moveNode
moved=True
q.dequeue()
# will need to change this to just combine variables 4x + 0x
if left: self.combine_like_terms(False)
else : self.combine_like_terms(True)
if left: self.combine_like_terms(False)
else: self.combine_like_terms(True)
return self.tree.get_expression()
def combine_like_terms(self, left):
''' Combines like terms on a designated side of the tree by simplifying like terms.
'''
self.tree.get_expression()
print("COMBINING LIKE TERMS")
if left:
print("evaluating left side")
self.tree.evaluate(self.tree.root, True)
else:
print("evaluating right side")
self.tree.evaluate(self.tree.root, False)
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):
''' Initializes the model itself, saves the equation, initial sides of the equation
and initial count of variable types.
'''
ccm.Model.__init__(self)
self.equation = equation
self.oldEquation = None
self.sides = self.getLeftandRightVals()
self.constantCount = self.getConstantCount()
self.state = None
self.end = False
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? "
user_step = input(outputString) #figure out what user step means
# call some get_state() function comparing user step to what state should be
user_step = input(outputString)
self.oldEquation = self.equation
self.equation = user_step
oldSides = self.sides
......@@ -114,14 +135,11 @@ class UserState(ccm.Model): #ccm.ProductionSystem
oldConstantCount = self.constantCount
self.constantCount = self.getConstantCount()
self.state = self.get_state(oldConstantCount)
#print("state is ", self.state)
#return oldConstantCount
def getLeftandRightVals(self):
''' Splits equation to get left and right sides of equation
'''
expressions = self.equation.split("=")
leftVars = None
rightVars = None
try:
leftVars = re.split("[+-]", expressions[0])
rightVars = re.split("[+-]", expressions[1])
......@@ -132,6 +150,8 @@ class UserState(ccm.Model): #ccm.ProductionSystem
return [leftVars, rightVars]
def getConstantCount(self):
''' Counts number of variables of each type on each side of the equation
'''
constantsOnLeft = 0
variablesOnLeft = 0
constantsOnRight = 0
......@@ -149,63 +169,46 @@ class UserState(ccm.Model): #ccm.ProductionSystem
return [variablesOnLeft, constantsOnLeft, variablesOnRight, constantsOnRight]
def isConstant(self, value):
''' Given a value, checks if it is a constant.
'''
if not value.isdigit():
return False
return True
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
print("old: ", oldConstantCount)
print("new: ", self.constantCount)
''' Based on number of variables and constants on each side of the equation
the agent guesses which state the user is moving to
'''
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
print("variables were moved")
self.state = "move_variables 1"
if oldConstantCount[0] < self.constantCount[0]: self.state = "move_variables 1"
else: self.state = "move_variables 0"
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"
else: self.state = "add_variables 0"
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
self.state = "move_constants 1"
else:
self.state = "move_constants 0"
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"
else:
self.state = "add_constants 0"
else:
print("nothing triggered")
# need to do this for division and simplifying fraction
if oldConstantCount[1] > self.constantCount[1]: self.state = "add_constants 1"
else: self.state = "add_constants 0"
else: print("nothing triggered")
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):
''' RBES for the tutor itself. Tracks states
'''
goal = Buffer()
user = UserState("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():
user.get_input()
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
tutorEq = tutor.move_values(bool(int(left)), True)
......@@ -228,7 +231,7 @@ class IntelligentTutor(ACTR):
goal.set("check_state " + tutorEq)
def check_state(goal = "check_state ?tutorEq"):
if tutorEq == user.equation:
if tutor.compare_Equations(tutorEq, user.equation):
user.get_input()
goal.set(user.state)
else:
......@@ -236,7 +239,6 @@ class IntelligentTutor(ACTR):
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.")
# need to print what correct state would have been
user.equation = tutorEqn
user.get_input()
goal.set(user.state)
......@@ -248,12 +250,11 @@ class IntelligentTutor(ACTR):
class EmptyEnvironment(ccm.Model):
pass
def main():
env_name = EmptyEnvironment()
agent_name = IntelligentTutor()
env_name.agent = agent_name
ccm.log_everything(env_name)
env_name.agent = agent_name
#ccm.log_everything(env_name) # log statement, uncomment if you want to log information
env_name.run()
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