Claude 3.7 Sonnetのコーディング能力を試す


以前の投稿で様々なAIモデルの使用感をまとめた.今回はClaude 3.7 Sonnetについてまとめたいと思う.Claudeを使うのもかなり久しぶりだ.

まずはWeasel Programを試してみた.すると,Reactを用いて恐ろしいほどリッチなものを作成してくれた.(多少の改善は必要そうであるが)

Weasel Program を可能な限りリッチな表現を用いたコードで書いて.
		
その過程をアニメーションとして出力できるようにして.

エラーが出ないように自己点検して.
import React, { useState, useEffect, useRef } from 'react';
import { Play, Pause, RotateCcw, Settings } from 'lucide-react';

// Richard Dawkins の有名な "Weasel Program" の実装
export default function WeaselProgram() {
  // 目標文字列と設定
  const [target, setTarget] = useState("METHINKS IT IS LIKE A WEASEL");
  const [currentGeneration, setCurrentGeneration] = useState(0);
  const [populationSize, setPopulationSize] = useState(100);
  const [mutationRate, setMutationRate] = useState(0.05);
  const [delay, setDelay] = useState(50);
  const [isRunning, setIsRunning] = useState(false);
  const [showSettings, setShowSettings] = useState(false);
  const [bestFitness, setBestFitness] = useState(0);
  const [history, setHistory] = useState([]);
  const [bestString, setBestString] = useState("");
  const [initialString, setInitialString] = useState("");

  // 使用可能な文字のセット
  const charset = "ABCDEFGHIJKLMNOPQRSTUVWXYZ ";
  const intervalRef = useRef(null);

  // 初期ランダム文字列の生成
  const generateRandomString = (length) => {
    let result = '';
    for (let i = 0; i < length; i++) {
      result += charset.charAt(Math.floor(Math.random() * charset.length));
    }
    return result;
  };

  // 適合度評価(目標文字列との一致度)
  const calculateFitness = (str) => {
    let score = 0;
    for (let i = 0; i < str.length; i++) {
      if (str[i] === target[i]) {
        score++;
      }
    }
    return score / target.length;
  };

  // 突然変異を適用
  const mutate = (str) => {
    let result = '';
    for (let i = 0; i < str.length; i++) {
      if (Math.random() < mutationRate) {
        result += charset.charAt(Math.floor(Math.random() * charset.length));
      } else {
        result += str[i];
      }
    }
    return result;
  };

  // 次世代の生成
  const evolvePopulation = () => {
    // 現在の最良個体を取得
    const currentBest = bestString;
    
    // 新しい個体群を生成
    let population = [];
    for (let i = 0; i < populationSize; i++) {
      population.push(mutate(currentBest));
    }
    
    // 最良個体を見つける
    let bestIndividual = population[0];
    let maxFitness = calculateFitness(bestIndividual);
    
    for (let i = 1; i < populationSize; i++) {
      const fitness = calculateFitness(population[i]);
      if (fitness > maxFitness) {
        maxFitness = fitness;
        bestIndividual = population[i];
      }
    }
    
    // 最良個体を更新
    setBestString(bestIndividual);
    setBestFitness(maxFitness);
    setCurrentGeneration(prev => prev + 1);
    
    // 歴史に追加(最大50個まで保存)
    setHistory(prev => {
      const newHistory = [...prev, { generation: currentGeneration + 1, string: bestIndividual, fitness: maxFitness }];
      if (newHistory.length > 50) {
        return newHistory.slice(newHistory.length - 50);
      }
      return newHistory;
    });
    
    // 目標達成でストップ
    if (maxFitness === 1) {
      clearInterval(intervalRef.current);
      setIsRunning(false);
    }
  };

  // 進化プロセスの開始/停止
  const toggleEvolution = () => {
    if (isRunning) {
      clearInterval(intervalRef.current);
      setIsRunning(false);
    } else {
      intervalRef.current = setInterval(evolvePopulation, delay);
      setIsRunning(true);
    }
  };

  // リセット機能
  const resetEvolution = () => {
    clearInterval(intervalRef.current);
    setIsRunning(false);
    setCurrentGeneration(0);
    const newInitialString = generateRandomString(target.length);
    setInitialString(newInitialString);
    setBestString(newInitialString);
    setBestFitness(calculateFitness(newInitialString));
    setHistory([{ generation: 0, string: newInitialString, fitness: calculateFitness(newInitialString) }]);
  };

  // 初期化
  useEffect(() => {
    resetEvolution();
    return () => clearInterval(intervalRef.current);
  }, [target]);

  // 文字表示用のスタイル付けコンポーネント
  const CharacterDisplay = ({ char, targetChar }) => {
    const isMatch = char === targetChar;
    return (
      <span className={`inline-block px-1 py-1 font-mono text-xl ${isMatch ? 'bg-green-200 text-green-800' : 'bg-red-100 text-red-800'}`}>
        {char}
      </span>
    );
  };

  return (
    <div className="p-6 max-w-4xl mx-auto bg-white rounded-xl shadow-md">
      <h1 className="text-2xl font-bold text-center mb-4">Richard Dawkins のウィーゼルプログラム</h1>
      <div className="flex flex-col space-y-6">
        {/* 現在の状態表示 */}
        <div className="bg-gray-50 p-4 rounded-lg">
          <div className="flex justify-between items-center mb-2">
            <span className="font-bold">目標文字列:</span>
            <div className="flex flex-wrap">
              {target.split('').map((char, i) => (
                <span key={i} className="inline-block px-1 py-1 font-mono text-xl bg-blue-100 text-blue-800">
                  {char}
                </span>
              ))}
            </div>
          </div>
          
          <div className="flex justify-between items-center mb-2">
            <span className="font-bold">現在の最良個体:</span>
            <div className="flex flex-wrap">
              {bestString && bestString.split('').map((char, i) => (
                <CharacterDisplay key={i} char={char} targetChar={target[i]} />
              ))}
            </div>
          </div>
          
          <div className="grid grid-cols-2 gap-4 mt-4">
            <div className="bg-gray-100 p-3 rounded-md">
              <span className="font-bold">世代数:</span> {currentGeneration}
            </div>
            <div className="bg-gray-100 p-3 rounded-md">
              <span className="font-bold">適合度:</span> {(bestFitness * 100).toFixed(2)}%
            </div>
          </div>
        </div>
        
        {/* コントロールパネル */}
        <div className="flex justify-center space-x-4">
          <button
            onClick={toggleEvolution}
            className={`flex items-center px-4 py-2 rounded-md ${isRunning ? 'bg-amber-500 hover:bg-amber-600' : 'bg-green-500 hover:bg-green-600'} text-white`}
          >
            {isRunning ? <><Pause size={20} /> 停止</> : <><Play size={20} /> 開始</>}
          </button>
          
          <button
            onClick={resetEvolution}
            className="flex items-center px-4 py-2 bg-blue-500 hover:bg-blue-600 text-white rounded-md"
          >
            <RotateCcw size={20} /> リセット
          </button>
          
          <button
            onClick={() => setShowSettings(!showSettings)}
            className="flex items-center px-4 py-2 bg-gray-500 hover:bg-gray-600 text-white rounded-md"
          >
            <Settings size={20} /> 設定
          </button>
        </div>
        
        {/* 設定パネル */}
        {showSettings && (
          <div className="bg-gray-50 p-4 rounded-lg">
            <h2 className="text-xl font-bold mb-4">設定</h2>
            <div className="grid grid-cols-1 md:grid-cols-2 gap-4">
              <div>
                <label className="block mb-2">目標文字列</label>
                <input
                  type="text"
                  value={target}
                  onChange={(e) => setTarget(e.target.value.toUpperCase())}
                  className="w-full p-2 border rounded"
                  disabled={isRunning}
                />
              </div>
              
              <div>
                <label className="block mb-2">集団サイズ</label>
                <input
                  type="number"
                  value={populationSize}
                  onChange={(e) => setPopulationSize(parseInt(e.target.value))}
                  min="10"
                  max="1000"
                  className="w-full p-2 border rounded"
                  disabled={isRunning}
                />
              </div>
              
              <div>
                <label className="block mb-2">突然変異率</label>
                <input
                  type="range"
                  value={mutationRate}
                  onChange={(e) => setMutationRate(parseFloat(e.target.value))}
                  min="0.01"
                  max="0.5"
                  step="0.01"
                  className="w-full"
                  disabled={isRunning}
                />
                <span>{(mutationRate * 100).toFixed(1)}%</span>
              </div>
              
              <div>
                <label className="block mb-2">更新速度 (ms)</label>
                <input
                  type="range"
                  value={delay}
                  onChange={(e) => setDelay(parseInt(e.target.value))}
                  min="10"
                  max="500"
                  step="10"
                  className="w-full"
                  disabled={isRunning}
                />
                <span>{delay}ms</span>
              </div>
            </div>
          </div>
        )}
        
        {/* 進化の履歴グラフ */}
        <div className="bg-gray-50 p-4 rounded-lg">
          <h2 className="text-xl font-bold mb-4">進化の履歴</h2>
          {history.length > 0 && (
            <div className="h-64 relative">
              {/* グラフ */}
              <div className="absolute inset-0">
                <svg className="w-full h-full">
                  <rect x="0" y="0" width="100%" height="100%" fill="#f9fafb" />
                  
                  {/* グリッド線 */}
                  {[0.2, 0.4, 0.6, 0.8].map((line) => (
                    <line
                      key={line}
                      x1="0"
                      y1={`${(1 - line) * 100}%`}
                      x2="100%"
                      y2={`${(1 - line) * 100}%`}
                      stroke="#e5e7eb"
                      strokeWidth="1"
                    />
                  ))}
                  
                  {/* 適合度ラインチャート */}
                  {history.length > 1 && (
                    <polyline
                      points={history.map((item, i) => `${(i / (history.length - 1)) * 100}%,${(1 - item.fitness) * 100}%`).join(' ')}
                      fill="none"
                      stroke="#4f46e5"
                      strokeWidth="2"
                    />
                  )}
                  
                  {/* データポイント */}
                  {history.map((item, i) => (
                    <circle
                      key={i}
                      cx={`${(i / (history.length - 1)) * 100}%`}
                      cy={`${(1 - item.fitness) * 100}%`}
                      r="3"
                      fill="#4f46e5"
                    />
                  ))}
                </svg>
                
                {/* Y軸ラベル */}
                <div className="absolute top-0 left-0 h-full flex flex-col justify-between text-xs text-gray-500 py-2">
                  <div>100%</div>
                  <div>80%</div>
                  <div>60%</div>
                  <div>40%</div>
                  <div>20%</div>
                  <div>0%</div>
                </div>
              </div>
            </div>
          )}
        </div>
        
        {/* 最近の進化履歴 */}
        <div className="bg-gray-50 p-4 rounded-lg">
          <h2 className="text-xl font-bold mb-4">進化の記録</h2>
          <div className="max-h-60 overflow-auto">
            <table className="min-w-full">
              <thead className="bg-gray-100">
                <tr>
                  <th className="px-4 py-2 text-left">世代</th>
                  <th className="px-4 py-2 text-left">文字列</th>
                  <th className="px-4 py-2 text-left">適合度</th>
                </tr>
              </thead>
              <tbody>
                {[...history].reverse().map((item) => (
                  <tr key={item.generation} className="border-b">
                    <td className="px-4 py-2">{item.generation}</td>
                    <td className="px-4 py-2 font-mono">{item.string}</td>
                    <td className="px-4 py-2">{(item.fitness * 100).toFixed(2)}%</td>
                  </tr>
                ))}
              </tbody>
            </table>
          </div>
        </div>
      </div>
    </div>
  );
}

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です