import random
import numpy as np
from tensorflow import keras
from sklearn.preprocessing import MinMaxScaler
from tensorflow.keras.optimizers import Adam
from collections import deque

# … (Your preceding code, including the imports, remains unchanged.)

# Corrected normalization for target and numbers separately
numbers_scale = (1, 100)
scaler = MinMaxScaler()
scaler.fit(np.array([[numbers_scale[0]] * 7, [numbers_scale[1]] * 7]))  # Adjusted to normalize only numbers


# … (The rest of your unchanged code until play_and_learn function.)

def play_and_learn(iters=1000, batch_size=32):
    for _ in range(iters):
        # Initialize game
        target, numbers = generate_game()
        done = False
        steps = 0
        max_steps = 5  # You should define some stopping criterion, e.g., max number of operations

        while not done:
            # Prepare the state
            state = np.array([[target] + numbers])
            state_scaled = scaler.transform(state)

            # Predict the best operation
            action_values = model.predict(state_scaled)
            action = np.argmax(action_values[0])

            # Choose two numbers to perform the operation on
            num1, num2 = random.sample(numbers, 2)
            if action == 3 and num2 == 0:  # Avoid division by zero
                continue

            # Apply the chosen operation
            result = operations[action](num1, num2)
            if action == 3 and not np.isfinite(result):  # Avoiding infinite results due to division
                continue
            reward = -abs(target - result)  # Reward is better as negative distance from the target

            # Check if game is done
            steps += 1
            if steps >= max_steps or result == target:
                done = True

            # Update numbers to reflect the operation
            numbers.remove(num1)
            numbers.remove(num2)
            numbers.append(result)  # Append the result back to the list of numbers

            # Prepare next_state
            next_state = np.array([[target] + numbers])
            next_state_scaled = scaler.transform(next_state)

            # Store the experience
            store_experience(state_scaled[0], action, reward, next_state_scaled[0], done)

            # Sample a minibatch from the memory
            if len(memory_buffer) >= batch_size:
                minibatch_states, minibatch_actions, minibatch_rewards, minibatch_next_states, minibatch_dones = sample_from_memory(
                    batch_size)
                # Compute Q values for minibatch
                Q_values_next = model.predict_on_batch(minibatch_next_states)
                Q_targets = minibatch_rewards + (np.amax(Q_values_next, axis=1) * (1 - minibatch_dones))
                # Update the Q values for the actions that were taken
                Q_values = model.predict_on_batch(minibatch_states)
                for i, action in enumerate(minibatch_actions):
                    Q_values[i, action] = Q_targets[i]
                # Train the model on the minibatch
                model.train_on_batch(minibatch_states, Q_values)

    print(“Training
    Completed”)

    # Call the function without recursion
    play_and_learn()