Skip to content

Instantly share code, notes, and snippets.

@ivanfioravanti
Created January 22, 2025 11:15
Show Gist options
  • Save ivanfioravanti/6f793bc4577c16485c182057f6d78b8f to your computer and use it in GitHub Desktop.
Save ivanfioravanti/6f793bc4577c16485c182057f6d78b8f to your computer and use it in GitHub Desktop.
o1-pro vs r1 rotating square with ball inside
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()
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