发布于 2026-01-06 6 阅读
0

Claude 3.7 Sonnet vs. Grok 3 vs. o3-mini-high:代码比较 DEV 的全球展示挑战赛,由 Mux 呈现:展示你的项目!

Claude 3.7 Sonnet 与 Grok 3 与 o3-mini-high:编码比较

由 Mux 主办的 DEV 全球展示挑战赛:展示你的项目!

最近几周,科技界对每隔几天就涌现出的各种新型人工智能模型感到惊讶。

震惊的用户

但问题是:大多数开发人员在日常编写代码时并不确定应该使用哪种 AI 模型。

就在几天前,Anthropic发布了他们迄今为止最智能的混合推理模型,他们声称这是最适合编程的模型。

克劳德 3.7 十四行诗

但让我们看看它与我们最好的两款型号Grok 3o3-mini-high相比如何。

太长不看

如果要直接得出结论,与这三个模型相比,Claude 3.7 Sonnet 在编写代码方面显然是赢家。

Grok 3 和 o3-mini-high 有些相似,但如果非要将它们进行比较,我会说Grok 3 生成的代码比 o3-mini-high 模型略好一些。

Grok 3 对比 openai o3-mini-high


克劳德3.7十四行诗简介

这款人工智能模型几天前才发布,就已经在“科技”圈内引起了热议。我之所以强调“科技”圈,是因为这款模型被广泛认为是目前为止最好的代码人工智能模型。

您可以观看bycloud制作的这段视频,简要了解一下这款机型的性能。

Claude 3.7 Sonnet 最多支持128K 个输出标记(测试版),比以前增加了 15 倍以上。这在生成更长、更高质量的代码时尤其方便。

它在SWE基准测试中遥遥领先于其他所有AI模型,平均准确率高达62.3%。其准确率甚至可以超过70%,是迄今为止所有AI模型中最高的。与OpenAI的顶级模型、之前的Anthropic模型Claude 3.5以及DeepSeek R1等开源模型相比,它的准确率差距达到了13%至20%

Claude 3.7 AI 模型软件工程基准测试

尽管功能强大,Claude 3.7 的总成本仍比之前的型号降低了 18%。它保持了基于代币的稳定定价模式,每百万个输入代币 3 美元每百万个输出代币 15 美元。

不仅如此,Anthropic还同时发布了Claude Code ,这是一个智能 AI CLI,它可以理解你的代码库,帮助解决问题,回答问题,并通过其 Git 集成帮助你了解项目历史。

观看此视频可快速了解其含义:


比较这些模型的编码能力

💁‍♂️ 不过就我个人而言,我对 Claude 3.7 Sonnet 抱有很高的期望。让我们看看 Sonnet 与其他两款机型在代码上是否有任何重大差异。

让我们先从一些有趣的事情开始:

1. 使用 Pygame 构建一个简单的 Minecraft 游戏

任务很简单,要求这三位LLM学生使用Pygame构建一个简单的Minecraft游戏。

提示:请使用 Python 中的 Pygame 为我编写一个非常简单的 Minecraft 游戏。

  • 克劳德的回应​​ 3.7 十四行诗

以下是它生成的代码:

import random
import sys

import pygame

# Initialize Pygame
pygame.init()

# Game settings
SCREEN_WIDTH = 800
SCREEN_HEIGHT = 600
BLOCK_SIZE = 40
GRAVITY = 0.5
JUMP_STRENGTH = 10
PLAYER_SPEED = 5

# Colors
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)

# Create screen
screen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT))
pygame.display.set_caption("Simple Minecraft")
clock = pygame.time.Clock()

# Block types
EMPTY = 0
GRASS = 1
DIRT = 2
STONE = 3
COAL = 4

# Block colors
block_colors = {
    GRASS: GRASS_COLOR,
    DIRT: DIRT_COLOR,
    STONE: STONE_COLOR,
    COAL: COAL_COLOR,
}

# Define player
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):
        # Move horizontally
        self.rect.x += dx
        self.check_collision(dx, 0, blocks)

        # Apply gravity
        self.velocity_y += GRAVITY
        self.rect.y += int(self.velocity_y)
        self.on_ground = False
        self.check_collision(0, self.velocity_y, blocks)

        # Keep player on screen
        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:  # Moving right
                            self.rect.right = block_rect.left
                        if dx < 0:  # Moving left
                            self.rect.left = block_rect.right
                        if dy > 0:  # Moving down
                            self.rect.bottom = block_rect.top
                            self.on_ground = True
                            self.velocity_y = 0
                        if dy < 0:  # Moving up
                            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)

# Create world
def create_world(width, height):
    world = [[EMPTY for _ in range(width)] for _ in range(height)]

    # Generate terrain
    ground_height = height - 5

    for x in range(width):
        height_variation = random.randint(-1, 1)
        ground_height += height_variation

        # Keep within bounds
        if ground_height < height - 10:
            ground_height = height - 10
        if ground_height > height - 3:
            ground_height = height - 3

        # Add grass at the top
        world[ground_height][x] = GRASS

        # Add dirt below grass
        for y in range(ground_height + 1, ground_height + 4):
            if y < height:
                world[y][x] = DIRT

        # Add stone below dirt
        for y in range(ground_height + 4, height):
            if y < height:
                if random.random() < 0.1:  # 10% chance for coal
                    world[y][x] = COAL
                else:
                    world[y][x] = STONE

    return world

# Initialize game
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)

# Game loop
running = True
while running:
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            running = False

        # Jump on space
        if event.type == pygame.KEYDOWN:
            if event.key == pygame.K_SPACE:
                player.jump()

            # Switch selected block
            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

        # Place or break blocks with mouse
        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

            # Make sure it's within world bounds
            if 0 <= block_x < world_width and 0 <= block_y < world_height:
                # Left click to place block
                if event.button == 1:
                    # Don't place if player is there
                    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

                # Right click to break block
                elif event.button == 3:
                    world[block_y][block_x] = EMPTY

    # Handle movement
    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)

    # Draw everything
    screen.fill(SKY_COLOR)

    # Draw blocks
    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),
                )

    # Draw player
    player.draw(screen)

    # Draw inventory
    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)

        # Highlight selected block
        if block_type == player.selected_block:
            pygame.draw.rect(screen, (255, 255, 255), block_rect, 2)

        # Draw number
        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))

    # Update display
    pygame.display.flip()
    clock.tick(60)

pygame.quit()
sys.exit()
Enter fullscreen mode Exit fullscreen mode

以下是程序的输出结果:

使用 Claude 3.7 构建的 Minecraft 示例项目

  • Grok 3 的回复

以下是它生成的代码:

import pygame

# Initialize Pygame
pygame.init()

# Screen settings
WIDTH = 800
HEIGHT = 600
BLOCK_SIZE = 40
screen = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption("Simple Minecraft 2D")

# Colors
WHITE = (255, 255, 255)
BLACK = (0, 0, 0)
BROWN = (139, 69, 19)
GREEN = (0, 255, 0)
BLUE = (0, 0, 255)

# Player class
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))

# Block class
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))

# Create game objects
player = Player(WIDTH // 2, HEIGHT // 2)
blocks = []

# Generate initial terrain
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"))

# Game loop
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()
            # Left click to break block
            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
            # Right click to place block
            elif event.button == 3 and inventory:
                grid_x = (mouse_x // BLOCK_SIZE) * BLOCK_SIZE
                grid_y = (mouse_y // BLOCK_SIZE) * BLOCK_SIZE
                # Check if space is empty
                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()))

    # Player movement
    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

    # Draw everything
    screen.fill(WHITE)
    for block in blocks:
        block.draw()
    player.draw()

    # Display inventory
    font = pygame.font.Font(None, 36)
    text = font.render(f"Inventory: {len(inventory)} blocks", True, BLACK)
    screen.blit(text, (10, 10))

    pygame.display.flip()
    clock.tick(60)

pygame.quit()
Enter fullscreen mode Exit fullscreen mode

以下是程序的输出结果:

使用 Grok 3 构建的 Minecraft 示例项目游戏玩法

我们收到的这个程序真是太让人失望了,完全不是我期待的那样。除了玩家移动之外,其他功能都运行不正常。它看起来一点也不像《我的世界》,反而更像个贪吃蛇游戏。😮‍💨

  • OpenAI o3-mini-high 的响应

以下是它生成的代码:

import pygame

# Initialize Pygame
pygame.init()

# Screen settings
WIDTH = 800
HEIGHT = 600
BLOCK_SIZE = 40
screen = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption("Simple Minecraft 2D")

# Colors
WHITE = (255, 255, 255)
BLACK = (0, 0, 0)
BROWN = (139, 69, 19)
GREEN = (0, 255, 0)
BLUE = (0, 0, 255)

# Player class
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))

# Block class
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))

# Create game objects
player = Player(WIDTH // 2, HEIGHT // 2)
blocks = []

# Generate initial terrain
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"))

# Game loop
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()
            # Left click to break block
            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
            # Right click to place block
            elif event.button == 3 and inventory:
                grid_x = (mouse_x // BLOCK_SIZE) * BLOCK_SIZE
                grid_y = (mouse_y // BLOCK_SIZE) * BLOCK_SIZE
                # Check if space is empty
                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()))

    # Player movement
    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

    # Draw everything
    screen.fill(WHITE)
    for block in blocks:
        block.draw()
    player.draw()

    # Display inventory
    font = pygame.font.Font(None, 36)
    text = font.render(f"Inventory: {len(inventory)} blocks", True, BLACK)
    screen.blit(text, (10, 10))

    pygame.display.flip()
    clock.tick(60)

pygame.quit()
Enter fullscreen mode Exit fullscreen mode

以下是程序的输出结果:

使用 o3-mini-high 构建的 Minecraft 示例项目

o3-mini-high 型号的输出效果令人非常失望,我们看到的只是一个添加了背景颜色的空白屏幕。🤦‍♂️

最终结论:可以说 Claude 3.7 以绝对优势胜出。✅ 所有功能都运行正常。它构建的游戏几乎包含了所有我设想的功能。

2. 旋转六边形中的多个球

让我们用一个相当标准的问题来快速测试一下所有这些模型,这个问题通常用于评判不同的LLM模型。

这是原题的修改版本,其中只有一个球在旋转的六边形内旋转。

提示:请用 Python 编写一个脚本,模拟 10 个球在一个快速旋转的六边形内的运动。

  • 克劳德的回应​​ 3.7 十四行诗

以下是它生成的代码:

import math
import random
import sys

import pygame

# Initialize pygame
pygame.init()

# Constants
WIDTH, HEIGHT = 800, 600
FPS = 60
WHITE = (255, 255, 255)
BLACK = (0, 0, 0)
GRAY = (100, 100, 100)
BALL_COLORS = [
    (255, 0, 0),  # Red
    (0, 255, 0),  # Green
    (0, 0, 255),  # Blue
    (255, 255, 0),  # Yellow
    (255, 0, 255),  # Magenta
    (0, 255, 255),  # Cyan
    (255, 165, 0),  # Orange
    (128, 0, 128),  # Purple
    (255, 192, 203),  # Pink
    (0, 128, 0),  # Dark Green
]

# Screen setup
screen = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption("Multiple Balls in a Fast-Spinning Hexagon")
clock = pygame.time.Clock()

# Hexagon properties
hex_radius = 180
hex_center = (WIDTH // 2, HEIGHT // 2)
hex_rotation = 0
hex_rotation_speed = 2.0  # Increased speed (degrees per frame)

# Ball properties
num_balls = 10
ball_radius = 15
balls = []

# Create balls with random positions and velocities
for i in range(num_balls):
    # Random position inside the hexagon (with some margin from the edges)
    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)

    # Random velocity
    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,
        }
    )

# Function to calculate hexagon vertices
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

# Function to check collision with a line segment
def check_line_collision(ball_pos, ball_radius, line_start, line_end):
    x1, y1 = line_start
    x2, y2 = line_end
    x0, y0 = ball_pos

    # Vector from line start to end
    dx, dy = x2 - x1, y2 - y1

    # Vector from line start to ball
    dx0, dy0 = x0 - x1, y0 - y1

    # Calculate projection length
    t = max(0, min(1, (dx0 * dx + dy0 * dy) / (dx * dx + dy * dy)))

    # Calculate closest point on the line
    closest_x = x1 + t * dx
    closest_y = y1 + t * dy

    # Distance from ball to closest point
    distance = math.sqrt((x0 - closest_x) ** 2 + (y0 - closest_y) ** 2)

    # Return collision info
    if distance <= ball_radius:
        # Normal vector for reflection
        if distance > 0:  # Avoid division by zero
            nx = (x0 - closest_x) / distance
            ny = (y0 - closest_y) / distance
        else:
            nx, ny = 0, -1  # Default normal if distance is zero

        return True, (nx, ny)
    return False, None

# Function to check collision between balls
def check_ball_collision(ball1, ball2):
    x1, y1 = ball1["pos"]
    x2, y2 = ball2["pos"]

    # Distance between balls
    distance = math.sqrt((x2 - x1) ** 2 + (y2 - y1) ** 2)

    # If balls are colliding
    if distance <= (ball1["radius"] + ball2["radius"]):
        # Normal vector
        if distance > 0:
            nx = (x2 - x1) / distance
            ny = (y2 - y1) / distance
        else:
            nx, ny = 0, 1  # Default if balls are at the same position

        return True, (nx, ny)
    return False, None

# Main game loop
gravity = 0.1
ball_speed_limit = 6
running = True

while running:
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            running = False

    # Update hexagon rotation
    hex_rotation += hex_rotation_speed
    if hex_rotation >= 360:
        hex_rotation -= 360

    # Get hexagon vertices
    hex_vertices = get_hexagon_vertices(hex_center, hex_radius, hex_rotation)

    # Update balls
    for ball in balls:
        # Apply gravity
        ball["velocity"][1] += gravity

        # Update ball position
        ball["pos"][0] += ball["velocity"][0]
        ball["pos"][1] += ball["velocity"][1]

        # Check collision with hexagon sides
        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:
                # Reflect velocity across the normal
                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

                # Apply some energy loss
                ball["velocity"][0] *= 0.9
                ball["velocity"][1] *= 0.9

                # Move ball away from the wall to avoid getting stuck
                ball["pos"][0] += nx * 2
                ball["pos"][1] += ny * 2

        # Limit ball speed
        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

    # Check collisions between balls
    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:
                # Calculate relative velocity
                nx, ny = normal
                vx1, vy1 = balls[i]["velocity"]
                vx2, vy2 = balls[j]["velocity"]

                # Relative velocity along the normal
                vrn = (vx1 - vx2) * nx + (vy1 - vy2) * ny

                # If balls are moving away from each other, skip
                if vrn < 0:
                    continue

                # Mass (assume equal mass)
                m1 = 1.0
                m2 = 1.0

                # Elastic collision response
                impulse = 2.0 * vrn / (m1 + m2)

                # Update velocities
                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

                # Move balls apart to avoid sticking
                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:
                    # Move proportional to their velocities
                    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

    # Draw everything
    screen.fill(BLACK)

    # Draw hexagon
    pygame.draw.polygon(screen, GRAY, hex_vertices, 3)

    # Draw balls
    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()
Enter fullscreen mode Exit fullscreen mode

这段代码只有一些小问题,例如,我们在没有检查值是否为真的情况下,就normal从方法返回的值中提取值check_ball_collisionNone

除此之外,一切看起来都很好。

以下是程序的输出结果:

旋转六边形内的多个球体 Claude 3.7 模型代码演示

  • Grok 3 的回复

以下是它生成的代码:

import math
import random

import pygame

# Initialize Pygame
pygame.init()

# Set up the display
WIDTH = 800
HEIGHT = 600
screen = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption("Balls in Spinning Hexagon")

# Colors
WHITE = (255, 255, 255)
RED = (255, 0, 0)
BLUE = (0, 0, 255)

# Hexagon properties
center_x = WIDTH // 2
center_y = HEIGHT // 2
hex_size = 200
hex_angle = 0
hex_speed = 0.05  # radians per frame

# Ball properties
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),
        )

# Create balls
balls = [Ball() for _ in range(10)]

# Get hexagon vertices
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

# Check collision with hexagon walls
def check_collision(ball, vertices):
    for i in range(6):
        x1, y1 = vertices[i]
        x2, y2 = vertices[(i + 1) % 6]

        # Line equation: ax + by + c = 0
        a = y2 - y1
        b = x1 - x2
        c = -(a * x1 + b * y1)

        # Distance from point to line
        distance = abs(a * ball.x + b * ball.y + c) / math.sqrt(a * a + b * b)

        if distance <= ball.radius:
            # Normal vector
            nx = b
            ny = -a
            norm_length = math.sqrt(nx * nx + ny * ny)
            nx /= norm_length
            ny /= norm_length

            # Reflect velocity
            dot = ball.dx * nx + ball.dy * ny
            ball.dx = ball.dx - 2 * dot * nx
            ball.dy = ball.dy - 2 * dot * ny

            # Push ball away from wall
            penetration = ball.radius - distance
            ball.x += nx * penetration
            ball.y += ny * penetration

# Main game loop
clock = pygame.time.Clock()
running = True

while running:
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            running = False

    # Clear screen
    screen.fill((0, 0, 0))

    # Update hexagon rotation
    hex_angle += hex_speed
    vertices = get_hexagon_vertices(center_x, center_y, hex_size, hex_angle)

    # Update balls
    for ball in balls:
        # Move ball
        ball.x += ball.dx
        ball.y += ball.dy

        # Check collision with hexagon
        check_collision(ball, vertices)

        # Draw ball
        pygame.draw.circle(screen, ball.color, (int(ball.x), int(ball.y)), ball.radius)

    # Draw hexagon
    pygame.draw.polygon(screen, WHITE, vertices, 2)

    # Update display
    pygame.display.flip()
    clock.tick(60)

pygame.quit()
Enter fullscreen mode Exit fullscreen mode

以下是程序的输出结果:

Grok 3 模型代码演示:旋转六边形内的多个球

这里有个问题,这些球不应该在六边形外面。

当我问它一个关于旋转六边形内有一个球的标准问题时,它轻松回答了,但当我稍微调整一下问题时,它就无法处理了。

可以肯定地说,这个模型无法很好地处理这个问题。

  • OpenAI o3-mini-high 的响应

以下是它生成的代码:

#!/usr/bin/env python3

import math
import random
import pygame

def get_hexagon_vertices(center, radius, angle_offset):
    """
    Returns a list of 6 pygame.Vector2 vertices for a regular hexagon.

    center      -- pygame.Vector2 at the center.
    radius      -- Distance from center to a vertex.
    angle_offset-- Overall rotation in radians.
    """
    vertices = []
    for i in range(6):
        # Each vertex is 60 degrees (pi/3 radians) apart.
        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):
    """
    Determines if point (x, y) is inside the polygon.
    poly is a list of (x, y) tuples.
    (Uses the ray-casting algorithm.)
    """
    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()

    # Window size and title
    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           # Distance from center to vertex.
    rotation_angle = 0         # Current rotation angle in radians.
    rotation_speed = 5         # Radians per second (fast spinning).

    ball_radius = 10
    n_balls = 10
    balls = []   # Each ball is a dictionary with keys: pos, vel, color.

    # Use the hexagon's initial (unrotated) vertices for placing balls.
    initial_vertices = get_hexagon_vertices(hex_center, hex_radius, rotation_angle)
    # Convert vertices to a list of (x,y) tuples for point_in_poly testing.
    initial_poly = [(v.x, v.y) for v in initial_vertices]

    # Create n_balls with random positions INSIDE the hexagon and random velocities.
    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)
        # Only accept if inside the polygon:
        if point_in_poly(test_x, test_y, initial_poly):
            ball = {
                'pos': pygame.Vector2(test_x, test_y),
                # Random velocity (pixels per second). Adjust the numbers for speed.
                '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   # Delta-time in seconds.

        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                running = False
            # Optionally press ESC to quit.
            elif event.type == pygame.KEYDOWN and event.key == pygame.K_ESCAPE:
                running = False

        # Increase the rotation angle.
        rotation_angle += rotation_speed * dt
        # (Keep angle bounded to 2pi if you like; not strictly necessary.)
        rotation_angle %= (2 * math.pi)

        # Compute the current hexagon vertices.
        hex_vertices = get_hexagon_vertices(hex_center, hex_radius, rotation_angle)

        # For collision computations we need the “inward” normal for each edge.
        # For a given edge from A to B, the inward normal is chosen so that:
        #    n = N/|N|, where N = (-(B.y-A.y), (B.x-A.x)) or its negative,
        # and it must point towards the hexagon center.
        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
            # Candidate normal (rotate 90 degrees)
            candidate = pygame.Vector2(-edge.y, edge.x)
            # Check – choose the normal that points toward the hexagon’s center.
            if candidate.dot(hex_center - A) < 0:
                candidate = -candidate
            n = candidate.normalize()
            edge_normals.append((A, n))

        # Update each ball’s position and check for collision with each edge.
        for ball in balls:
            ball['pos'] += ball['vel'] * dt

            # Check collision with every edge of the hexagon.
            for A, n in edge_normals:
                # Compute how far the ball's center is along the inward normal from edge A.
                # (The signed distance.)
                d = (ball['pos'] - A).dot(n)
                if d < ball_radius:
                    # Only reflect if the ball is moving against the wall.
                    if ball['vel'].dot(n) < 0:
                        ball['vel'] = ball['vel'] - 2 * ball['vel'].dot(n) * n
                    # Push the ball out of penetration.
                    ball['pos'] += (ball_radius - d) * n

        screen.fill((0, 0, 0))  # clear with black.

        # Draw the hexagon (outline only).
        # Convert hexagon vertices from Vector2 to tuples.
        hex_points = [(int(v.x), int(v.y)) for v in hex_vertices]
        pygame.draw.polygon(screen, (255, 255, 255), hex_points, 3)

        # Draw balls.
        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()

if __name__ == "__main__":
    main()
Enter fullscreen mode Exit fullscreen mode

以下是程序的输出结果:

旋转六边形内多个球体 o3-mini-high 模型代码演示

最终结论:对于这个问题,我相当惊讶地发现,除了 Grok 3 模型之外,Claude 3.7 和 o3-mini-high 都给出了正确答案✅。o3-mini-high 的表现似乎优于 Grok 3。

3. 构建一个基于浏览器的实时 Markdown 编辑器

考虑到 Claude 在游戏开发和动画制作测试中表现出色,以及他们对编码的可靠说法,让我们对这三个模型进行一次快速的 Web 应用程序测试。

克劳德赞扬推文

提示:使用 Next.js 中的 Tailwind 构建一个基于浏览器的小型 Markdown 编辑器,具备语法高亮、导出为 PDF 功能和极简的用户界面,所有更改都在一个文件中完成。

  • 克劳德的回应​​ 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";

// Configure marked with highlight.js for syntax highlighting
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>(
    '# Hello, Markdown!\n\nStart typing here...\n\n```

javascript\n// Code snippet example\nfunction hello() {\n  console.log("Hello, world!");\n}\n

```',
  );
  const [html, setHtml] = useState<string>("");
  const [isDark, setIsDark] = useState<boolean>(false);

  // Convert markdown to HTML when input changes
  useEffect(() => {
    const convertMarkdown = async () => {
      const parsedHtml = await marked.parse(markdown);
      setHtml(parsedHtml);
    };

    convertMarkdown();
  }, [markdown]);

  // Toggle dark mode
  const toggleDarkMode = () => {
    setIsDark(!isDark);
  };

  // Export to PDF
  const exportToPdf = async () => {
    const previewElement = document.getElementById("preview");

    if (!previewElement) return;

    // Set background to white for PDF export
    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 {
      // Reset styling after export
      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 */}
      <header
        className={`p-4 flex justify-between items-center ${isDark ? "bg-gray-800" : "bg-white border-b"}`}
      >
        <h1 className="text-xl font-bold">Markdown Editor</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"
          >
            Export PDF
          </button>
        </div>
      </header>

      {/* Editor and Preview */}
      <div className="flex flex-col md:flex-row h-[calc(100vh-64px)]">
        {/* Markdown Input */}
        <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="Type markdown here..."
          />
        </div>

        {/* Preview */}
        <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>
  );
}
Enter fullscreen mode Exit fullscreen mode

代码几乎都没问题,只是代码高亮显示不正常。这似乎是由于marked最近代码高亮设置中引入了一些破坏性变更导致的。

该模型可能并非基于该模块的最新数据进行训练。

以下是程序的输出结果:

基于浏览器的 Markdown 编辑器,采用 Claude 3.7 模型构建

以下是导出的PDF文件:

使用基于 Grok 3 模型构建的 Markdown 编辑器导出的 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>(`# Welcome to Markdown Editor

Write some **bold** text or *italic* text here.

\`\`\`javascript
// Code example
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;

    // Split content into lines and handle basic formatting
    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 Editor</h1>
        <button
          onClick={handleExportPDF}
          className="px-4 py-2 bg-blue-600 text-white rounded hover:bg-blue-700 transition-colors"
        >
          Export to PDF
        </button>
      </header>

      <main className="flex-1 flex p-6 gap-6 max-w-7xl mx-auto w-full">
        {/* Editor */}
        <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="Start writing markdown..."
          />
        </div>

        {/* Preview */}
        <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>
  );
}
Enter fullscreen mode Exit fullscreen mode

我们遇到了一些问题,首先是标题显示效果不佳。其次是字体对比度有问题,而且导出的PDF文件无法正确渲染Markdown内容。

以下是程序的输出结果:

基于浏览器的 Markdown 编辑器,采用 Grok 3 模型构建

In the exported PDF, we get raw text instead of formatted Markdown, with no emoji support.

使用基于 Grok 3 模型构建的 Markdown 编辑器导出的 PDF

  • Response from OpenAI o3-mini-high

Here's the code it generated:

"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(`# Welcome to the Markdown Editor

\`\`\`javascript
// Write some JavaScript here:
function greet(name) {
  return \`Hello, \${name}!\`;
}

console.log(greet("World"));
\`\`\`

**Enjoy editing!**
`);
  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 Editor</title>
        <meta name="viewport" content="width=device-width, initial-scale=1" />
        {/* Tailwind via CDN - remove this if you already have Tailwind configured */}
        <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 Editor</h1>
        <div className="flex flex-col md:flex-row gap-4">
          <textarea
            value={markdown}
            onChange={(e) => setMarkdown(e.target.value)}
            placeholder="Write your markdown here..."
            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"
          >
            Export to PDF
          </button>
        </div>
      </div>
    </>
  );
}
Enter fullscreen mode Exit fullscreen mode

Here's the output of the program:

基于 OpenAI o3-mini-high 模型构建的浏览器 Markdown 编辑器

Here's the exported PDF:

使用基于 OpenAI o3-mini-high 模型构建的 Markdown 编辑器导出的 PDF

Final Verdict: Here as well, Claude 3.7 is the clear winner compared to the other two models. ✅ Almost everything worked, but the other two models couldn't get it right. There were a couple of issues with text contrast, markdown rendering on the site and in the PDF.

4. Build a Code Diff Viewer

Let's try a simple web application example to see if all of them get it right.

This is a pretty standard question and somewhat easy to implement. I have high hopes that all three of them should get it right (even o3-mini-high 😮‍💨). So far, I'm somewhat disappointed with it, though.

Prompt: Write a simple web application for a code diff viewer, a tool that takes two text inputs and highlights differences side by side.

  • Response from Claude 3.7 Sonnet

Here's the code it generated:

"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]);

  // Simple diff algorithm to find differences between two texts
  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">
            Code Diff Viewer
          </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"
            >
              Original Code
            </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="Paste your original code here..."
            ></textarea>
          </div>
          <div>
            <label
              htmlFor="rightCode"
              className="block text-sm font-medium text-gray-700 mb-2"
            >
              Modified Code
            </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="Paste your modified code here..."
            ></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">Diff Result</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">
                Original
              </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">
                Modified
              </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>
            Enter code in both text areas to see the differences highlighted.
          </p>
        </div>
      </div>
    </div>
  );
}
Enter fullscreen mode Exit fullscreen mode

Here's the output of the program:

使用 Claude 3.7 模型构建的代码差异查看器

  • Response from Grok 3

Here's the code it generated:

"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">
          Code Diff Viewer
        </h1>

        <div className="grid grid-cols-2 gap-4">
          {/* Left Panel */}
          <div className="space-y-2">
            <label className="block text-sm font-medium text-gray-700">
              Original Text
            </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="Enter original text here..."
            />
          </div>

          {/* Right Panel */}
          <div className="space-y-2">
            <label className="block text-sm font-medium text-gray-700">
              Modified Text
            </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="Enter modified text here..."
            />
          </div>
        </div>

        {/* Diff Viewer */}
        <div className="mt-8">
          <h2 className="text-xl font-semibold mb-4">Differences</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>
  );
}
Enter fullscreen mode Exit fullscreen mode

Here's the output of the program:

使用 Grok 3 模型构建的代码差异查看器

Everything else seems to be working fine with this as well, but in the diff, it is not taking the line's indentation into account.

  • Response from OpenAI o3-mini-high

Here's the code it generated:

"use client";

import React, { JSX, useState } from "react";
import { diffWords } from "diff";

export default function Page() {
  // State for the two text inputs and for the computed diff result.
  const [leftText, setLeftText] = useState("");
  const [rightText, setRightText] = useState("");
  // diffResult stores an array of rows where each row is an object with two JSX elements:
  // one for the left side and one for the right side.
  const [diffResult, setDiffResult] = useState<
    { left: JSX.Element; right: JSX.Element }[]
  >([]);

  // compare the two texts line‐by‐line.
  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 the lines are identical, display them as plain text.
      if (leftLine === rightLine) {
        results.push({
          left: <span>{leftLine}</span>,
          right: <span>{rightLine}</span>,
        });
      } else {
        const diff = diffWords(leftLine, rightLine);

        // For the left side, ignore the parts that were "added"
        // and highlight (with red) the parts that were "removed".
        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>
          );
        });

        // For the right side, ignore the parts that were "removed"
        // and highlight (with green) the parts that were "added".
        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">
        Simple Code Diff Viewer
      </h1>

      {/* Two textareas side by side (stacked vertically on small screens) */}
      <div className="flex flex-col md:flex-row md:space-x-4">
        <textarea
          value={leftText}
          onChange={(e) => setLeftText(e.target.value)}
          placeholder="Enter left text"
          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="Enter right text"
          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"
        >
          Compare
        </button>
      </div>

      {/* Diff viewer: a grid with two columns for left and right side diff display */}
      <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>
  );
}
Enter fullscreen mode Exit fullscreen mode

Here's the output of the program:

使用 OpenAI o3-mini-high 模型构建的代码差异查看器

Here, we have an interesting result. It decided to use an external library diff and do highlighting per character and not per line.

Final Verdict: By functionality-wise, all three models got this problem correct (at least from testing at the surface level. There might be edge cases, though). I must say the overall code quality and output by o3-mini-high is comparatively better ✅ than both the Claude 3.7 Sonnet and Grok 3 model.

5. Manim code for Square to Pyramid Animation

Let's end our test with a final Manim question. Most LLMs pretty much suck at writing Manim code, and it gets even harder when it's a 3D scene.

Prompt: Create a Manim animation in Python where a 2D square smoothly lifts into 3D space to form a pyramid. Then, animate the pyramid shrinking back into a square.

  • Response from Claude 3.7 Sonnet

Here's the code it generated:

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):
        # Set up the scene
        self.set_camera_orientation(phi=75 * DEGREES, theta=30 * DEGREES)

        # Create a square
        square = Square(side_length=2, fill_opacity=0.7, fill_color=BLUE)

        # Show the square first
        self.play(Create(square))
        self.wait(1)

        # Prepare for 3D transformation
        # Add z-coordinate to the square's points to make it compatible with 3D
        square_3d = square.copy()
        square_points = square_3d.get_vertices()

        # Create a pyramid
        pyramid = Pyramid(square_length=2, height=2)
        pyramid.set_fill(BLUE, opacity=0.7)
        pyramid.set_stroke(WHITE, width=1)

        # The pyramid is initially hidden
        pyramid.set_opacity(0)
        self.add(pyramid)

        # Animate the square to the base of the pyramid
        self.play(
            square.animate.set_opacity(0), pyramid.animate.set_opacity(0.7), run_time=2
        )

        # Move the camera to get a better view of the pyramid
        self.move_camera(phi=60 * DEGREES, theta=45 * DEGREES, run_time=2)
        self.wait(1)

        # Rotate the pyramid to showcase its 3D nature
        self.play(Rotate(pyramid, angle=2 * PI, axis=UP), run_time=4)
        self.wait(1)

        # Animate the pyramid shrinking back to a square
        self.play(
            pyramid.animate.set_opacity(0), square.animate.set_opacity(0.7), run_time=2
        )

        # Return camera to original position
        self.move_camera(phi=75 * DEGREES, theta=30 * DEGREES, run_time=2)
        self.wait(1)

        # Finish by fading out the square
        self.play(FadeOut(square))
        self.wait(1)


class Pyramid(VGroup):
    def __init__(self, square_length=2, height=2, **kwargs):
        super().__init__(**kwargs)
        # Create the base square at z=0
        square = Square(side_length=square_length)
        square_vertices = square.get_vertices()

        # Apex of the pyramid at height above the center of the square
        apex = np.array([0, 0, height])

        # Create the faces of the pyramid
        faces = VGroup()

        # Base (the square itself)
        base = Polygon(*square_vertices, fill_opacity=0.7, fill_color=BLUE)
        faces.add(base)

        # Side triangular faces
        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)
Enter fullscreen mode Exit fullscreen mode

Here's the output of the program:

使用 Claude 3.7 模型制作的方形到金字塔动画

  • Response from Grok 3

Here's the code it generated:

from manim import (BLUE, DEGREES, ORIGIN, WHITE, Create, FadeOut, Polygon,
                   Square, ThreeDScene, VGroup)


class SquareToPyramid(ThreeDScene):
    def construct(self):
        # Set up the 3D camera
        self.set_camera_orientation(phi=75 * DEGREES, theta=30 * DEGREES)

        # Create the initial 2D square
        square = Square(side_length=2, fill_opacity=0.5, fill_color=BLUE)
        square.move_to(ORIGIN)

        # Show the initial square
        self.play(Create(square))
        self.wait(1)

        # Define the vertices of the square
        bottom_vertices = [
            [-1, -1, 0],  # Bottom left
            [1, -1, 0],  # Bottom right
            [1, 1, 0],  # Top right
            [-1, 1, 0],  # Top left
        ]

        # Define the apex of the pyramid (will rise to this point)
        apex = [0, 0, 2]

        # Create the pyramid (tetrahedron) using Polygon for faces
        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)

        # Animation to transform square into pyramid
        # First, fade out the square while raising the apex
        self.begin_ambient_camera_rotation(rate=0.2)
        self.play(FadeOut(square), Create(pyramid_faces), run_time=2)

        # Rotate the pyramid to show 3D structure
        self.wait(2)

        # Shrink pyramid back to square
        # Create a new square for the final state
        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)

        # Stop camera rotation and show final result
        self.stop_ambient_camera_rotation()
        self.wait(1)
Enter fullscreen mode Exit fullscreen mode

Here's the output of the program:

使用 Grok 3 模型构建的正方形到金字塔动画

  • Response from OpenAI o3-mini-high

Here's the code it generated:

#!/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):
        # Set an initial camera orientation so the 3D effect is clear.
        self.set_camera_orientation(phi=70 * DEGREES, theta=-45 * DEGREES)
        # Create a blue square (the base, side length = 2)
        square = Square(side_length=2, color=BLUE)
        self.play(Create(square))
        self.wait(1)

        # We'll use a ValueTracker to control the pyramid’s “apex height.”
        # When the value is 0, the apex is at the square’s center.
        # When the value is >0, the apex lifts upward (UP is the z-axis).
        height_tracker = ValueTracker(0)

        # Define a function that returns a VGroup drawing the pyramid for any given height.
        # It builds:
        #  • the base (i.e. 4 lines from the square’s corners, drawn in blue)
        #  • the 4 side edges (lines from each corner of the square up to the apex, drawn in green)
        def get_pyramid(height):
            # Get the square’s vertices (the corners); by default a Square’s vertices are ordered.
            corners = square.get_vertices()
            # Define the apex: start from the square’s center and add "height" in the upward (z) direction.
            apex = square.get_center() + height * UP
            # Create the base edges as a closed loop.
            base_lines = VGroup(
                *[
                    Line(corners[i], corners[(i + 1) % len(corners)], color=BLUE)
                    for i in range(len(corners))
                ]
            )
            # Create the side edges from each corner to the apex.
            side_lines = VGroup(
                *[Line(corner, apex, color=GREEN) for corner in corners]
            )
            return VGroup(base_lines, side_lines)

        # Create an always_redraw mobject so that whenever height_tracker changes,
        # the pyramid is redrawn with the new apex position.
        pyramid = always_redraw(lambda: get_pyramid(height_tracker.get_value()))

        # Transition from the static square (at height 0) into our pyramid object.
        # (At height=0, the side edges have zero length so the drawing looks identical to the square.)
        self.play(ReplacementTransform(square, pyramid), run_time=1)
        self.wait(0.5)

        # Animate the pyramid “lifting” by increasing the tracked height.
        self.play(height_tracker.animate.set_value(2), run_time=3)
        self.wait(1)

        # Now animate the pyramid “shrinking” back into a square by lowering the apex.
        self.play(height_tracker.animate.set_value(0), run_time=3)
        self.wait(1)

        # Optionally, perform a slight camera move to show off the 3D nature.
        self.move_camera(phi=75 * DEGREES, theta=-30 * DEGREES, run_time=2)
        self.wait(1)
Enter fullscreen mode Exit fullscreen mode

Here's the output of the program:

使用 OpenAI o3-mini-high 模型构建的正方形到金字塔动画

Here, this model really struggled with the 3D projection and failed to transform the square into a pyramid.

Final Verdict: The Claude 3.7 Sonnet and Grok 3 models got it completely correct, but in terms of animation, I prefer the output from the Grok 3 model ✅. o3-mini-high failed completely and couldn't even reach the solution.


Conclusion

It's fair to say that Claude 3.7 is exactly what it claims to be. In all five questions we compared, it was the clear winner almost every time.

Now is no time to wait for luck. Imagine an AI model building stuffs in no time with 62.3% accuracy. Start building stuffs and improving your coding skills, or get ready to be replaced by AI (probably 🤷🏼‍♂️).

It doesn't necessarily mean that Claude 3.7 is the answer to everything, and there will be a time when other AI models beat this model's benchmarks.

人工智能模型之间的竞赛永无止境,游戏永不停歇!

你觉得怎么样?请在下方评论区留言告诉我你的想法!👇🏻

文章来源:https://dev.to/composiodev/claude-37-sonnet-vs-grok-3-vs-o3-mini-high-coding-comparison-23oe