%matplotlib inline
import os
import subprocess
from scipy.stats.mstats import gmean
from __future__ import division

# 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

# 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
    )

# 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)

make("")
print testmaps()
print goodness(testmaps())

"""
Explanation: Descripción de la solución
Mapa del mundo
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.
Valor de una casilla
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:

La cantidad de trufa esperada en la casilla.
Un factor que penaliza los giros.
Un factor que premia el explorar nuevas casillas.

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.
Decisión de recoger
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.
Resultados obtenidos
Para medir la bondad de una solución, usamos durante el proceso la media geométrica de las mejoras respecto a la solución aleatoria inicial. Nótese que la media geométrica de las mejoras es comparable independientemente de la referencia.
End of explanation
"""


make("-DRANDOM")
print testmaps()
print goodness(testmaps())

"""
Explanation: Concluyendo así que nuestra solución es en media 4.11307 veces mejor que la aleatoria.
Otras estrategias
El resto de estrategias que se implementaron pueden observarse usando complilación condicional sobre el código dado.
Para cada solución se muestra su bondad respecto a la solución aleatoria y la lista ordenada alfabéticamente de valores sobre el mapa inicial.
Estrategia: Aleatoria
Escoje aleatoriamente una acción entre las cuatro. Sólo la usamos como referencia.
End of explanation
"""


make("-DRANDOMLY")
print testmaps()
print goodness(testmaps())

"""
Explanation: Estrategia: Casi aleatoriamente
Aleatoriamente, pero omitiendo la acción de observar. Mejora sólo sutilmente la aleatoria.
End of explanation
"""


make("-DWALLS")
print testmaps()
print goodness(testmaps())

"""
Explanation: Estrategia: Paredes
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.
End of explanation
"""


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)

from pylab import *
x = linspace(0,20000,41)
y = []
for xi in x:
    make("-DSUFICIENTE_TRUFA=" + str(xi))
    y.append(goodness(testmaps())) 

figure()
plot(x, y, 'r')
xlabel('factor')
ylabel('trufa')
title('SUFICIENTE TRUFA')
show()

"""
Explanation: Refinamientos de la solución
Estudio del factor 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.
End of explanation
"""


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())

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))

figure()
plot(x, y, 'r')
xlabel('factor')
ylabel('trufa')
title('SUFICIENTE TRUFA - Rapid')
show()

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))

figure()
plot(x, y, 'r')
xlabel('factor')
ylabel('trufa')
title('SUFICIENTE TRUFA - Lento')
show()

"""
Explanation: Obtenemos que el valor general más razonable para SUFICIENTE_TRUFA está alrededor de los 4500.
El factor en mapas de crecimiento rápido y lento
Podemos particularizar el estudio a cada uno de los dos posibles casos.
Veremos que el óptimo en los mapas rápidos será 4500, mientras que en los mapas lentos rondará los 7000.
End of explanation
"""


from pylab import *
x = linspace(1,1.5,11)
y = []
for xi in x:
    make("-DFACTOR_GIRO=" + str(xi))
    y.append(goodness(testmaps())) 

figure()
plot(x, y, 'r')
xlabel('factor')
ylabel('trufa')
title('FACTOR GIRO')
show()

"""
Explanation: Estudio del factor de giro
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.
End of explanation
"""


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)

"""
Explanation: Estudio del factor de olor
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.
End of explanation
"""


