import numpy as np
from itertools import product

# Parameters
lower_threshold = 0.8
upper_threshold = 1.2
learning_rate = 0.1
max_iterations = 500
bias_increment = 0.1
min_bias, max_bias = -0.9, 0.9

# XOR input data
inputs = np.array([[0, 0], [0, 1], [1, 0], [1, 1]])

# Generate all possible target tables
all_possible_targets = list(product([0, 1], repeat=4))

def train_network(inputs, targets, max_iterations, bias, weight_range=(-2, 2)):
    """Train network with a specific bias and weights range"""
    weights = np.random.uniform(*weight_range, size=2)
    epoch = 0

    while epoch < max_iterations:
        epoch += 1
        all_correct = True
        # Vectorized error calculation and weight updates
        for input_vector, target in zip(inputs, targets):
            weighted_sum = np.dot(input_vector, weights) + bias
            output = 1 if lower_threshold < weighted_sum < upper_threshold else 0
            error = target - output
            if error != 0:
                all_correct = False
                weights += learning_rate * error * input_vector
        if all_correct:
            return weights, epoch, True
    return weights, epoch, False

# Loop through all target tables and train
for table_index, targets in enumerate(all_possible_targets, start=1):
    print(f"\n=== Wahrheitstabelle {table_index}: Targets = {targets} ===")
    
    # Training without bias adjustment
    network_trained = False
    for bias in np.arange(0.1, max_bias + bias_increment, bias_increment):  # Start with bias incrementing upwards
        print(f"Attempt with bias {bias}:")
        final_weights, epoch, network_trained = train_network(inputs, targets, max_iterations, bias)
        if network_trained:
            print(f"Network trained in {epoch} epochs. Start weights: {final_weights}")
            break
    
    # If not trained, try with decreasing bias
    if not network_trained:
        print("Increasing bias until max, then try decreasing.")
        for bias in np.arange(max_bias, min_bias - bias_increment, -bias_increment):
            print(f"Attempt with bias {bias}:")
            final_weights, epoch, network_trained = train_network(inputs, targets, max_iterations, bias)
            if network_trained:
                print(f"Network trained in {epoch} epochs. Start weights: {final_weights}")
                break

    # If network still not trained, print the status
    if not network_trained:
        print(f"Network failed to train for truth table {table_index}.")
    else:
        print(f"Successfully trained network for table {table_index}. Final weights: {final_weights}")
