您现在的位置是:首页 >学无止境 >o3-mini与deepseek-r1的弹球游戏代码对比分析网站首页学无止境
o3-mini与deepseek-r1的弹球游戏代码对比分析
case提示词
编写一个 Python 程序,显示球在旋转的六边形内弹跳。球应该受到重力和摩擦力的影响,并且必须逼真地从旋转的墙壁上反弹
deepseek-r1的输出代码
import pygame
import math
import sys
# 初始化Pygame
pygame.init()
# 屏幕设置
WIDTH, HEIGHT = 800, 600
screen = pygame.display.set_mode((WIDTH, HEIGHT))
clock = pygame.time.Clock()
# 物理参数
GRAVITY = 0.4
DAMPING = 0.9
AIR_RESISTANCE = 0.9995
ROTATION_SPEED = 0.8
class RotatingHexagon:
def __init__(self, center, size):
self.center = center
self.size = size
self.angle = 0
self.sides = []
self.update_sides()
def update_sides(self):
self.sides = []
for i in range(6):
angle_deg = 60 * i + self.angle
x = self.center[0] + self.size * math.cos(math.radians(angle_deg))
y = self.center[1] + self.size * math.sin(math.radians(angle_deg))
self.sides.append((x, y))
def rotate(self):
self.angle = (self.angle + ROTATION_SPEED) % 360
self.update_sides()
class Ball:
def __init__(self, position, radius):
self.pos = list(position)
self.vel = [0, 0]
self.radius = radius
def update(self):
# 应用重力和空气阻力
self.vel[1] += GRAVITY
self.vel[0] *= AIR_RESISTANCE
self.vel[1] *= AIR_RESISTANCE
# 更新位置
self.pos[0] += self.vel[0]
self.pos[1] += self.vel[1]
def closest_point_on_line(p, a, b):
ap = (p[0]-a[0], p[1]-a[1])
ab = (b[0]-a[0], b[1]-a[1])
t = (ap[0]*ab[0] + ap[1]*ab[1]) / (ab[0]**2 + ab[1]**2 + 1e-6)
t = max(0, min(1, t))
return (a[0] + ab[0]*t, a[1] + ab[1]*t)
def check_collision(ball, hexagon):
for i in range(6):
a = hexagon.sides[i]
b = hexagon.sides[(i+1)%6]
closest = closest_point_on_line(ball.pos, a, b)
dx = closest[0] - ball.pos[0]
dy = closest[1] - ball.pos[1]
distance = math.hypot(dx, dy)
if distance < ball.radius:
# 计算法线向量
nx = (b[1]-a[1])
ny = -(b[0]-a[0])
length = math.hypot(nx, ny)
nx /= length
ny /= length
# 反射速度
dot = ball.vel[0]*nx + ball.vel[1]*ny
ball.vel[0] -= 2 * dot * nx
ball.vel[1] -= 2 * dot * ny
# 应用阻尼并修正位置
ball.vel[0] *= DAMPING
ball.vel[1] *= DAMPING
overlap = ball.radius - distance
ball.pos[0] -= overlap * dx / distance
ball.pos[1] -= overlap * dy / distance
# 初始化对象
hexagon = RotatingHexagon((WIDTH//2, HEIGHT//2), 250)
ball = Ball((WIDTH//2, HEIGHT//2 - 100), 12)
# 主循环
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
screen.fill((30, 30, 35))
# 更新六边形和球
hexagon.rotate()
ball.update()
check_collision(ball, hexagon)
# 绘制六边形
pygame.draw.polygon(screen, (100, 200, 255), hexagon.sides, 3)
# 绘制球
pygame.draw.circle(screen, (255, 80, 120), (int(ball.pos[0]), int(ball.pos[1])), ball.radius)
pygame.display.flip()
clock.tick(60)
o3-mini的输出代码
import pygame
import math
# 初始化 pygame
pygame.init()
WIDTH, HEIGHT = 800, 600
screen = pygame.display.set_mode((WIDTH, HEIGHT))
clock = pygame.time.Clock()
# 物理参数
gravity = 0.5 # 重力加速度(像素/帧²)
friction = 0.99 # 每帧空气阻力(摩擦系数)
restitution = 0.9 # 反弹系数
# 小球参数
ball_radius = 15
ball_pos = [WIDTH / 2, HEIGHT / 2]
ball_vel = [5, -5] # 初始速度
# 六边形参数
hex_radius = 250
hex_center = (WIDTH / 2, HEIGHT / 2)
hex_rotation = 0 # 初始旋转角度
hex_rot_speed = 0.01 # 六边形每帧旋转的角速度(弧度)
# 根据中心坐标、半径和旋转角度计算六边形的顶点
def get_hexagon_vertices(center, radius, rotation):
cx, cy = center
vertices = []
for i in range(6):
angle = rotation + i * (2 * math.pi / 6)
x = cx + radius * math.cos(angle)
y = cy + radius * math.sin(angle)
vertices.append((x, y))
return vertices
# 处理小球与六边形边界的碰撞
def reflect_ball(ball_pos, ball_vel, ball_radius, vertices):
new_vel = ball_vel[:] # 复制当前速度
# 遍历六边形的每一条边
for i in range(len(vertices)):
p1 = vertices[i]
p2 = vertices[(i + 1) % len(vertices)]
# 计算边向量及单位向量
edge_vec = (p2[0] - p1[0], p2[1] - p1[1])
edge_len = math.hypot(edge_vec[0], edge_vec[1])
if edge_len == 0:
continue
edge_unit = (edge_vec[0] / edge_len, edge_vec[1] / edge_len)
# 为了保证法向量指向内部,先计算两种可能的单位法向量
normal1 = (-edge_unit[1], edge_unit[0])
normal2 = (edge_unit[1], -edge_unit[0])
# 选择指向六边形内部的法向量(取边中点到六边形中心的方向)
midx, midy = (p1[0] + p2[0]) / 2, (p1[1] + p2[1]) / 2
to_center = (hex_center[0] - midx, hex_center[1] - midy)
if normal1[0] * to_center[0] + normal1[1] * to_center[1] > normal2[0] * to_center[0] + normal2[1] * to_center[1]:
normal = normal1
else:
normal = normal2
# 计算小球中心到直线的距离
vec_p1_ball = (ball_pos[0] - p1[0], ball_pos[1] - p1[1])
dist = abs(vec_p1_ball[0] * normal[0] + vec_p1_ball[1] * normal[1])
if dist < ball_radius:
# 获取小球在边所在直线上的投影,并判断投影是否在边界段上
proj = vec_p1_ball[0] * edge_unit[0] + vec_p1_ball[1] * edge_unit[1]
if proj < 0:
closest_point = p1
elif proj > edge_len:
closest_point = p2
else:
closest_point = (p1[0] + proj * edge_unit[0], p1[1] + proj * edge_unit[1])
# 检查小球与边的距离是否小于半径,若是则发生碰撞
dx = ball_pos[0] - closest_point[0]
dy = ball_pos[1] - closest_point[1]
if math.hypot(dx, dy) < ball_radius:
# 计算碰撞处墙面的速度(由旋转产生),墙面的线速度 = ω × r (二维中:( -ω*y, ω*x ))
rel_x = closest_point[0] - hex_center[0]
rel_y = closest_point[1] - hex_center[1]
wall_velocity = (-hex_rot_speed * rel_y, hex_rot_speed * rel_x)
# 计算小球相对于墙面的速度
rel_v = (ball_vel[0] - wall_velocity[0], ball_vel[1] - wall_velocity[1])
dot = rel_v[0] * normal[0] + rel_v[1] * normal[1]
# 只有当小球朝向墙体运动时才反弹
if dot < 0:
# 反射计算: v' = v - (1+e)(v·n)*n (作用于相对速度)
rel_v = (rel_v[0] - (1 + restitution) * dot * normal[0],
rel_v[1] - (1 + restitution) * dot * normal[1])
# 转换回绝对速度
new_vel = [wall_velocity[0] + rel_v[0], wall_velocity[1] + rel_v[1]]
# 将小球挤出墙外,简单处理:将小球沿法向量推到边界正好接触的位置
overlap = ball_radius - math.hypot(dx, dy)
ball_pos[0] += normal[0] * overlap
ball_pos[1] += normal[1] * overlap
return new_vel
# 主循环
running = True
while running:
dt = clock.tick(60) / 1000.0 # 帧率控制(秒)
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
# 更新小球物理状态(注意这里直接使用每帧物理更新,可根据 dt 做进一步调整)
ball_vel[1] += gravity # 重力作用
ball_vel[0] *= friction # 空气阻力
ball_vel[1] *= friction
ball_pos[0] += ball_vel[0]
ball_pos[1] += ball_vel[1]
# 更新六边形旋转角度
hex_rotation += hex_rot_speed
vertices = get_hexagon_vertices(hex_center, hex_radius, hex_rotation)
# 碰撞检测与反弹处理
ball_vel = reflect_ball(ball_pos, ball_vel, ball_radius, vertices)
# 绘制部分
screen.fill((0, 0, 0))
pygame.draw.polygon(screen, (255, 255, 255), vertices, 3)
pygame.draw.circle(screen, (255, 0, 0), (int(ball_pos[0]), int(ball_pos[1])), ball_radius)
pygame.display.flip()
pygame.quit()
效果图对比
deepseek-r1:
o3-mini:
对比上述两个弹球的表现,可以发现o3-mini代码中的弹球表现更加自然,对于球的回弹建模更加真实。
具体代码分析
一、整体架构方面
- deepseek采用了面向对象方式,将六边形和球分别封装成类
- o3采用了更函数式的方法,使用独立函数处理逻辑
二、物理建模方面的差异
1. 碰撞检测和反弹
- deepseek:
(1)使用点到线段的最短距离来检测碰撞
(2)反弹时只考虑了基本的速度反射,没有考虑六边形旋转带来的影响
(3)使用法向量来计算反弹方向
- o3:
(1)更完整地考虑了旋转物体的碰撞
(2)计算了碰撞点的切向速度(由旋转产生)
(3)使用相对速度来处理反弹
(4)考虑了法向量的内外方向
2. 物理参数设置
- deepseek:
* GRAVITY = 0.4
* DAMPING = 0.9
* AIR_RESISTANCE = 0.9995
- o3:
* gravity = 0.5
* friction = 0.99
* restitution = 0.9
感觉参数设置都还合理(从最终效果来看···)
三、 运动更新
- deepseek更简单直接
- o3考虑了时间步长dt
四、 代码质量和可维护性
- deepseek结构更清晰,但物理模拟相对简单
- o3物理模拟更准确,但代码组织相对松散
综上,最为关键的差异在于o3-mini对旋转碰撞的处理更加准确,即当六边形旋转时,碰撞面是有切向速度的,这对于球的反弹影响较大,而deepseek-r1忽略了这一点导致看起来效果较差。
使用deepseek-r1对于自身代码进行订正
使用提示词
根据下面一段话改进当前代码: “旋转碰撞的处理更加准确,即当六边形旋转时,碰撞面是有切向速度的,这对于球的反弹影响较大,而deepseek-r1忽略了这一点导致看起来效果较差” |
改进后的代码(部分)
def check_collision(ball, hexagon):
for i in range(6):
a = hexagon.sides[i]
b = hexagon.sides[(i+1)%6]
closest = closest_point_on_line(ball.pos, a, b)
dx = closest[0] - ball.pos[0]
dy = closest[1] - ball.pos[1]
distance = math.hypot(dx, dy)
if distance < ball.radius:
# 计算法线向量
nx = (b[1]-a[1])
ny = -(b[0]-a[0])
length = math.hypot(nx, ny)
nx /= length
ny /= length
# 新增:计算六边形旋转引起的边缘表面速度(六边形以 ROTATION_SPEED 每帧旋转,转换为弧度制)
omega = math.radians(ROTATION_SPEED)
rx = closest[0] - hexagon.center[0]
ry = closest[1] - hexagon.center[1]
surface_vx = -ry * omega
surface_vy = rx * omega
# 计算球相对于边缘的相对速度
rel_vx = ball.vel[0] - surface_vx
rel_vy = ball.vel[1] - surface_vy
# 反射相对速度
dot = rel_vx * nx + rel_vy * ny
rel_vx = rel_vx - 2 * dot * nx
rel_vy = rel_vy - 2 * dot * ny
# 将反射后的相对速度加回边缘表面速度,获得新的球速
ball.vel[0] = surface_vx + rel_vx
ball.vel[1] = surface_vy + rel_vy
# 应用阻尼并修正位置
ball.vel[0] *= DAMPING
ball.vel[1] *= DAMPING
overlap = ball.radius - distance
ball.pos[0] -= overlap * dx / distance
ball.pos[1] -= overlap * dy / distance
改进后效果
可以看到确实自然了不少:
PS
最近总是看到各大博客文章使用这个case作为对比,很好奇,便复现看看具体差异到底在哪里哈哈哈,看来还是o3稍微想得周到一点,但是其实也只多改一轮就行了,不太影响实际使用,反倒是o3-mini没有展示内部思考过程(差评)。