最近几周,科技圈因为新的人工智能模型层出不穷而感到惊讶。
但是问题来了:很多开发者在日常写代码时,不知道该用哪个AI模型。
就在前几天,Anthropic 推出了他们最新的混合推理模型,号称最适合编程的。
但让我们看看它与我们两款顶尖模型,Grok 3 和 o3-mini-high 相比表现如何。
TL;DR (长话短说)如果你想直接看结果,在这些模型中,Claude 3.7 Sonnet 在写代码方面明显更胜一筹。
虽然 Grok 3 和 o3-mini-high 有些相似,但 Grok 3 生成的代码稍微好一点。
如图所示,这是一张关于Grok 3与openai o3-mini-high的比较图片。(点击图片查看详细信息)
……此处省略……
克罗德 3.7 十四行简介说明这个AI模型才发布几天,就已经在科技圈子里火了起来。我特别要强调的是“科技”,是因为这个模型被广泛认为是写代码的AI模型中的最好的,至少是现在。
这个视频来自 bycloud,可以快速了解该模型的强大之处。
Claude 3.7 支持最多 128K 输出令牌(测试版),这个改进使得用户可以生成更长的内容,以前版本无法相比。这对于生成长且高质量的代码特别有用。
它在SWE Benchmark上以平均准确率62.3%超越了所有其他AI模型,其准确率甚至可以超过70%,这是迄今为止任何AI模型中的最佳表现。这比顶级OpenAI模型、之前的Anthropic模型Claude 3.5以及开源模型如DeepSeek R1的准确率高出13-20%,显示出显著的优势。
尽管Claude 3.7拥有这样的能力,它的总成本相比早期型号降低了18%。它保持了每百万输入token的定价为3美元,每百万输出token为15美元。
不仅如此,Anthropic 还发布了 代理式AI Claude Code,一个命令行工具,它能够理解你的代码库,帮助解决代码问题,回答问题,并通过其Git集成让你更好地把握项目的历史。
看看这个视频,快速了解一下情况。
此处省略内容
比较这些模型的编码能力💁♂️ 说真的,我个人对Claude 3.7 Sonnet寄予厚望。我们来看看Sonnet和其他两个模型在代码方面是否存在重大差异。
我们先来聊聊一些有趣的事情:
1. 用 Pygame 建造简易的 Minecraft
任务很简单,这三个人工智能模型(LLM)都被要求来用Pygame做一个简单的《我的世界》游戏。
请帮我使用 Python Pygame 创建一个简单的 Minecraft 风格的游戏。
Claude 3.7 的十四行诗回复
生成的代码如下:
import random
import sys
import pygame
# 初始化 Pygame
pygame.init()
# 游戏设置
SCREEN_WIDTH = 800
SCREEN_HEIGHT = 600
BLOCK_SIZE = 40
GRAVITY = 0.5
JUMP_STRENGTH = 10
PLAYER_SPEED = 5
# 颜色
SKY_COLOR = (135, 206, 235)
PLAYER_COLOR = (255, 0, 0)
GRASS_COLOR = (34, 139, 34)
DIRT_COLOR = (139, 69, 19)
STONE_COLOR = (105, 105, 105)
COAL_COLOR = (47, 47, 47)
INVENTORY_BG = (64, 64, 64, 128)
# 创建屏幕
screen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT))
pygame.display.set_caption("简易版 Minecraft")
clock = pygame.time.Clock()
# 地块类型
EMPTY = 0
GRASS = 1
DIRT = 2
STONE = 3
COAL = 4
# 地块颜色
block_colors = {
GRASS: GRASS_COLOR,
DIRT: DIRT_COLOR,
STONE: STONE_COLOR,
COAL: COAL_COLOR,
}
# 定义玩家
class Player:
def __init__(self, x, y):
self.rect = pygame.Rect(x, y, BLOCK_SIZE - 10, int(BLOCK_SIZE * 1.5))
self.velocity_y = 0
self.on_ground = False
self.selected_block = GRASS
def move(self, dx, dy, blocks):
# 水平移动
self.rect.x += dx
self.check_collision(dx, 0, blocks)
# 应用重力
self.velocity_y += GRAVITY
self.rect.y += int(self.velocity_y)
self.on_ground = False
self.check_collision(0, self.velocity_y, blocks)
# 保持玩家在屏幕上
if self.rect.left < 0:
self.rect.left = 0
if self.rect.right > SCREEN_WIDTH:
self.rect.right = SCREEN_WIDTH
def check_collision(self, dx, dy, blocks):
for y, row in enumerate(blocks):
for x, block in enumerate(row):
if block != EMPTY:
block_rect = pygame.Rect(
x * BLOCK_SIZE, y * BLOCK_SIZE, BLOCK_SIZE, BLOCK_SIZE
)
if self.rect.colliderect(block_rect):
if dx > 0: # 向右移动
self.rect.right = block_rect.left
if dx < 0: # 向左移动
self.rect.left = block_rect.right
if dy > 0: # 向下移动
self.rect.bottom = block_rect.top
self.on_ground = True
self.velocity_y = 0
if dy < 0: # 向上移动
self.rect.top = block_rect.bottom
self.velocity_y = 0
def jump(self):
if self.on_ground:
self.velocity_y = -JUMP_STRENGTH
def draw(self, surface):
pygame.draw.rect(surface, PLAYER_COLOR, self.rect)
# 创建世界
def create_world(width, height):
world = [[EMPTY for _ in range(width)] for _ in range(height)]
# 生成地形
ground_height = height - 5
for x in range(width):
height_variation = random.randint(-1, 1)
ground_height += height_variation
# 保持在范围内
if ground_height < height - 10:
ground_height = height - 10
if ground_height > height - 3:
ground_height = height - 3
# 在顶部放置草地
world[ground_height][x] = GRASS
# 在草地下方放置泥土
for y in range(ground_height + 1, ground_height + 4):
if y < height:
world[y][x] = DIRT
# 在泥土下方放置石头
for y in range(ground_height + 4, height):
if y < height:
if random.random() < 0.1: # 10% 的概率生成煤炭
world[y][x] = COAL
else:
world[y][x] = STONE
return world
# 初始化游戏
world_width = SCREEN_WIDTH // BLOCK_SIZE
world_height = SCREEN_HEIGHT // BLOCK_SIZE
world = create_world(world_width, world_height)
player = Player(SCREEN_WIDTH // 2, 0)
# 游戏循环
running = True
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
# 跳跃
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_SPACE:
player.jump()
# 切换地块类型
if event.key == pygame.K_1:
player.selected_block = GRASS
elif event.key == pygame.K_2:
player.selected_block = DIRT
elif event.key == pygame.K_3:
player.selected_block = STONE
elif event.key == pygame.K_4:
player.selected_block = COAL
# 用鼠标放置或破坏方块
if event.type == pygame.MOUSEBUTTONDOWN:
mouse_x, mouse_y = pygame.mouse.get_pos()
block_x = mouse_x // BLOCK_SIZE
block_y = mouse_y // BLOCK_SIZE
# 确保在世界范围内
if 0 <= block_x < world_width and 0 <= block_y < world_height:
# 左键放置方块
if event.button == 1:
# 如果玩家不在该位置
block_rect = pygame.Rect(
block_x * BLOCK_SIZE,
block_y * BLOCK_SIZE,
BLOCK_SIZE,
BLOCK_SIZE,
)
if not player.rect.colliderect(block_rect):
world[block_y][block_x] = player.selected_block
# 右键破坏方块
elif event.button == 3:
world[block_y][block_x] = EMPTY
# 处理移动
keys = pygame.key.get_pressed()
dx = 0
if keys[pygame.K_a] or keys[pygame.K_LEFT]:
dx = -PLAYER_SPEED
if keys[pygame.K_d] or keys[pygame.K_RIGHT]:
dx = PLAYER_SPEED
player.move(dx, 0, world)
# 绘制所有内容
screen.fill(SKY_COLOR)
# 绘制方块
for y, row in enumerate(world):
for x, block in enumerate(row):
if block != EMPTY:
pygame.draw.rect(
screen,
block_colors[block],
(x * BLOCK_SIZE, y * BLOCK_SIZE, BLOCK_SIZE, BLOCK_SIZE),
)
# 绘制玩家
player.draw(screen)
# 绘制库存
pygame.draw.rect(screen, INVENTORY_BG, (10, 10, 200, 50))
for i, block_type in enumerate([GRASS, DIRT, STONE, COAL]):
block_rect = pygame.Rect(20 + i * 50, 15, 40, 40)
pygame.draw.rect(screen, block_colors[block_type], block_rect)
# 高亮选中的方块
if block_type == player.selected_block:
pygame.draw.rect(screen, (255, 255, 255), block_rect, 2)
# 显示数字
font = pygame.font.SysFont(None, 24)
text = font.render(str(i + 1), True, (255, 255, 255))
screen.blit(text, (block_rect.x + 5, block_rect.y + 5))
# 更新显示
pygame.display.flip()
clock.tick(60)
pygame.quit()
sys.exit()
全屏(按一下退出)
程序的输出如下:
- Grok 3 的回应,
生成的代码如下:
import pygame
# 初始化 Pygame
pygame.init()
# 屏幕设置
WIDTH = 800
HEIGHT = 600
BLOCK_SIZE = 40
screen = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption("简单的二维 Minecraft 游戏")
# 颜色
WHITE = (255, 255, 255)
BLACK = (0, 0, 0)
BROWN = (139, 69, 19)
GREEN = (0, 255, 0)
BLUE = (0, 0, 255)
# 玩家类
class Player:
def __init__(self, x, y):
self.x = x
self.y = y
self.width = BLOCK_SIZE
self.height = BLOCK_SIZE
self.speed = 5
def draw(self):
pygame.draw.rect(screen, BLUE, (self.x, self.y, self.width, self.height))
# 方块类
class Block:
def __init__(self, x, y, block_type):
self.x = x
self.y = y
self.width = BLOCK_SIZE
self.height = BLOCK_SIZE
self.type = block_type
def draw(self):
if self.type == "dirt":
pygame.draw.rect(screen, BROWN, (self.x, self.y, self.width, self.height))
elif self.type == "grass":
pygame.draw.rect(screen, GREEN, (self.x, self.y, self.width, self.height))
# 生成初始地形
for x in range(0, WIDTH, BLOCK_SIZE):
for y in range(HEIGHT // 2, HEIGHT, BLOCK_SIZE):
if y == HEIGHT // 2:
blocks.append(Block(x, y, "grass"))
else:
blocks.append(Block(x, y, "dirt"))
# 游戏循环
clock = pygame.time.Clock()
running = True
inventory = []
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
elif event.type == pygame.MOUSEBUTTONDOWN:
mouse_x, mouse_y = pygame.mouse.get_pos()
# 点击左键破坏方块
if event.button == 1:
for block in blocks[:]:
if (
block.x <= mouse_x < block.x + BLOCK_SIZE
and block.y <= mouse_y < block.y + BLOCK_SIZE
):
inventory.append(block.type)
blocks.remove(block)
break
# 点击右键放置方块
elif event.button == 3 and inventory:
grid_x = (mouse_x // BLOCK_SIZE) * BLOCK_SIZE
grid_y = (mouse_y // BLOCK_SIZE) * BLOCK_SIZE
# 检查位置是否为空
occupied = False
for block in blocks:
if block.x == grid_x and block.y == grid_y:
occupied = True
break
if not occupied and grid_y >= HEIGHT // 2:
blocks.append(Block(grid_x, grid_y, inventory.pop()))
# 玩家移动
keys = pygame.key.get_pressed()
if keys[pygame.K_LEFT] and player.x > 0:
player.x -= player.speed
if keys[pygame.K_RIGHT] and player.x < WIDTH - player.width:
player.x += player.speed
if keys[pygame.K_UP] and player.y > 0:
player.y -= player.speed
if keys[pygame.K_DOWN] and player.y < HEIGHT - player.height:
player.y += player.speed
# 绘制所有内容
screen.fill(WHITE)
for block in blocks:
block.draw()
player.draw()
# 显示库存内容
font = pygame.font.Font(None, 36)
text = font.render(f"库存: {len(inventory)} 个", True, BLACK)
screen.blit(text, (10, 10))
pygame.display.flip()
clock.tick(60)
pygame.quit()
进入全屏 退出全屏
这是程序的显示:
这个程序相当令人失望,完全不符合我的预期。除了玩家移动功能正常,其他功能都不按预期工作。它根本不像Minecraft,更像是贪吃蛇游戏!
OpenAI o3-mini-high 的回应
这是它生成的代码如下:
import pygame
# 初始化 Pygame
pygame.init()
# 屏幕设置
WIDTH = 800
HEIGHT = 600
BLOCK_SIZE = 40
screen = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption("简单的二维我的世界")
# 颜色
WHITE = (255, 255, 255)
BLACK = (0, 0, 0)
BROWN = (139, 69, 19)
GREEN = (0, 255, 0)
BLUE = (0, 0, 255)
# 玩家类
class Player:
def __init__(self, x, y):
self.x = x
self.y = y
self.width = BLOCK_SIZE
self.height = BLOCK_SIZE
self.speed = 5
def draw(self):
pygame.draw.rect(screen, BLUE, (self.x, self.y, self.width, self.height))
# 方块类
class Block:
def __init__(self, x, y, block_type):
self.x = x
self.y = y
self.width = BLOCK_SIZE
self.height = BLOCK_SIZE
self.type = block_type
def draw(self):
if self.type == "dirt":
pygame.draw.rect(screen, BROWN, (self.x, self.y, self.width, self.height))
elif self.type == "grass":
pygame.draw.rect(screen, GREEN, (self.x, self.y, self.width, self.height))
# 创建游戏对象
player = Player(WIDTH // 2, HEIGHT // 2)
blocks = []
# 生成初始地面
for x in range(0, WIDTH, BLOCK_SIZE):
for y in range(HEIGHT // 2, HEIGHT, BLOCK_SIZE):
if y == HEIGHT // 2:
blocks.append(Block(x, y, "grass"))
else:
blocks.append(Block(x, y, "dirt"))
# 游戏循环
clock = pygame.time.Clock()
running = True
inventory = []
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
elif event.type == pygame.MOUSEBUTTONDOWN:
mouse_x, mouse_y = pygame.mouse.get_pos()
# 点击左键破坏方块
if event.button == 1:
for block in blocks[:]:
if (
block.x <= mouse_x < block.x + BLOCK_SIZE
and block.y <= mouse_y < block.y + BLOCK_SIZE
):
inventory.append(block.type)
blocks.remove(block)
break
# 点击右键放置方块
elif event.button == 3 and inventory:
grid_x = (mouse_x // BLOCK_SIZE) * BLOCK_SIZE
grid_y = (mouse_y // BLOCK_SIZE) * BLOCK_SIZE
# 检查是否已有方块
occupied = False
for block in blocks:
if block.x == grid_x and block.y == grid_y:
occupied = True
break
if not occupied and grid_y >= HEIGHT // 2:
blocks.append(Block(grid_x, grid_y, inventory.pop()))
# 玩家移动
keys = pygame.key.get_pressed()
if keys[pygame.K_LEFT] and player.x > 0:
player.x -= player.speed
if keys[pygame.K_RIGHT] and player.x < WIDTH - player.width:
player.x += player.speed
if keys[pygame.K_UP] and player.y > 0:
player.y -= player.speed
if keys[pygame.K_DOWN] and player.y < HEIGHT - player.height:
player.y += player.speed
# 绘制所有内容
screen.fill(WHITE)
for block in blocks:
block.draw()
player.draw()
# 显示库存数量
font = pygame.font.Font(None, 36)
text = font.render(f"库存: {len(inventory)}个方块", True, BLACK)
screen.blit(text, (10, 10))
pygame.display.flip()
clock.tick(60)
pygame.quit()
点击全屏按钮可以切换到全屏模式,再次点击则退出全屏。
以下是程序输出结果:
真是让人相当失望,只看到了一个带背景色的空白屏幕。🤔
最后结论:可以说得上是克劳德3.7以巨大的优势获胜。✅ 所有功能都如预期一样运行。它构建的游戏几乎涵盖了我所设想的所有功能✅
2. 几个球在一个旋转的六边形里
让我们快速地测试一下这些模型,用一个标准的问题来评判不同的大语言模型。
这是一个对问题的修改版本,在一个旋转的六边形内部,只有一个球被旋转。
请写一个Python脚本,让10个球在一个快速旋转的六边形里动起来。
- 克劳德3.7的十四行诗回复
这是生成的代码:
import math
import random
import sys
import pygame
# 初始化pygame
pygame.init()
# 常量
WIDTH, HEIGHT = 800, 600
FPS = 60
WHITE = (255, 255, 255)
BLACK = (0, 0, 0)
GRAY = (100, 100, 100)
BALL_COLORS = [
(255, 0, 0), # 红色
(0, 255, 0), # 绿色
(0, 0, 255), # 蓝色
(255, 255, 0), # 黄色
(255, 0, 255), # 品红色
(0, 255, 255), # 青色
(255, 165, 0), # 橙色
(128, 0, 128), # 紫色
(255, 192, 203), # 粉色
(0, 128, 0), # 暗绿色
]
# 设置屏幕
screen = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption("快速旋转的六边形中的多个球")
clock = pygame.time.Clock()
# 六边形属性
hex_radius = 180
hex_center = (WIDTH // 2, HEIGHT // 2)
hex_rotation = 0
hex_rotation_speed = 2.0 # 每帧增加旋转速度(度)
# 球属性
num_balls = 10
ball_radius = 15
balls = []
# 创建随机位置和速度的球
for i in range(num_balls):
# 在六边形内随机位置(距离边缘有一定间隔)
angle = random.uniform(0, 2 * math.pi)
distance = random.uniform(0, hex_radius - ball_radius * 2)
x = hex_center[0] + distance * math.cos(angle)
y = hex_center[1] + distance * math.sin(angle)
# 随机速度
velocity_x = random.uniform(-3, 3)
velocity_y = random.uniform(-3, 3)
balls.append(
{
"pos": [x, y],
"velocity": [velocity_x, velocity_y],
"color": BALL_COLORS[i],
"radius": ball_radius,
}
)
# 计算六边形顶点的函数
def get_hexagon_vertices(center, radius, rotation):
vertices = []
for i in range(6):
angle_deg = rotation + 60 * i
angle_rad = math.radians(angle_deg)
x = center[0] + radius * math.cos(angle_rad)
y = center[1] + radius * math.sin(angle_rad)
vertices.append((x, y))
return vertices
# 检查球与线段碰撞的函数
def check_line_collision(ball_pos, ball_radius, line_start, line_end):
x1, y1 = line_start
x2, y2 = line_end
x0, y0 = ball_pos
# 线段向量
dx, dy = x2 - x1, y2 - y1
# 线段起点到球的向量
dx0, dy0 = x0 - x1, y0 - y1
# 计算投影长度
t = max(0, min(1, (dx0 * dx + dy0 * dy) / (dx * dx + dy * dy)))
# 计算线段上最近点
closest_x = x1 + t * dx
closest_y = y1 + t * dy
# 计算球到最近点的距离
distance = math.sqrt((x0 - closest_x) ** 2 + (y0 - closest_y) ** 2)
# 返回碰撞信息
if distance <= ball_radius:
# 法线向量
if distance > 0: # 避免除零错误
nx = (x0 - closest_x) / distance
ny = (y0 - closest_y) / distance
else:
nx, ny = 0, -1 # 距离为零时的默认法线向量
return True, (nx, ny)
return False, None
# 检查球之间碰撞的函数
def check_ball_collision(ball1, ball2):
x1, y1 = ball1["pos"]
x2, y2 = ball2["pos"]
# 计算球间距离
distance = math.sqrt((x2 - x1) ** 2 + (y2 - y1) ** 2)
# 如果球相撞
if distance <= (ball1["radius"] + ball2["radius"]):
# 法线向量
if distance > 0:
nx = (x2 - x1) / distance
ny = (y2 - y1) / distance
else:
nx, ny = 0, 1 # 位置相同时的默认法线向量
return True, (nx, ny)
return False, None
# 主循环
gravity = 0.1
ball_speed_limit = 6
running = True
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
# 更新旋转角度
hex_rotation += hex_rotation_speed
if hex_rotation >= 360:
hex_rotation -= 360
# 获取六边形顶点
hex_vertices = get_hexagon_vertices(hex_center, hex_radius, hex_rotation)
# 更新球的状态
for ball in balls:
# 重力加速度
ball["velocity"][1] += gravity
# 更新球位置
ball["pos"][0] += ball["velocity"][0]
ball["pos"][1] += ball["velocity"][1]
# 检查球与六边形边的碰撞
for i in range(6):
start = hex_vertices[i]
end = hex_vertices[(i + 1) % 6]
collision, normal = check_line_collision(
ball["pos"], ball["radius"], start, end
)
if collision:
# 反射速度
nx, ny = normal
dot_product = ball["velocity"][0] * nx + ball["velocity"][1] * ny
ball["velocity"][0] -= 2 * dot_product * nx
ball["velocity"][1] -= 2 * dot_product * ny
# 施加能量损失
ball["velocity"][0] *= 0.9
ball["velocity"][1] *= 0.9
# 移动球避免卡住
ball["pos"][0] += nx * 2
ball["pos"][1] += ny * 2
# 限制球速度
speed = math.sqrt(ball["velocity"][0] ** 2 + ball["velocity"][1] ** 2)
if speed > ball_speed_limit:
ball["velocity"][0] = (ball["velocity"][0] / speed) * ball_speed_limit
ball["velocity"][1] = (ball["velocity"][1] / speed) * ball_speed_limit
# 检查球之间的碰撞
for i in range(num_balls):
for j in range(i + 1, num_balls):
collision, normal = check_ball_collision(balls[i], balls[j])
if collision:
# 计算相对速度
nx, ny = normal
vx1, vy1 = balls[i]["velocity"]
vx2, vy2 = balls[j]["velocity"]
# 相对速度沿法线方向
vrn = (vx1 - vx2) * nx + (vy1 - vy2) * ny
# 如果球相互远离,跳过处理
if vrn < 0:
continue
# 质量(假设质量相等)
m1 = 1.0
m2 = 1.0
# 弹性碰撞处理
impulse = 2.0 * vrn / (m1 + m2)
# 更新速度值
balls[i]["velocity"][0] -= impulse * m2 * nx
balls[i]["velocity"][1] -= impulse * m2 * ny
balls[j]["velocity"][0] += impulse * m1 * nx
balls[j]["velocity"][1] += impulse * m1 * ny
# 移动球避免卡住
overlap = (
balls[i]["radius"]
+ balls[j]["radius"]
- math.sqrt(
(balls[i]["pos"][0] - balls[j]["pos"][0]) ** 2
+ (balls[i]["pos"][1] - balls[j]["pos"][1]) ** 2
)
)
if overlap > 0:
# 按比例移动
balls[i]["pos"][0] -= nx * overlap * 0.5
balls[i]["pos"][1] -= ny * overlap * 0.5
balls[j]["pos"][0] += nx * overlap * 0.5
balls[j]["pos"][1] += ny * overlap * 0.5
# 绘制所有物体
screen.fill(BLACK)
# 绘制快速旋转的六边形
pygame.draw.polygon(screen, GRAY, hex_vertices, 3)
# 绘制球
for ball in balls:
pygame.draw.circle(
screen,
ball["color"],
(int(ball["pos"][0]), int(ball["pos"][1])),
ball["radius"],
)
pygame.display.flip()
clock.tick(FPS)
pygame.quit()
sys.exit()
点击切换到全屏,点击退出全屏
代码中只存在一些小问题,比如在调用 check_ball_collision
方法时,我们直接从 normal
提取值,而没有检查 normal
是否为 None
。
除了这个以外,其他都还好。
程序的输出如下:
- Grok 3, 回复*
生成的代码如下:
import math
import random
import pygame
# 初始化 Pygame
pygame.init()
# 设置显示
WIDTH = 800
HEIGHT = 600
screen = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption("旋转六边形内的小球")
# 颜色
WHITE = (255, 255, 255)
RED = (255, 0, 0)
BLUE = (0, 0, 255)
# 六边形属性
center_x = WIDTH // 2
center_y = HEIGHT // 2
hex_size = 200
hex_angle = 0
hex_speed = 0.05 # 每帧的弧度
# 定义一个名为Ball的类:
class Ball:
def __init__(self):
self.radius = 10
self.x = center_x
self.y = center_y
self.dx = random.uniform(-3, 3)
self.dy = random.uniform(-3, 3)
self.color = (
random.randint(0, 255),
random.randint(0, 255),
random.randint(0, 255),
)
# 创建小球
balls = [Ball() for _ in range(10)]
# 获取顶点
def get_hexagon_vertices(x, y, size, angle):
vertices = []
for i in range(6):
vertex_angle = angle + (math.pi / 3) * i
vx = x + size * math.cos(vertex_angle)
vy = y + size * math.sin(vertex_angle)
vertices.append((vx, vy))
return vertices
# 碰撞检测
def check_collision(ball, vertices):
for i in range(6):
x1, y1 = vertices[i]
x2, y2 = vertices[(i + 1) % 6]
# 直线方程 ax + by + c = 0
a = y2 - y1
b = x1 - x2
c = -(a * x1 + b * y1)
# 点到直线的垂直距离
distance = abs(a * ball.x + b * ball.y + c) / math.sqrt(a * a + b * b)
if distance <= ball.radius:
# 法向量
nx = b
ny = -a
norm_length = math.sqrt(nx * nx + ny * ny)
nx /= norm_length
ny /= norm_length
# 反射球的速度
dot = ball.dx * nx + ball.dy * ny
ball.dx = ball.dx - 2 * dot * nx
ball.dy = ball.dy - 2 * dot * ny
# 将球推离六边形边
penetration = ball.radius - distance
ball.x += nx * penetration
ball.y += ny * penetration
# 主循环
clock = pygame.time.Clock()
running = True
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
# 清屏
screen.fill((0, 0, 0))
# 更新旋转角度
hex_angle += hex_speed
vertices = get_hexagon_vertices(center_x, center_y, hex_size, hex_angle)
# 更新小球位置
for ball in balls:
# 移动
ball.x += ball.dx
ball.y += ball.dy
# 碰撞检测
check_collision(ball, vertices)
# 绘制小球
pygame.draw.circle(screen, ball.color, (int(ball.x), int(ball.y)), ball.radius)
# 绘制六边形
pygame.draw.polygon(screen, WHITE, vertices, 2)
# 更新屏幕
pygame.display.flip()
clock.tick(60)
pygame.quit()
全屏 退出全屏
下面是程序的输出结果:
球不应该在六边形外面,它们应该在里面。
当我问它关于一个球在旋转的六边形内的标准问题时,它能轻松回答,但当我稍微改动问题,它就无能为力了。
说实话,这个模型应付不了这个问题。
OpenAI 的 o3-mini-high 模型的回复
这是它生成的代码如下:
#!/usr/bin/env python3
import math
import random
import pygame
def get_hexagon_vertices(center, radius, angle_offset):
"""
返回一个正六边形的六个顶点组成的pygame.Vector2列表。
center -- 中心pygame.Vector2。
radius -- 中心到顶点的距离。
angle_offset-- 整体旋转角度(弧度)。
"""
vertices = []
for i in range(6):
# 每个顶点之间相隔60度(π/3弧度)。
angle = math.radians(60 * i) + angle_offset
vertex = center + pygame.Vector2(math.cos(angle) * radius,
math.sin(angle) * radius)
vertices.append(vertex)
return vertices
def point_in_poly(x, y, poly):
"""
判断点(x, y)是否在多边形内。
poly是一个由(x, y)元组组成的列表。
(使用射线投射算法进行判断。)
"""
inside = False
n = len(poly)
p1x, p1y = poly[0]
for i in range(n+1):
p2x, p2y = poly[i % n]
if (y > min(p1y, p2y)) and (y <= max(p1y, p2y)) and (x <= max(p1x, p2x)):
if p1y != p2y:
xinters = (y - p1y) * (p2x - p1x) / (p2y - p1y) + p1x
else:
xinters = p1x
if p1x == p2x or x <= xinters:
inside = not inside
p1x, p1y = p2x, p2y
return inside
def main():
pygame.init()
# 窗口大小和标题
width, height = 800, 600
screen = pygame.display.set_mode((width, height))
pygame.display.set_caption("10 Balls Inside a Fast Spinning Hexagon")
clock = pygame.time.Clock()
hex_center = pygame.Vector2(width/2, height/2)
hex_radius = 200 # 中心到顶点的距离。
rotation_angle = 0 # 当前的旋转角度(弧度)。
rotation_speed = 5 # 每秒弧度(快速旋转)。
ball_radius = 10
n_balls = 10
balls = [] # 每个球是一个字典,包含键:pos(位置)、vel(速度)、color(颜色)。
# 使用六边形的初始(未旋转)顶点来放置球。
initial_vertices = get_hexagon_vertices(hex_center, hex_radius, rotation_angle)
# 将顶点转换为(x, y)元组的列表,用于point_in_poly测试。
initial_poly = [(v.x, v.y) for v in initial_vertices]
# 创建n_balls个球,随机位置在六边形内,随机速度。
while len(balls) < n_balls:
test_x = random.uniform(hex_center.x - hex_radius, hex_center.x + hex_radius)
test_y = random.uniform(hex_center.y - hex_radius, hex_center.y + hex_radius)
# 只接受在多边形内的位置:
if point_in_poly(test_x, test_y, initial_poly):
ball = {
'pos': pygame.Vector2(test_x, test_y),
# 随机速度(像素/秒)。调整数字以改变速度。
'vel': pygame.Vector2(random.uniform(-200, 200),
random.uniform(-200, 200)),
'color': (random.randint(50,255), random.randint(50,255), random.randint(50,255))
}
balls.append(ball)
running = True
while running:
dt = clock.tick(60) / 1000.0 # 时间增量(秒)。
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
# 可选:按下ESC键退出。
elif event.type == pygame.KEYDOWN and event.key == pygame.K_ESCAPE:
running = False
# 增加旋转角度。
rotation_angle += rotation_speed * dt
# (可选:将角度限制在2π以内。)
rotation_angle %= (2 * math.pi)
# 计算当前六边形的顶点。
hex_vertices = get_hexagon_vertices(hex_center, hex_radius, rotation_angle)
# 对于每条边从A到B,向内法向量选择如下:
# n = N/|N|,其中 N = (-(B.y-A.y), (B.x-A.x)) 或其负数,
# 并且它应该指向六边形中心。
edge_normals = []
n_edges = len(hex_vertices)
for i in range(n_edges):
A = hex_vertices[i]
B = hex_vertices[(i+1) % n_edges]
edge = B - A
# 候选法向量(旋转90度)
candidate = pygame.Vector2(-edge.y, edge.x)
# 检查 —— 选择指向六边形中心的法向量。
if candidate.dot(hex_center - A) < 0:
candidate = -candidate
n = candidate.normalize()
edge_normals.append((A, n))
# 更新每个球的位置,并检查与每条边的碰撞情况。
for ball in balls:
ball['pos'] += ball['vel'] * dt
# 检查与六边形每条边的碰撞情况。
for A, n in edge_normals:
# 计算球心沿向内法向量到边A的距离。
# (带符号的距离。)
d = (ball['pos'] - A).dot(n)
if d < ball_radius:
# 仅在球向墙移动时反射。
if ball['vel'].dot(n) < 0:
ball['vel'] = ball['vel'] - 2 * ball['vel'].dot(n) * n
# 推出球以消除重叠。
ball['pos'] += (ball_radius - d) * n
screen.fill((0, 0, 0)) # 用黑色填充屏幕。
# 绘制六边形的轮廓。
# 将六边形顶点从Vector2转换为元组。
hex_points = [(int(v.x), int(v.y)) for v in hex_vertices]
pygame.draw.polygon(screen, (255, 255, 255), hex_points, 3)
# 绘制每个球。
for ball in balls:
pos = (int(ball['pos'].x), int(ball['pos'].y))
pygame.draw.circle(screen, ball['color'], pos, ball_radius)
pygame.display.flip()
pygame.quit() # 退出pygame
点击此处进入全屏模式,点击此处退出全屏。
程序的输出如下:
最终结果:在这个问题上,我相当惊讶地看到,除了Grok 3模型之外,Claude 3.7 和 o3-mini-high 都答对了✅。o3-mini-high 的表现似乎比Grok 3好。
3. 构建一个实时、基于浏览器的 Markdown 编辑器
鉴于克劳德在游戏开发和动画测试中表现非常出色,再加上他们在编程方面的扎实表现,我们来做个简单的网页应用测试。这三个模型。
任务:使用 Tailwind CSS 在 Next.js 中构建一个具有语法高亮、导出 PDF 功能和极简界面的轻量级浏览器 Markdown 编辑器,并将所有更改都写在一个文件里。
- 克劳德 3.7 的回复
以下是生成的代码:
"use client";
import { useState, useEffect } from "react";
import { marked } from "marked";
import hljs from "highlight.js";
import "highlight.js/styles/github.css";
import { jsPDF } from "jspdf";
import html2canvas from "html2canvas";
// 配置marked以支持语法高亮,使用highlight.js
marked.setOptions({
highlight: function (code: string, lang: string) {
if (lang && hljs.getLanguage(lang)) {
return hljs.highlight(code, { language: lang }).value;
}
return hljs.highlightAuto(code).value;
},
breaks: true,
});
export default function MarkdownEditor() {
const [markdown, setMarkdown] = useState<string>(
'# 开始在这里编辑Markdown\n\n在这里开始输入Markdown...\n\n```\njavascript\n// 代码示例\nfunction hello() {\n console.log("Hello, world!");\n}\n```\n',
);
const [html, setHtml] = useState<string>("");
const [isDark, setIsDark] = useState<boolean>(false);
// 当输入发生变化时,将Markdown转换为HTML
useEffect(() => {
const convertMarkdown = async () => {
const parsedHtml = await marked.parse(markdown);
setHtml(parsedHtml);
};
convertMarkdown();
}, [markdown]);
// 切换暗黑模式
const toggleDarkMode = () => {
setIsDark(!isDark);
};
// 在导出为PDF时将背景设为白色
const exportToPdf = async () => {
const previewElement = document.getElementById("preview");
if (!previewElement) return;
const originalBackground = previewElement.style.background;
const originalColor = previewElement.style.color;
previewElement.style.background = "white";
previewElement.style.color = "black";
previewElement.style.padding = "20px";
try {
const canvas = await html2canvas(previewElement, {
scale: 2,
useCORS: true,
logging: false,
});
const imgData = canvas.toDataURL("image/png");
const pdf = new jsPDF({
orientation: "portrait",
unit: "mm",
format: "a4",
});
const pdfWidth = pdf.internal.pageSize.getWidth();
const pdfHeight = pdf.internal.pageSize.getHeight();
const imgWidth = canvas.width;
const imgHeight = canvas.height;
const ratio = Math.min(pdfWidth / imgWidth, pdfHeight / imgHeight);
const imgX = (pdfWidth - imgWidth * ratio) / 2;
const imgY = 30;
pdf.addImage(
imgData,
"PNG",
imgX,
imgY,
imgWidth * ratio,
imgHeight * ratio,
);
pdf.save("markdown-document.pdf");
} finally {
previewElement.style.background = originalBackground;
previewElement.style.color = originalColor;
previewElement.style.padding = "";
}
};
return (
<div
className={`min-h-screen ${isDark ? "bg-gray-900 text-white" : "bg-gray-100 text-gray-800"}`}
>
<header
className={`p-4 flex justify-between items-center ${isDark ? "bg-gray-800" : "bg-white border-b"}`}
>
<h1 className="text-xl font-bold">Markdown编辑器</h1>
<div className="flex gap-2">
<button
onClick={toggleDarkMode}
className={`px-3 py-1 rounded ${isDark ? "bg-gray-700 hover:bg-gray-600" : "bg-gray-200 hover:bg-gray-300"}`}
>
{isDark ? "☀️ Light" : "🌙 Dark"}
</button>
<button
onClick={exportToPdf}
className="px-3 py-1 rounded bg-blue-500 text-white hover:bg-blue-600"
>
导出为PDF
</button>
</div>
</header>
<div className="flex flex-col md:flex-row h-[calc(100vh-64px)]">
<div className="w-full md:w-1/2 h-1/2 md:h-full p-2">
<textarea
className={`w-full h-full p-4 font-mono text-sm resize-none focus:outline-none rounded-md ${
isDark
? "bg-gray-800 text-gray-100"
: "bg-white border text-gray-800"
}`}
value={markdown}
onChange={(e) => setMarkdown(e.target.value)}
placeholder="在此编辑Markdown..."
/>
</div>
<div className="w-full md:w-1/2 h-1/2 md:h-full p-2">
<div
id="preview"
className={`w-full h-full p-4 overflow-auto rounded-md prose ${
isDark ? "bg-gray-800 prose-invert" : "bg-white border"
} max-w-none`}
dangerouslySetInnerHTML={{ __html: html }}
/>
</div>
</div>
</div>
);
}
全屏进入 退出全屏
代码几乎都很好,除了代码高亮功能不起作用。这似乎是因为最近 marked
更新时引入了一些不兼容的更改。
模型可能没有用最近的数据来训练,这些数据来自该模块。
程序的输出如下:
这里是你导出的PDF:
点击链接查看
- Grok 3 的回答
这里有一个生成的代码:
"use client";
import { useState } from "react";
import ReactMarkdown from "react-markdown";
import { Prism as SyntaxHighlighter } from "react-syntax-highlighter";
import { dracula } from "react-syntax-highlighter/dist/esm/styles/prism";
import { jsPDF } from "jspdf";
export default function MarkdownEditor() {
const [content, setContent] = useState<string>(`# 欢迎使用 Markdown 编辑工具
在这里可以输入一些 **加粗** 文本或 *斜体* 文本。
\`\`\`javascript
// 以下是一个代码示例
function hello() {
console.log("Hello, World!");
}
\`\`\`
`);
const handleExportPDF = () => {
const doc = new jsPDF();
const pageWidth = doc.internal.pageSize.getWidth();
const margin = 20;
const maxWidth = pageWidth - 2 * margin;
// 将内容拆分为行并应用基本格式
const lines = doc.splitTextToSize(content, maxWidth);
let y = margin;
lines.forEach((line: string) => {
if (y > 280) {
doc.addPage();
y = margin;
}
doc.text(line, margin, y);
y += 7;
});
doc.save("markdown-export.pdf");
};
return (
<div className="min-h-screen bg-gray-100 flex flex-col">
<header className="bg-white shadow-sm p-4 flex justify-between items-center">
<h1 className="text-xl font-semibold text-gray-800">Markdown 编辑器页面</h1>
<button
onClick={handleExportPDF}
className="px-4 py-2 bg-blue-600 text-white rounded hover:bg-blue-700 transition-colors"
>
导出为 PDF
</button>
</header>
<main className="flex-1 flex p-6 gap-6 max-w-7xl mx-auto w-full">
{/* 编辑器 */}
<div className="flex-1">
<textarea
value={content}
onChange={(e) => setContent(e.target.value)}
className="w-full h-[calc(100vh-120px)] p-4 bg-white rounded-lg shadow-sm border border-gray-200 focus:outline-none focus:ring-2 focus:ring-blue-500 font-mono text-sm resize-none"
placeholder="开始撰写您的 Markdown 文本..."
/>
</div>
{/* 预览 */}
<div className="flex-1">
<div className="w-full h-[calc(100vh-120px)] p-4 bg-white rounded-lg shadow-sm border border-gray-200 overflow-auto">
<ReactMarkdown
components={{
code({ node, inline, className, children, ...props }) {
const match = /language-(\w+)/.exec(className || "");
return !inline && match ? (
<SyntaxHighlighter
style={dracula}
language={match[1]}
PreTag="div"
{...props}
>
{String(children).replace(/\n$/, "")}
</SyntaxHighlighter>
) : (
<code className={className} {...props}>
{children}
</code>
);
},
}}
>
{content}
</ReactMarkdown>
</div>
</div>
</main>
</div>
);
}
全屏 退出全屏
我们遇到了几个问题,第一个问题是标题的效果不太好。字体对比度不够好,而且导出的PDF没有正确显示Markdown。
这里是程序的输出结果:
在导出的 PDF 中,我们得到的是原始文本而不是 Markdown 格式,也不支持表情符号。
- 来自OpenAI的o3-mini-high的回答
这是它生成的代码:
"use client";
import { useState, useRef } from "react";
import Head from "next/head";
import ReactMarkdown from "react-markdown";
import rehypeHighlight from "rehype-highlight";
import html2pdf from "html2pdf.js";
import "highlight.js/styles/github-dark.css";
export default function Home() {
const [markdown, setMarkdown] = useState(`# 欢迎来到 Markdown 编辑器
\`\`\`javascript
// 在这里写一些 JavaScript 代码:
function greet(name) {
return \`Hello, \${name}!\`;
}
console.log(greet("World"));
\`\`\`
**尽情编辑吧!**
`);
const previewRef = useRef(null);
const exportPDF = () => {
if (previewRef.current) {
const options = {
margin: 0.5,
filename: "markdown_document.pdf",
image: { type: "jpeg", quality: 0.98 },
html2canvas: { scale: 2 },
jsPDF: { unit: "in", format: "letter", orientation: "portrait" },
};
html2pdf().set(options).from(previewRef.current).save();
}
};
return (
<>
<Head>
<title>Markdown 编辑器</title>
<meta name="viewport" content="width=device-width, initial-scale=1" />
{/* 删除此行,如果已经配置了 Tailwind */}
<script src="https://cdn.tailwindcss.com"></script>
</Head>
<div className="min-h-screen bg-gray-100 p-4">
<h1 className="text-3xl font-bold text-center mb-4">Markdown 编辑器</h1>
<div className="flex flex-col md:flex-row gap-4">
<textarea
value={markdown}
onChange={(e) => setMarkdown(e.target.value)}
placeholder="在这里输入 Markdown 文本..."
className="w-full md:w-1/2 h-80 p-4 rounded border border-gray-300 focus:outline-none focus:ring-2 focus:ring-blue-500"
/>
<div
ref={previewRef}
className="w-full md:w-1/2 h-80 p-4 rounded border border-gray-300 bg-white overflow-auto"
>
<ReactMarkdown rehypePlugins={[rehypeHighlight]}>
{markdown}
</ReactMarkdown>
</div>
</div>
<div className="mt-4 flex justify-center">
<button
onClick={exportPDF}
className="px-4 py-2 bg-blue-500 text-white rounded hover:bg-blue-600 focus:outline-none"
>
导出
</button>
</div>
</div>
</>
);
}
进入全屏,退出全屏
这是程序的输出结果。
(点击可查看大图)
这是生成的PDF文件:
总结:同样地,Claude 3.7 也明显胜出其他两个模型。✅ 几乎所有都正常,而另外两个模型则无法做到。还有一些问题,比如文本对比问题、网站上和 PDF 中的 Markdown 渲染问题。
4. 做一个代码对比工具
我们来试试一个简单的网页应用例子,看看他们都懂了吗
这是一个相当标准的问题,实现起来容易。我对这三个都挺有信心,觉得它们都能做对(甚至包括o3-mini-high 😮💨)。到目前为止,我有点失望。
任务描述:开发一个简单的网页应用,用于显示代码差异,接受两个文本输入并在旁边突出显示差异。
- 克劳德 3.7 回复一首十四行诗
生成的代码如下:
"use client";
import { useState, useEffect } from "react";
export default function CodeDiffViewer() {
const [leftCode, setLeftCode] = useState("");
const [rightCode, setRightCode] = useState("");
const [diffLines, setDiffLines] = useState<{
left: { text: string; changed: boolean }[];
right: { text: string; changed: boolean }[];
}>({
left: [],
right: [],
});
useEffect(() => {
computeDiff(leftCode, rightCode);
}, [leftCode, rightCode]);
// 一个简单的差异算法,用于查找两个文本之间的不同
const computeDiff = (leftText: string, rightText: string) => {
const leftLines = leftText.split("\n");
const rightLines = rightText.split("\n");
const left: { text: string; changed: boolean }[] = [];
const right: { text: string; changed: boolean }[] = [];
const maxLength = Math.max(leftLines.length, rightLines.length);
for (let i = 0; i < maxLength; i++) {
const leftLine = i < leftLines.length ? leftLines[i] : "";
const rightLine = i < rightLines.length ? rightLines[i] : "";
const isChanged = leftLine !== rightLine;
left.push({ text: leftLine, changed: isChanged });
right.push({ text: rightLine, changed: isChanged });
}
setDiffLines({ left, right });
};
return (
<div className="min-h-screen bg-gray-100 py-8">
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<div className="text-center">
<h1 className="text-3xl font-bold text-gray-900 mb-8">
代码对比查看器
</h1>
</div>
<div className="grid grid-cols-1 md:grid-cols-2 gap-4 mb-8">
<div>
<label
htmlFor="leftCode"
className="block text-sm font-medium text-gray-700 mb-2"
>
原始版本的代码
</label>
<textarea
id="leftCode"
className="w-full text-black h-64 p-3 border border-gray-300 rounded-md shadow-sm focus:ring-indigo-500 focus:border-indigo-500"
value={leftCode}
onChange={(e) => setLeftCode(e.target.value)}
placeholder="请在此粘贴您的原始代码..."
></textarea>
</div>
<div>
<label
htmlFor="rightCode"
className="block text-sm font-medium text-gray-700 mb-2"
>
修改过的代码
</label>
<textarea
id="rightCode"
className="w-full text-black h-64 p-3 border border-gray-300 rounded-md shadow-sm focus:ring-indigo-500 focus:border-indigo-500"
value={rightCode}
onChange={(e) => setRightCode(e.target.value)}
placeholder="请在此粘贴您的修改过的代码..."
></textarea>
</div>
</div>
<div className="bg-white shadow-md rounded-lg overflow-hidden">
<div className="p-4 bg-gray-50 border-b border-gray-200">
<h2 className="text-lg font-medium text-gray-900">差异显示</h2>
</div>
<div className="grid grid-cols-1 md:grid-cols-2 gap-0">
<div className="border-r border-gray-200">
<div className="p-1 bg-gray-100 border-b border-gray-200 text-sm font-medium text-gray-700 text-center">
原始
</div>
<pre className="p-4 text-sm text-black overflow-x-auto">
<code>
{diffLines.left.map((line, idx) => (
<div
key={idx}
className={`${line.changed ? "bg-red-100" : ""} py-1 px-2 -mx-2 whitespace-pre`}
>
{line.text || " "}
</div>
))}
</code>
</pre>
</div>
<div>
<div className="p-1 bg-gray-100 border-b border-gray-200 text-sm font-medium text-gray-700 text-center">
修改后
</div>
<pre className="p-4 text-sm text-black overflow-x-auto">
<code>
{diffLines.right.map((line, idx) => (
<div
key={idx}
className={`${line.changed ? "bg-green-100" : ""} py-1 px-2 -mx-2 whitespace-pre`}
>
{line.text || " "}
</div>
))}
</code>
</pre>
</div>
</div>
</div>
<div className="mt-8 text-center text-gray-500 text-sm">
<p>
请在此粘贴代码查看差异。
</p>
</div>
</div>
</div>
);
}
全屏,退出全屏
程序的输出如下。
- Grok 3的回答
这是它生成的代码:
"use client";
import { useState } from "react";
function calculateDiff(text1: string, text2: string) {
const lines1 = text1.split("\n");
const lines2 = text2.split("\n");
const maxLength = Math.max(lines1.length, lines2.length);
const diff = [];
for (let i = 0; i < maxLength; i++) {
const line1 = lines1[i] || "";
const line2 = lines2[i] || "";
if (line1 !== line2) {
diff.push({
left: line1,
right: line2,
isChanged: true,
leftClass: line1 ? "bg-red-100" : "bg-gray-100",
rightClass: line2 ? "bg-green-100" : "bg-gray-100",
});
} else {
diff.push({
left: line1,
right: line2,
isChanged: false,
leftClass: "",
rightClass: "",
});
}
}
return diff;
}
export default function Home() {
const [leftText, setLeftText] = useState("");
const [rightText, setRightText] = useState("");
const diffResult = calculateDiff(leftText, rightText);
return (
<div className="min-h-screen bg-gray-100 p-8 text-black">
<div className="max-w-6xl mx-auto">
<h1 className="text-3xl font-bold text-center mb-8">
代码差异查看工具
</h1>
<div className="grid grid-cols-2 gap-4">
{/* 左侧区域 */}
<div className="space-y-2">
<label className="block text-sm font-medium text-gray-700">
原始代码
</label>
<textarea
className="w-full h-64 p-4 border rounded-lg font-mono text-sm resize-none focus:outline-none focus:ring-2 focus:ring-blue-500"
value={leftText}
onChange={(e) => setLeftText(e.target.value)}
placeholder="请输入原始代码..."
/>
</div>
{/* 右侧区域 */}
<div className="space-y-2">
<label className="block text-sm font-medium text-gray-700">
修改代码
</label>
<textarea
className="w-full h-64 p-4 border rounded-lg font-mono text-sm resize-none focus:outline-none focus:ring-2 focus:ring-blue-500"
value={rightText}
onChange={(e) => setRightText(e.target.value)}
placeholder="请输入修改代码..."
/>
</div>
</div>
{/* 差异显示 */}
<div className="mt-8">
<h2 className="text-xl font-semibold mb-4">差异</h2>
<div className="grid grid-cols-2 gap-1 border rounded-lg overflow-hidden">
<div className="bg-gray-50 p-4">
<div className="font-mono text-sm">
{diffResult.map((line, index) => (
<div
key={`left-${index}`}
className={`py-1 ${line.leftClass} ${
line.isChanged ? "text-gray-800" : "text-gray-600"
}`}
>
{line.left || " "}
</div>
))}
</div>
</div>
<div className="bg-gray-50 p-4">
<div className="font-mono text-sm">
{diffResult.map((line, index) => (
<div
key={`right-${index}`}
className={`py-1 ${line.rightClass} ${
line.isChanged ? "text-gray-800" : "text-gray-600"
}`}
>
{line.right || " "}
</div>
))}
</div>
</div>
</div>
</div>
</div>
</div>
);
}
全屏 退出全屏
程序的输出如下:
其他方面似乎都正常工作,但在比较中,并没有考虑行的缩进。
- OpenAI o3-mini-high 的回答
以下是生成的代码:
"use client";
import React, { JSX, useState } from "react";
import { diffWords } from "diff";
export default function Page() {
// 用于管理两个文本输入框及其计算出的差异结果的状态
const [leftText, setLeftText] = useState("");
const [rightText, setRightText] = useState("");
// diffResult 存储了一个由对象组成的数组,每个对象包含两个 JSX 元素,分别对应左右两侧
const [diffResult, setDiffResult] = useState<
{ left: JSX.Element; right: JSX.Element }[]
>([]);
// 按行比较两个文本
const computeDiff = () => {
const leftLines = leftText.split("\n");
const rightLines = rightText.split("\n");
const maxLines = Math.max(leftLines.length, rightLines.length);
const results: { left: JSX.Element; right: JSX.Element }[] = [];
for (let i = 0; i < maxLines; i++) {
const leftLine = leftLines[i] ?? "";
const rightLine = rightLines[i] ?? "";
// 如果行内容相同,就以普通文本形式显示
if (leftLine === rightLine) {
results.push({
left: <span>{leftLine}</span>,
right: <span>{rightLine}</span>,
});
} else {
const diff = diffWords(leftLine, rightLine);
// 对于左侧,忽略被添加的部分,用红色突出显示被删除的部分
const leftContent = diff.map((part, index) => {
if (part.added) return null;
return (
<span
key={index}
className={part.removed ? "bg-red-200" : undefined}
>
{part.value}
</span>
);
});
// 对于右侧,忽略被删除的部分,用绿色突出显示被添加的部分
const rightContent = diff.map((part, index) => {
if (part.removed) return null;
return (
<span
key={index}
className={part.added ? "bg-green-200" : undefined}
>
{part.value}
</span>
);
});
results.push({
left: <>{leftContent}</>,
right: <>{rightContent}</>,
});
}
}
setDiffResult(results);
};
return (
<div className="min-h-screen bg-gray-50 p-8 text-black">
<h1 className="text-3xl font-bold mb-6 text-center">
简易代码对比工具
</h1>
{/* 相对排列的两个文本输入框(在小屏幕上为垂直排列) */}
<div className="flex flex-col md:flex-row md:space-x-4">
<textarea
value={leftText}
onChange={(e) => setLeftText(e.target.value)}
placeholder="输入左侧文本"
className="flex-1 p-2 border border-gray-300 rounded mb-4 md:mb-0 resize-y min-h-[200px]"
/>
<textarea
value={rightText}
onChange={(e) => setRightText(e.target.value)}
placeholder="输入右侧文本"
className="flex-1 p-2 border border-gray-300 rounded resize-y min-h-[200px]"
/>
</div>
<div className="mt-4 text-center">
<button
onClick={computeDiff}
className="px-4 py-2 bg-blue-500 text-white rounded hover:bg-blue-600"
>
比对
</button>
</div>
{/* 差异查看器:一个两列的网格布局,分别显示左右两侧的差异 */}
<div className="mt-8 overflow-auto">
<div className="grid grid-cols-2 gap-4">
{diffResult.map((row, index) => (
<React.Fragment key={index}>
<div className="p-2 border border-gray-300 bg-white min-h-[40px] whitespace-pre font-mono">
{row.left}
</div>
<div className="p-2 border border-gray-300 bg-white min-h-[40px] whitespace-pre font-mono">
{row.right}
</div>
</React.Fragment>
))}
</div>
</div>
</div>
);
}
切换到全屏 退出全屏
程序的输出如下:
在这里,我们有一个有趣的结果。它选择了使用外部库diff
,并且高亮显示是按字符而不是按行进行的。
最终结论:从功能上来说,这三个模型都正确地解决了这个问题(至少从表面测试来看。可能还存在一些边缘情况)。不得不说,o3-mini-high 的整体代码质量和输出结果✅都比 Claude 3.7 Sonnet 和 Grok 3 模型要好。
5. Manim 代码用于正方形转成立方体的动画
让我们以一个最后的Manim问题来结束这次测试。大多数大型语言模型(LLMs)基本上都不擅长写Manim代码,特别是3D场景,这会更加困难。
任务:用Python制作一个Manim动画,其中,一个二维正方形平滑地升入三维空间,形成一个金字塔。接着,动画显示金字塔缩小回二维正方形。
- 克劳德 3.7 的十四行诗回复
这里生成的代码是:
import numpy as np
from manim import (BLUE, DEGREES, PI, UP, WHITE, Create, FadeOut, Polygon,
Rotate, Square, ThreeDScene, VGroup)
class SquareToPyramidAnimation(ThreeDScene):
def construct(self):
# 设置场景
self.set_camera_orientation(phi=75 * DEGREES, theta=30 * DEGREES)
# 为正方形的顶点添加z轴坐标,使其适应三维
square = Square(side_length=2, fill_opacity=0.7, fill_color=BLUE)
# 先展示正方形
self.play(Create(square))
self.wait(1)
# 准备进行3D变换
# 为正方形的顶点添加z轴坐标,使其适应三维
square_3d = square.copy()
square_points = square_3d.get_vertices()
# 创建一个金字塔,用于生成金字塔形状
pyramid = Pyramid(square_length=2, height=2)
pyramid.set_fill(BLUE, opacity=0.7)
pyramid.set_stroke(WHITE, width=1)
# 金字塔初始时是隐藏的
pyramid.set_opacity(0)
self.add(pyramid)
# 动画化正方形变为金字塔的底面
self.play(
square.animate.set_opacity(0), pyramid.animate.set_opacity(0.7), run_time=2
)
# 移动摄像机以获得更好的金字塔视角
self.move_camera(phi=60 * DEGREES, theta=45 * DEGREES, run_time=2)
self.wait(1)
# 旋转金字塔以展示其三维特性
self.play(Rotate(pyramid, angle=2 * PI, axis=UP), run_time=4)
self.wait(1)
# 动画化金字塔缩小回正方形
self.play(
pyramid.animate.set_opacity(0), square.animate.set_opacity(0.7), run_time=2
)
# 恢复摄像机到初始位置
self.move_camera(phi=75 * DEGREES, theta=30 * DEGREES, run_time=2)
self.wait(1)
# 最后让正方形淡出
self.play(FadeOut(square))
self.wait(1)
class Pyramid(VGroup):
def __init__(self, square_length=2, height=2, **kwargs):
super().__init__(**kwargs)
# 在z=0处创建正方形底面
square = Square(side_length=square_length)
square_vertices = square.get_vertices()
# 金字塔的顶点在正方形中心上方的高度位置
apex = np.array([0, 0, height])
# 创建金字塔的面
faces = VGroup()
# 底面(正方形本身)
base = Polygon(*square_vertices, fill_opacity=0.7, fill_color=BLUE)
faces.add(base)
# 侧面三角形面
for i in range(4):
v1 = square_vertices[i]
v2 = square_vertices[(i + 1) % 4]
face = Polygon(
v1, v2, apex, fill_opacity=0.7, fill_color=BLUE, shade_in_3d=True
)
faces.add(face)
self.add(faces)
点这里进入全屏 点击退出全屏
程序的输出如下所示:
- Grok 3 的回答
以下是它生成的代码:
from manim import *
class SquareToPyramid(ThreeDScene):
# 从二维正方形转换为三维金字塔的类定义
def construct(self):
# 设置相机视角为phi=75度,theta=30度
self.set_camera_orientation(phi=75 * DEGREES, theta=30 * DEGREES)
# 创建初始的2D正方形
square = Square(side_length=2, fill_opacity=0.5, fill_color=BLUE)
square.move_to(ORIGIN)
# 显示初始的正方形
self.play(Create(square))
self.wait(1)
# 定义正方形的底部顶点
bottom_vertices = [
[-1, -1, 0], # 左下角
[1, -1, 0], # 右下角
[1, 1, 0], # 右上角
[-1, 1, 0], # 左上角
]
# 定义金字塔的顶点(顶点将升至该位置)
apex = [0, 0, 2]
# 使用Polygon创建金字塔的四个面
pyramid_faces = VGroup()
for i in range(4):
face = Polygon(
bottom_vertices[i],
bottom_vertices[(i + 1) % 4],
apex,
fill_opacity=0.5,
fill_color=BLUE,
stroke_color=WHITE,
)
pyramid_faces.add(face)
# 动画将正方形变成金字塔
# 首先,正方形淡出,同时将顶点上升
self.begin_ambient_camera_rotation(rate=0.2)
self.play(FadeOut(square), Create(pyramid_faces), run_time=2)
# 旋转金字塔以展示其3D结构
self.wait(2)
# 将金字塔缩小回正方形
# 创建最终状态的新正方形
final_square = Square(side_length=2, fill_opacity=0.5, fill_color=BLUE)
final_square.move_to(ORIGIN)
self.play(FadeOut(pyramid_faces), Create(final_square), run_time=2)
# 停止相机旋转并展示最终结果
self.stop_ambient_camera_rotation()
self.wait(1)
切换到全屏/退出全屏
程序的输出如下:
- o3-mini-high 的回复
以下是生成的代码:
#!/usr/bin/env python
from manim import (BLUE, DEGREES, GREEN, UP, Create, Line,
ReplacementTransform, Square, ThreeDScene, ValueTracker,
VGroup, always_redraw)
class SquareToPyramid(ThreeDScene):
def construct(self):
# 设置初始相机角度,以更清晰地显示3D效果
self.set_camera_orientation(phi=70 * DEGREES, theta=-45 * DEGREES)
# 创建一个边长为2的蓝色正方形作为底面
square = Square(side_length=2, color=BLUE)
self.play(Create(square))
self.wait(1)
# 我们将使用一个ValueTracker来控制金字塔的“顶点高度”。
# 当值为0时,顶点位于正方形的中心。
# 当值大于0时,顶点向上移动(UP表示z轴)。
height_tracker = ValueTracker(0)
# 定义一个根据给定高度返回绘制金字塔的VGroup的函数。
# 它构建:
# • 底部(即正方形的四个角,用蓝色绘制的四条线)
# • 四个侧面(从正方形的每个角到顶点的四条边,用绿色绘制)
def get_pyramid(height):
# 获取正方形的顶点(即四个角);默认情况下,正方形的顶点是有序的。
corners = square.get_vertices()
# 定义顶点:从正方形的中心开始,并在向上(z)方向上添加"height"。
apex = square.get_center() + height * UP
# 创建底部边作为闭合环。
base_lines = VGroup(
*[Line(corners[i], corners[(i + 1) % len(corners)], color=BLUE) for i in range(len(corners))]
)
# 创建从每个角到顶点的侧面边。
side_lines = VGroup(
*[Line(corner, apex, color=GREEN) for corner in corners]
)
return VGroup(base_lines, side_lines)
# 创建一个always_redraw mobject,这样每当height_tracker变化时,
# 金字塔将根据新的顶点高度重新绘制。
pyramid = always_redraw(lambda: get_pyramid(height_tracker.get_value()))
# 从高度为0的静态正方形过渡到金字塔对象。(此时侧面边长为0,因此绘制看起来与正方形相同)
self.play(ReplacementTransform(square, pyramid), run_time=1)
self.wait(0.5)
# 动画展示金字塔‘提升’过程,通过增加跟踪的高度。
self.play(height_tracker.animate.set_value(2), run_time=3)
self.wait(1)
# 现在动画展示金字塔‘缩小’回正方形,通过降低顶点。
self.play(height_tracker.animate.set_value(0), run_time=3)
self.wait(1)
# 可选地,执行轻微的相机移动,以进一步展示3D特性。
self.move_camera(phi=75 * DEGREES, theta=-30 * DEGREES, run_time=2)
self.wait(1)
点击进入全屏模式,点击退出全屏模式.
程序的输出如下 :
在这里,这个模型在3D投影上表现得很差,无法成功将正方形转换为金字塔。
结论:Claude 3.7 Sonnet 和 Grok 3 模型都做得对,但在动画上,我更喜欢 Grok 3 做出来的动画 ✅。o3-mini-high 则完全不行,甚至连接近解决的办法都没有。
结语
可以说,Claude 3.7 确实做到了它所说的。在我们比较的五个问题中,它每次都占上风。
现在不是等待运气的时候。想象一个AI模型能够瞬间以62.3%的准确率快速搭建东西。开始动手搭建并提升你的编程能力,不然就得准备好被AI取代了(🤷♂️)。
这并不意味着克劳德 3.7 是解决所有问题的答案,未来可能会有其他 AI 模型击败这个模型的记录。
AI模型之间的竞赛永不停歇,永远没完没了!
你咋看?下面留言区告诉我你的想法!
点击这里访问shricodev页面,看这张图片 /shricodev
共同学习,写下你的评论
暂无评论
作者其他优质文章