Este proyecto implementa un sistema declarativo para modelar un mercado de transferencias de fútbol utilizando el paradigma de programación lógica en Prolog. El sistema permite evaluar, recomendar y optimizar la compra de jugadores considerando múltiples restricciones como presupuesto, cupos de extranjeros, necesidades de posición y restricciones de edad.
- Demostrar el uso de hechos y reglas en programación lógica
- Aplicar backtracking para encontrar soluciones
- Implementar restricciones complejas
- Utilizar listas y predicados recursivos
- Realizar filtrado por múltiples condiciones
- Generar combinaciones óptimas mediante búsqueda
prolog/
├── src/
│ ├── conocimiento.pl # Base de conocimiento (hechos)
│ ├── reglas.pl # Reglas lógicas y predicados
│ └── sistema_compras.pl # Archivo principal
├── frontend/
│ ├── app.py # Interfaz web (Streamlit)
│ ├── prolog_bridge.py # Conexión con SWI-Prolog
│ ├── visual_data.py # Logos, fotos, rivales
│ └── requirements.txt
├── examples/
├── docs/
└── README.md
Para dejar todo listo para entregar:
- Requisitos instalados: SWI-Prolog y Python 3.10+.
- Probar consola Prolog:
swipl→[src/sistema_compras].→ alguna consulta. - Probar frontend: desde la raíz del proyecto:
python3 -m venv .venv && source .venv/bin/activate # o .venv\Scripts\activate en Windows pip install -r frontend/requirements.txt python3 frontend/prepare_assets.py streamlit run frontend/app.py
- Abrir en el navegador la URL que indique Streamlit (p. ej.
http://localhost:8501). - La portada de la app resume el proyecto; las pestañas permiten demostrar scouting, recomendaciones, combinación óptima, conocimiento y galería de jugadores.
- El selector principal de equipo incluye el Top 20 de clubes y muestra la plantilla del equipo seleccionado con fotos y logos en local (siempre que se haya ejecutado
prepare_assets.py). - Para equipos modelados en Prolog (como
Real Madrid,Barcelona,Manchester City,Bayern Munich,PSG,Liverpool) se habilitan además análisis lógicos completos (fichaje, recomendaciones y combinación óptima). - Activar el Modo explicación académica en la barra lateral para justificar decisiones de fichaje con trazabilidad.
- Ejecutar chequeo final:
swipl -q -f test_sintaxis.pl -t halt swipl -q -f tests/prolog_logic_tests.pl -g run_tests -t halt python3 -m unittest tests/test_prolog_bridge.py -v python3 benchmarks/run_benchmarks.py
- SWI-Prolog (versión 8.0 o superior)
Interfaz web para mostrar el sistema sin usar la consola de Prolog.
-
Instalar dependencias de Python:
python3 -m venv .venv source .venv/bin/activate pip install -r frontend/requirements.txt -
Iniciar la app:
streamlit run frontend/app.py
-
Abrir en navegador la URL que imprima Streamlit (normalmente
http://localhost:8501).
Qué incluye la interfaz: resumen por equipo, evaluación de fichajes con explicación, recomendaciones con tabla y gráfica, mejor fichaje, combinación óptima con química, calculadora de química entre dos jugadores, comparador con radar (rendimiento, goles, asistencias, costo-eficiencia, proyección), galería de jugadores con búsqueda y filtro por posición, filtro por rivales y fotos (reales cuando hay URL cargada; avatar único para el resto).
-
Iniciar SWI-Prolog:
swipl
-
Cargar el sistema:
?- [src/sistema_compras].
-
Realizar consultas:
?- puede_firmar('Real Madrid', 'Robert Lewandowski').
El sistema se basa en hechos que representan información estática del dominio:
jugador(Nombre, Posicion, Edad, Precio, Nacionalidad, Goles, Asistencias).Ejemplo:
jugador('Kylian Mbappé', delantero, 25, 180, francia, 35, 10).necesita(Equipo, Posicion).
presupuesto(Equipo, Monto).
cupo_extranjeros(Equipo, Maximo).
nacionalidad_equipo(Equipo, Nacionalidad).Ejemplo:
presupuesto('Real Madrid', 200).
necesita('Real Madrid', delantero).
cupo_extranjeros('Real Madrid', 5).Las reglas definen la lógica de negocio mediante predicados que relacionan hechos:
puede_firmar(Equipo, Jugador) :-
puede_pagar(Equipo, Jugador),
juega_posicion_necesaria(Jugador, Equipo),
cumple_restricciones_edad(Jugador, Equipo).Esta regla declara las condiciones necesarias para que un equipo pueda firmar a un jugador, sin especificar cómo se evalúa (el motor de Prolog se encarga mediante backtracking).
Verifica si un equipo puede firmar a un jugador considerando presupuesto, posición y edad.
?- puede_firmar('Real Madrid', 'Robert Lewandowski').
true.Recomienda jugadores para un equipo ordenados por rendimiento (goles + asistencias).
?- recomendar('Barcelona', Jugador).
Jugador = 'Erling Haaland' ;
Jugador = 'Kylian Mbappé' ;
...Encuentra el jugador con mayor rendimiento que puede ser firmado.
?- mejor_fichaje('Liverpool', Jugador).
Jugador = 'Bukayo Saka'.Genera la mejor combinación de jugadores que maximiza el rendimiento total (incluyendo química) respetando todas las restricciones (presupuesto, cupos, reglas de liga).
?- combinacion_optima('Real Madrid', ListaJugadores).
ListaJugadores = [combinación óptima considerando química y restricciones].Lista todos los jugadores que un equipo puede comprar.
?- jugadores_disponibles('Manchester City', Lista).
Lista = ['Erling Haaland', 'Kevin De Bruyne', ...].Filtra jugadores disponibles por posición.
?- jugadores_por_posicion('Real Madrid', delantero, Lista).
Lista = ['Robert Lewandowski', 'Karim Benzema', ...].Calcula la química entre dos jugadores.
?- quimica_entre('Pedri', 'Gavi', Puntuacion).
Puntuacion = 9.Calcula el rendimiento mejorado considerando química entre jugadores.
?- rendimiento_con_quimica(['Pedri', 'Gavi'], Rendimiento).
Rendimiento = 20.9.Verifica si una combinación cumple todas las restricciones de liga.
?- cumple_reglas_liga(['Pedri', 'Gavi', 'Erling Haaland'], 'Barcelona').
true/false.Analiza una combinación mostrando todos sus aspectos.
?- analizar_combinacion(['Pedri', 'Gavi'], 'Barcelona', Analisis).
Analisis = [costo=190, rendimiento_base=20, ...].Prolog utiliza backtracking automático para explorar todas las soluciones posibles. Cuando una consulta falla, retrocede y prueba alternativas.
Ejemplo:
?- puede_firmar('Real Madrid', Jugador).
Jugador = 'Robert Lewandowski' ; % Primera solución
Jugador = 'Karim Benzema' ; % Backtracking encuentra otra
Jugador = 'Pedri' ; % Y otra...La unificación permite que las variables se instancien con valores que hacen verdadera una consulta.
?- jugador(Jugador, delantero, 25, Precio, _, _, _).
Jugador = 'Kylian Mbappé',
Precio = 180.Los predicados recursivos procesan listas y estructuras complejas:
costo_total([], 0).
costo_total([Jugador|Resto], CostoTotal) :-
precio_jugador(Jugador, Precio),
costo_total(Resto, CostoSubtotal),
CostoTotal is Precio + CostoSubtotal.Las restricciones se modelan como condiciones que deben cumplirse:
combinacion_valida(ListaJugadores, Equipo) :-
todos_pueden_firmar(ListaJugadores, Equipo),
cabe_en_presupuesto(ListaJugadores, Equipo),
respeta_cupo_extranjeros(ListaJugadores, Equipo).El predicado subconjunto/2 genera todas las combinaciones posibles mediante backtracking:
subconjunto([], []).
subconjunto([X|Xs], [X|Ys]) :- subconjunto(Xs, Ys).
subconjunto(Xs, [_|Ys]) :- subconjunto(Xs, Ys).Para cubrir explícitamente contenidos del paradigma funcional del programa de la materia, se incluye:
functional/funcional_racket.rktdocs/COMPARATIVA_PROLOG_RACKET.md
Este módulo funcional ahora implementa una versión comparativa real del problema:
puede-firmar?,recomendaciones,mejor-fichaje,comb-valida?,mejor-combinacion- modelado inmutable de datos (
struct jugador,struct equipo) - recursión explícita (
subconjuntos) + funciones de orden superior (map,filter,foldl,sort) - menú interactivo de consola para demo
- tests con
rackunit(raco test)
Ejecutar:
racket functional/funcional_racket.rkt
raco test functional/funcional_racket.rktSi
racketno está instalado:
- Ubuntu/Debian:
sudo apt install racket
?- puede_firmar('Real Madrid', 'Kylian Mbappé').
false.
?- puede_firmar('Real Madrid', 'Robert Lewandowski').
true.Explicación: Mbappé cuesta 180M y excede el presupuesto de 200M si ya hay otros gastos, mientras que Lewandowski (25M) es asequible.
?- recomendar('Barcelona', Jugador).
Jugador = 'Erling Haaland' ;
Jugador = 'Kylian Mbappé' ;
Jugador = 'Bukayo Saka' ;
...Explicación: El sistema ordena jugadores por rendimiento (goles + asistencias) que pueden ser firmados.
?- combinacion_optima('Real Madrid', Lista).
Lista = ['Erling Haaland', 'Pedri', 'Ronald Araújo'].Explicación: El sistema encuentra la combinación que maximiza el rendimiento total respetando presupuesto (200M) y cupo de extranjeros (5).
?- jugadores_por_posicion('Real Madrid', delantero, Lista).
Lista = ['Robert Lewandowski', 'Karim Benzema', 'Cristiano Ronaldo'].?- presupuesto_restante('Real Madrid', ['Robert Lewandowski', 'Pedri'], Restante).
Restante = 75.Ambos lenguajes son declarativos, pero con enfoques diferentes:
- Programación lógica: Basada en relaciones y búsqueda de pruebas
- Backtracking automático: Explora todas las soluciones
- Unificación: Variables se instancian automáticamente
- Búsqueda completa: Encuentra todas las soluciones posibles
- Programación funcional: Basada en funciones y transformaciones
- Evaluación estricta: Flujo de evaluación explícito
- Tipado dinámico: Flexibilidad para prototipado académico
- Inmutabilidad: Los datos no se modifican
puede_firmar(Equipo, Jugador) :-
puede_pagar(Equipo, Jugador),
juega_posicion_necesaria(Jugador, Equipo).Características:
- Declarativo: Describe QUÉ condiciones deben cumplirse
- Bidireccional: Puede encontrar jugadores para un equipo o equipos para un jugador
- Backtracking: Encuentra todas las soluciones automáticamente
(define (puede-firmar? eq j)
(and (puede-pagar? eq j)
(cubre-necesidad? eq j)
(cumple-edad? eq j)))Características:
- Funcional: Define una función que transforma entrada en salida
- Unidireccional: Calcula un valor booleano
- Explícito: Necesita
filtero list comprehensions para encontrar todas las soluciones
- Búsqueda automática: No necesita escribir loops o recursión explícita para encontrar soluciones
- Bidireccionalidad: Los predicados funcionan en múltiples direcciones
- Backtracking integrado: Explora el espacio de soluciones automáticamente
- Modelado natural: Las relaciones se expresan de forma intuitiva
- Tipado fuerte: Previene errores en tiempo de compilación
- Composabilidad: Funciones pequeñas se combinan fácilmente
- Rendimiento: Optimizaciones automáticas y evaluación perezosa
- Expresividad: List comprehensions y funciones de orden superior
Prolog es ideal para problemas de búsqueda y restricciones donde necesitamos explorar múltiples soluciones. El enfoque funcional en Racket/Scheme es fuerte para transformaciones de datos, composición y control explícito de estrategia. Este proyecto utiliza ambos enfoques de forma comparativa para cubrir los contenidos de la materia.
Decisión: Separar conocimiento.pl (hechos) de reglas.pl (lógica).
Razón: Facilita el mantenimiento y permite actualizar la base de datos sin modificar la lógica.
Decisión: Crear predicados auxiliares como precio_jugador/2, edad_jugador/2.
Razón: Mejora la legibilidad y permite reutilización de código.
Decisión: Usar subconjunto/2 para generar combinaciones y verificar restricciones.
Razón: Aprovecha el backtracking de Prolog para explorar el espacio de soluciones sin algoritmos explícitos.
Decisión: Usar goles + asistencias como métrica de rendimiento.
Razón: Es una métrica simple y efectiva que permite comparar jugadores de diferentes posiciones.
Decisión: Hacer las restricciones de edad opcionales usando -> (implicación condicional).
Razón: No todos los equipos tienen restricciones de edad, y esto permite flexibilidad.
- Nombres descriptivos: Los predicados tienen nombres claros que indican su propósito
- Comentarios: Cada sección y predicado importante está documentado
- Modularidad: El código está organizado en módulos lógicos
- Consistencia: Se sigue un estilo uniforme en todo el proyecto
Los predicados recursivos siguen el patrón estándar:
- Caso base: Define el resultado para la estructura vacía
- Caso recursivo: Procesa el primer elemento y llama recursivamente
Ejemplo:
costo_total([], 0).
costo_total([Jugador|Resto], CostoTotal) :-
precio_jugador(Jugador, Precio),
costo_total(Resto, CostoSubtotal),
CostoTotal is Precio + CostoSubtotal.-
Verificar restricciones básicas:
?- puede_firmar('Real Madrid', 'Kylian Mbappé'). % Debe fallar (presupuesto) -
Verificar filtrado:
?- jugadores_por_posicion('Barcelona', delantero, Lista). -
Verificar optimización:
?- combinacion_optima('Manchester City', Lista). -
Verificar restricciones complejas:
?- combinacion_valida(['Erling Haaland', 'Pedri'], 'Real Madrid').
# Validación sintáctica base
swipl -q -f test_sintaxis.pl -t halt
# Tests lógicos en Prolog (plunit)
swipl -q -f tests/prolog_logic_tests.pl -g run_tests -t halt
# Tests del bridge Python-Prolog
python3 -m unittest tests/test_prolog_bridge.py -v
# Benchmark reproducible de consultas
python3 benchmarks/run_benchmarks.pyResultados de benchmark:
benchmarks/latest_benchmark.csvbenchmarks/latest_benchmark.md
El sistema incluye un modelo de química que representa qué tan bien rinden juntos los jugadores:
- Química explícita: Combinaciones conocidas (ej: Pedri + Gavi = 9/10)
- Química automática: Misma nacionalidad = +3 puntos
- Bonus de rendimiento: La química mejora el rendimiento total
Ejemplo:
?- quimica_entre('Pedri', 'Gavi', Puntuacion).
Puntuacion = 9.
?- rendimiento_con_quimica(['Pedri', 'Gavi'], Rendimiento).
Rendimiento = 20.9. % 20 (base) + 0.9 (química)Ver documentación completa en: docs/QUIMICA_Y_LIGA.md
El sistema verifica que las combinaciones cumplan reglas de liga:
- Límite de misma nacionalidad: Máximo de jugadores de una nacionalidad
- Cupo mínimo de cantera: Mínimo de jugadores formados localmente
- Límite de edad: Balance generacional
Ejemplo:
?- cumple_reglas_liga(['Pedri', 'Gavi', 'Erling Haaland'], 'Barcelona').
false. % Puede fallar por no cumplir cupo de canteraVer documentación completa en: docs/QUIMICA_Y_LIGA.md
Posibles mejoraciones al sistema:
- Restricciones de salario: Agregar salarios y límites salariales
- Historial de lesiones: Considerar jugadores con historial médico
- Compatibilidad de estilo: Filtrar por estilo de juego
- Optimización multi-objetivo: Balancear rendimiento, precio y edad
- Análisis temporal: Considerar contratos y fechas de expiración
- SWI-Prolog Documentation: https://www.swi-prolog.org/pldoc/
- Learn Prolog Now!: http://www.learnprolognow.org/
- Programming in Prolog (Clocksin & Mellish)
- Racket Documentation: https://docs.racket-lang.org/
- Revisión contra programa de la materia (UNNOBA):
docs/REVISION_PROGRAMA_UNNOBA.md - Checklist de defensa final:
docs/CHECKLIST_DEFENSA_FINAL.md - Comparativa formal Prolog vs Racket:
docs/COMPARATIVA_PROLOG_RACKET.md
Proyecto desarrollado para el curso de Programación Lógica y Funcional.
Este proyecto es de carácter académico y educativo.
Inspirado en el repositorio de referencia:
Última actualización: 2026