Mundo de la aspiradora¶

Este notebook presenta un agente que interactúa en el mundo de la aspiradora, tal como se presenta en Russel & Norvig (2021). Una versión más compleja de este mundo puede encontrarse en https://github.com/rayheberer/AI-A-Modern-Approach/tree/master/Chapter%202%20Intelligent%20Agents.

Describiremos la forma en que se puede crear este mundo, para que un agente aspiradora actúe en éste.

Definición de los cuartos¶

En primer lugar construiremos los cuartos o cuadrados en los que va interactuar el agente aspiradora. El ambiente se conformará de un conjunto de cuartos dispuestos en ciertas posiciones.

In [1]:
class Square(object):
    def __init__(self, name):
        """
        Crea un objeto square, que representa los cuadrados del ambiente.

        Argumentos
        ----------
        name : str
          Nombre del cuadrado ('A', 'B', etc.)
        """
        self.name = name
        self.dirt = 0
        
        # Hasta que no se coloque con cuadrados vecinos, los movimientos no llevan a ningún lado
        self.left = self
        self.right = self
        self.up = self
        self.down = self
        
    def __str__(self):
        return self.name

Creación del ambiente¶

El ambiente del mundo de la aspiradora consta de un conjunto de cuadros estructurados en diferentes posiciones; estos cuadros pueden estar al lado de otros, o bien abajo o arriba de estos.

En este ejemplo, consideramos un mundo con cuatro cuadros en los que se puede posicionar el agente. El agente puede moverse en 4 posiciones, izquierda, derecha, arriba y abajo. La configuración en los cuartos que exploramos está configurada de la siguiente forma:

$$[A] [B] \\ [C] [D]$$

Los nombres de los cuartos son A, B, C y D. Los cuadros A y B están en la parte superior y C y D en la parte inferior. A y C están del lado izquierdo y B, D del lado derecho. Asimismo, uno o ambos cuadrados pueden estar sucios (lo que se indica con 1) o limpios (indicado con 0). El ambiente indicará, de manera, aleatoria qué cuadrado está o no sucio.

In [2]:
import random
random.seed(12345)

class VacuumWorld(object):
    def __init__(self, dirt_init='random'):
        """
        Objeto que crea el ambiente para el mundo de la aspiradora:
        Se conforma de:
        - Dos cuadrados: A y B (A a la izquierda de B)
        - Indicación de la limpieza (0) o suciedad (1) de los cuadrados.

        Argumentos
        ----------
        dirt_init : str
          Forma en que se inicializará la suciedad de los cuadrados.
        """
        self.squares = []
        self.A,self.B,self.C,self.D = Square('A'),Square('B'),Square('C'),Square('D')
        self.squares.append(self.A)
        self.squares.append(self.B)
        self.squares.append(self.C)
        self.squares.append(self.D)

        #Configuración de los cuartos
        self.A.left,self.A.right,self.A.up,self.A.down = self.A,self.B,self.A,self.C
        self.B.left,self.B.right,self.B.up,self.B.down = self.A,self.B,self.B,self.D
        self.C.left,self.C.right,self.C.up,self.C.down = self.C,self.D,self.A,self.C
        self.D.left,self.D.right,self.D.up,self.D.down = self.C,self.D,self.B,self.D

        #Inicialización de suciedad
        self.dirt_init = dirt_init
        self.initialize_dirt()
        
    def __str__(self):
        return '[({},{}) ({},{})]\n[({},{}) ({},{})]'.format(self.A.name,self.A.dirt, self.B.name,self.B.dirt,
                                                   self.C.name,self.C.dirt, self.D.name,self.D.dirt)
        
    def initialize_dirt(self):
        """
        Inicializa la suciedad de los cuadrados:

        Opciones (se indican en init)
        --------
        random : Inicializa con uno de los cuadrados sucios y otro limpio de forma aleatoria.
        dirty : Inicializa con los dos cuadrados sucios
        clean : Inicializa con los dos cuadrados limpios
        """
        if self.dirt_init=='random':
            for square in self.squares:
                if random.random() > 0.5:
                    square.dirt = 1
                else:
                    square.dirt = 0

        elif self.dirt_init=='dirty':
            for square in self.squares:
                square.dirt = 1

        else:
            for square, value in zip(self.squares, self.dirt_init):
                square.dirt = value

Creación del ambiente¶

Finalmente, podemos crear el ambiente y observar como se comporta la suciedad en ésta. Cada cuarto tiene asociado un 0 (limpio) o un 1 (sucio).

In [3]:
env = VacuumWorld(dirt_init='random')
print(env)
[(A,0) (B,0)]
[(C,1) (D,0)]

Creación del agente¶

El agente será un objeto que pueda interactuar con el ambiente. Este se compondrá de los siguientes elementos:

  • Sensores: El agente tiene acceso a la información del lugar (cuadrado) en que está y si este cuadrado está sucio.
  • Actuadores: El agente puede realizar las siguientes acciones:
    1. Moverse a la izquierda ('Left').
    2. Moverse a la derecha ('Right').
    3. Moverse hacia arriba ('Up').
    4. Moverse hacia abajo ('Down').
    5. Limpiar ('Clean').
In [4]:
class Agent(VacuumWorld):
    """
    Agente de aspiradora. Cuyas acciones son limpiar, o moverse en las cuatro direcciones.
    Asimismo, el agente guarda una secuencia de percepciones.
    """
    def __init__(self, location=None):
        self.location = location
        self.perceptions = [(self.location.name, self.location.dirt)]
    
    def act(self,action):
        """Función para actuar"""
        if action == 'Clean':
            self.location.dirt = 0
            self.perceptions.append((self.location.name,self.location.dirt))
        elif action == 'Left':
            self.location = self.location.left
            self.perceptions.append((self.location.name,self.location.dirt))
        elif action == 'Right':
            self.location = self.location.right
            self.perceptions.append((self.location.name,self.location.dirt))
        elif action == 'Up':
            self.location = self.location.up
            self.perceptions.append((self.location.name,self.location.dirt))
        elif action == 'Down':
            self.location = self.location.down
            self.perceptions.append((self.location.name,self.location.dirt))
        else:
            print('No es una acción permitida')

Ahora podemos generar el agente posicionándolo en alguno de los cuartos del ambiente.

In [5]:
agent = Agent(location=env.A)
print(env)
print(agent.location)
[(A,0) (B,0)]
[(C,1) (D,0)]
A

Asimismo, el agente puede realizar acciones que se le indiquen, de tal forma que estas afecten a la disposición del ambiente:

In [6]:
agent.act(action='Clean')
print('Ambiente después de la acción:\n{} {}'.format(env,agent.location))
agent.act(action='Right')
print('Ambiente después de la acción:\n{} {}'.format(env,agent.location))
agent.act(action='Clean')
print('Ambiente después de la acción:\n{} {}'.format(env,agent.location))
agent.act(action='Down')
print('Ambiente después de la acción:\n{} {}'.format(env,agent.location))
agent.act(action='Clean')
print('Ambiente después de la acción:\n{} {}'.format(env,agent.location))
agent.act(action='Left')
print('Ambiente después de la acción:\n{} {}'.format(env,agent.location))
agent.act(action='Clean')
print('Ambiente después de la acción:\n{} {}'.format(env,agent.location))
Ambiente después de la acción:
[(A,0) (B,0)]
[(C,1) (D,0)] A
Ambiente después de la acción:
[(A,0) (B,0)]
[(C,1) (D,0)] B
Ambiente después de la acción:
[(A,0) (B,0)]
[(C,1) (D,0)] B
Ambiente después de la acción:
[(A,0) (B,0)]
[(C,1) (D,0)] D
Ambiente después de la acción:
[(A,0) (B,0)]
[(C,1) (D,0)] D
Ambiente después de la acción:
[(A,0) (B,0)]
[(C,1) (D,0)] C
Ambiente después de la acción:
[(A,0) (B,0)]
[(C,0) (D,0)] C

Finalmente, podemos ver que el agente guarda una secuencia de percepciones:

In [7]:
print(agent.perceptions)
[('A', 0), ('A', 0), ('B', 0), ('B', 0), ('D', 0), ('D', 0), ('C', 1), ('C', 0)]
In [ ]: