fix this bug: line 407
    global wall_height
    ^^^^^^^^^^^^^^^^^^
SyntaxError: name 'wall_height' is assigned to before global declaration

here is the code:import pygame
import math
import sys

# Initialize Pygame
pygame.init()

# Set up the screen dimensions
SCREEN_WIDTH = 1300   # Increased width to accommodate full y-range
SCREEN_HEIGHT = 800  # Total height, including the UI area
SIM_HEIGHT = 650     # Height of the simulation area (excluding UI area)
UI_HEIGHT = SCREEN_HEIGHT - SIM_HEIGHT

# Set up the display
screen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT))
pygame.display.set_caption("Ball Trajectory Simulation with Adjustable Wall Height")

# Fonts
font_small = pygame.font.SysFont(None, 24)
font_large = pygame.font.SysFont(None, 36)

# Define colors
WHITE = (255, 255, 255)
BLACK = (0, 0, 0)
GRAY = (230, 230, 230)
DARK_GRAY = (100, 100, 100)
RED = (200, 0, 0)
BLUE = (0, 0, 200)
GREEN = (0, 150, 0)
LIGHT_BLUE = (173, 216, 230)
BROWN = (139, 69, 19)

# Physics constants
GRAVITY = -650  # Acceleration in z-direction
BALL_RADIUS = 96.3881  # Ball radius in units
BALL_MAX_SPEED = 6000
MAX_Y = 3581
MIN_Y = -3581
MAX_Z = 1820  # Ceiling height

# Wall properties
WALL_Y_POSITION = 1474  # Default wall position
DEFAULT_WALL_HEIGHT = 400  # Default wall height
wall_height = DEFAULT_WALL_HEIGHT  # Current wall height

# World dimensions
WORLD_MIN_Y = MIN_Y
WORLD_MAX_Y = MAX_Y
WORLD_MIN_Z = 0
WORLD_MAX_Z = MAX_Z

# Compute scaling factors
SCALE_Y = SCREEN_WIDTH / (WORLD_MAX_Y - WORLD_MIN_Y)  # Pixels per unit y
SCALE_Z = SIM_HEIGHT / (WORLD_MAX_Z - WORLD_MIN_Z)  # Pixels per unit z

# Map world coordinates (y,z) to screen coordinates (x,y)
def world_to_screen(y_world, z_world):
    x_screen = (y_world - WORLD_MIN_Y) * SCALE_Y
    y_screen = SIM_HEIGHT - (z_world - WORLD_MIN_Z) * SCALE_Z
    return int(x_screen), int(y_screen)

# Map screen coordinates back to world coordinates
def screen_to_world(x_screen, y_screen):
    y_world = x_screen / SCALE_Y + WORLD_MIN_Y
    z_world = (SIM_HEIGHT - y_screen) / SCALE_Z + WORLD_MIN_Z
    return y_world, z_world

# Function to predict if the ball will make it over the wall
def will_ball_make_it_over_wall(y0, z0, Vy0, Vz0):
    # Constants
    a = GRAVITY  # gravity
    r = BALL_RADIUS  # ball radius
    y_wall = WALL_Y_POSITION  # wall position
    h_wall = wall_height  # current wall height

    # Compute time of flight until the ball hits the ground (z = r)
    A = 0.5 * a
    B = Vz0
    C = z0 - r  # Adjusted for when the lower edge of the ball hits the ground
    D = B**2 - 4 * A * C  # Discriminant
    if D < 0:
        return False  # No real solutions; the ball doesn't hit the ground
    sqrt_D = math.sqrt(D)
    t1 = (-B + sqrt_D) / (2 * A)
    t2 = (-B - sqrt_D) / (2 * A)
    t_land_candidates = [t for t in [t1, t2] if t >= 0]
    if not t_land_candidates:
        return False  # No valid landing time
    t_land = min(t_land_candidates)

    # If the ball starts beyond the wall and is moving away, it has already made it
    if (Vy0 > 0 and y0 - r >= y_wall) or (Vy0 < 0 and y0 + r <= y_wall):
        return True

    # If the ball is moving parallel to the wall, we need to check if it's already on the other side
    if Vy0 == 0:
        if y0 + r <= y_wall:
            return False
        elif y0 - r >= y_wall:
            return True
        else:
            # Ball is touching the wall, check if it's above the wall height
            if z0 - r > h_wall:
                return True
            else:
                return False

    # Time when the ball reaches the wall horizontally
    delta_y = y_wall - y0 - (r if Vy0 > 0 else -r)
    t_wall = delta_y / Vy0

    if t_wall < 0 or t_wall > t_land:
        # The ball doesn't reach the wall before landing
        return False

    # Vertical position at time t_wall
    z_wall = z0 + Vz0 * t_wall + 0.5 * a * t_wall ** 2
    z_lower = z_wall - r
    if z_lower > h_wall:
        return True
    else:
        return False

# Function to compute the predicted trajectory points
def compute_trajectory(y0, z0, Vy0, Vz0, time_step=0.05, max_time=10):
    trajectory = []
    time = 0.0
    y = y0
    z = z0
    Vy = Vy0
    Vz = Vz0
    in_air = True
    while time <= max_time and in_air:
        # Save position
        trajectory.append((y, z))

        # Update positions and velocities
        y += Vy * time_step
        z += Vz * time_step + 0.5 * GRAVITY * time_step ** 2
        Vz += GRAVITY * time_step

        # Handle bouncing from the ceiling
        if z + BALL_RADIUS > MAX_Z:
            z = 2*(MAX_Z - BALL_RADIUS) - z
            Vz *= -1

        # Handle bouncing from side boundaries
        if y + BALL_RADIUS > WORLD_MAX_Y:
            y = 2*(WORLD_MAX_Y - BALL_RADIUS) - y
            Vy *= -1
        elif y - BALL_RADIUS < WORLD_MIN_Y:
            y = 2*(WORLD_MIN_Y + BALL_RADIUS) - y
            Vy *= -1

        # Handle collision with ground
        if z - BALL_RADIUS <= 0:
            z = BALL_RADIUS
            Vz = 0
            Vy = 0
            in_air = False  # Ball stops moving upon hitting the ground

        # Check collision with wall
        y_wall = WALL_Y_POSITION
        h_wall = wall_height
        collides_with_wall = False
        if Vy != 0:
            if Vy > 0 and y + BALL_RADIUS >= y_wall >= y - BALL_RADIUS:
                # Moving towards positive y and crossing wall
                z_lower = z - BALL_RADIUS
                if z_lower <= h_wall:
                    # Collision occurs
                    collides_with_wall = True
                    y = 2 * (y_wall - BALL_RADIUS) - y
                    Vy *= -1
            elif Vy < 0 and y - BALL_RADIUS <= y_wall <= y + BALL_RADIUS:
                # Moving towards negative y and crossing wall
                z_lower = z - BALL_RADIUS
                if z_lower <= h_wall:
                    # Collision occurs
                    collides_with_wall = True
                    y = 2 * (y_wall + BALL_RADIUS) - y
                    Vy *= -1

        time += time_step

    return trajectory

class Ball:
    def __init__(self, y, z):
        self.y = y
        self.z = z
        self.Vy = 0.0
        self.Vz = 0.0
        self.radius = BALL_RADIUS

        self.initial_y = y
        self.initial_z = z
        self.initial_Vy = self.Vy
        self.initial_Vz = self.Vz

        self.in_motion = False
        self.time_elapsed = 0.0

    def reset(self):
        self.y = self.initial_y
        self.z = self.initial_z
        self.Vy = self.initial_Vy
        self.Vz = self.initial_Vz
        self.in_motion = False
        self.time_elapsed = 0.0

    def update(self, dt):
        if not self.in_motion:
            return

        # Update time
        self.time_elapsed += dt

        # Save previous position
        prev_y = self.y
        prev_z = self.z

        # Update positions using equations of motion
        self.y += self.Vy * dt
        self.z += self.Vz * dt + 0.5 * GRAVITY * dt**2

        # Update velocities
        self.Vz += GRAVITY * dt

        # Handle bouncing from the ceiling
        if self.z + self.radius > MAX_Z:
            self.z = 2*(MAX_Z - self.radius) - self.z
            self.Vz *= -1

        # Handle bouncing from side boundaries
        if self.y + self.radius > WORLD_MAX_Y:
            self.y = 2*(WORLD_MAX_Y - self.radius) - self.y
            self.Vy *= -1
        elif self.y - self.radius < WORLD_MIN_Y:
            self.y = 2*(WORLD_MIN_Y + self.radius) - self.y
            self.Vy *= -1

        # Handle collision with ground (ball stops moving)
        if self.z - self.radius <= 0:
            self.z = self.radius
            self.Vz = 0
            self.Vy = 0
            self.in_motion = False  # Ball stops moving upon hitting the ground

        # Check collision with wall
        y_wall = WALL_Y_POSITION
        h_wall = wall_height
        collides_with_wall = False
        if self.Vy != 0:
            if self.Vy > 0 and prev_y + self.radius <= y_wall and self.y + self.radius >= y_wall:
                # Moving towards positive y
                t_collision = (y_wall - self.radius - prev_y) / self.Vy
                z_collision = prev_z + self.Vz * t_collision + 0.5 * GRAVITY * t_collision ** 2
                z_lower = z_collision - self.radius
                if z_lower <= h_wall:
                    # Collision occurs
                    self.y = y_wall - self.radius
                    self.z = z_collision + self.Vz * (dt - t_collision) + 0.5 * GRAVITY * (dt - t_collision) ** 2
                    self.Vz += GRAVITY * (dt - t_collision)
                    self.Vy *= -1
            elif self.Vy < 0 and prev_y - self.radius >= y_wall and self.y - self.radius <= y_wall:
                # Moving towards negative y
                t_collision = (y_wall + self.radius - prev_y) / self.Vy
                z_collision = prev_z + self.Vz * t_collision + 0.5 * GRAVITY * t_collision ** 2
                z_lower = z_collision - self.radius
                if z_lower <= h_wall:
                    # Collision occurs
                    self.y = y_wall + self.radius
                    self.z = z_collision + self.Vz * (dt - t_collision) + 0.5 * GRAVITY * (dt - t_collision) ** 2
                    self.Vz += GRAVITY * (dt - t_collision)
                    self.Vy *= -1

    def draw(self, surface):
        x_screen, y_screen = world_to_screen(self.y, self.z)
        radius_screen = int(self.radius * SCALE_Z)
        pygame.draw.circle(surface, RED, (x_screen, y_screen), radius_screen)

    def set_position(self, y, z):
        # Prevent the ball from intersecting with the walls, ceiling, and ground
        y_wall = WALL_Y_POSITION

        # Side boundaries
        if y + self.radius > WORLD_MAX_Y:
            y = WORLD_MAX_Y - self.radius
        elif y - self.radius < WORLD_MIN_Y:
            y = WORLD_MIN_Y + self.radius

        # Wall
        if y + self.radius > y_wall and y - self.radius < y_wall:
            y = y_wall - self.radius if y < y_wall else y_wall + self.radius

        # Ceiling and ground
        z = max(min(z, MAX_Z - self.radius), self.radius)

        self.y = y
        self.z = z
        self.initial_y = self.y
        self.initial_z = self.z

    def set_velocity(self, V_total, angle_degrees):
        angle_radians = math.radians(angle_degrees)
        self.Vy = V_total * math.cos(angle_radians)
        self.Vz = V_total * math.sin(angle_radians)
        self.initial_Vy = self.Vy
        self.initial_Vz = self.Vz

# Slider class for UI
class Slider:
    def __init__(self, x, y, width, min_val, max_val, initial_val, label):
        self.rect = pygame.Rect(x, y, width, 20)
        self.min_val = min_val
        self.max_val = max_val
        self.val = initial_val
        self.label = label
        self.dragging = False

    def handle_event(self, event):
        if event.type == pygame.MOUSEBUTTONDOWN:
            if self.rect.collidepoint(event.pos):
                self.dragging = True
                self.update_value(event.pos)
        elif event.type == pygame.MOUSEBUTTONUP:
            self.dragging = False
        elif event.type == pygame.MOUSEMOTION:
            if self.dragging:
                self.update_value(event.pos)

    def update_value(self, pos):
        x = pos[0]
        ratio = (x - self.rect.x) / self.rect.width
        ratio = max(0, min(1, ratio))
        self.val = self.min_val + ratio * (self.max_val - self.min_val)

    def draw(self, surface):
        # Draw the slider background
        pygame.draw.rect(surface, DARK_GRAY, self.rect)
        # Draw the slider handle
        handle_x = self.rect.x + ((self.val - self.min_val) / (self.max_val - self.min_val)) * self.rect.width
        pygame.draw.rect(surface, BLUE, (handle_x - 5, self.rect.y - 5, 10, self.rect.height + 10))
        # Draw the label
        label_text = font_small.render(f"{self.label}: {self.val:.1f}", True, BLACK)
        surface.blit(label_text, (self.rect.x, self.rect.y - 25))

# Main loop
running = True
clock = pygame.time.Clock()

ball = Ball(0.0, BALL_RADIUS)  # Start ball at y=0, z=radius
moving_ball = False
trajectory_points = []

# Sliders for initial velocity, angle, and wall height
slider_speed = Slider(20, SIM_HEIGHT + 60, 200, 0, BALL_MAX_SPEED, 1000, "Initial Speed")
slider_angle = Slider(20, SIM_HEIGHT + 120, 200, -180, 180, 45, "Launch Angle")
slider_wall_height = Slider(20, SIM_HEIGHT + 180, 200, 0, MAX_Z, DEFAULT_WALL_HEIGHT, "Wall Height")

# Define buttons
button_width = 120
button_height = 40
button_margin = 20
button_y = SIM_HEIGHT + 10

launch_button_rect = pygame.Rect(240, button_y, button_width, button_height)
reset_ball_button_rect = pygame.Rect(240 + button_width + button_margin, button_y, button_width, button_height)
reset_wall_button_rect = pygame.Rect(240 + 2 * (button_width + button_margin), button_y, button_width, button_height)

# Function to draw the trajectory
def draw_trajectory(surface, trajectory):
    if len(trajectory) < 2:
        return
    points = [world_to_screen(y, z) for y, z in trajectory]
    pygame.draw.lines(surface, LIGHT_BLUE, False, points, 2)

while running:
    dt = clock.tick(60) / 1000.0  # Delta time in seconds
    sliders_changed = False  # Flag to track if sliders were adjusted

    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            running = False
            break

        # Handle mouse events
        elif event.type == pygame.MOUSEBUTTONDOWN:
            mouse_x, mouse_y = event.pos

            if launch_button_rect.collidepoint(mouse_x, mouse_y):
                # Clicked on Launch button
                ball.set_velocity(slider_speed.val, slider_angle.val)
                ball.in_motion = True
                ball.time_elapsed = 0.0
                ball.y = ball.initial_y
                ball.z = ball.initial_z
                ball.Vy = ball.initial_Vy
                ball.Vz = ball.initial_Vz
            elif reset_ball_button_rect.collidepoint(mouse_x, mouse_y):
                # Clicked on Reset Ball button
                ball.reset()
                trajectory_points = []
            elif reset_wall_button_rect.collidepoint(mouse_x, mouse_y):
                # Clicked on Reset Wall button
                global wall_height
                wall_height = DEFAULT_WALL_HEIGHT
                slider_wall_height.val = DEFAULT_WALL_HEIGHT
                trajectory_points = []
            else:
                # Check if click is on the ball
                x_ball_screen, y_ball_screen = world_to_screen(ball.y, ball.z)
                distance = math.hypot(mouse_x - x_ball_screen, mouse_y - y_ball_screen)
                if distance <= ball.radius * SCALE_Z:
                    if event.button == 1:
                        moving_ball = True
                else:
                    moving_ball = False

        elif event.type == pygame.MOUSEBUTTONUP:
            if event.button == 1 and moving_ball:
                moving_ball = False

        elif event.type == pygame.MOUSEMOTION:
            if moving_ball:
                mouse_x, mouse_y = event.pos
                y_world, z_world = screen_to_world(mouse_x, mouse_y)
                ball.set_position(y_world, z_world)
                trajectory_points = []
            else:
                sliders_changed = True  # Assume any mouse motion could be adjusting sliders

        # Handle events for sliders
        slider_speed.handle_event(event)
        slider_angle.handle_event(event)
        slider_wall_height.handle_event(event)
        sliders_changed = sliders_changed or slider_speed.dragging or slider_angle.dragging or slider_wall_height.dragging

    # Update wall height from slider
    wall_height = slider_wall_height.val

    # Update the ball
    if ball.in_motion:
        ball.update(dt)

    # Update trajectory if sliders changed or ball position changed
    if sliders_changed or moving_ball:
        ball.set_velocity(slider_speed.val, slider_angle.val)
        trajectory_points = compute_trajectory(ball.initial_y, ball.initial_z, ball.initial_Vy, ball.initial_Vz)

    # Determine prediction result
    prediction_result = will_ball_make_it_over_wall(ball.initial_y, ball.initial_z, ball.initial_Vy, ball.initial_Vz)

    # Draw everything
    screen.fill(WHITE)

    # Draw the simulation area background
    pygame.draw.rect(screen, GRAY, (0, 0, SCREEN_WIDTH, SIM_HEIGHT))

    # Draw the walls, ceiling, and ground as rectangles
    # Ground
    ground_rect = pygame.Rect(0, SIM_HEIGHT - 5, SCREEN_WIDTH, 5)
    pygame.draw.rect(screen, BROWN, ground_rect)

    # Ceiling
    ceiling_rect = pygame.Rect(0, 0, SCREEN_WIDTH, 5)
    pygame.draw.rect(screen, DARK_GRAY, ceiling_rect)

    # Side boundaries
    left_wall_rect = pygame.Rect(0, 0, 5, SIM_HEIGHT)
    right_wall_rect = pygame.Rect(SCREEN_WIDTH - 5, 0, 5, SIM_HEIGHT)
    pygame.draw.rect(screen, DARK_GRAY, left_wall_rect)
    pygame.draw.rect(screen, DARK_GRAY, right_wall_rect)

    # Wall
    y_wall_world = WALL_Y_POSITION
    h_wall = wall_height
    x_wall_screen, _ = world_to_screen(y_wall_world, 0)
    _, wall_top_screen_y = world_to_screen(0, h_wall)
    wall_rect = pygame.Rect(x_wall_screen - 10, wall_top_screen_y, 20, SIM_HEIGHT - wall_top_screen_y)
    pygame.draw.rect(screen, DARK_GRAY, wall_rect)

    # Draw the predicted trajectory
    if trajectory_points:
        draw_trajectory(screen, trajectory_points)

    # Draw the ball
    ball.draw(screen)

    # Draw the UI area (buttons and text)
    pygame.draw.rect(screen, WHITE, (0, SIM_HEIGHT, SCREEN_WIDTH, UI_HEIGHT))

    # Draw buttons
    pygame.draw.rect(screen, GREEN, launch_button_rect)
    pygame.draw.rect(screen, GREEN, reset_ball_button_rect)
    pygame.draw.rect(screen, GREEN, reset_wall_button_rect)

    # Draw button labels
    launch_text = font_large.render('Launch', True, BLACK)
    reset_ball_text = font_large.render('Reset Ball', True, BLACK)
    reset_wall_text = font_large.render('Reset Wall', True, BLACK)
    screen.blit(launch_text, (launch_button_rect.x + 10, launch_button_rect.y + 5))
    screen.blit(reset_ball_text, (reset_ball_button_rect.x + 10, reset_ball_button_rect.y + 5))
    screen.blit(reset_wall_text, (reset_wall_button_rect.x + 10, reset_wall_button_rect.y + 5))

    # Draw sliders
    slider_speed.draw(screen)
    slider_angle.draw(screen)
    slider_wall_height.draw(screen)

    # Display ball information
    info_x = 240 + 3 * (button_width + button_margin)
    info_y = SIM_HEIGHT + 10
    info_lines = [
        f"Initial Position: y = {ball.initial_y:.2f}, z = {ball.initial_z:.2f}",
        f"Current Position: y = {ball.y:.2f}, z = {ball.z:.2f}",
        f"Initial Velocity: Vy = {ball.initial_Vy:.2f}, Vz = {ball.initial_Vz:.2f}",
        f"Current Velocity: Vy = {ball.Vy:.2f}, Vz = {ball.Vz:.2f}",
        f"Wall Height: {wall_height:.2f}"
    ]
    for i, line in enumerate(info_lines):
        text_surface = font_small.render(line, True, BLACK)
        screen.blit(text_surface, (info_x, info_y + i * 20))

    # Display prediction result
    prediction_text = "Prediction: Ball WILL make it over the wall" if prediction_result else "Prediction: Ball will NOT make it over the wall"
    text_surface = font_large.render(prediction_text, True, RED if not prediction_result else GREEN)
    screen.blit(text_surface, (info_x, SIM_HEIGHT + 130))

    pygame.display.update()

pygame.quit()