import numpy as np
import itertools

# Initialisierung der Schwellenwerte
lower_threshold = 0.8
upper_threshold = 1.2

# Lernrate
learning_rate = 0.1

# Eingabekombinationen (2 Eingänge)
inputs = [[0, 0], [0, 1], [1, 0], [1, 1]]

# Generiere alle 16 möglichen Wertetabellen (Targets)
all_truth_tables = list(itertools.product([0, 1], repeat=4))

# Trainingsloop für jede Wertetabelle
max_iterations = 1000
restart_threshold = 100  # Neustart der Gewichte nach 100 erfolglosen Iterationen
results = []  # Zum Speichern der Ergebnisse für jede Wertetabelle

for truth_table_idx, targets in enumerate(all_truth_tables):
    print(f"\nLerne Wertetabelle {truth_table_idx + 1}: Targets = {targets}")

    epoch = 0
    network_trained = False
    start_weights = None
    final_weights = None
    all_epoch_outputs = []  # Outputs jeder Epoche (Debugging und Transparenz)

    # Initialisiere Startgewichte
    current_weights = np.random.rand(2)

    # Trainingsprozess für die aktuelle Wertetabelle
    while epoch < max_iterations:
        epoch += 1
        all_correct = True  # Flag, um zu überprüfen, ob alle Ausgaben korrekt sind

        epoch_outputs = []  # Outputs der aktuellen Epoche

        for input_vector, target in zip(inputs, targets):
            # Berechnung der gewichteten Summe
            weighted_sum = np.dot(input_vector, current_weights)

            # Aktivierungsfunktion (Schwellenwert-Funktion)
            output = 1 if lower_threshold < weighted_sum < upper_threshold else 0

            # Fehlerberechnung
            error = target - output

            # Gewichtsanpassung (Hebb-artig)
            if error != 0:
                all_correct = False
                current_weights += learning_rate * error * np.array(input_vector)

            epoch_outputs.append((input_vector, output, target))

        all_epoch_outputs.append(epoch_outputs)

        # Überprüfe, ob alle Ausgaben korrekt sind
        if all_correct:
            network_trained = True
            final_weights = current_weights  # Speichere die finalen Gewichte
            break

        # Neustart der Gewichte nach `restart_threshold` Iterationen
        if epoch % restart_threshold == 0:
            print(f"  -> Neustart der Startgewichte nach {epoch} Iterationen.")
            current_weights = np.random.rand(2)

    # Ergebnisse speichern
    results.append({
        "truth_table_idx": truth_table_idx + 1,
        "targets": targets,
        "epochs": epoch,
        "network_trained": network_trained,
        "final_weights": final_weights,
        "all_epoch_outputs": all_epoch_outputs
    })

    # Statusmeldung
    if network_trained:
        print(f"  -> Wertetabelle korrekt gelernt nach {epoch} Iterationen.")
    else:
        print(f"  -> Netzwerk konnte die Wertetabelle nicht lernen.")

# Alle Ergebnisse anzeigen
print("\nZusammenfassung der Ergebnisse:")
for result in results:
    print(f"Wertetabelle {result['truth_table_idx']}: Targets = {result['targets']}")
    print(f"  -> Erfolg: {'Ja' if result['network_trained'] else 'Nein'}")
    print(f"  -> Benötigte Epochen: {result['epochs']}")
    if result["network_trained"]:
        print(f"  -> Finale Gewichte: {result['final_weights']}")
    print()
