diff --git a/TutorEnvs/fractions.py b/TutorEnvs/fractions.py index 521c2fde9ed2e433011535fd30b9da371c538048..181a9666f36c2d3c5a7f127740c6cddeae02fa12 100644 --- a/TutorEnvs/fractions.py +++ b/TutorEnvs/fractions.py @@ -1,18 +1,256 @@ +from random import randint +from random import choice + import gym from gym import error, spaces, utils from gym.utils import seeding + +class FractionArithSymbolic: + + def __init__(self): + """ + Creates a state and sets a random problem. + """ + self.set_random_problem() + # self.reset("", "", "", "", "") + + def reset(self, num1, denom1, operator, num2, denom2): + """ + Sets the state to a new fraction arithmetic problem as specified by the + provided arguments. + """ + self.state = { + 'initial_num_left': num1, + 'initial_denom_left': denom1, + 'initial_operator': operator, + 'initial_num_right': num2, + 'initial_denom_right': denom2, + 'check_convert': '', + 'convert_num_left': '', + 'convert_denom_left': '', + 'convert_operator': operator, + 'convert_num_right': '', + 'convert_denom_right': '', + 'answer_num': '', + 'answer_denom': '', + } + + def get_state(self): + """ + Returns the current state as a dict. + """ + state_output = {attr: + {'id': attr, 'value': self.state[attr], 'type': 'TextField', + 'contentEditable': self.state[attr] == ""} + for attr in self.state} + state_output['done'] = { + 'id': 'done', + 'type': 'Button' + } + + return state_output + + def set_random_problem(self): + num1 = str(randint(1, 15)) + num2 = str(randint(1, 15)) + denom1 = str(randint(2, 15)) + denom2 = str(randint(2, 15)) + operator = choice(['+', '*']) + + self.reset(num1, denom1, operator, num2, denom2) + + def apply_sai(self, selection, action, inputs): + """ + Give a SAI, it applies it. This method returns feedback (i.e., -1 or 1). + """ + reward = self.evaluate_sai(selection, action, inputs) + + if reward == -1.0: + return reward + + if selection == "done": + self.set_random_problem() + + else: + self.state[selection] = inputs['value'] + + return reward + + def evaluate_sai(self, selection, action, inputs): + """ + Given a SAI, returns whether it is correct or incorrect. + """ + # done step + if selection == "done": + + if action != "ButtonPressed": + return -1.0 + + if self.state['answer_num'] != "" and self.state['answer_denom'] != "": + return 1.0 + else: + return -1.0 + + # we can only edit selections that are editable + if self.state[selection] != "": + return -1.0 + + if (self.state['initial_operator'] == '+' and + self.state['initial_denom_left'] == self.state['initial_denom_right']): + # add same denoms + if (selection == 'answer_num' and (inputs['value'] == + str(int(self.state['initial_num_left']) + + int(self.state['initial_num_right'])))): + return 1.0 + + if (selection == 'answer_denom' and inputs['value'] == + self.state['initial_denom_left']): + return 1.0 + + return -1.0 + + if (self.state['initial_operator'] == "+" and + self.state['initial_denom_left'] != self.state['initial_denom_right']): + # add, different denoms + if selection == "check_convert": + return 1.0 + + if (selection == "convert_num_left" and + self.state['convert_denom_left'] != "" and + inputs['value'] == str(int(self.state['initial_num_left']) * + int(self.state['initial_denom_right']))): + return 1.0 + + if (selection == "convert_denom_left" and + self.state['check_convert'] != "" and + inputs['value'] == str(int(self.state['initial_denom_left']) * + int(self.state['initial_denom_right']))): + return 1.0 + + if (selection == "convert_num_right" and + self.state['convert_denom_right'] != "" and + inputs['value'] == str(int(self.state['initial_num_right']) * + int(self.state['initial_denom_left']))): + return 1.0 + + if (selection == "convert_denom_right" and + self.state['convert_denom_left'] != "" and + inputs['value'] == str(int(self.state['initial_denom_left']) * + int(self.state['initial_denom_right']))): + return 1.0 + + if (selection == 'answer_num' and + self.state['convert_num_left'] != "" and + self.state['convert_num_right'] != "" and + (inputs['value'] == str(int(self.state['convert_num_left']) + + int(self.state['convert_num_right'])))): + return 1.0 + + if (selection == 'answer_denom' and + self.state['convert_num_left'] != "" and + self.state['convert_num_right'] != "" and + inputs['value'] == self.state['convert_denom_right']): + return 1.0 + + return -1.0 + + if (self.state['initial_operator'] == "*"): + # multiply + if (selection == 'answer_num' and (inputs['value'] == + str(int(self.state['initial_num_left']) * + int(self.state['initial_num_right'])))): + return 1.0 + + if (selection == 'answer_denom' and (inputs['value'] == + str(int(self.state['initial_denom_left']) * + int(self.state['initial_denom_right'])))): + return 1.0 + + return -1.0 + + raise Exception("evaluate_sai logic missing") + + def request_demo(self): + """ + Returns a correct next-step SAI + """ + if (self.state['initial_operator'] == '+' and + self.state['initial_denom_left'] == self.state['initial_denom_right']): + if self.state['answer_num'] == "": + return ('answer_num', "UpdateField", + {'value': str(int(self.state['initial_num_left']) + + int(self.state['initial_num_right']))}) + + if self.state['answer_denom'] == "": + return ('answer_denom', "UpdateField", + {'value': self.state['initial_denom_left']}) + + return ('done', "ButtonPressed", {'value': -1}) + + if (self.state['initial_operator'] == "+" and + self.state['initial_denom_left'] != self.state['initial_denom_right']): + + if self.state['check_convert'] == "": + return ('check_convert', 'UpdateField', {"value": 'x'}) + + if self.state['convert_denom_left'] == "": + return ('convert_denom_left', "UpdateField", + {'value': str(int(self.state['initial_denom_left']) * + int(self.state['initial_denom_right']))}) + + if self.state['convert_num_left'] == "": + return ('convert_num_left', "UpdateField", + {'value': str(int(self.state['initial_num_left']) * + int(self.state['initial_denom_right']))}) + + if self.state['convert_denom_right'] == "": + return ('convert_denom_right', "UpdateField", + {'value': str(int(self.state['initial_denom_left']) * + int(self.state['initial_denom_right']))}) + + if self.state['convert_num_right'] == "": + return ('convert_num_right', "UpdateField", + {'value': str(int(self.state['initial_denom_left']) * + int(self.state['initial_num_right']))}) + + if self.state['answer_num'] == "": + return ('answer_num', "UpdateField", + {'value': str(int(self.state['convert_num_left']) + + int(self.state['convert_num_right']))}) + + if self.state['answer_denom'] == "": + return ('answer_denom', "UpdateField", + {'value': self.state['convert_denom_right']}) + + return ('done', "ButtonPressed", {'value': -1}) + + if (self.state['initial_operator'] == "*"): + if self.state['answer_num'] == "": + return ('answer_num', "UpdateField", + {'value': str(int(self.state['initial_num_left']) * + int(self.state['initial_num_right']))}) + + if self.state['answer_denom'] == "": + return ('answer_denom', "UpdateField", + {'value': str(int(self.state['initial_denom_left']) * + int(self.state['initial_denom_right']))}) + + return ('done', "ButtonPressed", {'value': -1}) + + raise Exception("request demo - logic missing") + class FractionArithEnv(gym.Env): - metadata = {'render.modes': ['human']} + metadata = {'render.modes': ['human']} - def __init__(self): - pass + def __init__(self): + pass - def step(self, action): - pass + def step(self, action): + pass - def reset(self): - pass + def reset(self): + pass - def render(self, mode='human', close=False): - pass + def render(self, mode='human', close=False): + pass