Skip to content
Snippets Groups Projects
Commit 2f3cea5a authored by Jon's avatar Jon
Browse files

Remove old code and add new solutions

parent a73e9ee7
No related branches found
No related tags found
No related merge requests found
import math
import random
import numpy as np
from une_ai.vacuum import DISPLAY_HEIGHT, DISPLAY_WIDTH, TILE_SIZE
from une_ai.models import GridMap
from vacuum_agent import VacuumAgent
DIRECTIONS = VacuumAgent.WHEELS_DIRECTIONS
"""
Test agent:
- If the vacuum power is off, it starts cleaning
- At each time, it chooses a random direction for the wheels
"""
def test_behaviour(percepts, actuators):
actions = []
# if the power is off, we start cleaning
if actuators['vacuum-power'] != 1:
actions.append('start-cleaning')
# we choose a new random direction
new_direction = random.choice(DIRECTIONS)
actions.append('change-direction-{0}'.format(new_direction))
return actions
"""
Simple reflex agent:
- If the vacuum power is off, it starts cleaning
- If there is dirt on the current tile (i.e. 'dirt-sensor-center'),
it activates the suction mechanism
- If the agent hits a wall, it changes the direction of the wheels randomly
- If the agent senses dirt on the surrounding tiles,
it changes the direction of the wheels towards the dirt
"""
def simple_reflex_behaviour(percepts, actuators):
actions = []
# if the power is off we start cleaning
if actuators['vacuum-power'] != 1:
actions.append('start-cleaning')
# if there is dirt, we activate the suction mechanism
if percepts['dirt-sensor-center'] == True:
actions.append('activate-suction-mechanism')
elif actuators['suction-power'] == 1:
# if not and the suction mechanism is on, we shut it down to conserve battery
actions.append('deactivate-suction-mechanism')
cur_direction = actuators['wheels-direction']
new_direction = cur_direction
# if we bumped into a wall we change direction
if percepts['bumper-sensor-{0}'.format(cur_direction)] == True:
directions = DIRECTIONS.copy()
# we remove the current direction from the list
directions.remove(cur_direction)
new_direction = random.choice(directions)
# we look if there is dirt in the adjacent cells
for dir in DIRECTIONS:
if percepts['dirt-sensor-{0}'.format(dir)] == True:
# there is dirt, that's a better direction to move to
new_direction = dir
break
# if we changed direction, we add an action to change it
if new_direction != cur_direction:
actions.append('change-direction-{0}'.format(new_direction))
return actions
"""
Model-based reflex agent:
- The agent keeps track of the walls it crashed against by using a GridMap
- Based on the current wheels direction, if the next tile is a wall,
the agent will change direction
- In all the other situations, the agent will behave like the simple-reflex agent
"""
w_env = int(DISPLAY_WIDTH/ TILE_SIZE)
h_env = int(DISPLAY_HEIGHT/TILE_SIZE)
environment_map = GridMap(w_env, h_env, None)
def future_state(model, cur_location, direction):
offset = {
'north': (0, -1),
'south': (0, 1),
'west': (-1, 0),
'east': (1, 0)
}
cur_x, cur_y = cur_location
new_x, new_y = (cur_x + offset[direction][0], cur_y + offset[direction][1])
try:
value = model.get_item_value(new_x, new_y)
new_location = (new_x, new_y)
except:
# if here it means that the next location will be out of bounds
# so that's a wall
value = 'W'
new_location = None
return value, new_location
def model_based_reflex_behaviour(percepts, actuators):
# we can start from the actions determined by the simple reflex agent
actions = simple_reflex_behaviour(percepts, actuators)
# if there was a collision, I need to update the model
cur_direction = actuators['wheels-direction']
agent_location = percepts['location-sensor']
if percepts['bumper-sensor-{0}'.format(cur_direction)] == True:
_, future_location = future_state(environment_map, agent_location, cur_direction)
if future_location is not None:
environment_map.set_item_value(future_location[0], future_location[1], 'W')
# we check if among the actions
# selected by the simple-reflex behaviour
# there is one to change direction
new_direction = cur_direction
for action in actions:
if action.startswith('change-direction'):
# this means that we hit a wall or there is dirt around the agent
# we save this as future direction
tokens = action.split('-')
new_direction = tokens[2]
# and remove it from the actions
actions.remove(action)
# we need to check the adjacent cells for walls
valid_directions = []
for direction in DIRECTIONS:
future_state_value, _ = future_state(environment_map, agent_location, direction)
if future_state_value != 'W':
valid_directions.append(direction)
# now we can check if the new direction is among the
# valid directions with no walls
# if not, we need to change it with a valid one
if new_direction not in valid_directions:
new_direction = random.choice(valid_directions)
# if we changed direction, we add an action to change it
if new_direction != cur_direction:
actions.append('change-direction-{0}'.format(new_direction))
return actions
"""
Goal-based agent:
- The agent keeps track of previously explored tiles by using a GridMap
- Based on the current wheels direction, if the next tile was already explored,
the agent will change direction towards an unexplored tile (if any, otherwise
it will proceed in the same direction)
- In all the other situations, the agent will behave like the model-based reflex agent
- The agent will stop cleaning once the environment is fully explored
"""
def goal_based_behaviour(percepts, actuators):
# we can start from the actions determined by the model-based reflex agent
# doing this will also update the map with walls
actions = model_based_reflex_behaviour(percepts, actuators)
# we can also update the current cell as visited
agent_location = percepts['location-sensor']
environment_map.set_item_value(agent_location[0], agent_location[1], 'X')
# we check if among the actions
# selected by the model-based reflex behaviour
# there is one to change direction
cur_direction = actuators['wheels-direction']
new_direction = cur_direction
for action in actions:
if action.startswith('change-direction'):
# this means that we hit a wall,
# there is dirt around the agent,
# or there is a wall towards us
# we save this as future direction
tokens = action.split('-')
new_direction = tokens[2]
# and remove it from the actions
actions.remove(action)
# we determine the valid directions
# with previously unexplored tiles
valid_directions = []
for direction in DIRECTIONS:
future_state_value, _ = future_state(environment_map, agent_location, direction)
if future_state_value is None:
valid_directions.append(direction)
# we check if the new direction is among the valid ones
# and that valid_directions is not empty
# if not, we change it
if len(valid_directions) > 0 and new_direction not in valid_directions:
# we change direction
new_direction = random.choice(valid_directions)
# if we changed direction, we add an action to change it
if new_direction != cur_direction:
actions.append('change-direction-{0}'.format(new_direction))
# if we visited all the environment, we shut down the power
if len(environment_map.find_value(None)) == 0:
actions.append('stop-cleaning')
return actions
"""
Utility-based agent:
The agent also stores information about dirt on the adjacent cells detected by the dirt sensors.
The agent then chooses the next direction via a utility function.
This utility function takes a direction as input, and implement the following steps:
- The agent examines its internal model of the world and retrieves a list of cell values
in the specified direction.
- It filters out any cells that are obstructed by a wall, considering only the unobstructed cells.
- If there is dirt in the considered direction, the utility is returned as a high value such as 999
otherwise
- The agent calculates the minimum distance (min_dist) from an unexplored cell in this
filtered list. If there are no unexplored cells, min_dist is set to a high value such as 999.
- The utility value is determined as -1 multiplied by min_dist,
reflecting the notion that the agent values smaller distances to unexplored cells.
"""
def utility_function(model, cur_location, direction):
x, y = cur_location
# take the cells in the given direction
if direction == 'north':
cells = model.get_column(x)
cells = np.flip(cells[0:y])
elif direction == 'south':
cells = model.get_column(x)
cells = cells[y+1:]
elif direction == 'west':
cells = model.get_row(y)
cells = np.flip(cells[0:x])
elif direction == 'east':
cells = model.get_row(y)
cells = cells[x+1:]
else:
cells = []
# remove the cells obstructed by a wall
filtered_cells = []
for cell in cells:
if cell != 'W':
filtered_cells.append(cell)
else:
# wall
break
# check if there is dirt in that direction
for cell in cells:
if cell == 'D':
# there is dirt, return high utility
return 999
# compute the min distance from unexplored cells
min_dist = 999
i = 0
for cell in filtered_cells:
if cell is None:
min_dist = i
break
i += 1
# return the utility as -1*min_dist
return -1*min_dist
def utility_based_behaviour(percepts, actuators):
# we can start from the actions determined by the goal-based agent
# doing this will also update the map with walls and explored cells
actions = goal_based_behaviour(percepts, actuators)
# we update the environment map with information about the dirt on adjacent cells
agent_location = percepts['location-sensor']
for direction in DIRECTIONS:
if percepts['dirt-sensor-{0}'.format(direction)] == True:
_, new_location = future_state(environment_map, agent_location, direction)
environment_map.set_item_value(new_location[0], new_location[1], 'D')
# we remove from the actions any change of direction
# as we determine the best direction based on the utility function
for action in actions:
if action.startswith('change-direction'):
actions.remove(action)
# we determine the best direction
cur_direction = actuators['wheels-direction']
max_value = None
best_dir = None
for direction in DIRECTIONS:
cur_utility = utility_function(environment_map, agent_location, direction)
if max_value is None or cur_utility > max_value:
max_value = cur_utility
best_dir = direction
# if we changed direction, we add an action to change it
if best_dir != cur_direction:
actions.append('change-direction-{0}'.format(best_dir))
return actions
\ No newline at end of file
import math
import random
import numpy as np
from une_ai.vacuum import VacuumAgent, DISPLAY_HEIGHT, DISPLAY_WIDTH, TILE_SIZE
from une_ai.models import GridMap
DIRECTIONS = VacuumAgent.WHEELS_DIRECTIONS
"""
Simple reflex agent:
- If the vacuum is on, turn it on
- If there is dirt on the current tile, activate the suction mechanism
- If the agent hit a wall, change the direction of the wheels randomly
- If the agent senses dirt on the surrounding tiles, change the direction of the wheels towards the dirt
"""
def simple_reflex_behaviour(percepts, actuators):
actions = []
# if the vacuum is off, turn it on
if actuators['vacuum-power'] == 0:
actions.append('start-cleaning')
# if there is dirt on the current tile, clean it
if percepts['dirt-sensor-center']:
actions.append('activate-suction-mechanism')
else:
actions.append('deactivate-suction-mechanism')
# if the agent hit a wall, change direction
for direction in DIRECTIONS:
if percepts['bumper-sensor-{0}'.format(direction)] == True:
new_direction = actuators['wheels-direction']
while new_direction == actuators['wheels-direction']:
new_direction = random.choice(DIRECTIONS)
actions.append('change-direction-{0}'.format(new_direction))
# if there is dirt on the surronding tiles, move in that direction
for direction in DIRECTIONS:
if percepts['dirt-sensor-{0}'.format(direction)]:
actions.append('change-direction-{0}'.format(direction))
break
return actions
"""
Model-based reflex agent:
- The agent keeps track of the explored tiles
- If the environment is fully explored, then turn off
- ELSE
- behave like the simple-reflex agent
"""
w_env = math.floor(DISPLAY_WIDTH / TILE_SIZE)
h_env = math.floor(DISPLAY_HEIGHT / TILE_SIZE)
explored_map = GridMap(w_env, h_env)
# compute offsets for the next movement
movement_offsets = {
'north': (0, -1),
'south': (0, 1),
'west': (-1, 0),
'east': (1, 0)
}
def model_based_reflex_behaviour(percepts, actuators):
# stopping when the whole environment is explored
n_unexplored_tiles = len(explored_map.find_value(False))
if n_unexplored_tiles == 0:
return ['stop-cleaning']
# if here, it means that there are unexplored tiles
# the actions are the same of the simple-reflex agent
actions = simple_reflex_behaviour(percepts, actuators)
# we also need to update the model of the environment
agent_x, agent_y = percepts['location-sensor']
try:
explored_map.set_item_value(agent_x, agent_y, True)
except:
# out of bounds, no recordings on the map
pass
# checking if the agent bumped into a wall and update
# the map accordingly (as the agent did not move there)
present_direction = actuators['wheels-direction']
offset_x, offset_y = movement_offsets[present_direction]
next_x, next_y = (agent_x + offset_x, agent_y + offset_y)
for direction in DIRECTIONS:
if percepts['bumper-sensor-{0}'.format(direction)] == True:
try:
explored_map.set_item_value(next_x, next_y, True)
except:
# out of bounds, that's ok
pass
return actions
"""
Goal-based agent:
- We keep track of the explored tiles
- The maps are used to predict if the next movement will lead to a previously explored tile and...
... If so, the agent changes direction towards an unexplored tile
ELSE
- the agent behaves like the model-based reflex agent
"""
def goal_based_reflex_behaviour(percepts, actuators):
# start with the actions from the model-based reflex agent
# this will also update the model of the environment
actions = model_based_reflex_behaviour(percepts, actuators)
# if there is the action stop-cleaning
# it means we visited the whole environment
if 'stop-cleaning' in actions:
return actions
# else, we check if we are going in a direction with an unexplored tile
chosen_direction = None
for action in actions:
if action.startswith('change-direction-'):
chosen_direction = action.split('-')[2]
if chosen_direction is None:
chosen_direction = actuators['wheels-direction']
# making predictions about the future
# to check if it aligns with our goal of cleaning
# the whole environment
first_option = chosen_direction
direction_found = False
i = 0
directions = DIRECTIONS.copy()
# shuffle the directions so to simulate a random choice
random.shuffle(directions)
agent_x, agent_y = percepts['location-sensor']
while not direction_found:
offset_x, offset_y = movement_offsets[chosen_direction]
new_x, new_y = (agent_x + offset_x, agent_y + offset_y)
try:
is_explored = explored_map.get_item_value(new_x, new_y)
except:
# out of bounds, set it as explored
is_explored = True
if is_explored:
# we already visited the next tile
# change direction with the next one
if i < len(directions):
chosen_direction = directions[i]
i += 1
# and try again
else:
# it seems that everything was visited
# we go with the first option we got
chosen_direction = first_option
break
else:
# we found an unvisited tile
direction_found = True
# append the action, only the last change in the list will take place
actions.append('change-direction-{0}'.format(chosen_direction))
return actions
"""
Utility-based reflex agent:
We behave exactly as the goal-based agent but we try to work more efficiently, so
- Behave as the goal-based agent
- IF the chosen direction leads to a previously explored tile it means that the agent explored all the surroundings...
... so we choose a direction leading to the closer unexplored tile that is not obstructed by a wall
"""
# for this agent we also need to keep track of the walls
walls_map = GridMap(w_env, h_env)
def find_best_direction(x, y):
r = explored_map.get_row(y)
c = explored_map.get_column(x)
rw = walls_map.get_row(y)
cw = walls_map.get_column(x)
tiles_by_dir = {
'east': (r[x+1:], rw[x+1:]),
'west': (np.flip(r[:x]), np.flip(rw[:x])),
'north': (np.flip(c[:y]), np.flip(cw[:y])),
'south': (c[y+1:], cw[y+1:])
}
min_dist = None
min_dist_dir = None
for direction in DIRECTIONS:
cur_dist = None
# check if there is an unexplored tile towards this direction
try:
cur_dist = min(np.argwhere(tiles_by_dir[direction][0] == False))[0]
except:
# no empty tiles in this direction, skip
continue
# if we are here it means that there is an unexplored tile
# towards this direction, let's see if it is unobstructed
wall_dist = None
try:
wall_dist = min(np.argwhere(tiles_by_dir[direction][1] == True))[0]
except:
# there are no walls, wall_dist remains to None
pass
if wall_dist is not None and cur_dist > wall_dist:
# unexplored tile directly not reachable, skip
continue
# computing the min distance
if min_dist is None or cur_dist < min_dist:
min_dist = cur_dist
min_dist_dir = direction
return min_dist_dir
def utility_based_reflex_behaviour(percepts, actuators):
agent_x, agent_y = percepts['location-sensor']
# we start behaving like the goal-based agent
actions = goal_based_reflex_behaviour(percepts, actuators)
# checking if the agent bumped into a wall during the previous
# iteration and update the walls map accordingly
present_direction = actuators['wheels-direction']
offset_x, offset_y = movement_offsets[present_direction]
next_x, next_y = (agent_x + offset_x, agent_y + offset_y)
for direction in DIRECTIONS:
if percepts['bumper-sensor-{0}'.format(direction)] == True:
try:
walls_map.set_item_value(next_x, next_y, True)
except:
# out of bounds, nothing to record
pass
# now we check the chosen new direction
# if it leads to a previously explored tile,
# it means that the goal-based agent policy did not find
# any new surrounding tile to explore
chosen_direction = actuators['wheels-direction']
for action in actions:
if action.startswith('change-direction-'):
chosen_direction = action.split('-')[2]
offset_x, offset_y = movement_offsets[chosen_direction]
next_x, next_y = (agent_x + offset_x, agent_y + offset_y)
try:
is_explored = explored_map.get_item_value(next_x, next_y)
except:
# out of bounds, set it as explored
is_explored = True
new_best_dir = chosen_direction
if is_explored:
# We did not find any unvisited tile in the surroundings
# we need to find the best direction to go leading to the closer unobstructed unexplored tile
new_best_dir = find_best_direction(agent_x, agent_y)
if new_best_dir is None:
# this may happen when in a well cleaned area, let's re-use the direction from the previous agent
new_best_dir = chosen_direction
# append it, if there is more than a change direction action, only the last in the list will take place
actions.append('change-direction-{0}'.format(new_best_dir))
return actions
\ No newline at end of file
from une_ai.vacuum import VacuumGame, DISPLAY_HEIGHT, DISPLAY_WIDTH
from agent_programs import simple_reflex_behaviour, model_based_reflex_behaviour, goal_based_reflex_behaviour, utility_based_reflex_behaviour
if __name__ == "__main__":
# To test the different agent programs, change the function passed as parameter to create the instance of VacuumGame
game = VacuumGame(utility_based_reflex_behaviour, DISPLAY_WIDTH, DISPLAY_HEIGHT)
\ No newline at end of file
from une_ai.models import Agent
class VacuumAgent(Agent):
WHEELS_DIRECTIONS = ['north', 'south', 'west', 'east']
def __init__(self, agent_program):
super().__init__(
agent_name='vacuum_agent',
agent_program=agent_program
)
def add_all_sensors(self):
self.add_sensor('battery-level', 0, lambda v: isinstance(v, float) or isinstance(v, int) and v >= 0)
self.add_sensor('location-sensor', (0, 0), lambda v: isinstance(v, tuple) and isinstance(v[0], int) and isinstance(v[1], int))
directions = VacuumAgent.WHEELS_DIRECTIONS.copy()
directions.append('center')
for direction in directions:
self.add_sensor('dirt-sensor-{0}'.format(direction), False, lambda v: v in [True, False])
if direction != 'center':
self.add_sensor('bumper-sensor-{0}'.format(direction), False, lambda v: v in [True, False])
def add_all_actuators(self):
self.add_actuator(
'wheels-direction',
'north',
lambda v: v in VacuumAgent.WHEELS_DIRECTIONS
)
self.add_actuator(
'vacuum-power',
0,
lambda v: v in [0, 1]
)
self.add_actuator(
'suction-power',
0,
lambda v: v in [0, 1]
)
def add_all_actions(self):
self.add_action(
'start-cleaning',
lambda: {'vacuum-power': 1} if not self.is_out_of_charge() else {}
)
self.add_action(
'stop-cleaning',
lambda: {
'vacuum-power': 0
}
)
self.add_action(
'activate-suction-mechanism',
lambda: {'suction-power': 1} if not self.is_out_of_charge() else {}
)
self.add_action(
'deactivate-suction-mechanism',
lambda: {
'suction-power': 0
}
)
for direction in VacuumAgent.WHEELS_DIRECTIONS:
self.add_action(
'change-direction-{0}'.format(direction),
lambda d=direction: {'wheels-direction': d} if not self.is_out_of_charge() else {}
)
def get_pos_x(self):
return self.read_sensor_value('location-sensor')[0]
def get_pos_y(self):
return self.read_sensor_value('location-sensor')[1]
def is_out_of_charge(self):
return self.read_sensor_value('battery-level') == 0
def get_battery_level(self):
return int(self.read_sensor_value('battery-level'))
def collision_detected(self):
directions = VacuumAgent.WHEELS_DIRECTIONS.copy()
for direction in directions:
bumper = self.read_sensor_value('bumper-sensor-{0}'.format(direction))
if bumper:
return direction
return None
def did_collide(self):
return False if self.collision_detected() is None else True
\ No newline at end of file
from une_ai.vacuum import VacuumGame
from vacuum_agent import VacuumAgent
from agent_programs import test_behaviour, simple_reflex_behaviour, model_based_reflex_behaviour, goal_based_behaviour, utility_based_behaviour
if __name__ == "__main__":
# creating the vacuum agent
# To test the different agent programs, change the function passed
# as parameter when instantiating the class VacuumAgent
agent = VacuumAgent(utility_based_behaviour)
# running the game with the instantiated agent
# DO NOT EDIT THIS INSTRUCTION!
game = VacuumGame(agent)
\ No newline at end of file
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment