"""Ejemplo 2 - Motor de inferencia con encadenamiento hacia adelante."""

# Base de conocimiento de un dominio muy acotado: diagnostico de una PC que no arranca.
# Cada regla es un diccionario con premisas (lista) y conclusion (string).

REGLAS = [
    {"id": "R1", "si": ["no_enciende", "sin_luz_fuente"], "entonces": "problema_alimentacion"},
    {"id": "R2", "si": ["problema_alimentacion", "cable_ok"], "entonces": "fuente_danada"},
    {"id": "R3", "si": ["no_enciende", "hay_luz_fuente"], "entonces": "problema_placa"},
    {"id": "R4", "si": ["fuente_danada"], "entonces": "reemplazar_fuente"},
    {"id": "R5", "si": ["problema_placa", "pitidos"], "entonces": "revisar_memoria"},
]

# Hechos observados por el usuario.
HECHOS = {"no_enciende", "sin_luz_fuente", "cable_ok"}


def encadenamiento_adelante(reglas, hechos):
    """Parte de los hechos y deriva todo lo que se pueda concluir."""
    hechos = set(hechos)
    usadas = set()
    traza = []
    cambio = True

    while cambio:
        cambio = False
        for r in reglas:
            if r["id"] in usadas:
                continue
            if all(p in hechos for p in r["si"]):
                hechos.add(r["entonces"])
                usadas.add(r["id"])
                traza.append((r["id"], r["si"], r["entonces"]))
                cambio = True
    return hechos, traza


conclusiones, traza = encadenamiento_adelante(REGLAS, HECHOS)

print("Hechos iniciales:", sorted(HECHOS))
print("\nDisparo de reglas:")
for rid, premisas, conclusion in traza:
    print(f"  {rid}: {' y '.join(premisas)}  =>  {conclusion}")

print("\nConclusiones derivadas:", sorted(conclusiones - HECHOS))
print("Reglas que nunca se dispararon:",
      sorted({r['id'] for r in REGLAS} - {t[0] for t in traza}))
