diff --git a/aima/agents.py b/aima/agents.py index d159466cc..e3ac190fe 100644 --- a/aima/agents.py +++ b/aima/agents.py @@ -191,7 +191,7 @@ def rule_match(state, rules): # ______________________________________________________________________________ -loc_A, loc_B = (0, 0), (1, 0) # The two locations for the Vacuum world +loc_A, loc_B, loc_C, loc_D = (0, 0), (1, 0), (0, 1), (1, 1) # The four locations for the Vacuum world def RandomVacuumAgent(): @@ -203,7 +203,7 @@ def RandomVacuumAgent(): >>> environment.status == {(1,0):'Clean' , (0,0) : 'Clean'} True """ - return Agent(RandomAgentProgram(['Right', 'Left', 'Suck', 'NoOp'])) + return Agent(RandomAgentProgram(['Right', 'Left','Up','Down', 'Suck', 'NoOp'])) def TableDrivenVacuumAgent(): @@ -807,7 +807,9 @@ class TrivialVacuumEnvironment(Environment): def __init__(self): super().__init__() self.status = {loc_A: random.choice(['Clean', 'Dirty']), - loc_B: random.choice(['Clean', 'Dirty'])} + loc_B: random.choice(['Clean', 'Dirty']), + loc_C: random.choice(['Clean', 'Dirty']), + loc_D: random.choice(['Clean', 'Dirty'])} def thing_classes(self): """Return the Thing/Agent classes that may populate this vacuum world.""" @@ -820,11 +822,18 @@ def percept(self, agent): def execute_action(self, agent, action): """Change agent's location and/or location's status; track performance. Score 10 for each dirt cleaned; -1 for each move.""" + a, b = agent.location if action == 'Right': - agent.location = loc_B + agent.location = (a + 1, b) agent.performance -= 1 elif action == 'Left': - agent.location = loc_A + agent.location = (a - 1, b) + agent.performance -= 1 + elif action == 'Up': + agent.location = (a, b + 1) + agent.performance -= 1 + elif action == 'Down': + agent.location = (a, b - 1) agent.performance -= 1 elif action == 'Suck': if self.status[agent.location] == 'Dirty': @@ -833,7 +842,7 @@ def execute_action(self, agent, action): def default_location(self, thing): """Agents start in either location at random.""" - return random.choice([loc_A, loc_B]) + return random.choice([loc_A, loc_B, loc_C, loc_D]) # ______________________________________________________________________________ diff --git a/aima/notebook_utils.py b/aima/notebook_utils.py index 7b881d29c..e98b79391 100644 --- a/aima/notebook_utils.py +++ b/aima/notebook_utils.py @@ -50,7 +50,15 @@ def psource(*functions): from pygments.lexers import PythonLexer from pygments import highlight - display(HTML(highlight(source_code, PythonLexer(), HtmlFormatter(full=True)))) + # Render an HTML fragment with inline token colors. ``full=True`` emits + # a complete document whose global ``body`` CSS leaks into VS Code's + # shared notebook webview and can make every Markdown cell unreadable. + highlighted = highlight(source_code, PythonLexer(), + HtmlFormatter(noclasses=True, style='dracula', + nobackground=True)) + # Give unstyled tokens a readable foreground instead of inheriting + # palette instead of inheriting a color from the notebook theme. + display(HTML('
class Agent(Thing):\n",
+ " """An Agent is a subclass of Thing with one required instance attribute \n",
+ " (aka slot), .program, which should hold a function that takes one argument,\n",
+ " the 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, then the\n",
+ " program could 'cheat' and look at aspects of the agent. It's not supposed\n",
+ " to do that: the program can only look at the percepts. An agent program\n",
+ " that needs a model of the world (and of the agent itself) will have to\n",
+ " build and maintain its own model. There is an optional slot, .performance,\n",
+ " which is a number giving the performance measure of the agent in its\n",
+ " environment."""\n",
+ "\n",
+ " def __init__(self, program=None):\n",
+ " self.alive = True\n",
+ " self.bump = False\n",
+ " self.holding = []\n",
+ " self.performance = 0\n",
+ " if program is None or not isinstance(program, collections.abc.Callable):\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",
+ " 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",
+ "class Environment:\n",
+ " """Abstract class representing an Environment. 'Real' Environment classes\n",
+ " inherit from this. Your Environment will typically need to implement::\n",
+ "\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",
+ "\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 the list of Thing subclasses that may appear in this environment."""\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 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",
- "\n",
- "\n",
- " \n",
- " \n",
- " \n",
- "\n",
- "\n",
- "\n",
+ " def thing_classes(self):\n",
+ " """Return the Thing/Agent classes that may populate this vacuum world."""\n",
+ " return [Wall, Dirt, ReflexVacuumAgent, RandomVacuumAgent, TableDrivenVacuumAgent, ModelBasedVacuumAgent]\n",
"\n",
- "class TrivialVacuumEnvironment(Environment):\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",
- " """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",
+ " 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",
+ " a, b = agent.location\n",
+ " if action == 'Right':\n",
+ " agent.location = (a + 1, b)\n",
+ " agent.performance -= 1\n",
+ " elif action == 'Left':\n",
+ " agent.location = (a - 1, b)\n",
+ " agent.performance -= 1\n",
+ " elif action == 'Up':\n",
+ " agent.location = (a, b + 1)\n",
+ " agent.performance -= 1\n",
+ " elif action == 'Down':\n",
+ " agent.location = (a, b - 1)\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 __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,\n",
- " 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",
+ " def default_location(self, thing):\n",
+ " """Agents start in either location at random."""\n",
+ " return random.choice([loc_A, loc_B, loc_C, loc_D])\n",
"
\n",
- "\n",
- "\n"
+ "