Claude 4.5 Sonnetのコーディング能力を試す
Claude 4.5がリリースされた. よって, 今回もWeasel Programのシミュレーションを行った.
Claude 4の時点ですでに非常に素晴らしい出来栄えであったが, さらにアニメーションなどがリッチになった.
Weasel Program を可能な限りリッチな表現を用いたコードで書いて.
その過程をアニメーションとして出力できるようにして.
import React, { useState, useEffect, useRef } from 'react';
import { Play, Pause, RotateCcw, Zap } from 'lucide-react';
const WeaselProgram = () => {
const TARGET = "METHINKS IT IS LIKE A WEASEL";
const CHARS = "ABCDEFGHIJKLMNOPQRSTUVWXYZ ";
const POPULATION_SIZE = 100;
const MUTATION_RATE = 0.05;
const [current, setCurrent] = useState("");
const [generation, setGeneration] = useState(0);
const [fitness, setFitness] = useState(0);
const [isRunning, setIsRunning] = useState(false);
const [speed, setSpeed] = useState(50);
const [history, setHistory] = useState([]);
const [particles, setParticles] = useState([]);
const intervalRef = useRef(null);
const calculateFitness = (str) => {
let score = 0;
for (let i = 0; i < TARGET.length; i++) {
if (str[i] === TARGET[i]) score++;
}
return score;
};
const randomChar = () => CHARS[Math.floor(Math.random() * CHARS.length)];
const randomString = () => {
return Array.from({ length: TARGET.length }, randomChar).join('');
};
const mutate = (str) => {
return str.split('').map(char =>
Math.random() < MUTATION_RATE ? randomChar() : char
).join('');
};
const createParticle = (index) => ({
id: Math.random(),
x: (index / TARGET.length) * 100,
y: Math.random() * 100,
delay: Math.random() * 0.5
});
const evolveGeneration = () => {
setGeneration(prev => {
const gen = prev + 1;
setCurrent(prevCurrent => {
const curr = prevCurrent || randomString();
const currentFitness = calculateFitness(curr);
if (currentFitness === TARGET.length) {
setIsRunning(false);
return curr;
}
const population = [curr];
for (let i = 1; i < POPULATION_SIZE; i++) {
population.push(mutate(curr));
}
const best = population.reduce((best, str) => {
const f = calculateFitness(str);
return f > calculateFitness(best) ? str : best;
}, population[0]);
const newFitness = calculateFitness(best);
setFitness(newFitness);
if (newFitness > currentFitness) {
const newParticles = [];
for (let i = 0; i < TARGET.length; i++) {
if (best[i] === TARGET[i] && curr[i] !== TARGET[i]) {
newParticles.push(createParticle(i));
}
}
setParticles(newParticles);
setTimeout(() => setParticles([]), 1000);
}
setHistory(prev => {
const updated = [...prev, { gen, str: best, fitness: newFitness }];
return updated.slice(-50);
});
return best;
});
return gen;
});
};
useEffect(() => {
if (isRunning) {
intervalRef.current = setInterval(evolveGeneration, 101 - speed);
} else {
if (intervalRef.current) clearInterval(intervalRef.current);
}
return () => {
if (intervalRef.current) clearInterval(intervalRef.current);
};
}, [isRunning, speed]);
const reset = () => {
setIsRunning(false);
setCurrent("");
setGeneration(0);
setFitness(0);
setHistory([]);
setParticles([]);
};
const progress = (fitness / TARGET.length) * 100;
return (
<div className="min-h-screen bg-gradient-to-br from-indigo-900 via-purple-900 to-pink-900 p-8 overflow-hidden">
<div className="max-w-6xl mx-auto">
{/* Header */}
<div className="text-center mb-12 relative">
<h1 className="text-6xl font-bold text-white mb-4 tracking-tight drop-shadow-2xl">
Weasel Program
</h1>
<p className="text-xl text-purple-200 font-light">
進化的アルゴリズムによる文字列最適化
</p>
<div className="absolute inset-0 blur-3xl opacity-30 bg-gradient-to-r from-pink-500 via-purple-500 to-indigo-500 -z-10"></div>
</div>
{/* Main Display */}
<div className="bg-white/10 backdrop-blur-xl rounded-3xl p-8 mb-8 border border-white/20 shadow-2xl relative overflow-hidden">
<div className="absolute inset-0 bg-gradient-to-br from-white/5 to-transparent pointer-events-none"></div>
<div className="text-center mb-6 relative z-10">
<div className="text-sm text-purple-300 mb-2 tracking-widest font-semibold">TARGET</div>
<div className="text-2xl font-mono text-white/50 mb-8 tracking-wider">
{TARGET}
</div>
<div className="text-sm text-pink-300 mb-3 tracking-widest font-semibold">CURRENT</div>
<div className="relative inline-block">
<div className="text-4xl font-mono font-bold tracking-wider relative">
{(current || randomString()).split('').map((char, i) => (
<span
key={i}
className={`inline-block transition-all duration-300 ${
char === TARGET[i]
? 'text-green-400 scale-110'
: 'text-white'
}`}
style={{
textShadow: char === TARGET[i]
? '0 0 20px rgba(74, 222, 128, 0.8), 0 0 40px rgba(74, 222, 128, 0.4)'
: '0 2px 10px rgba(0,0,0,0.5)',
animation: char === TARGET[i] ? 'pulse 2s infinite' : 'none'
}}
>
{char}
</span>
))}
</div>
{/* Particles */}
{particles.map(p => (
<div
key={p.id}
className="absolute w-2 h-2 bg-green-400 rounded-full pointer-events-none"
style={{
left: `${p.x}%`,
top: `${p.y}%`,
animation: `particle-rise 1s ease-out ${p.delay}s forwards`,
boxShadow: '0 0 10px rgba(74, 222, 128, 0.8)'
}}
/>
))}
</div>
</div>
{/* Progress Bar */}
<div className="relative h-4 bg-white/10 rounded-full overflow-hidden mb-6 backdrop-blur-sm">
<div
className="absolute inset-y-0 left-0 bg-gradient-to-r from-green-400 via-emerald-500 to-teal-500 transition-all duration-500 ease-out rounded-full"
style={{ width: `${progress}%` }}
>
<div className="absolute inset-0 bg-white/30 animate-pulse"></div>
</div>
<div className="absolute inset-0 flex items-center justify-center text-xs font-bold text-white drop-shadow-lg">
{progress.toFixed(1)}%
</div>
</div>
{/* Stats */}
<div className="grid grid-cols-2 gap-4 mb-6">
<div className="bg-white/5 rounded-2xl p-4 backdrop-blur-sm border border-white/10">
<div className="text-purple-300 text-sm mb-1 font-semibold">世代</div>
<div className="text-3xl font-bold text-white">{generation}</div>
</div>
<div className="bg-white/5 rounded-2xl p-4 backdrop-blur-sm border border-white/10">
<div className="text-pink-300 text-sm mb-1 font-semibold">適合度</div>
<div className="text-3xl font-bold text-white">{fitness} / {TARGET.length}</div>
</div>
</div>
{/* Controls */}
<div className="flex gap-4 items-center justify-center">
<button
onClick={() => setIsRunning(!isRunning)}
className="bg-gradient-to-r from-purple-500 to-pink-500 hover:from-purple-600 hover:to-pink-600 text-white px-8 py-3 rounded-full font-semibold shadow-lg hover:shadow-xl transition-all duration-300 flex items-center gap-2 transform hover:scale-105"
>
{isRunning ? <Pause size={20} /> : <Play size={20} />}
{isRunning ? '一時停止' : '開始'}
</button>
<button
onClick={reset}
className="bg-white/10 hover:bg-white/20 text-white px-6 py-3 rounded-full font-semibold backdrop-blur-sm border border-white/20 transition-all duration-300 flex items-center gap-2 transform hover:scale-105"
>
<RotateCcw size={20} />
リセット
</button>
</div>
{/* Speed Control */}
<div className="mt-6 flex items-center gap-4">
<Zap size={20} className="text-yellow-400" />
<input
type="range"
min="1"
max="100"
value={speed}
onChange={(e) => setSpeed(Number(e.target.value))}
className="flex-1 h-2 bg-white/20 rounded-full appearance-none cursor-pointer"
style={{
background: `linear-gradient(to right, rgb(234, 179, 8) 0%, rgb(234, 179, 8) ${speed}%, rgba(255,255,255,0.2) ${speed}%, rgba(255,255,255,0.2) 100%)`
}}
/>
<span className="text-white font-semibold min-w-[80px]">速度: {speed}</span>
</div>
</div>
{/* History */}
<div className="bg-white/10 backdrop-blur-xl rounded-3xl p-6 border border-white/20 shadow-2xl max-h-96 overflow-y-auto">
<h3 className="text-xl font-bold text-white mb-4 flex items-center gap-2">
<div className="w-1 h-6 bg-gradient-to-b from-purple-400 to-pink-400 rounded-full"></div>
進化の履歴
</h3>
<div className="space-y-2">
{history.slice().reverse().map((h, i) => (
<div
key={h.gen}
className="bg-white/5 rounded-xl p-3 backdrop-blur-sm border border-white/10 hover:bg-white/10 transition-all duration-300"
style={{ animation: i < 3 ? `slideIn 0.3s ease-out ${i * 0.1}s backwards` : 'none' }}
>
<div className="flex justify-between items-center mb-1">
<span className="text-purple-300 text-sm font-semibold">世代 {h.gen}</span>
<span className="text-pink-300 text-sm font-semibold">適合度: {h.fitness}</span>
</div>
<div className="font-mono text-white/90 text-sm tracking-wide">
{h.str.split('').map((c, j) => (
<span key={j} className={c === TARGET[j] ? 'text-green-400' : ''}>
{c}
</span>
))}
</div>
</div>
))}
</div>
</div>
</div>
<style jsx>{`
@keyframes pulse {
0%, 100% { opacity: 1; }
50% { opacity: 0.7; }
}
@keyframes particle-rise {
0% {
transform: translateY(0) scale(1);
opacity: 1;
}
100% {
transform: translateY(-100px) scale(0);
opacity: 0;
}
}
@keyframes slideIn {
from {
opacity: 0;
transform: translateX(-20px);
}
to {
opacity: 1;
transform: translateX(0);
}
}
input[type="range"]::-webkit-slider-thumb {
appearance: none;
width: 20px;
height: 20px;
border-radius: 50%;
background: linear-gradient(135deg, #fbbf24, #f59e0b);
cursor: pointer;
box-shadow: 0 0 10px rgba(251, 191, 36, 0.5);
}
input[type="range"]::-moz-range-thumb {
width: 20px;
height: 20px;
border-radius: 50%;
background: linear-gradient(135deg, #fbbf24, #f59e0b);
cursor: pointer;
border: none;
box-shadow: 0 0 10px rgba(251, 191, 36, 0.5);
}
`}</style>
</div>
);
};
export default WeaselProgram;
参考文献
- Dawkins, R. (1996). The blind watchmaker: Why the evidence of evolution reveals a universe without design. WW Norton & Company.