EY-Office ブログ

Claude 3.7 Sonnetがリリースされたので、前回と同じReactコンポーネントを作ってもらいました。これは良いですね 😀

Claude 3.7 Sonnetがリリースされたましたね!

早速仕事でも使っていますが、テストコードの生成やリファクタリング等で生成されるコードも3.5に比べ良くなってる気がします。ただしこのあたりのコードは明確な比較が難しいですし、3.5でも良いコードを生成していたのでプラセボ効果かも知れません。

AI-code-generate Bing Image Creatorが生成した画像を使っています

前回と同じReactコンポーネントを作ってもらいました

少し前に、Claude 3.5 SonnetでReactコンポーネントを生成してもらいましたが、アプリケーション・バーのメニューが無くなってしまったり、ボタン等のセンター合わせが無くなってしまい、今一つでしたね。😅

しかし、今回の3.7はかなり良くなりました!😀

  • 元画像
  • 3.5
  • 3.7

表の枠やボタン、アプリケーション・バーのデザインなど細かい点では、まだ問題がありますがこれを元にコードを完成させようと思いますよね。
また、前回はジャンケンのコードは完成していませんでしたが、今回はちゃんとしたジャンケンのコードになっています!

指向課程

3.7では(CursorのModel選択をclaude-3.7-sonnet-thinkingにすると)思考プロセスを表示してくれます。

<think>
The user has shared an image of what looks like a Japanese rock-paper-scissors game interface ("じゃんけんポン!" is the Japanese phrase for playing rock-paper-scissors). The image shows:

1. A header with "じゃんけんポン!" and navigation tabs for results and scores
2. A section titled "対戦結果" (match results)
3. Three buttons labeled "グー" (rock), "チョキ" (scissors), and "パー" (paper)
4. A table showing game history with columns for:
   - 時間 (time)
   - 人間 (human)
   - コンピューター (computer)
   - 結果 (result)
5. The results are color-coded (blue for wins, black for draws, red for losses)

The user wants me to create a React component that replicates this interface using TypeScript and Tailwind CSS.

I'll need to:
1. Create the overall layout structure
2. Implement the header with tabs
3. Create the button row for game choices
4. Implement the results table
5. Add the game logic to handle user choices, computer moves, and determine winners
6. Track and display game history

Let me create this component for the user's page.tsx file.
</think>

さらに、日本語で解説してもらいました。素晴らしいですね!

<think>
ユーザーが日本語での解説を求めているので、先ほど作成したじゃんけんゲームについて日本語で説明します。日本語で、コンポーネントの構造、使用している技術、機能の詳細について解説します。
</think>

# じゃんけんゲームコンポーネントの解説

このReactコンポーネントは、画像で示されたような「じゃんけんポン!」ゲームを実装しています。以下に主な機能と実装の詳細を説明します。

## 主な機能

1. **ヘッダー部分**: 「じゃんけんポン!」というタイトルと、「対戦結果」と「対戦成績」の2つのタブを表示します。
2. **ゲームボタン**: プレイヤーが選択できる「グー」「チョキ」「パー」の3つのボタン。
3. **結果テーブル**: 各対戦の時間、プレイヤーの選択、コンピューターの選択、勝敗結果を表示します。

## 技術的な実装

### データ型の定義
```typescript
type Choice = "グー" | "チョキ" | "パー";
type Result = "勝ち" | "負け" | "引き分け";

interface GameRecord {
  time: string;
  playerChoice: Choice;
  computerChoice: Choice;
  result: Result;
}
```

### ステート管理
```typescript
const [gameRecords, setGameRecords] = useState<GameRecord[]>([]);
const [activeTab, setActiveTab] = useState<"対戦結果" | "対戦成績">("対戦結果");
```
- `gameRecords`: ゲームの履歴を保存する配列
- `activeTab`: 現在選択されているタブを管理

### ゲームロジック
`play`関数では:
1. コンピューターがランダムに選択(グー、チョキ、パー)
2. プレイヤーの選択とコンピューターの選択を比較して勝敗を決定
3. 現在の時刻を取得し、フォーマット
4. 新しいゲーム記録を作成し、履歴の先頭に追加

### 色の管理
- `getResultColor`関数: 勝敗結果に応じて色を返す(勝ち: 青、負け: 赤、引き分け: 黒)
- `getChoiceColor`関数: 選択に応じて色を返す(グー: 青、チョキ: 黒、パー: 赤)

### UI実装
- TailwindCSSを使用してスタイリング
- レスポンシブなデザイン(`max-w-2xl mx-auto`で中央寄せ)
- テーブルレイアウトで履歴を表示

## 使い方

1. グー、チョキ、パーのいずれかのボタンをクリック
2. コンピューターがランダムに選択し、勝敗が決まる
3. 結果がテーブルに追加され、最新の結果が上に表示される

現状では「対戦成績」タブは実装されていませんが、必要に応じて勝敗の集計機能を追加することができます。

生成されたコード

生成されたコードは以下のようになりました。改善の余地はありますが合格ですよね。😁

"use client";

import { useState, useEffect } from "react";

type Choice = "グー" | "チョキ" | "パー";
type Result = "勝ち" | "負け" | "引き分け";

interface GameRecord {
  time: string;
  playerChoice: Choice;
  computerChoice: Choice;
  result: Result;
}

export default function Home() {
  const [gameRecords, setGameRecords] = useState<GameRecord[]>([]);
  const [activeTab, setActiveTab] = useState<"対戦結果" | "対戦成績">("対戦結果");

  const play = (playerChoice: Choice) => {
    const choices: Choice[] = ["グー", "チョキ", "パー"];
    const computerChoice = choices[Math.floor(Math.random() * 3)];

    let result: Result;
    if (playerChoice === computerChoice) {
      result = "引き分け";
    } else if (
      (playerChoice === "グー" && computerChoice === "チョキ") ||
      (playerChoice === "チョキ" && computerChoice === "パー") ||
      (playerChoice === "パー" && computerChoice === "グー")
    ) {
      result = "勝ち";
    } else {
      result = "負け";
    }

    const now = new Date();
    const timeString = `${now.getHours().toString().padStart(2, '0')}:${now.getMinutes().toString().padStart(2, '0')}:${now.getSeconds().toString().padStart(2, '0')}`;

    const newRecord: GameRecord = {
      time: timeString,
      playerChoice,
      computerChoice,
      result,
    };

    setGameRecords([newRecord, ...gameRecords]);
  };

  const getResultColor = (result: Result) => {
    switch (result) {
      case "勝ち": return "text-blue-500";
      case "負け": return "text-red-500";
      case "引き分け": return "text-black";
    }
  };

  const getChoiceColor = (choice: Choice) => {
    switch (choice) {
      case "グー": return "text-blue-500";
      case "チョキ": return "text-black";
      case "パー": return "text-red-500";
    }
  };

  return (
    <div className="max-w-2xl mx-auto">
      {/* Header */}
      <div className="bg-blue-500 p-4 text-white flex items-center">
        <h1 className="text-xl font-bold mr-8">じゃんけんポン!</h1>
        <div className="flex space-x-4">
          <button
            className={`${activeTab === "対戦結果" ? "font-bold" : ""}`}
            onClick={() => setActiveTab("対戦結果")}
          >
            対戦結果
          </button>
          <button
            className={`${activeTab === "対戦成績" ? "font-bold" : ""}`}
            onClick={() => setActiveTab("対戦成績")}
          >
            対戦成績
          </button>
        </div>
      </div>

      {/* Content */}
      <div className="p-4">
        <h2 className="text-xl font-bold text-center my-4">対戦結果</h2>

        {/* Game buttons */}
        <div className="flex justify-center space-x-4 my-6">
          <button
            className="bg-blue-500 text-white px-8 py-2 rounded"
            onClick={() => play("グー")}
          >
            グー
          </button>
          <button
            className="bg-blue-500 text-white px-8 py-2 rounded"
            onClick={() => play("チョキ")}
          >
            チョキ
          </button>
          <button
            className="bg-blue-500 text-white px-8 py-2 rounded"
            onClick={() => play("パー")}
          >
            パー
          </button>
        </div>

        {/* Results table */}
        <div className="border rounded overflow-hidden">
          <table className="min-w-full">
            <thead className="bg-gray-100">
              <tr>
                <th className="py-2 px-4 text-left">時間</th>
                <th className="py-2 px-4 text-left">人間</th>
                <th className="py-2 px-4 text-left">コンピューター</th>
                <th className="py-2 px-4 text-left">結果</th>
              </tr>
            </thead>
            <tbody>
              {gameRecords.map((record, index) => (
                <tr key={index} className="border-t">
                  <td className="py-2 px-4 text-blue-500">{record.time}</td>
                  <td className={`py-2 px-4 ${getChoiceColor(record.playerChoice)}`}>{record.playerChoice}</td>
                  <td className={`py-2 px-4 ${getChoiceColor(record.computerChoice)}`}>{record.computerChoice}</td>
                  <td className={`py-2 px-4 ${getResultColor(record.result)}`}>{record.result}</td>
                </tr>
              ))}
            </tbody>
          </table>
        </div>
      </div>
    </div>
  );
}

まとめ

Claude 3.5 Sonnetではデザイン画像からのReactコンポーネント作成は今一つでしたが、Claude 3.7 Sonnetではかなり良くなってきましたね!
これなら、生成されたコードを元にコードを作成しようと思えますね、たぶん気に入らない点を指摘すると書き変えてくれれます。

次にのClaude 3.9(?)が生成するコードは普通のプログラマー並になり、Claude 4(?)ではベテランプログラマー並になるのでしょうかね。🤩

- about -

EY-Office代表取締役
・プログラマー
吉田裕美の
開発者向けブログ