Created
January 22, 2025 11:15
-
-
Save ivanfioravanti/6f793bc4577c16485c182057f6d78b8f to your computer and use it in GitHub Desktop.
o1-pro vs r1 rotating square with ball inside
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import pygame | |
import math | |
import sys | |
pygame.init() | |
# Screen setup | |
WIDTH, HEIGHT = 800, 600 | |
screen = pygame.display.set_mode((WIDTH, HEIGHT)) | |
pygame.display.set_caption("Bouncing Ball in Rotating Square") | |
clock = pygame.time.Clock() | |
FPS = 60 | |
# Colors | |
BLACK = (0, 0, 0) | |
YELLOW = (255, 255, 0) | |
WHITE = (255, 255, 255) | |
# Square parameters | |
square_size = 300 # length of one side | |
half_size = square_size / 2 | |
square_center = (WIDTH // 2, HEIGHT // 2) | |
rotation_angle = 0.0 # current rotation of the square | |
rotation_speed = 0.01 # radians per frame (slow rotation) | |
# Ball parameters | |
ball_radius = 15 | |
ball_pos = [WIDTH // 2, HEIGHT // 2 - 100] # start a bit above center | |
ball_vel = [3, 2] # initial velocity in x, y | |
# Helper function to rotate a point (px, py) around (ox, oy) by angle | |
def rotate_point(px, py, ox, oy, angle): | |
# Translate point to origin | |
tx, ty = px - ox, py - oy | |
# Rotate around origin | |
rx = tx * math.cos(angle) - ty * math.sin(angle) | |
ry = tx * math.sin(angle) + ty * math.cos(angle) | |
# Translate back | |
return rx + ox, ry + oy | |
# Get corners of the square in local space (centered at origin for convenience) | |
# We'll draw the square from these corners after rotating them about (square_center) | |
local_corners = [ | |
(-half_size, -half_size), | |
( half_size, -half_size), | |
( half_size, half_size), | |
(-half_size, half_size) | |
] | |
def get_rotated_corners(cx, cy, angle): | |
""" Return the 4 corners of the square after rotation about (cx, cy). """ | |
corners = [] | |
for (lx, ly) in local_corners: | |
# Rotate each corner around the square's center | |
rx, ry = rotate_point(lx+cx, ly+cy, cx, cy, angle) | |
corners.append((rx, ry)) | |
return corners | |
def reflect_velocity_off_edge(ball_center, velocity, p1, p2): | |
""" | |
Given an edge p1->p2 and the ball center + velocity, | |
check if the ball is 'outside' the edge. If so, reflect | |
the velocity about the edge's normal, and push the ball inside. | |
""" | |
(bx, by) = ball_center | |
(x1, y1), (x2, y2) = p1, p2 | |
# Edge vector | |
ex, ey = (x2 - x1, y2 - y1) | |
# Outward normal (for a CCW polygon, rotate edge vector by +90 deg) | |
# ex, ey -> -ey, ex or ey, -ex depending on ordering | |
# For a typical square defined CCW, we can do one consistent pattern: | |
nx, ny = (ey, -ex) | |
# Length of normal (for normalization) | |
length_n = math.hypot(nx, ny) | |
if length_n == 0: | |
return # degenerate edge | |
# Normalize the outward normal | |
nx /= length_n | |
ny /= length_n | |
# Vector from p1 to ball center | |
vx = bx - x1 | |
vy = by - y1 | |
# Signed distance from line | |
# (dot with normal) | |
dist = vx * nx + vy * ny | |
# The line extends infinitely. We also want to check if the projection | |
# is "between" p1 and p2 (for large polygons, corner checks matter). | |
# But for a square, we can assume if dist < 0 or dist > edge length, it's not relevant. | |
# However, for a bounding approach, let's clamp the projection to the edge segment: | |
edge_len = math.hypot(ex, ey) | |
# Projection of (vx, vy) onto the edge direction | |
t = (vx * ex + vy * ey) / (edge_len * edge_len) | |
# If t < 0 or t > 1, the ball is "beyond" the edge segment | |
if t < 0 or t > 1: | |
return | |
# If the ball is outside the edge (dist > radius means inside for CCW), | |
# but for an outward normal, if dist < -ball_radius => outside | |
# This depends on how we define outward normal. Let's check sign carefully: | |
# If we used (ey, -ex) with corners in CCW, the inside is dist < 0. | |
# That means if dist < -ball_radius, the ball center is outside the polygon. | |
# We'll do that reflection: | |
if dist < -ball_radius: | |
# The ball has penetrated the edge. Let's reflect. | |
# Reflect the velocity across (nx, ny): | |
# velocity' = v - 2(v·n)*n | |
dot_vn = velocity[0] * nx + velocity[1] * ny | |
velocity[0] = velocity[0] - 2 * dot_vn * nx | |
velocity[1] = velocity[1] - 2 * dot_vn * ny | |
# Also push the ball just inside: | |
# If dist = - (ball_radius + overlap), we need to shift center by overlap along normal | |
overlap = -ball_radius - dist | |
ball_center[0] += overlap * nx | |
ball_center[1] += overlap * ny | |
def main(): | |
global rotation_angle | |
running = True | |
while running: | |
dt = clock.tick(FPS) / 1000.0 # time in seconds since last frame | |
for event in pygame.event.get(): | |
if event.type == pygame.QUIT: | |
running = False | |
# Update square rotation | |
rotation_angle += rotation_speed | |
# Update ball position | |
ball_pos[0] += ball_vel[0] | |
ball_pos[1] += ball_vel[1] | |
# Get current corners of the rotating square | |
corners = get_rotated_corners(square_center[0], square_center[1], rotation_angle) | |
# Build the edges list (4 edges) | |
edges = [] | |
for i in range(len(corners)): | |
p1 = corners[i] | |
p2 = corners[(i+1) % len(corners)] | |
edges.append((p1, p2)) | |
# Check collision with each edge | |
for (p1, p2) in edges: | |
reflect_velocity_off_edge(ball_pos, ball_vel, p1, p2) | |
# Rendering | |
screen.fill(BLACK) | |
# Draw rotating square (as a polygon or lines) | |
pygame.draw.polygon(screen, WHITE, corners, width=2) | |
# Draw the ball | |
pygame.draw.circle(screen, YELLOW, (int(ball_pos[0]), int(ball_pos[1])), ball_radius) | |
pygame.display.flip() | |
pygame.quit() | |
sys.exit() | |
if __name__ == "__main__": | |
main() |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import pygame | |
import math | |
# Initialize Pygame | |
pygame.init() | |
# Set up display | |
screen_width = 800 | |
screen_height = 600 | |
screen = pygame.display.set_mode((screen_width, screen_height)) | |
pygame.display.set_caption("Bouncing Ball in Rotating Square") | |
# Colors | |
BLACK = (0, 0, 0) | |
YELLOW = (255, 255, 0) | |
WHITE = (255, 255, 255) | |
# Square properties | |
square_size = 200 | |
square_center = (screen_width//2, screen_height//2) | |
square_border = 2 | |
# Ball properties | |
ball_radius = 10 | |
ball_pos = list(square_center) | |
ball_velocity = [3, 3] | |
# Rotation properties | |
angle = 0 | |
rotation_speed = 1 # Degrees per frame | |
# Create square surface | |
square_surface = pygame.Surface((square_size, square_size), pygame.SRCALPHA) | |
pygame.draw.rect(square_surface, WHITE, (0, 0, square_size, square_size), square_border) | |
# Main loop | |
clock = pygame.time.Clock() | |
running = True | |
while running: | |
for event in pygame.event.get(): | |
if event.type == pygame.QUIT: | |
running = False | |
# Update rotation angle | |
angle = (angle + rotation_speed) % 360 | |
radian_angle = math.radians(angle) | |
cos_theta = math.cos(radian_angle) | |
sin_theta = math.sin(radian_angle) | |
# Move the ball | |
ball_pos[0] += ball_velocity[0] | |
ball_pos[1] += ball_velocity[1] | |
# Convert to square's local coordinates | |
local_x = ball_pos[0] - square_center[0] | |
local_y = ball_pos[1] - square_center[1] | |
# Rotate to unrotated coordinate system | |
rotated_x = local_x * cos_theta + local_y * sin_theta | |
rotated_y = -local_x * sin_theta + local_y * cos_theta | |
half_size = square_size // 2 | |
collision = False | |
# Check collisions in unrotated system | |
new_rot_x, new_rot_y = rotated_x, rotated_y | |
local_vx = ball_velocity[0] * cos_theta + ball_velocity[1] * sin_theta | |
local_vy = -ball_velocity[0] * sin_theta + ball_velocity[1] * cos_theta | |
# X-axis collision | |
if rotated_x < -half_size: | |
overflow = rotated_x + half_size | |
new_rot_x = -half_size - overflow | |
local_vx = -local_vx | |
collision = True | |
elif rotated_x > half_size: | |
overflow = rotated_x - half_size | |
new_rot_x = half_size - overflow | |
local_vx = -local_vx | |
collision = True | |
# Y-axis collision | |
if rotated_y < -half_size: | |
overflow = rotated_y + half_size | |
new_rot_y = -half_size - overflow | |
local_vy = -local_vy | |
collision = True | |
elif rotated_y > half_size: | |
overflow = rotated_y - half_size | |
new_rot_y = half_size - overflow | |
local_vy = -local_vy | |
collision = True | |
if collision: | |
# Convert back to rotated local coordinates | |
new_local_x = new_rot_x * cos_theta - new_rot_y * sin_theta | |
new_local_y = new_rot_x * sin_theta + new_rot_y * cos_theta | |
# Update ball position and velocity | |
ball_pos = [square_center[0] + new_local_x, square_center[1] + new_local_y] | |
ball_velocity[0] = local_vx * cos_theta - local_vy * sin_theta | |
ball_velocity[1] = local_vx * sin_theta + local_vy * cos_theta | |
# Clear screen | |
screen.fill(BLACK) | |
# Draw rotated square | |
rotated_square = pygame.transform.rotate(square_surface, angle) | |
rect = rotated_square.get_rect(center=square_center) | |
screen.blit(rotated_square, rect.topleft) | |
# Draw ball | |
pygame.draw.circle(screen, YELLOW, (int(ball_pos[0]), int(ball_pos[1])), ball_radius) | |
pygame.display.flip() | |
clock.tick(60) | |
pygame.quit() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment