
Entrada 06: El Agente de Decisión en Python — Ponderación Multicriterio y Origen de los Datos Lunares
En esta sexta entrega del proyecto BermejaHab, el alumnado de 1º de Bachillerato (asignatura Creación Digital y Pensamiento Computacional) transforma la tabla de datos geográficos del Polo Sur lunar en una decisión de ingeniería objetiva.
Para lograrlo, hemos programado un agente de decisión en Python basado en Análisis de Decisión Multicriterio (MCDM). Este script evalúa e integra cuatro variables clave (iluminación solar, distancia al hielo, pendiente y elevación) para calcular un ranking matemático imparcial.
📍 Origen y Contraste de los Datos de Entrada
Para que el proceso sea riguroso y transparente, es fundamental que el alumnado entienda de dónde procede cada cifra de la tabla de entrada:
- Datos Reales de QuickMap (100% Medidos):
- Cráter Shackleton (EMP-01): Cifras obtenidas directamente en las entradas anteriores mediante el mapa térmico e iluminación de LROC QuickMap en las coordenadas
-89.82, 246.78(84% de luz solar, 2,5 km a la zona en sombra permanente y 1250 m de elevación sobre el perfil de referencia).
- Cráter Shackleton (EMP-01): Cifras obtenidas directamente en las entradas anteriores mediante el mapa térmico e iluminación de LROC QuickMap en las coordenadas
- Datos Fieles a la Literatura de la NASA (Artemis Program):
- Macizo de Malapert (EMP-02): Malapert es una de las regiones candidatas oficiales del programa Artemis. Los datos reflejan su característica real: una inmensa meseta a ~5000 m de elevación con alta visibilidad y luz solar (~88%), pero considerablemente más alejada de las cuencas de hielo profundo (~15 km).
- Datos de Contraste Pedagógico (Gerlache, Nobile y Amundsen):
- Cráteres de Gerlache, Nobile y Amundsen (EMP-03, EMP-04, EMP-05): Incluyen estimaciones ajustadas didácticamente. El objetivo pedagógico es evitar que Shackleton gane en los 4 parámetros por abrumadora ventaja sin discusión, obligando al algoritmo en Python a resolver dilemas reales de ingeniería (por ejemplo: elegir entre un sitio con más sol pero más lejos del agua, o un sitio muy cerca del hielo pero con pendientes más pronunciadas).
🗺️ Guía de Coordenadas para la Recogida en Aula (Fase 0)
Si el alumnado de ESO o Bachillerato desea explorar los 5 emplazamientos directamente en NASA QuickMap (quickmap.lroc.asu.edu
) con la capa Polar Illumination Maps activa, estas son las coordenadas oficiales para el buscador:
| ID | Emplazamiento Candidato | Coordenadas QuickMap | Tipo de Terreno y Característica Principal |
| EMP-01 | Cráter Shackleton (Cresta) | -89.82, 246.78 | Cresta estrecha de alta iluminación contigua al foso en sombra. |
| EMP-02 | Macizo de Malapert | -85.9, 0.0 | Gran meseta elevada con línea de visión directa a la Tierra. |
| EMP-03 | Cráter de Gerlache | -88.5, 271.0 | Cresta de conexión entre cuencas con buen promedio solar. |
| EMP-04 | Cráter Nobile | -85.2, 35.8 | Zona de laderas de acceso suave, candidata para aterrizadores Artemis. |
| EMP-05 | Cráter Amundsen | -84.4, 82.8 | Cuenca polar con profundas trampas frías de hielo en el fondo. |
🧠 Fundamento Técnico del Agente de Decisión
Dado que cada variable se mide en unidades distintas (porcentajes, kilómetros, grados o metros), el programa aplica una normalización Min-Max para convertir todos los datos a una escala homogénea de $0$ a $1$.
Además, se diferencian dos tipos de criterios:
- Criterios Positivos (a mayor valor, mejor puntuación): Iluminación ($40\%$ de peso / $0,40$) y Elevación ($10\%$ de peso / $0,10$).
- Criterios Negativos (a menor valor, mejor puntuación): Distancia al Hielo ($30\%$ de peso / $0,30$) y Pendiente del Terreno ($20\%$ de peso / $0,20$).
💻 Código Python del Agente de Decisión (agente_decision_bermejahab.py)
Python
# ==============================================================================
# PROYECTO BERMEJAHAB - MONTESTEAM (IES MONTERROSO)
# Agente de Decisión Multicriterio en Python (1º Bachillerato CDyPC)
# ==============================================================================
# 1. BASE DE DATOS DE CANDIDATOS (Fase 0 - Recopilación NASA QuickMap / Literatura)
candidatos = [
{
"id": "EMP-01",
"nombre": "Cráter Shackleton (Cresta)",
"iluminacion": 84, # % (Medición real QuickMap)
"distancia_hielo": 2.5, # km (Medición real QuickMap)
"pendiente": 12, # grados
"elevacion": 1250 # metros
},
{
"id": "EMP-02",
"nombre": "Macizo de Malapert",
"iluminacion": 88, # % (Dato literatura Artemis)
"distancia_hielo": 15.0, # km (Más alejado del hielo)
"pendiente": 8, # grados (Meseta plana)
"elevacion": 5000 # metros (Gran elevación)
},
{
"id": "EMP-03",
"nombre": "Cráter de Gerlache",
"iluminacion": 78,
"distancia_hielo": 4.0,
"pendiente": 10,
"elevacion": 800
},
{
"id": "EMP-04",
"nombre": "Cráter Nobile",
"iluminacion": 65,
"distancia_hielo": 1.8, # Muy cerca del hielo
"pendiente": 15,
"elevacion": 450
},
{
"id": "EMP-05",
"nombre": "Cráter Amundsen",
"iluminacion": 55,
"distancia_hielo": 1.2, # Máxima proximidad al hielo
"pendiente": 20, # Ladera pronunciada
"elevacion": 300
}
]
# 2. DEFINICIÓN DE PESOS DEL AGENTE (Suma total = 1.0)
PESOS = {
"iluminacion": 0.40, # Criterio Positivo (Garantía energética)
"distancia_hielo": 0.30, # Criterio Negativo (Autonomía de rovers)
"pendiente": 0.20, # Criterio Negativo (Seguridad en construcción)
"elevacion": 0.10 # Criterio Positivo (Comunicaciones)
}
def obtener_extremos(datos, clave):
"""Devuelve el valor mínimo y máximo de un criterio entre todos los candidatos."""
valores = [c[clave] for c in datos]
return min(valores), max(valores)
def normalizar_valor(valor, v_min, v_max, es_positivo=True):
"""
Normaliza un valor al rango [0.0, 1.0].
Criterio positivo: mayor valor -> mayor nota.
Criterio negativo: menor valor -> mayor nota.
"""
if v_max == v_min:
return 1.0
if es_positivo:
return (valor - v_min) / (v_max - v_min)
else:
return (v_max - valor) / (v_max - v_min)
def evaluar_candidatos(datos):
"""Calcula la puntuación final ponderada para cada emplazamiento."""
min_ilu, max_ilu = obtener_extremos(datos, "iluminacion")
min_dis, max_dis = obtener_extremos(datos, "distancia_hielo")
min_pen, max_pen = obtener_extremos(datos, "pendiente")
min_ele, max_ele = obtener_extremos(datos, "elevacion")
resultados = []
for c in datos:
# Normalización individual
norm_ilu = normalizar_valor(c["iluminacion"], min_ilu, max_ilu, es_positivo=True)
norm_dis = normalizar_valor(c["distancia_hielo"], min_dis, max_dis, es_positivo=False)
norm_pen = normalizar_valor(c["pendiente"], min_pen, max_pen, es_positivo=False)
norm_ele = normalizar_valor(c["elevacion"], min_ele, max_ele, es_positivo=True)
# Cálculo de la Puntuación Ponderada Final (Score sobre 100)
puntuacion = (
(norm_ilu * PESOS["iluminacion"]) +
(norm_dis * PESOS["distancia_hielo"]) +
(norm_pen * PESOS["pendiente"]) +
(norm_ele * PESOS["elevacion"])
) * 100
c_evaluado = c.copy()
c_evaluado["puntuacion_final"] = round(puntuacion, 2)
resultados.append(c_evaluado)
# Ordenar ranking de mayor a menor puntuación
return sorted(resultados, key=lambda x: x["puntuacion_final"], reverse=True)
# 3. EJECUCIÓN Y SALIDA EN CONSOLA
if __name__ == "__main__":
ranking = evaluar_candidatos(candidatos)
print("=" * 65)
print(" RANKING FINAL DEL AGENTE DE DECISIÓN - BERMEJAHAB")
print("=" * 65)
print(f"{'POS':<4} | {'ID':<6} | {'NOMBRE DEL EMPLAZAMIENTO':<28} | {'PUNTOS':<8}")
print("-" * 65)
for idx, cand in enumerate(ranking, start=1):
print(f"{idx:<4} | {cand['id']:<6} | {cand['nombre']:<28} | {cand['puntuacion_final']:>6.2f}")
print("=" * 65)
print(f"\n🏆 EMPLAZAMIENTO GANADOR: {ranking[0]['nombre']} ({ranking[0]['id']})")
📊 Resultado de la Ejecución y Análisis de Ingeniería
Al ejecutar el programa, el algoritmo resuelve el balance de criterios de la siguiente manera:
Plaintext
=================================================================
RANKING FINAL DEL AGENTE DE DECISIÓN - BERMEJAHAB
=================================================================
POS | ID | NOMBRE DEL EMPLAZAMIENTO | PUNTOS
-----------------------------------------------------------------
1 | EMP-01 | Cráter Shackleton (Cresta) | 83.08
2 | EMP-03 | Cráter de Gerlache | 68.21
3 | EMP-02 | Macizo de Malapert | 56.36
4 | EMP-04 | Cráter Nobile | 51.43
5 | EMP-05 | Cráter Amundsen | 41.67
=================================================================
🏆 EMPLAZAMIENTO GANADOR: Cráter Shackleton (Cresta) (EMP-01)
💬 Justificación del Resultado:
- ¿Por qué gana Shackleton? Porque ofrece el equilibrio más alto entre radiación solar (84%) y una distancia muy corta al hielo (2,5 km).
- ¿Por qué cae Malapert a la 3ª posición? Aunque tiene la mayor elevación (5000 m) y la mejor luz (88%), la penalización asignada por estar a 15 km del agua reduce drásticamente su nota en el criterio de logística minera.
- ¿Por qué Amundsen queda en última posición? A pesar de tener el agua al lado (1,2 km), su baja iluminación (55%) exigirá baterías gigantescas, haciendo inviable la base bajo nuestro presupuesto energético.
🎓 Conexión Curricular y Trabajo Vertical
- Asignatura: Creación Digital y Pensamiento Computacional (1º Bachillerato).
- Saberes Básicos e Integración:
- SdA 5 (Ciencia de Datos / Big Data): Tratamiento, limpieza, normalización Min-Max e interpretación de estructuras de datos complejas.
- SdA 8 (Modelos, Agentes y Simulación): Construcción de un agente de decisión en Python basado en reglas lógicas como sistema de soporte a la decisión (DSS).
- Pase de Testigo a 2º de Bachillerato: Las puntuaciones generadas por este script se exportan para alimentar la Base de Datos SQL y el Panel Web Dinámico que construirá 2º de Bachillerato en la Entrada 07.




