import numpy as np

# Initialisierung der Schwellenwerte
lower_threshold = 0.8
upper_threshold = 1.2

# Lernrate
learning_rate = 0.1

# Trainingsdaten (XOR-Problem)
inputs = [[0, 0], [0, 1], [1, 0], [1, 1]]
targets = [0, 1, 1, 0]

# Trainingsloop mit max. 1000 Iterationen
max_iterations = 1000
epoch = 0
network_trained = False
start_weights = None
final_weights = None
start_bias = None
final_bias = None
all_epoch_outputs = []  # Store outputs of all epochs for debugging and transparency

# Initialize weights and bias
current_weights = np.random.rand(2)  # Initial random weights
bias = np.random.rand()  # Random initial bias

while epoch < max_iterations:
    epoch += 1
    all_correct = True  # Flag, um zu überprüfen, ob alle Ausgaben korrekt sind

    if epoch == 1:  # Die erste Iteration nach Initialisierung
        start_weights = current_weights  # Speichere die Startgewichte
        start_bias = bias  # Speichere den Start-Bias

    epoch_outputs = []  # To store outputs of this epoch

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

        # Aktivierungsfunktion (einfache Schwellenwertfunktion)
        output = 1 if lower_threshold < weighted_sum < upper_threshold else 0

        # Fehlerberechnung
        error = target - output

        # Wenn ein Fehler vorliegt, dann weise die Gewichte und den Bias an
        if error != 0:
            all_correct = False
            current_weights += learning_rate * error * np.array(input_vector)  # Update weights
            bias += learning_rate * error  # Update bias

        epoch_outputs.append((input_vector, output, target))  # Save each iteration's output

    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
        final_bias = bias  # Speichere den finalen Bias
        break  # Stoppe, wenn alle Ausgaben korrekt sind

    # Wenn XOR nach 100 Iterationen nicht gelernt wurde, setze neue zufällige Startgewichte und Bias
    if epoch % 100 == 0:  # 100 statt 20
        print(f"Nicht funktionierende Startgewichte: {start_weights} und Bias: {start_bias}")
        current_weights = np.random.rand(2)  # Setze neue Startgewichte
        bias = np.random.rand()  # Setze neuen Bias

if network_trained:
    print(f"Das Netzwerk hat XOR korrekt nach {epoch} Iterationen gelernt.")
    print(f"Die Working Startgewichte waren: {start_weights} und Bias: {start_bias}")
    print(f"Die finalen Gewichte sind: {final_weights} und der finale Bias ist: {final_bias}")
else:
    print(f"Das Netzwerk hat XOR nach {epoch} Iterationen nicht korrekt gelernt.")

# Testen des Netzwerks nach den Lern-Iterationen
print("\nFinal Test Output:")
for input_vector, target in zip(inputs, targets):
    weighted_sum = np.dot(input_vector, final_weights) + final_bias
    output = 1 if lower_threshold < weighted_sum < upper_threshold else 0
    print(f"Input: {input_vector}, Target: {target}, Output: {output}")

# Optionally, print out the outputs of each epoch for transparency
print("\nEpoch Outputs:")
for epoch_index, epoch_outputs in enumerate(all_epoch_outputs):
    print(f"Epoch {epoch_index + 1}:")
    for input_vector, output, target in epoch_outputs:
        print(f"  Input: {input_vector}, Output: {output}, Target: {target}")
