Skip to content

Commit

Permalink
Adding Tkinter GUI (aimacode#693)
Browse files Browse the repository at this point in the history
* Added Vacuum Agent

* Minor Fix

* Improved Font

* Added XYVacuumEnv

* Minor Fix

* Review changes
  • Loading branch information
apb7 authored and norvig committed Jan 22, 2018
1 parent b13bb02 commit 47e6089
Show file tree
Hide file tree
Showing 2 changed files with 338 additions and 0 deletions.
160 changes: 160 additions & 0 deletions gui/vacuum_agent.py
Original file line number Diff line number Diff line change
@@ -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()
178 changes: 178 additions & 0 deletions gui/xy_vacuum_environment.py
Original file line number Diff line number Diff line change
@@ -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()

0 comments on commit 47e6089

Please sign in to comment.