updated to me random projects
This commit is contained in:
parent
b96c0a2973
commit
db5400fcaa
235
tragectory-prediction/main.py
Normal file
235
tragectory-prediction/main.py
Normal file
@ -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()
|
Loading…
Reference in New Issue
Block a user