El agente que proponemos es un agente reactivo con memoria, que tiene en cuenta la historia sensorial previa para crear una representación icónica del mundo que le rodea. En particular, el agente almacena un mapa que toma como punto de referencia la posición inicial del agente en el mapa real. Sobre ese mapa se almacena si se han visitado o no las casillas y si en ellas había una pared o estaban libres. Teniendo en cuenta esto, el agente evita moverse hacia las casillas ocupadas.
Para decidir hacia qué casilla se dirigirá a cada paso, el agente calcula un valor para cada una de las casillas. Este valor tiene en cuenta:
Con todo esto, el agente decide dirigirse a la casilla de más valor. El cálculo del valor de una casilla puede verse como un uso simplificado del concepto de mapa de potencial.
Para decidir si en una determinada casilla el robot debe recoger trufas o seguir moviéndose, se usa el valor esperado de trufas en esa casilla. Este es un valor que se calcula a partir del crecimiento probabilístico esperado a cada paso, y, si supera un cierto umbral marcado por el valor SUFICIENTE_TRUFA
, se decide recoger.
In [2]:
%matplotlib inline
import os
import subprocess
from scipy.stats.mstats import gmean
from __future__ import division
In [3]:
# Test the agent against all the different maps in 'map'.
allmaps = os.listdir('map')
def testmaps(maps = allmaps):
test = []
for filename in maps:
result = subprocess.check_output('./agent '+ './map/' + filename, shell=True)
test.append((float(result),filename))
return test
In [4]:
# Builds the executable
def make(strategy):
return subprocess.check_output(
"g++ main.cpp environment.cpp agent.cpp evaluator.cpp random_num_gen.cpp -o agent -fpermissive -O3 " + strategy,
shell=True
)
In [8]:
# Measures the goodness of a solution
standard_results = [349.2, 634.5, 408.3, 530.6, 660.1, 389.7, 477.7, 498.2]
standard_fast_results = [349.2, 408.3, 389.7, 498.2]
standard_slow_results = [634.5, 530.6, 660.1, 477.7]
def goodness(results, st=standard_results):
proportions = [a[0]/b for a,b in zip(results,st)]
return gmean(proportions)
In [9]:
make("")
print testmaps()
print goodness(testmaps())
Concluyendo así que nuestra solución es en media 4.11307
veces mejor que la aleatoria.
In [131]:
make("-DRANDOM")
print testmaps()
print goodness(testmaps())
In [132]:
make("-DRANDOMLY")
print testmaps()
print goodness(testmaps())
En esta primera estrategia, se combinaba la aleatoriedad con una representación icónica del mundo. Evitaba caminar contra una pared y evita en lo posible girar hacia donde luego encontrará una pared. La solución final fue un refinamiento de esta solución con el cálculo de valores para cada casilla.
In [133]:
make("-DWALLS")
print testmaps()
print goodness(testmaps())
SUFICIENTE_TRUFA
La constante SUFICIENTE_TRUFA
determina cuándo el agente cree que ha crecido suficiente trufa para ser recolectada. Lo que hará será empezar a recolectar una vez haya suficiente. La variación de esta constante variará la frecuencia con la
que se recolecta.
Un factor razonable es 5.000
, porque se llega a él en el tiempo esperado de crecimiento de una trufa con crecimiento del p = 1.5
. Pero probar valores por encima y por debajo puede ser interesante. Se presenta una gráfica con la variación de la bondad según la variación de esta constante.
In [134]:
make("-DSUFICIENTE_TRUFA=" + str(4500))
print testmaps()
print goodness(testmaps())
print testmaps(rapidmaps)
print goodness(testmaps(rapidmaps),standard_fast_results)
print testmaps(slowmaps)
print goodness(testmaps(slowmaps),standard_slow_results)
In [135]:
from pylab import *
x = linspace(0,20000,41)
y = []
for xi in x:
make("-DSUFICIENTE_TRUFA=" + str(xi))
y.append(goodness(testmaps()))
In [136]:
figure()
plot(x, y, 'r')
xlabel('factor')
ylabel('trufa')
title('SUFICIENTE TRUFA')
show()
Obtenemos que el valor general más razonable para SUFICIENTE_TRUFA
está alrededor de los 4500
.
In [10]:
rapidmaps = ['mapa3_rap.map',
'agent_rap.map',
'mapa2_rap.map',
'mapa1_rap.map']
slowmaps = ['mapa2.map',
'mapa3.map',
'mapa1.map',
'agent.map']
print testmaps(rapidmaps)
print goodness(testmaps(rapidmaps),standard_fast_results)
print testmaps(slowmaps)
print goodness(testmaps(slowmaps),standard_slow_results)
print testmaps()
print goodness(testmaps())
In [138]:
from pylab import *
x = linspace(0,10000,41)
y = []
for xi in x:
make("-DMAP -DSUFICIENTE_TRUFA=" + str(xi))
y.append(goodness(testmaps(rapidmaps), standard_fast_results))
In [139]:
figure()
plot(x, y, 'r')
xlabel('factor')
ylabel('trufa')
title('SUFICIENTE TRUFA - Rapid')
show()
In [141]:
from pylab import *
x = linspace(0,10000,41)
y = []
for xi in x:
make("-DSUFICIENTE_TRUFA=" + str(xi))
y.append(goodness(testmaps(slowmaps), standard_slow_results))
In [142]:
figure()
plot(x, y, 'r')
xlabel('factor')
ylabel('trufa')
title('SUFICIENTE TRUFA - Lento')
show()
El factor de giro mide la relación entre la bondad de una casilla y el coste en tiempo de girar para alcanzarla. Cuanto mayor sea, menos valor se le otorga a las casillas a las que se deba girar para alcanzarlas. El factor original era de 1.5
. Probaremos valores razonables entre 1
y 2
.
Encontramos que no podemos establecer ninguna correlación razonable. Tomaremos un factor de giro de 1.1
.
In [143]:
from pylab import *
x = linspace(1,1.5,11)
y = []
for xi in x:
make("-DFACTOR_GIRO=" + str(xi))
y.append(goodness(testmaps()))
In [144]:
figure()
plot(x, y, 'r')
xlabel('factor')
ylabel('trufa')
title('FACTOR GIRO')
show()
Va a estudiar también un factor para decidir si está en un mapa de crecimiento lento o rápido. El valor es la media de oler varias veces entre los turnos 200 y 260. Este valor se analiza para varios casos.
In [11]:
make("-DOLOR -DSUFICIENTE_TRUFA=" + str(4500))
print testmaps()
print goodness(testmaps())
print testmaps(rapidmaps)
print goodness(testmaps(rapidmaps),standard_fast_results)
print testmaps(slowmaps)
print goodness(testmaps(slowmaps),standard_slow_results)
Como puede apreciarse, no mejora de manera estadísticamente significativa. De hecho, empeora la solución global.