tutor.py 8.78 KB
 mrk022 committed Nov 26, 2019 1 ``````import ccm `````` mrk022 committed Nov 27, 2019 2 ``````import re `````` mrk022 committed Nov 26, 2019 3 ``````from ccm.lib.actr import * `````` mrk022 committed Dec 04, 2019 4 5 ``````import tree import queue `````` mrk022 committed Nov 26, 2019 6 `````` `````` mrk022 committed Dec 04, 2019 7 ``````class TutorState: `````` mrk022 committed Dec 07, 2019 8 9 10 `````` ''' 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 ''' `````` mrk022 committed Nov 27, 2019 11 `````` def __init__(self,equation): `````` mrk022 committed Dec 07, 2019 12 `````` '''Initializes tree with an equation, and generates equation tree from this equation.''' `````` mrk022 committed Nov 27, 2019 13 `````` self.equation = equation `````` mrk022 committed Dec 04, 2019 14 15 16 `````` self.expressions = equation.split("=") self.tree = tree.generate_tree(equation) `````` mrk022 committed Dec 06, 2019 17 `````` def move_values(self, left, constants): `````` mrk022 committed Dec 07, 2019 18 19 20 `````` ''' If the user is moving either variables or constants, the tutor will call this function to rearrange nodes in the tree accordingly ''' `````` mrk022 committed Dec 04, 2019 21 `````` moveNode = None `````` mrk022 committed Dec 06, 2019 22 23 24 25 26 27 28 29 30 31 32 33 34 35 `````` newOp = None q = queue.Queue() if left: # if moving values from right to left node = self.tree.root.right q.enqueue(self.tree.root.left) else: # if moving values from left to right node = self.tree.root.left q.enqueue(self.tree.root.right) if constants: # if moving constants nodeType = tree.NumNode emptyNode = tree.VariableNode("0x") else: # if moving variables nodeType = tree.VariableNode emptyNode = tree.NumNode(0) `````` mrk022 committed Dec 04, 2019 36 37 38 `````` if isinstance(node, tree.PlusNode) or isinstance(node, tree.MinusNode): # if plus or minus node if node.canEval() == False: `````` mrk022 committed Dec 07, 2019 39 `````` # if can't evaluate children, need to move one to the other side `````` mrk022 committed Dec 04, 2019 40 `````` newOp = node.getInverse() `````` mrk022 committed Dec 06, 2019 41 `````` if isinstance(node.left, nodeType): `````` mrk022 committed Dec 04, 2019 42 `````` moveNode = node.left `````` mrk022 committed Dec 06, 2019 43 44 `````` node.left = emptyNode elif isinstance(node.right, nodeType): `````` mrk022 committed Dec 04, 2019 45 `````` moveNode = node.right `````` mrk022 committed Dec 06, 2019 46 `````` node.right = emptyNode `````` mrk022 committed Dec 07, 2019 47 `````` moved = False # trackes if a node has been moved yet `````` mrk022 committed Dec 04, 2019 48 49 50 `````` while not q.isEmpty() and not moved: currentNode = q.front() if currentNode.left != None: `````` mrk022 committed Dec 06, 2019 51 `````` if isinstance(currentNode.left, nodeType): `````` mrk022 committed Dec 04, 2019 52 53 54 55 56 57 58 `````` oldNum = currentNode.left currentNode.left = newOp currentNode.left.left = oldNum currentNode.left.right = moveNode moved = True q.enqueue(currentNode.left) if currentNode.right != None and not moved: `````` mrk022 committed Dec 06, 2019 59 `````` if isinstance(currentNode.right, nodeType): `````` mrk022 committed Dec 04, 2019 60 61 62 63 64 65 `````` oldNum = currentNode.right currentNode.right = newOp currentNode.right.left = oldNum currentNode.right.right = moveNode moved = True q.enqueue(currentNode.right) `````` mrk022 committed Dec 06, 2019 66 `````` if isinstance(currentNode, nodeType) and not moved: `````` mrk022 committed Dec 07, 2019 67 `````` # case where there is no operator and want to shift node over `````` mrk022 committed Dec 06, 2019 68 69 70 71 72 73 74 75 76 77 `````` oldNum =currentNode if left: self.tree.root.left = newOp self.tree.root.left.left = oldNum self.tree.root.left.right = moveNode else: self.tree.root.right = newOp self.tree.root.right.left = oldNum self.tree.root.right.right = moveNode moved=True `````` mrk022 committed Dec 04, 2019 78 `````` q.dequeue() `````` mrk022 committed Dec 07, 2019 79 80 `````` if left: self.combine_like_terms(False) else: self.combine_like_terms(True) `````` mrk022 committed Dec 04, 2019 81 `````` return self.tree.get_expression() `````` mrk022 committed Dec 04, 2019 82 83 `````` def combine_like_terms(self, left): `````` mrk022 committed Dec 07, 2019 84 85 `````` ''' Combines like terms on a designated side of the tree by simplifying like terms. ''' `````` mrk022 committed Dec 04, 2019 86 87 88 89 90 `````` self.tree.get_expression() if left: self.tree.evaluate(self.tree.root, True) else: self.tree.evaluate(self.tree.root, False) `````` mrk022 committed Dec 06, 2019 91 `````` return self.tree.get_expression() `````` mrk022 committed Dec 04, 2019 92 `````` `````` mrk022 committed Dec 07, 2019 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 `````` 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 `````` mrk022 committed Nov 27, 2019 109 `````` `````` mrk022 committed Nov 26, 2019 110 `````` `````` mrk022 committed Dec 07, 2019 111 112 113 ``````class UserState(ccm.Model): ''' Class for the user state to track where the user is in their solution ''' `````` mrk022 committed Nov 27, 2019 114 `````` def __init__(self, equation): `````` mrk022 committed Dec 07, 2019 115 116 117 `````` ''' Initializes the model itself, saves the equation, initial sides of the equation and initial count of variable types. ''' `````` mrk022 committed Nov 27, 2019 118 `````` ccm.Model.__init__(self) `````` mrk022 committed Nov 26, 2019 119 `````` self.equation = equation `````` mrk022 committed Dec 04, 2019 120 `````` self.oldEquation = None `````` mrk022 committed Nov 27, 2019 121 `````` self.sides = self.getLeftandRightVals() `````` mrk022 committed Dec 02, 2019 122 `````` self.constantCount = self.getConstantCount() `````` mrk022 committed Dec 04, 2019 123 `````` self.state = None `````` mrk022 committed Nov 26, 2019 124 `````` `````` mrk022 committed Nov 27, 2019 125 `````` def get_input(self): `````` mrk022 committed Dec 07, 2019 126 127 128 `````` ''' 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. ''' `````` mrk022 committed Dec 04, 2019 129 `````` outputString = "Equation is currently: " + str(self.equation) + "\nWhat is your next step? " `````` mrk022 committed Dec 07, 2019 130 `````` user_step = input(outputString) `````` mrk022 committed Dec 04, 2019 131 `````` self.oldEquation = self.equation `````` mrk022 committed Dec 02, 2019 132 `````` self.equation = user_step `````` mrk022 committed Dec 04, 2019 133 134 135 136 `````` oldSides = self.sides self.sides = self.getLeftandRightVals() oldConstantCount = self.constantCount self.constantCount = self.getConstantCount() `````` mrk022 committed Dec 06, 2019 137 `````` self.state = self.get_state(oldConstantCount) `````` mrk022 committed Nov 27, 2019 138 139 `````` def getLeftandRightVals(self): `````` mrk022 committed Dec 07, 2019 140 141 `````` ''' Splits equation to get left and right sides of equation ''' `````` mrk022 committed Nov 27, 2019 142 `````` expressions = self.equation.split("=") `````` mrk022 committed Dec 04, 2019 143 144 145 146 147 148 149 `````` try: leftVars = re.split("[+-]", expressions[0]) rightVars = re.split("[+-]", expressions[1]) except: print("INVALID INPUT, try again") self.equation=self.oldEquation self.get_input() `````` mrk022 committed Nov 27, 2019 150 151 `````` return [leftVars, rightVars] `````` mrk022 committed Dec 02, 2019 152 `````` def getConstantCount(self): `````` mrk022 committed Dec 07, 2019 153 154 `````` ''' Counts number of variables of each type on each side of the equation ''' `````` mrk022 committed Nov 27, 2019 155 156 157 158 159 160 161 `````` constantsOnLeft = 0 variablesOnLeft = 0 constantsOnRight = 0 variablesOnRight = 0 for value in self.sides[0]: if self.isConstant(value): constantsOnLeft += 1 `````` mrk022 committed Dec 06, 2019 162 `````` elif 'x' in value: `````` mrk022 committed Nov 27, 2019 163 164 165 166 `````` variablesOnLeft += 1 for value in self.sides[1]: if self.isConstant(value): constantsOnRight += 1 `````` mrk022 committed Dec 06, 2019 167 `````` elif 'x' in value: `````` mrk022 committed Nov 27, 2019 168 `````` variablesOnRight += 1 `````` mrk022 committed Dec 02, 2019 169 `````` return [variablesOnLeft, constantsOnLeft, variablesOnRight, constantsOnRight] `````` mrk022 committed Nov 27, 2019 170 171 `````` def isConstant(self, value): `````` mrk022 committed Dec 07, 2019 172 173 `````` ''' Given a value, checks if it is a constant. ''' `````` mrk022 committed Dec 06, 2019 174 `````` if not value.isdigit(): `````` mrk022 committed Nov 27, 2019 175 176 177 `````` return False return True `````` mrk022 committed Dec 06, 2019 178 `````` `````` mrk022 committed Dec 04, 2019 179 `````` def get_state(self, oldConstantCount): `````` mrk022 committed Dec 07, 2019 180 181 182 `````` ''' Based on number of variables and constants on each side of the equation the agent guesses which state the user is moving to ''' `````` mrk022 committed Dec 04, 2019 183 `````` if oldConstantCount[0] != self.constantCount[0] and oldConstantCount[2] != self.constantCount[2]: `````` mrk022 committed Dec 02, 2019 184 `````` # if not same number of variables as before for both sides then moved variables `````` mrk022 committed Dec 07, 2019 185 186 `````` if oldConstantCount[0] < self.constantCount[0]: self.state = "move_variables 1" else: self.state = "move_variables 0" `````` mrk022 committed Dec 04, 2019 187 `````` elif oldConstantCount[0] != self.constantCount[0] or oldConstantCount[2] != self.constantCount[2]: `````` mrk022 committed Dec 06, 2019 188 189 `````` if oldConstantCount[0] > self.constantCount[0]: self.state = "add_variables 1" else: self.state = "add_variables 0" `````` mrk022 committed Dec 04, 2019 190 `````` elif oldConstantCount[1] != self.constantCount[1] and oldConstantCount[3] != self.constantCount[3]: `````` mrk022 committed Dec 06, 2019 191 192 193 194 `````` if oldConstantCount[1] < self.constantCount[1]: #moving constants to left self.state = "move_constants 1" else: self.state = "move_constants 0" `````` mrk022 committed Dec 04, 2019 195 `````` elif oldConstantCount[1] != self.constantCount[1] or oldConstantCount[3] != self.constantCount[3]: `````` mrk022 committed Dec 07, 2019 196 197 198 `````` if oldConstantCount[1] > self.constantCount[1]: self.state = "add_constants 1" else: self.state = "add_constants 0" else: print("nothing triggered") `````` mrk022 committed Dec 04, 2019 199 `````` return self.state `````` mrk022 committed Nov 27, 2019 200 `````` `````` mrk022 committed Nov 26, 2019 201 ``````class IntelligentTutor(ACTR): `````` mrk022 committed Dec 07, 2019 202 203 `````` ''' RBES for the tutor itself. Tracks states ''' `````` mrk022 committed Nov 26, 2019 204 `````` goal = Buffer() `````` mrk022 committed Nov 27, 2019 205 `````` user = UserState("4x+7=5x+2") `````` mrk022 committed Dec 04, 2019 206 `````` tutor = TutorState("4x + 7 = 5x + 2") `````` mrk022 committed Nov 26, 2019 207 `````` `````` mrk022 committed Nov 27, 2019 208 `````` def init(): `````` mrk022 committed Dec 06, 2019 209 `````` user.get_input() `````` mrk022 committed Dec 04, 2019 210 `````` print("USER STATE IS ", user.state) # need to get user input to translate to state `````` mrk022 committed Dec 07, 2019 211 `````` goal.set(user.state) `````` mrk022 committed Dec 06, 2019 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 `````` def moved_constants(goal="move_constants ?left"): #true if moving constants left tutorEq = tutor.move_values(bool(int(left)), True) print("user equation ",user.equation) goal.set("check_state " + tutorEq) def add_constants(goal= "add_constants ?left"): tutorEq = tutor.combine_like_terms(bool(int(left))) print("user equation ",user.equation) goal.set("check_state " + tutorEq) def moved_variables(goal="move_variables ?left"): tutorEq = tutor.move_values(bool(int(left)), False) print("user equation ",user.equation) goal.set("check_state " + tutorEq) def add_variables(goal = "add_variables ?left"): tutorEq = tutor.combine_like_terms(bool(int(left))) print("user equation", user.equation) goal.set("check_state " + tutorEq) `````` mrk022 committed Nov 26, 2019 232 `````` `````` mrk022 committed Dec 06, 2019 233 `````` def check_state(goal = "check_state ?tutorEq"): `````` mrk022 committed Dec 07, 2019 234 `````` if tutor.compare_Equations(tutorEq, user.equation): `````` mrk022 committed Dec 04, 2019 235 236 237 `````` user.get_input() goal.set(user.state) else: `````` mrk022 committed Dec 06, 2019 238 `````` goal.set("invalid_state " + tutorEq) `````` mrk022 committed Nov 26, 2019 239 `````` `````` mrk022 committed Dec 06, 2019 240 241 242 `````` 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.") user.equation = tutorEqn `````` mrk022 committed Dec 02, 2019 243 244 245 `````` user.get_input() goal.set(user.state) `````` mrk022 committed Nov 26, 2019 246 247 248 249 250 251 252 253 254 255 `````` def end_process(goal = "end_process"): goal.set("Simulation Complete!") self.stop() class EmptyEnvironment(ccm.Model): pass def main(): env_name = EmptyEnvironment() agent_name = IntelligentTutor() `````` mrk022 committed Dec 07, 2019 256 257 `````` env_name.agent = agent_name #ccm.log_everything(env_name) # log statement, uncomment if you want to log information `````` mrk022 committed Nov 26, 2019 258 259 260 `````` env_name.run() main()``````