Claude 4 Sonnetのコーディング能力を試す 4
Claude 4 が話題を呼んでいる.今回はClaude 3.7との比較でWeasel Programのシミュレーションを行ってみた.
すると,これ以上改善点がないくらい素晴らしいものを作成してくれた.
Weasel Programのシミュレーションを行うコードを書いて.
Reactで動的に処理できるようにして.
エラーが出ないように自己点検して.
一切の遠慮は不要です!!最高品質を追い求めて.
import React, { useState, useEffect, useCallback, useRef } from 'react';
import { Play, Pause, RotateCcw, Settings, TrendingUp } from 'lucide-react';
const WeaselProgramSimulator = () => {
const [target, setTarget] = useState("METHINKS IT IS LIKE A WEASEL");
const [populationSize, setPopulationSize] = useState(100);
const [mutationRate, setMutationRate] = useState(0.05);
const [currentGeneration, setCurrentGeneration] = useState(0);
const [bestIndividual, setBestIndividual] = useState("");
const [bestFitness, setBestFitness] = useState(0);
const [fitnessHistory, setFitnessHistory] = useState([]);
const [isRunning, setIsRunning] = useState(false);
const [isComplete, setIsComplete] = useState(false);
const [showSettings, setShowSettings] = useState(false);
const [speed, setSpeed] = useState(100);
const [population, setPopulation] = useState([]);
const intervalRef = useRef(null);
const CHARACTERS = "ABCDEFGHIJKLMNOPQRSTUVWXYZ ";
// 初期個体群の生成
const generateRandomString = useCallback((length) => {
return Array.from({ length }, () =>
CHARACTERS[Math.floor(Math.random() * CHARACTERS.length)]
).join('');
}, []);
// フィットネス計算(正しい文字の数)
const calculateFitness = useCallback((individual, target) => {
let matches = 0;
for (let i = 0; i < Math.min(individual.length, target.length); i++) {
if (individual[i] === target[i]) matches++;
}
return matches;
}, []);
// 突然変異
const mutate = useCallback((individual, mutationRate) => {
return individual.split('').map(char => {
if (Math.random() < mutationRate) {
return CHARACTERS[Math.floor(Math.random() * CHARACTERS.length)];
}
return char;
}).join('');
}, []);
// 初期化
const initialize = useCallback(() => {
const initialPop = Array.from({ length: populationSize }, () =>
generateRandomString(target.length)
);
const populationWithFitness = initialPop.map(individual => ({
string: individual,
fitness: calculateFitness(individual, target)
}));
populationWithFitness.sort((a, b) => b.fitness - a.fitness);
setPopulation(populationWithFitness);
setBestIndividual(populationWithFitness[0].string);
setBestFitness(populationWithFitness[0].fitness);
setCurrentGeneration(0);
setFitnessHistory([populationWithFitness[0].fitness]);
setIsComplete(populationWithFitness[0].fitness === target.length);
}, [target, populationSize, generateRandomString, calculateFitness]);
// 一世代の進化
const evolveOneGeneration = useCallback(() => {
if (population.length === 0) return;
// 親の選択(上位個体を選択)
const parent = population[0].string;
// 子個体群の生成
const newPopulation = Array.from({ length: populationSize }, () => {
const child = mutate(parent, mutationRate);
return {
string: child,
fitness: calculateFitness(child, target)
};
});
// 親も含める
newPopulation.push({
string: parent,
fitness: population[0].fitness
});
// フィットネスでソート
newPopulation.sort((a, b) => b.fitness - a.fitness);
// 上位個体のみ保持
const survivors = newPopulation.slice(0, populationSize);
setPopulation(survivors);
setBestIndividual(survivors[0].string);
setBestFitness(survivors[0].fitness);
setCurrentGeneration(prev => prev + 1);
setFitnessHistory(prev => [...prev.slice(-99), survivors[0].fitness]);
if (survivors[0].fitness === target.length) {
setIsComplete(true);
setIsRunning(false);
}
}, [population, populationSize, mutationRate, target, calculateFitness, mutate]);
// 自動実行
useEffect(() => {
if (isRunning && !isComplete) {
intervalRef.current = setInterval(evolveOneGeneration, 1000 / speed);
} else {
if (intervalRef.current) {
clearInterval(intervalRef.current);
intervalRef.current = null;
}
}
return () => {
if (intervalRef.current) {
clearInterval(intervalRef.current);
}
};
}, [isRunning, isComplete, evolveOneGeneration, speed]);
// 初期化(マウント時とターゲット変更時)
useEffect(() => {
initialize();
}, [initialize]);
const handleStart = () => {
if (isComplete) {
initialize();
}
setIsRunning(true);
};
const handlePause = () => {
setIsRunning(false);
};
const handleReset = () => {
setIsRunning(false);
initialize();
};
const handleStep = () => {
if (!isComplete) {
evolveOneGeneration();
}
};
const fitnessPercentage = target.length > 0 ? (bestFitness / target.length) * 100 : 0;
return (
<div className="min-h-screen bg-gradient-to-br from-purple-900 via-blue-900 to-indigo-900 p-6">
<div className="max-w-6xl mx-auto">
<div className="text-center mb-8">
<h1 className="text-4xl font-bold text-white mb-2">
🧬 Weasel Program Evolution Simulator
</h1>
<p className="text-purple-200">
Richard Dawkinsの進化シミュレーション - ランダムから目標文字列への進化
</p>
</div>
<div className="grid grid-cols-1 lg:grid-cols-3 gap-6">
{/* メインディスプレイ */}
<div className="lg:col-span-2 space-y-6">
{/* 現在の最良個体 */}
<div className="bg-white/10 backdrop-blur-md rounded-2xl p-6 border border-white/20">
<h2 className="text-xl font-semibold text-white mb-4 flex items-center gap-2">
<TrendingUp className="w-5 h-5" />
最良個体(第{currentGeneration}世代)
</h2>
<div className="space-y-4">
<div className="bg-black/30 rounded-lg p-4 font-mono text-lg">
<div className="text-gray-400 text-sm mb-2">目標:</div>
<div className="text-green-400 mb-3">{target}</div>
<div className="text-gray-400 text-sm mb-2">現在:</div>
<div className="text-white">
{bestIndividual.split('').map((char, i) => (
<span
key={i}
className={char === target[i] ? 'text-green-400 bg-green-400/20' : 'text-red-400'}
>
{char}
</span>
))}
</div>
</div>
<div className="flex justify-between items-center">
<div className="text-white">
適合度: <span className="font-bold text-green-400">{bestFitness}</span>/{target.length}
</div>
<div className="text-white">
進捗: <span className="font-bold text-blue-400">{fitnessPercentage.toFixed(1)}%</span>
</div>
</div>
<div className="w-full bg-gray-700 rounded-full h-3">
<div
className="bg-gradient-to-r from-blue-500 to-green-500 h-3 rounded-full transition-all duration-300"
style={{ width: `${fitnessPercentage}%` }}
></div>
</div>
</div>
</div>
{/* 制御パネル */}
<div className="bg-white/10 backdrop-blur-md rounded-2xl p-6 border border-white/20">
<div className="flex flex-wrap gap-3 justify-center">
<button
onClick={isRunning ? handlePause : handleStart}
disabled={isComplete && bestFitness === target.length}
className="flex items-center gap-2 bg-gradient-to-r from-green-500 to-blue-500 text-white px-6 py-3 rounded-xl font-semibold hover:from-green-600 hover:to-blue-600 transition-all disabled:opacity-50 disabled:cursor-not-allowed"
>
{isRunning ? <Pause className="w-5 h-5" /> : <Play className="w-5 h-5" />}
{isRunning ? '一時停止' : isComplete ? '再開始' : '開始'}
</button>
<button
onClick={handleStep}
disabled={isRunning || isComplete}
className="flex items-center gap-2 bg-gradient-to-r from-purple-500 to-pink-500 text-white px-6 py-3 rounded-xl font-semibold hover:from-purple-600 hover:to-pink-600 transition-all disabled:opacity-50 disabled:cursor-not-allowed"
>
ステップ実行
</button>
<button
onClick={handleReset}
className="flex items-center gap-2 bg-gradient-to-r from-orange-500 to-red-500 text-white px-6 py-3 rounded-xl font-semibold hover:from-orange-600 hover:to-red-600 transition-all"
>
<RotateCcw className="w-5 h-5" />
リセット
</button>
<button
onClick={() => setShowSettings(!showSettings)}
className="flex items-center gap-2 bg-gradient-to-r from-gray-500 to-gray-600 text-white px-6 py-3 rounded-xl font-semibold hover:from-gray-600 hover:to-gray-700 transition-all"
>
<Settings className="w-5 h-5" />
設定
</button>
</div>
</div>
{/* フィットネス履歴グラフ */}
<div className="bg-white/10 backdrop-blur-md rounded-2xl p-6 border border-white/20">
<h3 className="text-lg font-semibold text-white mb-4">適合度の推移</h3>
<div className="h-40 relative bg-black/30 rounded-lg p-4">
<svg width="100%" height="100%" className="overflow-visible">
{fitnessHistory.length > 1 && (
<polyline
fill="none"
stroke="url(#gradient)"
strokeWidth="2"
points={fitnessHistory.map((fitness, i) => {
const x = (i / (fitnessHistory.length - 1)) * 100;
const y = 100 - (fitness / target.length) * 100;
return `${x},${y}`;
}).join(' ')}
vectorEffect="non-scaling-stroke"
transform="scale(2.4,1.2)"
/>
)}
<defs>
<linearGradient id="gradient" x1="0%" y1="0%" x2="100%" y2="0%">
<stop offset="0%" stopColor="#3b82f6" />
<stop offset="100%" stopColor="#10b981" />
</linearGradient>
</defs>
</svg>
</div>
</div>
</div>
{/* サイドパネル */}
<div className="space-y-6">
{/* 統計情報 */}
<div className="bg-white/10 backdrop-blur-md rounded-2xl p-6 border border-white/20">
<h3 className="text-lg font-semibold text-white mb-4">統計情報</h3>
<div className="space-y-3 text-white">
<div className="flex justify-between">
<span>世代数:</span>
<span className="font-bold text-blue-400">{currentGeneration}</span>
</div>
<div className="flex justify-between">
<span>個体数:</span>
<span className="font-bold text-purple-400">{populationSize}</span>
</div>
<div className="flex justify-between">
<span>突然変異率:</span>
<span className="font-bold text-pink-400">{(mutationRate * 100).toFixed(1)}%</span>
</div>
<div className="flex justify-between">
<span>実行速度:</span>
<span className="font-bold text-green-400">{speed}x</span>
</div>
{isComplete && (
<div className="mt-4 p-3 bg-green-500/20 rounded-lg border border-green-500/30">
<div className="text-green-400 font-bold text-center">
🎉 進化完了!
</div>
</div>
)}
</div>
</div>
{/* 設定パネル */}
{showSettings && (
<div className="bg-white/10 backdrop-blur-md rounded-2xl p-6 border border-white/20">
<h3 className="text-lg font-semibold text-white mb-4">パラメータ設定</h3>
<div className="space-y-4">
<div>
<label className="block text-white text-sm mb-2">目標文字列:</label>
<input
type="text"
value={target}
onChange={(e) => setTarget(e.target.value.toUpperCase())}
className="w-full bg-black/30 text-white border border-white/30 rounded-lg px-3 py-2 focus:border-blue-500 focus:outline-none"
disabled={isRunning}
/>
</div>
<div>
<label className="block text-white text-sm mb-2">
個体数: {populationSize}
</label>
<input
type="range"
min="20"
max="200"
value={populationSize}
onChange={(e) => setPopulationSize(Number(e.target.value))}
className="w-full"
disabled={isRunning}
/>
</div>
<div>
<label className="block text-white text-sm mb-2">
突然変異率: {(mutationRate * 100).toFixed(1)}%
</label>
<input
type="range"
min="0.01"
max="0.2"
step="0.01"
value={mutationRate}
onChange={(e) => setMutationRate(Number(e.target.value))}
className="w-full"
disabled={isRunning}
/>
</div>
<div>
<label className="block text-white text-sm mb-2">
実行速度: {speed}x
</label>
<input
type="range"
min="1"
max="500"
value={speed}
onChange={(e) => setSpeed(Number(e.target.value))}
className="w-full"
/>
</div>
</div>
</div>
)}
{/* アルゴリズム説明 */}
<div className="bg-white/10 backdrop-blur-md rounded-2xl p-6 border border-white/20">
<h3 className="text-lg font-semibold text-white mb-4">アルゴリズム</h3>
<div className="text-purple-200 text-sm space-y-2">
<p>1. ランダムな文字列を生成</p>
<p>2. 最も適合度の高い個体を選択</p>
<p>3. その個体を親として子個体を生成</p>
<p>4. 突然変異を適用</p>
<p>5. 目標に到達するまで繰り返し</p>
</div>
</div>
</div>
</div>
</div>
</div>
);
};
export default WeaselProgramSimulator;
