您现在的位置是:首页 >学无止境 >o3-mini与deepseek-r1的弹球游戏代码对比分析网站首页学无止境

o3-mini与deepseek-r1的弹球游戏代码对比分析

johnteller 2025-02-21 12:01:02
简介python制作球碰到墙壁会反弹的小游戏

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没有展示内部思考过程(差评)。

风语者!平时喜欢研究各种技术,目前在从事后端开发工作,热爱生活、热爱工作。