diff --git a/agents4e.py b/agents4e.py index 75369a69a..e59103bad 100644 --- a/agents4e.py +++ b/agents4e.py @@ -84,12 +84,15 @@ class Agent(Thing): There is an optional slot, .performance, which is a number giving the performance measure of the agent in its environment.""" - def __init__(self, program=None): + def __init__(self, program=None, direction = None): self.alive = True self.bump = False self.holding = [] self.performance = 0 - if program is None or not isinstance(program, collections.abc.Callable): + if program is None: + # or \ + # (not isinstance(program, collections.abc.Callable) \ + # and not isinstance(program, function)): print("Can't find a valid program for {}, falling back to default.".format(self.__class__.__name__)) def program(percept): @@ -97,6 +100,9 @@ def program(percept): self.program = program + + self.direction = direction + def can_grab(self, thing): """Return True if this agent can grab this thing. Override for appropriate subclasses of Agent and Thing.""" @@ -110,7 +116,8 @@ def TraceAgent(agent): def new_program(percept): action = old_program(percept) - print('{} perceives {} and does {}'.format(agent, percept, action)) + print('{} perceives {} and does {} with performance {}'\ + .format(agent, percept, action, agent.performance)) return action agent.program = new_program @@ -662,7 +669,7 @@ def run(self, steps=1000, delay=1): self.reveal() """ - def run(self, steps=1000, delay=1): + def run(self, steps=2000, delay=1): """Run the Environment for given number of time steps, but update the GUI too.""" for step in range(steps): @@ -743,7 +750,7 @@ def __init__(self, width=10, height=10): self.add_walls() def thing_classes(self): - return [Wall, Dirt, ReflexVacuumAgent, RandomVacuumAgent, + return [Wall, Dirt, XYReflexVacuumAgent, ReflexVacuumAgent, RandomVacuumAgent, TableDrivenVacuumAgent, ModelBasedVacuumAgent] def percept(self, agent): @@ -768,6 +775,15 @@ def execute_action(self, agent, action): if action != 'NoOp': agent.performance -= 1 + def is_done(self): + # Elsa defined + num_dirty = 0 + for idx in range(10): + num_dirty += len([self.some_things_at([idx,j], Dirt) \ + for j in range(10)]) + return False if num_dirty else True + + class TrivialVacuumEnvironment(Environment): """This environment has two locations, A and B. Each can be Dirty diff --git a/aibc_agents.elsa.ipynb b/aibc_agents.elsa.ipynb new file mode 100644 index 000000000..dfcd8292b --- /dev/null +++ b/aibc_agents.elsa.ipynb @@ -0,0 +1,8373 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [], + "source": [ + "from agents4e import *\n", + "from notebook import psource\n", + "\n", + "%load_ext autoreload\n", + "%autoreload 2" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Constructing Environments" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## The Environment class\n", + "Is a shell for all environments. It owns **things** and **agents**. It specifies:\n", + "* The thing classes it can hold. These things can be just things (like dirt) or agents (like vacuums that can do stuff)\n", + "* What it can perceive (percept classes -- like what sensors are on my robot?)\n", + "* What it can do. Like if a vacuum sucks up dirt, then it can change the amount of dirt in its environment.\n", + "* Specify a default location for new things, like where more dirt might go.\n", + "* Specify changes we won't allow (\"exogenous_change\")\n", + "* Tell us if all of the agents are dead\n", + "* Perform one time step in our environmental \"game\" definition.\n", + " * Each agent gets to perceive its state\n", + " * Each agent gets to perform an action\n", + "* Perform a bunch of steps\n", + "* List all the things at a location\n", + "* List some things at a location?\n", + "* Add a thing at a location (or default location)\n", + "* Delete a specified thing" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "\n", + "\n", + "\n", + "
\n", + "class Environment:\n",
+ " """Abstract class representing an Environment. 'Real' Environment classes\n",
+ " inherit from this. Your Environment will typically need to implement:\n",
+ " percept: Define the percept that an agent sees.\n",
+ " execute_action: Define the effects of executing an action.\n",
+ " Also update the agent.performance slot.\n",
+ " The environment keeps a list of .things and .agents (which is a subset\n",
+ " of .things). Each agent has a .performance slot, initialized to 0.\n",
+ " Each thing has a .location slot, even though some environments may not\n",
+ " need this."""\n",
+ "\n",
+ " def __init__(self):\n",
+ " self.things = []\n",
+ " self.agents = []\n",
+ "\n",
+ " def thing_classes(self):\n",
+ " return [] # List of classes that can go into environment\n",
+ "\n",
+ " def percept(self, agent):\n",
+ " """Return the percept that the agent sees at this point. (Implement this.)"""\n",
+ " raise NotImplementedError\n",
+ "\n",
+ " def execute_action(self, agent, action):\n",
+ " """Change the world to reflect this action. (Implement this.)"""\n",
+ " raise NotImplementedError\n",
+ "\n",
+ " def default_location(self, thing):\n",
+ " """Default location to place a new thing with unspecified location."""\n",
+ " return None\n",
+ "\n",
+ " def exogenous_change(self):\n",
+ " """If there is spontaneous change in the world, override this."""\n",
+ " pass\n",
+ "\n",
+ " def is_done(self):\n",
+ " """By default, we're done when we can't find a live agent."""\n",
+ " return not any(agent.is_alive() for agent in self.agents)\n",
+ "\n",
+ " def step(self):\n",
+ " """Run the environment for one time step. If the\n",
+ " actions and exogenous changes are independent, this method will\n",
+ " do. If there are interactions between them, you'll need to\n",
+ " override this method."""\n",
+ " if not self.is_done():\n",
+ " actions = []\n",
+ " for agent in self.agents:\n",
+ " if agent.alive:\n",
+ " actions.append(agent.program(self.percept(agent)))\n",
+ " else:\n",
+ " actions.append("")\n",
+ " for (agent, action) in zip(self.agents, actions):\n",
+ " self.execute_action(agent, action)\n",
+ " self.exogenous_change()\n",
+ "\n",
+ " def run(self, steps=1000):\n",
+ " """Run the Environment for given number of time steps."""\n",
+ " for step in range(steps):\n",
+ " if self.is_done():\n",
+ " return\n",
+ " self.step()\n",
+ "\n",
+ " def list_things_at(self, location, tclass=Thing):\n",
+ " """Return all things exactly at a given location."""\n",
+ " if isinstance(location, numbers.Number):\n",
+ " return [thing for thing in self.things\n",
+ " if thing.location == location and isinstance(thing, tclass)]\n",
+ " return [thing for thing in self.things\n",
+ " if all(x == y for x, y in zip(thing.location, location)) and isinstance(thing, tclass)]\n",
+ "\n",
+ " def some_things_at(self, location, tclass=Thing):\n",
+ " """Return true if at least one of the things at location\n",
+ " is an instance of class tclass (or a subclass)."""\n",
+ " return self.list_things_at(location, tclass) != []\n",
+ "\n",
+ " def add_thing(self, thing, location=None):\n",
+ " """Add a thing to the environment, setting its location. For\n",
+ " convenience, if thing is an agent program we make a new agent\n",
+ " for it. (Shouldn't need to override this.)"""\n",
+ " if not isinstance(thing, Thing):\n",
+ " thing = Agent(thing)\n",
+ " if thing in self.things:\n",
+ " print("Can't add the same thing twice")\n",
+ " else:\n",
+ " thing.location = location if location is not None else self.default_location(thing)\n",
+ " self.things.append(thing)\n",
+ " if isinstance(thing, Agent):\n",
+ " thing.performance = 0\n",
+ " self.agents.append(thing)\n",
+ "\n",
+ " def delete_thing(self, thing):\n",
+ " """Remove a thing from the environment."""\n",
+ " try:\n",
+ " self.things.remove(thing)\n",
+ " except ValueError as e:\n",
+ " print(e)\n",
+ " print(" in Environment delete_thing")\n",
+ " print(" Thing to be removed: {} at {}".format(thing, thing.location))\n",
+ " print(" from list: {}".format([(thing, thing.location) for thing in self.things]))\n",
+ " if thing in self.agents:\n",
+ " self.agents.remove(thing)\n",
+ "class Direction:\n",
+ " """A direction class for agents that want to move in a 2D plane\n",
+ " Usage:\n",
+ " d = Direction("down")\n",
+ " To change directions:\n",
+ " d = d + "right" or d = d + Direction.R #Both do the same thing\n",
+ " Note that the argument to __add__ must be a string and not a Direction object.\n",
+ " Also, it (the argument) can only be right or left."""\n",
+ "\n",
+ " R = "right"\n",
+ " L = "left"\n",
+ " U = "up"\n",
+ " D = "down"\n",
+ "\n",
+ " def __init__(self, direction):\n",
+ " self.direction = direction\n",
+ "\n",
+ " def __add__(self, heading):\n",
+ " """\n",
+ " >>> d = Direction('right')\n",
+ " >>> l1 = d.__add__(Direction.L)\n",
+ " >>> l2 = d.__add__(Direction.R)\n",
+ " >>> l1.direction\n",
+ " 'up'\n",
+ " >>> l2.direction\n",
+ " 'down'\n",
+ " >>> d = Direction('down')\n",
+ " >>> l1 = d.__add__('right')\n",
+ " >>> l2 = d.__add__('left')\n",
+ " >>> l1.direction == Direction.L\n",
+ " True\n",
+ " >>> l2.direction == Direction.R\n",
+ " True\n",
+ " """\n",
+ " if self.direction == self.R:\n",
+ " return {\n",
+ " self.R: Direction(self.D),\n",
+ " self.L: Direction(self.U),\n",
+ " }.get(heading, None)\n",
+ " elif self.direction == self.L:\n",
+ " return {\n",
+ " self.R: Direction(self.U),\n",
+ " self.L: Direction(self.D),\n",
+ " }.get(heading, None)\n",
+ " elif self.direction == self.U:\n",
+ " return {\n",
+ " self.R: Direction(self.R),\n",
+ " self.L: Direction(self.L),\n",
+ " }.get(heading, None)\n",
+ " elif self.direction == self.D:\n",
+ " return {\n",
+ " self.R: Direction(self.L),\n",
+ " self.L: Direction(self.R),\n",
+ " }.get(heading, None)\n",
+ "\n",
+ " def move_forward(self, from_location):\n",
+ " """\n",
+ " >>> d = Direction('up')\n",
+ " >>> l1 = d.move_forward((0, 0))\n",
+ " >>> l1\n",
+ " (0, -1)\n",
+ " >>> d = Direction(Direction.R)\n",
+ " >>> l1 = d.move_forward((0, 0))\n",
+ " >>> l1\n",
+ " (1, 0)\n",
+ " """\n",
+ " # get the iterable class to return\n",
+ " iclass = from_location.__class__\n",
+ " x, y = from_location\n",
+ " if self.direction == self.R:\n",
+ " return iclass((x + 1, y))\n",
+ " elif self.direction == self.L:\n",
+ " return iclass((x - 1, y))\n",
+ " elif self.direction == self.U:\n",
+ " return iclass((x, y - 1))\n",
+ " elif self.direction == self.D:\n",
+ " return iclass((x, y + 1))\n",
+ "class XYEnvironment(Environment):\n",
+ " """This class is for environments on a 2D plane, with locations\n",
+ " labelled by (x, y) points, either discrete or continuous.\n",
+ "\n",
+ " Agents perceive things within a radius. Each agent in the\n",
+ " environment has a .location slot which should be a location such\n",
+ " as (0, 1), and a .holding slot, which should be a list of things\n",
+ " that are held."""\n",
+ "\n",
+ " def __init__(self, width=10, height=10):\n",
+ " super().__init__()\n",
+ "\n",
+ " self.width = width\n",
+ " self.height = height\n",
+ " self.observers = []\n",
+ " # Sets iteration start and end (no walls).\n",
+ " self.x_start, self.y_start = (0, 0)\n",
+ " self.x_end, self.y_end = (self.width, self.height)\n",
+ "\n",
+ " perceptible_distance = 1\n",
+ "\n",
+ " def things_near(self, location, radius=None):\n",
+ " """Return all things within radius of location."""\n",
+ " if radius is None:\n",
+ " radius = self.perceptible_distance\n",
+ " radius2 = radius * radius\n",
+ " return [(thing, radius2 - distance_squared(location, thing.location))\n",
+ " for thing in self.things if distance_squared(\n",
+ " location, thing.location) <= radius2]\n",
+ "\n",
+ " def percept(self, agent):\n",
+ " """By default, agent perceives things within a default radius."""\n",
+ " return self.things_near(agent.location)\n",
+ "\n",
+ " def execute_action(self, agent, action):\n",
+ " agent.bump = False\n",
+ " if action == 'TurnRight':\n",
+ " agent.direction += Direction.R\n",
+ " elif action == 'TurnLeft':\n",
+ " agent.direction += Direction.L\n",
+ " elif action == 'Forward':\n",
+ " agent.bump = self.move_to(agent, agent.direction.move_forward(agent.location))\n",
+ " # elif action == 'Grab':\n",
+ " # things = [thing for thing in self.list_things_at(agent.location)\n",
+ " # if agent.can_grab(thing)]\n",
+ " # if things:\n",
+ " # agent.holding.append(things[0])\n",
+ " elif action == 'Release':\n",
+ " if agent.holding:\n",
+ " agent.holding.pop()\n",
+ "\n",
+ " def default_location(self, thing):\n",
+ " location = self.random_location_inbounds()\n",
+ " while self.some_things_at(location, Obstacle):\n",
+ " # we will find a random location with no obstacles\n",
+ " location = self.random_location_inbounds()\n",
+ " return location\n",
+ "\n",
+ " def move_to(self, thing, destination):\n",
+ " """Move a thing to a new location. Returns True on success or False if there is an Obstacle.\n",
+ " If thing is holding anything, they move with him."""\n",
+ " thing.bump = self.some_things_at(destination, Obstacle)\n",
+ " if not thing.bump:\n",
+ " thing.location = destination\n",
+ " for o in self.observers:\n",
+ " o.thing_moved(thing)\n",
+ " for t in thing.holding:\n",
+ " self.delete_thing(t)\n",
+ " self.add_thing(t, destination)\n",
+ " t.location = destination\n",
+ " return thing.bump\n",
+ "\n",
+ " def add_thing(self, thing, location=None, exclude_duplicate_class_items=False):\n",
+ " """Add things to the world. If (exclude_duplicate_class_items) then the item won't be\n",
+ " added if the location has at least one item of the same class."""\n",
+ " if location is None:\n",
+ " super().add_thing(thing)\n",
+ " elif self.is_inbounds(location):\n",
+ " if (exclude_duplicate_class_items and\n",
+ " any(isinstance(t, thing.__class__) for t in self.list_things_at(location))):\n",
+ " return\n",
+ " super().add_thing(thing, location)\n",
+ "\n",
+ " def is_inbounds(self, location):\n",
+ " """Checks to make sure that the location is inbounds (within walls if we have walls)"""\n",
+ " x, y = location\n",
+ " return not (x < self.x_start or x > self.x_end or y < self.y_start or y > self.y_end)\n",
+ "\n",
+ " def random_location_inbounds(self, exclude=None):\n",
+ " """Returns a random location that is inbounds (within walls if we have walls)"""\n",
+ " location = (random.randint(self.x_start, self.x_end),\n",
+ " random.randint(self.y_start, self.y_end))\n",
+ " if exclude is not None:\n",
+ " while location == exclude:\n",
+ " location = (random.randint(self.x_start, self.x_end),\n",
+ " random.randint(self.y_start, self.y_end))\n",
+ " return location\n",
+ "\n",
+ " def delete_thing(self, thing):\n",
+ " """Deletes thing, and everything it is holding (if thing is an agent)"""\n",
+ " if isinstance(thing, Agent):\n",
+ " for obj in thing.holding:\n",
+ " super().delete_thing(obj)\n",
+ " for obs in self.observers:\n",
+ " obs.thing_deleted(obj)\n",
+ "\n",
+ " super().delete_thing(thing)\n",
+ " for obs in self.observers:\n",
+ " obs.thing_deleted(thing)\n",
+ "\n",
+ " def add_walls(self):\n",
+ " """Put walls around the entire perimeter of the grid."""\n",
+ " for x in range(self.width):\n",
+ " self.add_thing(Wall(), (x, 0))\n",
+ " self.add_thing(Wall(), (x, self.height - 1))\n",
+ " for y in range(1, self.height - 1):\n",
+ " self.add_thing(Wall(), (0, y))\n",
+ " self.add_thing(Wall(), (self.width - 1, y))\n",
+ "\n",
+ " # Updates iteration start and end (with walls).\n",
+ " self.x_start, self.y_start = (1, 1)\n",
+ " self.x_end, self.y_end = (self.width - 1, self.height - 1)\n",
+ "\n",
+ " def add_observer(self, observer):\n",
+ " """Adds an observer to the list of observers.\n",
+ " An observer is typically an EnvGUI.\n",
+ "\n",
+ " Each observer is notified of changes in move_to and add_thing,\n",
+ " by calling the observer's methods thing_moved(thing)\n",
+ " and thing_added(thing, loc)."""\n",
+ " self.observers.append(observer)\n",
+ "\n",
+ " def turn_heading(self, heading, inc):\n",
+ " """Return the heading to the left (inc=+1) or right (inc=-1) of heading."""\n",
+ " return turn_heading(heading, inc)\n",
+ "class GraphicEnvironment(XYEnvironment):\n",
+ " def __init__(self, width=10, height=10, boundary=True, color={}, display=False):\n",
+ " """Define all the usual XYEnvironment characteristics,\n",
+ " but initialise a BlockGrid for GUI too."""\n",
+ " super().__init__(width, height)\n",
+ " self.grid = BlockGrid(width, height, fill=(200, 200, 200))\n",
+ " if display:\n",
+ " self.grid.show()\n",
+ " self.visible = True\n",
+ " else:\n",
+ " self.visible = False\n",
+ " self.bounded = boundary\n",
+ " self.colors = color\n",
+ "\n",
+ " def get_world(self):\n",
+ " """Returns all the items in the world in a format\n",
+ " understandable by the ipythonblocks BlockGrid."""\n",
+ " result = []\n",
+ " x_start, y_start = (0, 0)\n",
+ " x_end, y_end = self.width, self.height\n",
+ " for x in range(x_start, x_end):\n",
+ " row = []\n",
+ " for y in range(y_start, y_end):\n",
+ " row.append(self.list_things_at((x, y)))\n",
+ " result.append(row)\n",
+ " return result\n",
+ "\n",
+ " """\n",
+ " def run(self, steps=1000, delay=1):\n",
+ " "" "Run the Environment for given number of time steps,\n",
+ " but update the GUI too." ""\n",
+ " for step in range(steps):\n",
+ " sleep(delay)\n",
+ " if self.visible:\n",
+ " self.reveal()\n",
+ " if self.is_done():\n",
+ " if self.visible:\n",
+ " self.reveal()\n",
+ " return\n",
+ " self.step()\n",
+ " if self.visible:\n",
+ " self.reveal()\n",
+ " """\n",
+ "\n",
+ " def run(self, steps=2000, delay=1):\n",
+ " """Run the Environment for given number of time steps,\n",
+ " but update the GUI too."""\n",
+ " for step in range(steps):\n",
+ " self.update(delay)\n",
+ " if self.is_done():\n",
+ " break\n",
+ " self.step()\n",
+ " self.update(delay)\n",
+ "\n",
+ " def update(self, delay=1):\n",
+ " sleep(delay)\n",
+ " self.reveal()\n",
+ "\n",
+ " def reveal(self):\n",
+ " """Display the BlockGrid for this world - the last thing to be added\n",
+ " at a location defines the location color."""\n",
+ " self.draw_world()\n",
+ " # wait for the world to update and\n",
+ " # apply changes to the same grid instead\n",
+ " # of making a new one.\n",
+ " clear_output(1)\n",
+ " self.grid.show()\n",
+ " self.visible = True\n",
+ "\n",
+ " def draw_world(self):\n",
+ " self.grid[:] = (200, 200, 200)\n",
+ " world = self.get_world()\n",
+ " for x in range(0, len(world)):\n",
+ " for y in range(0, len(world[x])):\n",
+ " if len(world[x][y]):\n",
+ " self.grid[y, x] = self.colors[world[x][y][-1].__class__.__name__]\n",
+ "\n",
+ " def conceal(self):\n",
+ " """Hide the BlockGrid for this world"""\n",
+ " self.visible = False\n",
+ " display(HTML(''))\n",
+ "\n",
+ "\n",
+ "
class TrivialVacuumEnvironment(Environment):\n",
+ " """This environment has two locations, A and B. Each can be Dirty\n",
+ " or Clean. The agent perceives its location and the location's\n",
+ " status. This serves as an example of how to implement a simple\n",
+ " Environment."""\n",
+ "\n",
+ " def __init__(self):\n",
+ " super().__init__()\n",
+ " self.status = {loc_A: random.choice(['Clean', 'Dirty']),\n",
+ " loc_B: random.choice(['Clean', 'Dirty'])}\n",
+ "\n",
+ " def thing_classes(self):\n",
+ " return [Wall, Dirt, ReflexVacuumAgent, RandomVacuumAgent, TableDrivenVacuumAgent, ModelBasedVacuumAgent]\n",
+ "\n",
+ " def percept(self, agent):\n",
+ " """Returns the agent's location, and the location status (Dirty/Clean)."""\n",
+ " return agent.location, self.status[agent.location]\n",
+ "\n",
+ " def execute_action(self, agent, action):\n",
+ " """Change agent's location and/or location's status; track performance.\n",
+ " Score 10 for each dirt cleaned; -1 for each move."""\n",
+ " if action == 'Right':\n",
+ " agent.location = loc_B\n",
+ " agent.performance -= 1\n",
+ " elif action == 'Left':\n",
+ " agent.location = loc_A\n",
+ " agent.performance -= 1\n",
+ " elif action == 'Suck':\n",
+ " if self.status[agent.location] == 'Dirty':\n",
+ " agent.performance += 10\n",
+ " self.status[agent.location] = 'Clean'\n",
+ "\n",
+ " def default_location(self, thing):\n",
+ " """Agents start in either location at random."""\n",
+ " return random.choice([loc_A, loc_B])\n",
+ "class VacuumEnvironment(XYEnvironment):\n",
+ " """The environment of [Ex. 2.12]. Agent perceives dirty or clean,\n",
+ " and bump (into obstacle) or not; 2D discrete world of unknown size;\n",
+ " performance measure is 100 for each dirt cleaned, and -1 for\n",
+ " each turn taken."""\n",
+ "\n",
+ " def __init__(self, width=10, height=10):\n",
+ " super().__init__(width, height)\n",
+ " self.add_walls()\n",
+ "\n",
+ " def thing_classes(self):\n",
+ " return [Wall, Dirt, XYReflexVacuumAgent, ReflexVacuumAgent, RandomVacuumAgent,\n",
+ " TableDrivenVacuumAgent, ModelBasedVacuumAgent]\n",
+ "\n",
+ " def percept(self, agent):\n",
+ " """The percept is a tuple of ('Dirty' or 'Clean', 'Bump' or 'None').\n",
+ " Unlike the TrivialVacuumEnvironment, location is NOT perceived."""\n",
+ " status = ('Dirty' if self.some_things_at(\n",
+ " agent.location, Dirt) else 'Clean')\n",
+ " bump = ('Bump' if agent.bump else 'None')\n",
+ " return status, bump\n",
+ "\n",
+ " def execute_action(self, agent, action):\n",
+ " agent.bump = False\n",
+ " if action == 'Suck':\n",
+ " dirt_list = self.list_things_at(agent.location, Dirt)\n",
+ " if dirt_list != []:\n",
+ " dirt = dirt_list[0]\n",
+ " agent.performance += 100\n",
+ " self.delete_thing(dirt)\n",
+ " else:\n",
+ " super().execute_action(agent, action)\n",
+ "\n",
+ " if action != 'NoOp':\n",
+ " agent.performance -= 1\n",
+ "\n",
+ " def is_done(self):\n",
+ " # Elsa defined\n",
+ " num_dirty = 0\n",
+ " for idx in range(10):\n",
+ " num_dirty += len([self.some_things_at([idx,j], Dirt) \\\n",
+ " for j in range(10)])\n",
+ " return False if num_dirty else True\n",
+ "class Thing:\n",
+ " """This represents any physical object that can appear in an Environment.\n",
+ " You subclass Thing to get the things you want. Each thing can have a\n",
+ " .__name__ slot (used for output only)."""\n",
+ "\n",
+ " def __repr__(self):\n",
+ " return '<{}>'.format(getattr(self, '__name__', self.__class__.__name__))\n",
+ "\n",
+ " def is_alive(self):\n",
+ " """Things that are 'alive' should return true."""\n",
+ " return hasattr(self, 'alive') and self.alive\n",
+ "\n",
+ " def show_state(self):\n",
+ " """Display the agent's internal state. Subclasses should override."""\n",
+ " print("I don't know how to show_state.")\n",
+ "\n",
+ " def display(self, canvas, x, y, width, height):\n",
+ " """Display an image of this Thing on the canvas."""\n",
+ " # Do we need this?\n",
+ " pass\n",
+ "class Agent(Thing):\n",
+ " """An Agent is a subclass of Thing with one required slot,\n",
+ " .program, which should hold a function that takes one argument, the\n",
+ " percept, and returns an action. (What counts as a percept or action\n",
+ " will depend on the specific environment in which the agent exists.)\n",
+ " Note that 'program' is a slot, not a method. If it were a method,\n",
+ " then the program could 'cheat' and look at aspects of the agent.\n",
+ " It's not supposed to do that: the program can only look at the\n",
+ " percepts. An agent program that needs a model of the world (and of\n",
+ " the agent itself) will have to build and maintain its own model.\n",
+ " There is an optional slot, .performance, which is a number giving\n",
+ " the performance measure of the agent in its environment."""\n",
+ "\n",
+ " def __init__(self, program=None, direction = None):\n",
+ " self.alive = True\n",
+ " self.bump = False\n",
+ " self.holding = []\n",
+ " self.performance = 0\n",
+ " if program is None:\n",
+ " # or \\\n",
+ " # (not isinstance(program, collections.abc.Callable) \\\n",
+ " # and not isinstance(program, function)):\n",
+ " print("Can't find a valid program for {}, falling back to default.".format(self.__class__.__name__))\n",
+ "\n",
+ " def program(percept):\n",
+ " return eval(input('Percept={}; action? '.format(percept)))\n",
+ "\n",
+ " self.program = program\n",
+ "\n",
+ "\n",
+ " self.direction = direction \n",
+ "\n",
+ " def can_grab(self, thing):\n",
+ " """Return True if this agent can grab this thing.\n",
+ " Override for appropriate subclasses of Agent and Thing."""\n",
+ " return False\n",
+ "def TraceAgent(agent):\n",
+ " """Wrap the agent's program to print its input and output. This will let\n",
+ " you see what the agent is doing in the environment."""\n",
+ " old_program = agent.program\n",
+ "\n",
+ " def new_program(percept):\n",
+ " action = old_program(percept)\n",
+ " print('{} perceives {} and does {} with performance {}'\\\n",
+ " .format(agent, percept, action, agent.performance))\n",
+ " return action\n",
+ "\n",
+ " agent.program = new_program\n",
+ " return agent\n",
+ "def RandomAgentProgram(actions):\n",
+ " """An agent that chooses an action at random, ignoring all percepts.\n",
+ " >>> list = ['Right', 'Left', 'Suck', 'NoOp']\n",
+ " >>> program = RandomAgentProgram(list)\n",
+ " >>> agent = Agent(program)\n",
+ " >>> environment = TrivialVacuumEnvironment()\n",
+ " >>> environment.add_thing(agent)\n",
+ " >>> environment.run()\n",
+ " >>> environment.status == {(1, 0): 'Clean' , (0, 0): 'Clean'}\n",
+ " True\n",
+ " """\n",
+ " return lambda percept: random.choice(actions)\n",
+ "def RandomVacuumAgent():\n",
+ " """Randomly choose one of the actions from the vacuum environment.\n",
+ " >>> agent = RandomVacuumAgent()\n",
+ " >>> environment = TrivialVacuumEnvironment()\n",
+ " >>> environment.add_thing(agent)\n",
+ " >>> environment.run()\n",
+ " >>> environment.status == {(1,0):'Clean' , (0,0) : 'Clean'}\n",
+ " True\n",
+ " """\n",
+ " return Agent(RandomAgentProgram(['Right', 'Left', 'Suck', 'NoOp']))\n",
+ "def TableDrivenAgentProgram(table):\n",
+ " """\n",
+ " [Figure 2.7]\n",
+ " This agent selects an action based on the percept sequence.\n",
+ " It is practical only for tiny domains.\n",
+ " To customize it, provide as table a dictionary of all\n",
+ " {percept_sequence:action} pairs.\n",
+ " """\n",
+ " percepts = []\n",
+ "\n",
+ " def program(percept):\n",
+ " percepts.append(percept)\n",
+ " action = table.get(tuple(percepts))\n",
+ " return action\n",
+ "\n",
+ " return program\n",
+ "def TableDrivenVacuumAgent():\n",
+ " """Tabular approach towards vacuum world as mentioned in [Figure 2.3]\n",
+ " >>> agent = TableDrivenVacuumAgent()\n",
+ " >>> environment = TrivialVacuumEnvironment()\n",
+ " >>> environment.add_thing(agent)\n",
+ " >>> environment.run()\n",
+ " >>> environment.status == {(1,0):'Clean' , (0,0) : 'Clean'}\n",
+ " True\n",
+ " """\n",
+ " table = {((loc_A, 'Clean'),): 'Right',\n",
+ " ((loc_A, 'Dirty'),): 'Suck',\n",
+ " ((loc_B, 'Clean'),): 'Left',\n",
+ " ((loc_B, 'Dirty'),): 'Suck',\n",
+ " ((loc_A, 'Dirty'), (loc_A, 'Clean')): 'Right',\n",
+ " ((loc_A, 'Clean'), (loc_B, 'Dirty')): 'Suck',\n",
+ " ((loc_B, 'Clean'), (loc_A, 'Dirty')): 'Suck',\n",
+ " ((loc_B, 'Dirty'), (loc_B, 'Clean')): 'Left',\n",
+ " ((loc_A, 'Dirty'), (loc_A, 'Clean'), (loc_B, 'Dirty')): 'Suck',\n",
+ " ((loc_B, 'Dirty'), (loc_B, 'Clean'), (loc_A, 'Dirty')): 'Suck'}\n",
+ " return Agent(TableDrivenAgentProgram(table))\n",
+ "def SimpleReflexAgentProgram(rules, interpret_input):\n",
+ " """\n",
+ " [Figure 2.10]\n",
+ " This agent takes action based solely on the percept.\n",
+ " """\n",
+ "\n",
+ " def program(percept):\n",
+ " state = interpret_input(percept)\n",
+ " rule = rule_match(state, rules)\n",
+ " action = rule.action\n",
+ " return action\n",
+ "\n",
+ " return program\n",
+ "\n",
+ "
\n",
+ "
def ReflexVacuumAgent():\n",
+ " """\n",
+ " [Figure 2.8]\n",
+ " A reflex agent for the two-state vacuum environment.\n",
+ " >>> agent = ReflexVacuumAgent()\n",
+ " >>> environment = TrivialVacuumEnvironment()\n",
+ " >>> environment.add_thing(agent)\n",
+ " >>> environment.run()\n",
+ " >>> environment.status == {(1,0):'Clean' , (0,0) : 'Clean'}\n",
+ " True\n",
+ " """\n",
+ "\n",
+ " def program(percept):\n",
+ " location, status = percept\n",
+ " if status == 'Dirty':\n",
+ " return 'Suck'\n",
+ " elif location == loc_A:\n",
+ " return 'Right'\n",
+ " elif location == loc_B:\n",
+ " return 'Left'\n",
+ "\n",
+ " return Agent(program)\n",
+ "