From 6392897bc105c642869533b455187c482a051f5a Mon Sep 17 00:00:00 2001 From: tolusalako Date: Wed, 23 Mar 2016 00:54:32 -0700 Subject: [PATCH 1/3] Created a Directions class to for 2D-Environments and started work on the WumpusEnvironment --- agents.ipynb | 49 +++++++++++++++++---- agents.py | 119 ++++++++++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 152 insertions(+), 16 deletions(-) diff --git a/agents.ipynb b/agents.ipynb index 78d3ffb1f..d7cd85484 100644 --- a/agents.ipynb +++ b/agents.ipynb @@ -15,7 +15,7 @@ }, { "cell_type": "code", - "execution_count": 29, + "execution_count": 1, "metadata": { "collapsed": false, "scrolled": true @@ -43,7 +43,7 @@ }, { "cell_type": "code", - "execution_count": 30, + "execution_count": 2, "metadata": { "collapsed": false }, @@ -82,7 +82,7 @@ }, { "cell_type": "code", - "execution_count": 43, + "execution_count": 3, "metadata": { "collapsed": false }, @@ -95,8 +95,8 @@ " pass\n", "\n", "class Park(Environment):\n", - " '''prints & return a list of things that are in our dog's location'''\n", " def percept(self, agent):\n", + " '''prints & return a list of things that are in our agent's location'''\n", " things = self.list_things_at(agent.location)\n", " print(things)\n", " return things\n", @@ -151,7 +151,7 @@ }, { "cell_type": "code", - "execution_count": 44, + "execution_count": 4, "metadata": { "collapsed": false }, @@ -191,7 +191,7 @@ }, { "cell_type": "code", - "execution_count": 45, + "execution_count": 5, "metadata": { "collapsed": false }, @@ -230,17 +230,48 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "That's how easy it is to implement an agent, its program, and environment. But that was a very simple case. What if our environment was 2-Dimentional instead of 1? And what if we had multiple agents?" + "That's how easy it is to implement an agent, its program, and environment. But that was a very simple case. What if our environment was 2-Dimentional instead of 1? And what if we had multiple agents?\n", + "\n", + "To make our Park 2D, we will need to make it a subclass of XYEnvironment instead of Environment. Also, let's add a person to play fetch with the dog." ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 6, "metadata": { "collapsed": true }, "outputs": [], - "source": [] + "source": [ + "class Park(XYEnvironment):\n", + " def percept(self, agent):\n", + " '''prints & return a list of things that are in our agent's location'''\n", + " things = self.list_things_at(agent.location)\n", + " print(things)\n", + " return things\n", + " \n", + " def execute_action(self, agent, action):\n", + " '''changes the state of the environment based on what the agent does.'''\n", + " if action == \"move down\":\n", + " agent.movedown()\n", + " elif action == \"eat\":\n", + " items = self.list_things_at(agent.location, tclass=Food)\n", + " if len(items) != 0:\n", + " if agent.eat(items[0]): #Have the dog pick eat the first item\n", + " self.delete_thing(items[0]) #Delete it from the Park after.\n", + " elif action == \"drink\":\n", + " items = self.list_things_at(agent.location, tclass=Water)\n", + " if len(items) != 0:\n", + " if agent.drink(items[0]): #Have the dog drink the first item\n", + " self.delete_thing(items[0]) #Delete it from the Park after.\n", + " \n", + " def is_done(self):\n", + " '''By default, we're done when we can't find a live agent, \n", + " but to prevent killing our cute dog, we will or it with when there is no more food or water'''\n", + " no_edibles = not any(isinstance(thing, Food) or isinstance(thing, Water) for thing in self.things)\n", + " dead_agents = not any(agent.is_alive() for agent in self.agents)\n", + " return dead_agents or no_edibles" + ] } ], "metadata": { diff --git a/agents.py b/agents.py index 60ba0f393..0e4c69c88 100644 --- a/agents.py +++ b/agents.py @@ -313,6 +313,46 @@ def delete_thing(self, thing): if thing in self.agents: self.agents.remove(thing) +class Direction(): + '''A direction class for agents that want to move in a 2D plane + Usage: + d = Direction("Down") + To change directions: + d = d + "right" or d = d + Direction.R #Both do the same thing + Note that the argument to __add__ must be a string and not a Direction object. + Also, it (the argument) can only be right or left. ''' + + R = "right" + L = "left" + U = "up" + D = "down" + + def __init__(self, direction): + self.direction = direction + + def __add__(self, heading): + print(heading, self.direction, heading == self.direction) + if self.direction == self.R: + return{ + self.R: Direction(self.D), + self.L: Direction(self.U), + }.get(heading, None) + elif self.direction == self.L: + return{ + self.R: Direction(U), + self.L: Direction(L), + }.get(heading, None) + elif self.direction == self.U: + return{ + self.R: Direction(R), + self.L: Direction(L), + }.get(heading, None) + elif self.direction == self.D: + return{ + self.R: Direction(self.L), + self.L: Direction(self.R), + }.get(heading, None) + class XYEnvironment(Environment): @@ -327,7 +367,8 @@ class XYEnvironment(Environment): def __init__(self, width=10, height=10): super(XYEnvironment, self).__init__() update(self, width=width, height=height, observers=[]) - + + perceptible_distance = 1 def things_near(self, location, radius=None): "Return all things within radius of location." if radius is None: @@ -336,8 +377,6 @@ def things_near(self, location, radius=None): return [thing for thing in self.things if distance2(location, thing.location) <= radius2] - perceptible_distance = 1 - def percept(self, agent): "By default, agent perceives things within a default radius." return [self.thing_percept(thing, agent) @@ -387,7 +426,8 @@ def delete_thing(self, thing): # Any more to do? Thing holding anything or being held? for obs in self.observers: obs.thing_deleted(thing) - + + has_walls = False def add_walls(self): "Put walls around the entire perimeter of the grid." for x in range(self.width): @@ -396,6 +436,7 @@ def add_walls(self): for y in range(self.height): self.add_thing(Wall(), (0, y)) self.add_thing(Wall(), (self.width-1, y)) + self.has_walls = True def add_observer(self, observer): """Adds an observer to the list of observers. @@ -515,6 +556,9 @@ class Gold(Thing): class Pit(Thing): pass +class Breeze(Thing): + pass + class Arrow(Thing): pass @@ -523,17 +567,78 @@ class Arrow(Thing): class Wumpus(Agent): pass +class Stench(Thing): + pass class Explorer(Agent): - pass + direction = Direction("right") class WumpusEnvironment(XYEnvironment): - + pit_probability = 0.2 #Probability to spawn a pit in a location + def __init__(self, width=10, height=10): super(WumpusEnvironment, self).__init__(width, height) self.add_walls() - + self.init_world() + + def add_thing(self, thing, location=(1, 1), exclude_duplicate_class_items = True): + '''Adds thing to the world''' + if (isinstance(thing, Wall) or self.is_inbounds(location)): + if (exclude_duplicate_class_items and + any(isinstance(t, thing.__class__) for t in self.list_things_at(location))): + return + super(WumpusEnvironment, self).add_thing(thing, location) + + def init_world(self): + '''Spawn items to the world based on probabilities from the book''' + "PITS" + for x in range(self.width): + for y in range(self.height): + if random.random() < self.pit_probability: + self.add_thing(Pit(), (x,y)) + self.add_thing(Breeze(), (x - 1,y)) + self.add_thing(Breeze(), (x,y - 1)) + self.add_thing(Breeze(), (x + 1,y)) + self.add_thing(Breeze(), (x,y + 1)) + + "WUMPUS" + w_x, w_y = self.random_location(exclude = (1,1)) + self.add_thing(Wumpus(), (w_x, w_y)) + self.add_thing(Stench(), (w_x - 1, w_y)) + self.add_thing(Stench(), (w_x + 1, w_y)) + self.add_thing(Stench(), (w_x, w_y - 1)) + self.add_thing(Stench(), (w_x, w_y + 1)) + + "GOLD" + self.add_thing(Gold(), self.random_location(exclude = (1,1))) + + "AGENT" + self.add_thing(Explorer(), (1,1)) + + def is_inbounds(self, location): + '''Checks to make sure that the location is inbounds (within walls)''' + x,y = location + return not (x < 0 or x >= self.width or y < 0 or y >= self.height) + + def random_location(self, exclude = None): + '''Returns a random location that is inbounds''' + location = (random.randint(1, self.width), random.randint(1, self.height)) + if exclude is not None: + while(location == exclude): + location = (random.randint(1, self.width), random.randint(1, self.height)) + return location + + def print_world(self, show_walls = False): + '''Prints the world''' + x_start,y_start = (1,1) if not show_walls else (0,0) + x_end,y_end = (self.width-1, self.height-1) if not show_walls else (self.width, self.height) + for x in range(x_start, x_end): + for y in range(y_start, y_end): + print(self.list_things_at((x,y)), end = "") + print() + + def thing_classes(self): return [Wall, Gold, Pit, Arrow, Wumpus, Explorer] From 3df865acc8cde1a0f8d1edcd570fe9abc2b60a11 Mon Sep 17 00:00:00 2001 From: tolusalako Date: Thu, 24 Mar 2016 02:39:06 -0700 Subject: [PATCH 2/3] Fully implemented WumpusEnvironment following details from the book. (WumpusEnvironment) Fixed bug where dead agents still get to act (Environment) Improved Direction class to ease 2D Movements (Direction) Implemented an easier way to iterate with and without walls (XYEnvironment) Added is_inbounds() method to check if location is within the width and height. Works within walls also. (XYEnvironment) Multiple Minor Fixes(agents.py) --- agents.py | 291 +++++++++++++++++++++++++----------- tolusalako wumpustest.ipynb | 130 ++++++++++++++++ 2 files changed, 335 insertions(+), 86 deletions(-) create mode 100755 tolusalako wumpustest.ipynb diff --git a/agents.py b/agents.py index 0e4c69c88..776df123e 100644 --- a/agents.py +++ b/agents.py @@ -84,6 +84,8 @@ class Agent(Thing): def __init__(self, program=None): self.alive = True self.bump = False + self.holding = [] + self.performance = 0 if program is None: def program(percept): return eval(input('Percept={}; action? ' .format(percept))) @@ -266,7 +268,7 @@ def step(self): override this method.""" if not self.is_done(): actions = [agent.program(self.percept(agent)) - for agent in self.agents] + for agent in self.agents if agent.alive] for (agent, action) in zip(self.agents, actions): self.execute_action(agent, action) self.exogenous_change() @@ -331,7 +333,6 @@ def __init__(self, direction): self.direction = direction def __add__(self, heading): - print(heading, self.direction, heading == self.direction) if self.direction == self.R: return{ self.R: Direction(self.D), @@ -339,13 +340,13 @@ def __add__(self, heading): }.get(heading, None) elif self.direction == self.L: return{ - self.R: Direction(U), - self.L: Direction(L), + self.R: Direction(self.U), + self.L: Direction(self.L), }.get(heading, None) elif self.direction == self.U: return{ - self.R: Direction(R), - self.L: Direction(L), + self.R: Direction(self.R), + self.L: Direction(self.L), }.get(heading, None) elif self.direction == self.D: return{ @@ -353,6 +354,17 @@ def __add__(self, heading): self.L: Direction(self.R), }.get(heading, None) + def move_forward(self, from_location): + x,y = from_location + if self.direction == self.R: + return (x+1, y) + elif self.direction == self.L: + return (x-1, y) + elif self.direction == self.U: + return (x, y-1) + elif self.direction == self.D: + return (x, y+1) + class XYEnvironment(Environment): @@ -368,28 +380,31 @@ def __init__(self, width=10, height=10): super(XYEnvironment, self).__init__() update(self, width=width, height=height, observers=[]) + #Sets iteration start and end (no walls). + self.x_start,self.y_start = (0,0) + self.x_end,self.y_end = (self.width, self.height) + perceptible_distance = 1 def things_near(self, location, radius=None): "Return all things within radius of location." if radius is None: radius = self.perceptible_distance radius2 = radius * radius - return [thing for thing in self.things + return [(thing, radius2 - distance2(location, thing.location)) for thing in self.things if distance2(location, thing.location) <= radius2] def percept(self, agent): - "By default, agent perceives things within a default radius." - return [self.thing_percept(thing, agent) - for thing in self.things_near(agent.location)] + '''By default, agent perceives things within a default radius.''' + return self.things_near(agent.location) def execute_action(self, agent, action): agent.bump = False if action == 'TurnRight': - agent.heading = self.turn_heading(agent.heading, -1) + agent.direction = agent.direction + Direction.R elif action == 'TurnLeft': - agent.heading = self.turn_heading(agent.heading, +1) + agent.direction = agent.direction + Direction.L elif action == 'Forward': - self.move_to(agent, vector_add(agent.heading, agent.location)) + agent.bump = self.move_to(agent, agent.direction.move_forward(agent.location)) # elif action == 'Grab': # things = [thing for thing in self.list_things_at(agent.location) # if agent.can_grab(thing)] @@ -399,45 +414,72 @@ def execute_action(self, agent, action): if agent.holding: agent.holding.pop() - def thing_percept(self, thing, agent): # ??? Should go to thing? - "Return the percept for this thing." - return thing.__class__.__name__ - def default_location(self, thing): return (random.choice(self.width), random.choice(self.height)) def move_to(self, thing, destination): - "Move a thing to a new location." + '''Move a thing to a new location. Returns True on success or False if there is an Obstacle + If thing is grabbing anything, they move with him ''' thing.bump = self.some_things_at(destination, Obstacle) if not thing.bump: thing.location = destination for o in self.observers: o.thing_moved(thing) - - def add_thing(self, thing, location=(1, 1)): - super(XYEnvironment, self).add_thing(thing, location) - thing.holding = [] - thing.held = None - for obs in self.observers: - obs.thing_added(thing) + for t in thing.holding: + self.delete_thing(t) + self.add_thing(t, destination) + t.location = destination + return thing.bump + + # def add_thing(self, thing, location=(1, 1)): + # super(XYEnvironment, self).add_thing(thing, location) + # thing.holding = [] + # thing.held = None + # for obs in self.observers: + # obs.thing_added(thing) + + def add_thing(self, thing, location=(1, 1), exclude_duplicate_class_items = False): + '''Adds things to the world. + If (exclude_duplicate_class_items) then the item won't be added if the location + has at least one item of the same class''' + if (self.is_inbounds(location)): + if (exclude_duplicate_class_items and + any(isinstance(t, thing.__class__) for t in self.list_things_at(location))): + return + super(XYEnvironment, self).add_thing(thing, location) + + def is_inbounds(self, location): + '''Checks to make sure that the location is inbounds (within walls if we have walls)''' + x,y = location + return not (x < self.x_start or x >= self.x_end or y < self.y_start or y >= self.y_end) + + def random_location_inbounds(self, exclude = None): + '''Returns a random location that is inbounds (within walls if we have walls)''' + location = (random.randint(self.x_start, self.x_end), random.randint(self.y_start, self.y_end)) + if exclude is not None: + while(location == exclude): + location = (random.randint(self.x_start, self.x_end), random.randint(self.y_start, self.y_end)) + return location def delete_thing(self, thing): super(XYEnvironment, self).delete_thing(thing) # Any more to do? Thing holding anything or being held? for obs in self.observers: obs.thing_deleted(thing) - - has_walls = False + def add_walls(self): - "Put walls around the entire perimeter of the grid." + '''Put walls around the entire perimeter of the grid.''' for x in range(self.width): self.add_thing(Wall(), (x, 0)) self.add_thing(Wall(), (x, self.height-1)) for y in range(self.height): self.add_thing(Wall(), (0, y)) self.add_thing(Wall(), (self.width-1, y)) - self.has_walls = True - + + #Updates iteration start and end (with walls). + self.x_start,self.y_start = (1,1) + self.x_end,self.y_end = (self.width-1, self.height-1) + def add_observer(self, observer): """Adds an observer to the list of observers. An observer is typically an EnvGUI. @@ -550,8 +592,17 @@ def default_location(self, thing): class Gold(Thing): + + def __eq__(self, rhs): + '''All Gold are equal''' + return rhs.__class__ == Gold pass +class Bump(Thing): + pass + +class Glitter(Thing): + pass class Pit(Thing): pass @@ -563,88 +614,156 @@ class Breeze(Thing): class Arrow(Thing): pass +class Scream(Thing): + pass + -class Wumpus(Agent): +class Wumpus(Thing): pass class Stench(Thing): pass class Explorer(Agent): + holding = [] + arrow_count = 1 + killed_by = "" direction = Direction("right") + + def can_grab(self, thing): + '''Explorer can only grab gold''' + return thing.__class__ == Gold class WumpusEnvironment(XYEnvironment): - pit_probability = 0.2 #Probability to spawn a pit in a location - - def __init__(self, width=10, height=10): + pit_probability = 0.2 #Probability to spawn a pit in a location. (From Chapter 7.2) + #Room should be 4x4 grid of rooms. The extra 2 for walls + def __init__(self, agent_program, width=6, height=6): super(WumpusEnvironment, self).__init__(width, height) - self.add_walls() - self.init_world() + self.init_world(agent_program) - def add_thing(self, thing, location=(1, 1), exclude_duplicate_class_items = True): - '''Adds thing to the world''' - if (isinstance(thing, Wall) or self.is_inbounds(location)): - if (exclude_duplicate_class_items and - any(isinstance(t, thing.__class__) for t in self.list_things_at(location))): - return - super(WumpusEnvironment, self).add_thing(thing, location) - - def init_world(self): + def init_world(self, program): '''Spawn items to the world based on probabilities from the book''' + + "WALLS" + self.add_walls() + "PITS" - for x in range(self.width): - for y in range(self.height): + for x in range(self.x_start, self.x_end): + for y in range(self.y_start, self.y_end): if random.random() < self.pit_probability: - self.add_thing(Pit(), (x,y)) - self.add_thing(Breeze(), (x - 1,y)) - self.add_thing(Breeze(), (x,y - 1)) - self.add_thing(Breeze(), (x + 1,y)) - self.add_thing(Breeze(), (x,y + 1)) + self.add_thing(Pit(), (x,y), True) + self.add_thing(Breeze(), (x - 1,y), True) + self.add_thing(Breeze(), (x,y - 1), True) + self.add_thing(Breeze(), (x + 1,y), True) + self.add_thing(Breeze(), (x,y + 1), True) "WUMPUS" - w_x, w_y = self.random_location(exclude = (1,1)) - self.add_thing(Wumpus(), (w_x, w_y)) - self.add_thing(Stench(), (w_x - 1, w_y)) - self.add_thing(Stench(), (w_x + 1, w_y)) - self.add_thing(Stench(), (w_x, w_y - 1)) - self.add_thing(Stench(), (w_x, w_y + 1)) + w_x, w_y = self.random_location_inbounds(exclude = (1,1)) + self.add_thing(Wumpus(), (w_x, w_y), True) + self.add_thing(Stench(), (w_x - 1, w_y), True) + self.add_thing(Stench(), (w_x + 1, w_y), True) + self.add_thing(Stench(), (w_x, w_y - 1), True) + self.add_thing(Stench(), (w_x, w_y + 1), True) "GOLD" - self.add_thing(Gold(), self.random_location(exclude = (1,1))) - + self.add_thing(Gold(), self.random_location_inbounds(exclude = (1,1)), True) + #self.add_thing(Gold(), (2,1), True) Making debugging a whole lot easier + "AGENT" - self.add_thing(Explorer(), (1,1)) + self.add_thing(Explorer(program), (1,1), True) - def is_inbounds(self, location): - '''Checks to make sure that the location is inbounds (within walls)''' - x,y = location - return not (x < 0 or x >= self.width or y < 0 or y >= self.height) - - def random_location(self, exclude = None): - '''Returns a random location that is inbounds''' - location = (random.randint(1, self.width), random.randint(1, self.height)) - if exclude is not None: - while(location == exclude): - location = (random.randint(1, self.width), random.randint(1, self.height)) - return location - - def print_world(self, show_walls = False): - '''Prints the world''' - x_start,y_start = (1,1) if not show_walls else (0,0) - x_end,y_end = (self.width-1, self.height-1) if not show_walls else (self.width, self.height) + def get_world(self, show_walls = True): + '''returns the items in the world''' + result = [] + x_start,y_start = (0,0) if show_walls else (1,1) + x_end,y_end = (self.width, self.height) if show_walls else (self.width - 1, self.height - 1) for x in range(x_start, x_end): + row = [] for y in range(y_start, y_end): - print(self.list_things_at((x,y)), end = "") - print() + row.append(self.list_things_at((x,y))) + result.append(row) + return result + + def percepts_from(self, agent, location, tclass = Thing): + '''Returns percepts from a given location, and replaces some items with percepts from chapter 7.''' + thing_percepts = { + Gold: Glitter(), + Wall: Bump(), + Wumpus: Stench(), + Pit: Breeze() + } + '''Agents don't need to get their percepts''' + thing_percepts[agent.__class__] = None + + '''Gold only glitters in its cell''' + if location != agent.location: + thing_percepts[Gold] = None + result = [thing_percepts.get(thing.__class__, thing) for thing in self.things + if thing.location == location and isinstance(thing, tclass)] + return result if len(result) else [None] + + def percept(self, agent): + '''Returns things in adjacent (not diagonal) cells of the agent. + Result format: [Left, Right, Up, Down, Center / Current location]''' + x,y = agent.location + result = [] + result.append(self.percepts_from(agent, (x - 1,y))) + result.append(self.percepts_from(agent, (x + 1,y))) + result.append(self.percepts_from(agent, (x,y - 1))) + result.append(self.percepts_from(agent, (x,y + 1))) + result.append(self.percepts_from(agent, (x,y))) + return result - def thing_classes(self): - return [Wall, Gold, Pit, Arrow, Wumpus, Explorer] - - # Needs a lot of work ... - - + def execute_action(self, agent, action): + '''Modify the state of the environment based on the agent's actions + Performance score taken directly out of the book''' + + agent.bump = False + if action == 'TurnRight': + agent.direction = agent.direction + Direction.R + agent.performance -= 1 + elif action == 'TurnLeft': + agent.direction = agent.direction + Direction.L + agent.performance -= 1 + elif action == 'Forward': + agent.bump = self.move_to(agent, agent.direction.move_forward(agent.location)) + agent.performance -= 1 + elif action == 'Grab': + things = [thing for thing in self.list_things_at(agent.location) + if agent.can_grab(thing)] + print("Grabbing", things[0].__class__.__name__) + if len(things): + agent.holding.append(things[0]) + agent.performance -= 1 + elif action == 'Climb': + if agent.location == (1,1): #Agent can only climb out of (1,1) + agent.performance += 1000 if Gold() in agent.holding else 0 + self.delete_thing(agent) + + '''Check if agent is in danger, if he is, kill him''' + for thing in self.list_things_at(agent.location): + if isinstance(thing, Wumpus) or isinstance(thing, Pit): + agent.alive = False + agent.performance -= 1000 + agent.killed_by = thing.__class__.__name__ + + def is_done(self): + '''The game is over when the Explorer is killed + or if he climbs out of the cave only at (1,1)''' + explorer = [agent for agent in self.agents if isinstance(agent, Explorer) ] + if len(explorer): + if explorer[0].alive: + return False + else: + print("Death by {} [-1000].".format(explorer[0].killed_by)) + else: + print("Explorer climbed out {}." + .format("with Gold [+1000]!" if Gold() not in self.things else "without Gold [+0]")) + return True + + #Almost done. Arrow needs to be implemented # ______________________________________________________________________________ def compare_agents(EnvFactory, AgentFactories, n=10, steps=1000): diff --git a/tolusalako wumpustest.ipynb b/tolusalako wumpustest.ipynb new file mode 100755 index 000000000..6fedf930b --- /dev/null +++ b/tolusalako wumpustest.ipynb @@ -0,0 +1,130 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 1, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "from ipythonblocks import BlockGrid\n", + "from agents import *\n", + "\n", + "color = {\"Breeze\": (225, 225, 225),\n", + " \"Pit\": (0,0,0),\n", + " \"Gold\": (253, 208, 23),\n", + " \"Glitter\": (253, 208, 23),\n", + " \"Wumpus\": (43, 27, 23),\n", + " \"Stench\": (128, 128, 128),\n", + " \"Explorer\": (0, 0, 255),\n", + " \"Wall\": (44, 53, 57)\n", + " }\n", + "\n", + "def program(percepts):\n", + " '''Returns an action based on it's percepts'''\n", + " print(percepts)\n", + " return input()\n", + "\n", + "w = WumpusEnvironment(program, 7, 7) \n", + "grid = BlockGrid(w.width, w.height, fill=(123, 234, 123))\n", + "\n", + "def draw_grid(world):\n", + " global grid\n", + " grid[:] = (123, 234, 123)\n", + " for x in range(0, len(world)):\n", + " for y in range(0, len(world[x])):\n", + " if len(world[x][y]):\n", + " grid[y, x] = color[world[x][y][-1].__class__.__name__]\n", + "\n", + "def step():\n", + " global grid, w\n", + " draw_grid(w.get_world())\n", + " grid.show()\n", + " w.step()\n", + " \n", + " \n", + " " + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/html": [ + "
" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Explorer climbed out without Gold [+0].\n" + ] + } + ], + "source": [ + "step()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.5.1" + } + }, + "nbformat": 4, + "nbformat_minor": 0 +} From 474fe8784606a0fc274da399196584ee454c1fdb Mon Sep 17 00:00:00 2001 From: tolusalako Date: Sun, 27 Mar 2016 16:54:27 -0700 Subject: [PATCH 3/3] Fixed issue where, if there are multiple agents, dead agents get the actions of alive ones. Implemented arrow shooting in Wumpus World. Wumpus Environment is now complete. --- agents.py | 60 +++++++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 49 insertions(+), 11 deletions(-) diff --git a/agents.py b/agents.py index 776df123e..743f7836d 100644 --- a/agents.py +++ b/agents.py @@ -267,8 +267,12 @@ def step(self): do. If there are interactions between them, you'll need to override this method.""" if not self.is_done(): - actions = [agent.program(self.percept(agent)) - for agent in self.agents if agent.alive] + actions = [] + for agent in self.agents: + if agent.alive: + actions.append(agent.program(self.percept(agent))) + else: + actions.append("") for (agent, action) in zip(self.agents, actions): self.execute_action(agent, action) self.exogenous_change() @@ -462,8 +466,14 @@ def random_location_inbounds(self, exclude = None): return location def delete_thing(self, thing): + '''Deletes thing, and everything it is holding (if thing is an agent)''' + if isinstance(thing, Agent): + for obj in thing.holding: + super(XYEnvironment, self).delete_thing(obj) + for obs in self.observers: + obs.thing_deleted(obj) + super(XYEnvironment, self).delete_thing(thing) - # Any more to do? Thing holding anything or being held? for obs in self.observers: obs.thing_deleted(thing) @@ -618,7 +628,8 @@ class Scream(Thing): pass -class Wumpus(Thing): +class Wumpus(Agent): + screamed = False pass class Stench(Thing): @@ -626,7 +637,7 @@ class Stench(Thing): class Explorer(Agent): holding = [] - arrow_count = 1 + has_arrow = True killed_by = "" direction = Direction("right") @@ -660,7 +671,7 @@ def init_world(self, program): "WUMPUS" w_x, w_y = self.random_location_inbounds(exclude = (1,1)) - self.add_thing(Wumpus(), (w_x, w_y), True) + self.add_thing(Wumpus(lambda x: ""), (w_x, w_y), True) self.add_thing(Stench(), (w_x - 1, w_y), True) self.add_thing(Stench(), (w_x + 1, w_y), True) self.add_thing(Stench(), (w_x, w_y - 1), True) @@ -700,6 +711,7 @@ def percepts_from(self, agent, location, tclass = Thing): if location != agent.location: thing_percepts[Gold] = None + result = [thing_percepts.get(thing.__class__, thing) for thing in self.things if thing.location == location and isinstance(thing, tclass)] return result if len(result) else [None] @@ -714,12 +726,22 @@ def percept(self, agent): result.append(self.percepts_from(agent, (x,y - 1))) result.append(self.percepts_from(agent, (x,y + 1))) result.append(self.percepts_from(agent, (x,y))) + + '''The wumpus gives out a a loud scream once it's killed.''' + wumpus = [thing for thing in self.things if isinstance(thing, Wumpus)] + if len(wumpus) and not wumpus[0].alive and not wumpus[0].screamed: + result[-1].append(Scream()) + wumpus[0].screamed = True + return result def execute_action(self, agent, action): '''Modify the state of the environment based on the agent's actions Performance score taken directly out of the book''' + if isinstance(agent, Explorer) and self.in_danger(agent): + return + agent.bump = False if action == 'TurnRight': agent.direction = agent.direction + Direction.R @@ -733,21 +755,37 @@ def execute_action(self, agent, action): elif action == 'Grab': things = [thing for thing in self.list_things_at(agent.location) if agent.can_grab(thing)] - print("Grabbing", things[0].__class__.__name__) if len(things): - agent.holding.append(things[0]) + print("Grabbing", things[0].__class__.__name__) + if len(things): + agent.holding.append(things[0]) agent.performance -= 1 elif action == 'Climb': if agent.location == (1,1): #Agent can only climb out of (1,1) agent.performance += 1000 if Gold() in agent.holding else 0 self.delete_thing(agent) - - '''Check if agent is in danger, if he is, kill him''' + elif action == 'Shoot': + '''The arrow travels straight down the path the agent is facing''' + if agent.has_arrow: + arrow_travel = agent.direction.move_forward(agent.location) + while(self.is_inbounds(arrow_travel)): + wumpus = [thing for thing in self.list_things_at(arrow_travel) + if isinstance(thing, Wumpus)] + if len(wumpus): + wumpus[0].alive = False + break + arrow_travel = agent.direction.move_forward(agent.location) + agent.has_arrow = False + + def in_danger(self, agent): + '''Checks if Explorer is in danger (Pit or Wumpus), if he is, kill him''' for thing in self.list_things_at(agent.location): - if isinstance(thing, Wumpus) or isinstance(thing, Pit): + if isinstance(thing, Pit) or (isinstance(thing, Wumpus) and thing.alive): agent.alive = False agent.performance -= 1000 agent.killed_by = thing.__class__.__name__ + return True + return False def is_done(self): '''The game is over when the Explorer is killed