diff --git a/compialer.py b/python-c-compialer/compialer.py similarity index 100% rename from compialer.py rename to python-c-compialer/compialer.py diff --git a/main.c b/python-c-compialer/main.c similarity index 100% rename from main.c rename to python-c-compialer/main.c diff --git a/runtime.py b/python-c-compialer/runtime.py similarity index 100% rename from runtime.py rename to python-c-compialer/runtime.py diff --git a/std.h b/python-c-compialer/std.h similarity index 100% rename from std.h rename to python-c-compialer/std.h diff --git a/tragectory-prediction/main.py b/tragectory-prediction/main.py new file mode 100644 index 0000000..7240d31 --- /dev/null +++ b/tragectory-prediction/main.py @@ -0,0 +1,235 @@ +import pygame +import math +import random + +# Initialize PyGame and create window +pygame.init() +width, height = 800, 600 +screen = pygame.display.set_mode((width, height)) +pygame.display.set_caption("Drag to Shoot with Prediction & Trails") +clock = pygame.time.Clock() + +# Constants +GRAVITY = 300 # pixels per second^2 +VELOCITY_SCALE = 0.8 # multiplier for the drag-to-velocity conversion (a stronger shot) +PREDICTION_TIME = 3.0 # seconds to simulate the predicted trajectory +PREDICTION_DT = 0.02 # simulation time step for prediction +BALL_RADIUS = 8 # radius for each ball +PREDICTION_DISPLAY_TIME = 5.0 # seconds to display the prediction after launch +MAX_TRAIL_LENGTH = PREDICTION_DISPLAY_TIME * VELOCITY_SCALE * PREDICTION_TIME * 1000 # maximum number of positions stored for ball trail + +# List to hold active projectiles (each is a dict with position, velocity, lifetime, and a trail) +projectiles = [] + +# For storing the last prediction (list of positions) and a timer to display it after shot +last_prediction = None +prediction_timer = 0 + +# ----------------------- +# Collision Handling Code +# ----------------------- + +def handle_collisions(balls): + """ + For each pair of balls in the list, check if they overlap. + If so, separate them and update their velocities using a simple + elastic collision formula (assuming equal masses). + """ + for i in range(len(balls)): + for j in range(i + 1, len(balls)): + ball1 = balls[i] + ball2 = balls[j] + dx = ball1["pos"][0] - ball2["pos"][0] + dy = ball1["pos"][1] - ball2["pos"][1] + dist_sq = dx * dx + dy * dy + min_dist = ball1["radius"] + ball2["radius"] + if dist_sq < min_dist * min_dist: + dist = math.sqrt(dist_sq) if dist_sq != 0 else 0.1 + # Calculate overlap amount + overlap = 0.5 * (min_dist - dist) + nx = dx / dist + ny = dy / dist + # Separate the balls so they just touch + ball1["pos"][0] += nx * overlap + ball1["pos"][1] += ny * overlap + ball2["pos"][0] -= nx * overlap + ball2["pos"][1] -= ny * overlap + # Relative velocity in normal direction + dvx = ball1["vel"][0] - ball2["vel"][0] + dvy = ball1["vel"][1] - ball2["vel"][1] + dot = dvx * nx + dvy * ny + if dot < 0: + # Adjust velocities for a simple elastic collision + ball1["vel"][0] -= dot * nx + ball1["vel"][1] -= dot * ny + ball2["vel"][0] += dot * nx + ball2["vel"][1] += dot * ny + +# ----------------------- +# Prediction Simulation +# ----------------------- + +def simulate_prediction(initial_pos, initial_vel, other_balls, prediction_time=PREDICTION_TIME, dt_sim=PREDICTION_DT): + """ + Simulate the predicted path of a new ball starting at 'initial_pos' + with initial velocity 'initial_vel'. The simulation takes into account + gravity, wall bounces (using the ball radius), and collisions with other balls. + Returns a list of (x, y) positions for the predicted ball. + """ + # Create a copy for the predicted ball (with the same BALL_RADIUS) + predicted_ball = {"pos": [initial_pos[0], initial_pos[1]], + "vel": [initial_vel[0], initial_vel[1]], + "radius": BALL_RADIUS} + # Copy the other balls from the current simulation (so as not to modify them) + predicted_others = [] + for ball in other_balls: + new_ball = {"pos": [ball["pos"][0], ball["pos"][1]], + "vel": [ball["vel"][0], ball["vel"][1]], + "radius": ball.get("radius", BALL_RADIUS)} + predicted_others.append(new_ball) + balls = [predicted_ball] + predicted_others + + positions = [] + steps = int(prediction_time / dt_sim) + for _ in range(steps): + # Update every ball in simulation + for ball in balls: + ball["vel"][1] += GRAVITY * dt_sim + ball["pos"][0] += ball["vel"][0] * dt_sim + ball["pos"][1] += ball["vel"][1] * dt_sim + # Bounce off left/right walls (accounting for ball radius) + if ball["pos"][0] < ball["radius"]: + ball["pos"][0] = ball["radius"] + ball["vel"][0] = -ball["vel"][0] + elif ball["pos"][0] > width - ball["radius"]: + ball["pos"][0] = width - ball["radius"] + ball["vel"][0] = -ball["vel"][0] + # Bounce off top/bottom walls + if ball["pos"][1] < ball["radius"]: + ball["pos"][1] = ball["radius"] + ball["vel"][1] = -ball["vel"][1] + elif ball["pos"][1] > height - ball["radius"]: + ball["pos"][1] = height - ball["radius"] + ball["vel"][1] = -ball["vel"][1] + # Handle collisions among balls + handle_collisions(balls) + # Record the predicted ball's position + positions.append((int(predicted_ball["pos"][0]), int(predicted_ball["pos"][1]))) + return positions + +# ----------------------- +# Main Simulation Loop +# ----------------------- + +# Variables for the drag-to-aim mechanic +dragging = False +start_pos = (0, 0) +current_pos = (0, 0) + +running = True +while running: + dt = clock.tick(60) / 1000.0 # delta time in seconds + + for event in pygame.event.get(): + if event.type == pygame.QUIT: + running = False + + # Start dragging + elif event.type == pygame.MOUSEBUTTONDOWN and event.button == 1: + dragging = True + start_pos = event.pos + current_pos = event.pos + + # Update drag position while holding down the mouse + elif event.type == pygame.MOUSEMOTION and dragging: + current_pos = event.pos + + # On mouse release, launch a new ball and compute its prediction. + elif event.type == pygame.MOUSEBUTTONUP and event.button == 1: + if dragging: + # Compute initial velocity (invert drag vector so dragging backward shoots forward) + v0 = ((start_pos[0] - current_pos[0]) * VELOCITY_SCALE, + (start_pos[1] - current_pos[1]) * VELOCITY_SCALE) + # Compute and store the prediction simulation (will be displayed for a short time) + last_prediction = simulate_prediction(start_pos, v0, projectiles) + prediction_timer = PREDICTION_DISPLAY_TIME + # Create a new ball with an initial empty trail + new_ball = {"pos": [start_pos[0], start_pos[1]], + "vel": [v0[0], v0[1]], + "radius": BALL_RADIUS, + "lifetime": random.uniform(3, 5), + "trail": []} + projectiles.append(new_ball) + dragging = False + + # Update active projectiles (balls) + for ball in projectiles: + # Append current position to the trail + ball["trail"].append((ball["pos"][0], ball["pos"][1])) + if len(ball["trail"]) > MAX_TRAIL_LENGTH: + ball["trail"].pop(0) + + # Update velocity (apply gravity) and position + ball["vel"][1] += GRAVITY * dt + ball["pos"][0] += ball["vel"][0] * dt + ball["pos"][1] += ball["vel"][1] * dt + + # Bounce off the walls, accounting for ball radius + if ball["pos"][0] < ball["radius"]: + ball["pos"][0] = ball["radius"] + ball["vel"][0] = -ball["vel"][0] + elif ball["pos"][0] > width - ball["radius"]: + ball["pos"][0] = width - ball["radius"] + ball["vel"][0] = -ball["vel"][0] + if ball["pos"][1] < ball["radius"]: + ball["pos"][1] = ball["radius"] + ball["vel"][1] = -ball["vel"][1] + elif ball["pos"][1] > height - ball["radius"]: + ball["pos"][1] = height - ball["radius"] + ball["vel"][1] = -ball["vel"][1] + + # Decrease lifetime (ball will vanish after its lifetime expires) + ball["lifetime"] -= dt + + # Remove expired balls + projectiles = [ball for ball in projectiles if ball["lifetime"] > 0] + + # Handle collisions among active projectiles + handle_collisions(projectiles) + + # Decrease the prediction display timer + if prediction_timer > 0: + prediction_timer -= dt + if prediction_timer <= 0: + last_prediction = None + + # ----------------------- + # Drawing Section + # ----------------------- + screen.fill((255, 255, 255)) + + # While dragging, draw drag line and live predicted trajectory + if dragging: + pygame.draw.line(screen, (0, 0, 0), start_pos, current_pos, 2) + v0 = ((start_pos[0] - current_pos[0]) * VELOCITY_SCALE, + (start_pos[1] - current_pos[1]) * VELOCITY_SCALE) + live_prediction = simulate_prediction(start_pos, v0, projectiles) + if len(live_prediction) > 1: + pygame.draw.lines(screen, (0, 0, 255), False, live_prediction, 2) + + # Draw the stored prediction (after shot) if available + if last_prediction is not None: + pygame.draw.lines(screen, (128, 0, 128), False, last_prediction, 2) + + # Draw projectiles and their trails + for ball in projectiles: + # Draw the trail as a line (if there are at least 2 points) + if len(ball["trail"]) > 1: + pygame.draw.lines(screen, (150, 150, 150), False, ball["trail"], 2) + # Draw the ball as a filled circle + pos = (int(ball["pos"][0]), int(ball["pos"][1])) + pygame.draw.circle(screen, (255, 0, 0), pos, ball["radius"]) + + pygame.display.flip() + +pygame.quit()