diff --git a/gui/vacuum_agent.py b/gui/vacuum_agent.py new file mode 100644 index 000000000..23292efb3 --- /dev/null +++ b/gui/vacuum_agent.py @@ -0,0 +1,160 @@ +from tkinter import * +import random +import sys +import os.path +sys.path.append(os.path.join(os.path.dirname(__file__), '..')) +from agents import * + +loc_A, loc_B = (0, 0), (1, 0) # The two locations for the Vacuum world + + +class Gui(Environment): + + """This GUI environment has two locations, A and B. Each can be Dirty + or Clean. The agent perceives its location and the location's + status.""" + + def __init__(self, root, height=300, width=380): + super().__init__() + self.status = {loc_A: 'Clean', + loc_B: 'Clean'} + self.root = root + self.height = height + self.width = width + self.canvas = None + self.buttons = [] + self.create_canvas() + self.create_buttons() + + def thing_classes(self): + """The list of things which can be used in the environment.""" + return [Wall, Dirt, ReflexVacuumAgent, RandomVacuumAgent, + TableDrivenVacuumAgent, ModelBasedVacuumAgent] + + def percept(self, agent): + """Returns the agent's location, and the location status (Dirty/Clean).""" + return (agent.location, self.status[agent.location]) + + def execute_action(self, agent, action): + """Change the location status (Dirty/Clean); track performance. + Score 10 for each dirt cleaned; -1 for each move.""" + if action == 'Right': + agent.location = loc_B + agent.performance -= 1 + elif action == 'Left': + agent.location = loc_A + agent.performance -= 1 + elif action == 'Suck': + if self.status[agent.location] == 'Dirty': + if agent.location == loc_A: + self.buttons[0].config(bg='white', activebackground='light grey') + else: + self.buttons[1].config(bg='white', activebackground='light grey') + agent.performance += 10 + self.status[agent.location] = 'Clean' + + def default_location(self, thing): + """Agents start in either location at random.""" + return random.choice([loc_A, loc_B]) + + def create_canvas(self): + """Creates Canvas element in the GUI.""" + self.canvas = Canvas( + self.root, + width=self.width, + height=self.height, + background='powder blue') + self.canvas.pack(side='bottom') + + def create_buttons(self): + """Creates the buttons required in the GUI.""" + button_left = Button(self.root, height=4, width=12, padx=2, pady=2, bg='white') + button_left.config(command=lambda btn=button_left: self.dirt_switch(btn)) + self.buttons.append(button_left) + button_left_window = self.canvas.create_window(130, 200, anchor=N, window=button_left) + button_right = Button(self.root, height=4, width=12, padx=2, pady=2, bg='white') + button_right.config(command=lambda btn=button_right: self.dirt_switch(btn)) + self.buttons.append(button_right) + button_right_window = self.canvas.create_window(250, 200, anchor=N, window=button_right) + + def dirt_switch(self, button): + """Gives user the option to put dirt in any tile.""" + bg_color = button['bg'] + if bg_color == 'saddle brown': + button.config(bg='white', activebackground='light grey') + elif bg_color == 'white': + button.config(bg='saddle brown', activebackground='light goldenrod') + + def read_env(self): + """Reads the current state of the GUI.""" + for i, btn in enumerate(self.buttons): + if i == 0: + if btn['bg'] == 'white': + self.status[loc_A] = 'Clean' + else: + self.status[loc_A] = 'Dirty' + else: + if btn['bg'] == 'white': + self.status[loc_B] = 'Clean' + else: + self.status[loc_B] = 'Dirty' + + def update_env(self, agent): + """Updates the GUI according to the agent's action.""" + self.read_env() + # print(self.status) + before_step = agent.location + self.step() + # print(self.status) + # print(agent.location) + move_agent(self, agent, before_step) + + +def create_agent(env, agent): + """Creates the agent in the GUI and is kept independent of the environment.""" + env.add_thing(agent) + # print(agent.location) + if agent.location == (0, 0): + env.agent_rect = env.canvas.create_rectangle(80, 100, 175, 180, fill='lime green') + env.text = env.canvas.create_text(128, 140, font="Helvetica 10 bold italic", text="Agent") + else: + env.agent_rect = env.canvas.create_rectangle(200, 100, 295, 180, fill='lime green') + env.text = env.canvas.create_text(248, 140, font="Helvetica 10 bold italic", text="Agent") + + +def move_agent(env, agent, before_step): + """Moves the agent in the GUI when 'next' button is pressed.""" + if agent.location == before_step: + pass + else: + if agent.location == (1, 0): + env.canvas.move(env.text, 120, 0) + env.canvas.move(env.agent_rect, 120, 0) + elif agent.location == (0, 0): + env.canvas.move(env.text, -120, 0) + env.canvas.move(env.agent_rect, -120, 0) + + +# TODO: Add more agents to the environment. +# TODO: Expand the environment to XYEnvironment. +def main(): + """The main function of the program.""" + root = Tk() + root.title("Vacuum Environment") + root.geometry("420x380") + root.resizable(0, 0) + frame = Frame(root, bg='black') + # reset_button = Button(frame, text='Reset', height=2, width=6, padx=2, pady=2, command=None) + # reset_button.pack(side='left') + next_button = Button(frame, text='Next', height=2, width=6, padx=2, pady=2) + next_button.pack(side='left') + frame.pack(side='bottom') + env = Gui(root) + agent = ReflexVacuumAgent() + create_agent(env, agent) + next_button.config(command=lambda: env.update_env(agent)) + root.mainloop() + + +if __name__ == "__main__": + main() diff --git a/gui/xy_vacuum_environment.py b/gui/xy_vacuum_environment.py new file mode 100644 index 000000000..72d2f2434 --- /dev/null +++ b/gui/xy_vacuum_environment.py @@ -0,0 +1,178 @@ +from tkinter import * +import random +import sys +import os.path +sys.path.append(os.path.join(os.path.dirname(__file__), '..')) +from agents import * + + +class Gui(VacuumEnvironment): + """This is a two-dimensional GUI environment. Each location may be + dirty, clean or can have a wall. The user can change these at each step. + """ + xi, yi = (0, 0) + + def __init__(self, root, width=7, height=7, elements=['D', 'W']): + super().__init__(width, height) + self.root = root + self.create_frames() + self.create_buttons() + self.create_walls() + self.elements = elements + + def create_frames(self): + """Adds frames to the GUI environment.""" + self.frames = [] + for _ in range(7): + frame = Frame(self.root, bg='grey') + frame.pack(side='bottom') + self.frames.append(frame) + + def create_buttons(self): + """Adds buttons to the respective frames in the GUI.""" + self.buttons = [] + for frame in self.frames: + button_row = [] + for _ in range(7): + button = Button(frame, height=3, width=5, padx=2, pady=2) + button.config( + command=lambda btn=button: self.display_element(btn)) + button.pack(side='left') + button_row.append(button) + self.buttons.append(button_row) + + def create_walls(self): + """Creates the outer boundary walls which do not move.""" + for row, button_row in enumerate(self.buttons): + if row == 0 or row == len(self.buttons) - 1: + for button in button_row: + button.config(text='W', state='disabled', + disabledforeground='black') + else: + button_row[0].config( + text='W', state='disabled', disabledforeground='black') + button_row[len(button_row) - 1].config(text='W', + state='disabled', disabledforeground='black') + # Place the agent in the centre of the grid. + self.buttons[3][3].config( + text='A', state='disabled', disabledforeground='black') + + def display_element(self, button): + """Show the things on the GUI.""" + txt = button['text'] + if txt != 'A': + if txt == 'W': + button.config(text='D') + elif txt == 'D': + button.config(text='') + elif txt == '': + button.config(text='W') + + def execute_action(self, agent, action): + """Determines the action the agent performs.""" + xi, yi = ((self.xi, self.yi)) + if action == 'Suck': + dirt_list = self.list_things_at(agent.location, Dirt) + if dirt_list != []: + dirt = dirt_list[0] + agent.performance += 100 + self.delete_thing(dirt) + self.buttons[xi][yi].config(text='', state='normal') + xf, yf = agent.location + self.buttons[xf][yf].config( + text='A', state='disabled', disabledforeground='black') + + else: + agent.bump = False + if action == 'TurnRight': + agent.direction += Direction.R + elif action == 'TurnLeft': + agent.direction += Direction.L + elif action == 'Forward': + agent.bump = self.move_to(agent, agent.direction.move_forward(agent.location)) + if not agent.bump: + self.buttons[xi][yi].config(text='', state='normal') + xf, yf = agent.location + self.buttons[xf][yf].config( + text='A', state='disabled', disabledforeground='black') + + if action != 'NoOp': + agent.performance -= 1 + + def read_env(self): + """Reads the current state of the GUI environment.""" + for i, btn_row in enumerate(self.buttons): + for j, btn in enumerate(btn_row): + if (i != 0 and i != len(self.buttons) - 1) and (j != 0 and j != len(btn_row) - 1): + agt_loc = self.agents[0].location + if self.some_things_at((i, j)) and (i, j) != agt_loc: + for thing in self.list_things_at((i, j)): + self.delete_thing(thing) + if btn['text'] == self.elements[0]: + self.add_thing(Dirt(), (i, j)) + elif btn['text'] == self.elements[1]: + self.add_thing(Wall(), (i, j)) + + def update_env(self): + """Updates the GUI environment according to the current state.""" + self.read_env() + agt = self.agents[0] + previous_agent_location = agt.location + self.xi, self.yi = previous_agent_location + self.step() + xf, yf = agt.location + + +def XYReflexAgentProgram(percept): + """The modified SimpleReflexAgentProgram for the GUI environment.""" + status, bump = percept + if status == 'Dirty': + return 'Suck' + + if bump == 'Bump': + value = random.choice((1, 2)) + else: + value = random.choice((1, 2, 3, 4)) # 1-right, 2-left, others-forward + + if value == 1: + return 'TurnRight' + elif value == 2: + return 'TurnLeft' + else: + return 'Forward' + + +class XYReflexAgent(Agent): + """The modified SimpleReflexAgent for the GUI environment.""" + + def __init__(self, program=None): + super().__init__(program) + self.location = (3, 3) + self.direction = Direction("up") + + +# TODO: Check the coordinate system. +def main(): + """The main function.""" + root = Tk() + root.title("Vacuum Environment") + root.geometry("420x440") + root.resizable(0, 0) + frame = Frame(root, bg='black') + # create a reset button + # reset_button = Button(frame, text='Reset', height=2, + # width=6, padx=2, pady=2, command=None) + # reset_button.pack(side='left') + next_button = Button(frame, text='Next', height=2, + width=6, padx=2, pady=2) + next_button.pack(side='left') + frame.pack(side='bottom') + env = Gui(root) + agt = XYReflexAgent(program=XYReflexAgentProgram) + env.add_thing(agt, location=(3, 3)) + next_button.config(command=env.update_env) + root.mainloop() + + +if __name__ == "__main__": + main()