Claude Opus 4.5でコーディングを試す.
以前の投稿でClaude Sonnet 4.5でのコーディング能力を試すためにWeasel Programを組んだ. 今回はその上位版であるClaude Opus 4.5でコーディング能力を試す.
プロンプトと出力結果は下記である.
Weasel Program を可能な限りリッチな表現を用いたコードで書いて.
その過程をアニメーションとして出力できるようにして.
import React, { useState, useEffect, useRef, useCallback } from 'react';
const TARGET = "METHINKS IT IS LIKE A WEASEL";
const CHARS = "ABCDEFGHIJKLMNOPQRSTUVWXYZ ";
const MUTATION_RATE = 0.05;
const POPULATION_SIZE = 100;
const WeaselProgram = () => {
const [generation, setGeneration] = useState(0);
const [bestCandidate, setBestCandidate] = useState('');
const [fitness, setFitness] = useState(0);
const [isRunning, setIsRunning] = useState(false);
const [isComplete, setIsComplete] = useState(false);
const [history, setHistory] = useState([]);
const [particles, setParticles] = useState([]);
const animationRef = useRef(null);
const populationRef = useRef([]);
const randomChar = () => CHARS[Math.floor(Math.random() * CHARS.length)];
const createRandomString = useCallback(() => {
return Array.from({ length: TARGET.length }, () => randomChar()).join('');
}, []);
const calculateFitness = useCallback((candidate) => {
let score = 0;
for (let i = 0; i < TARGET.length; i++) {
if (candidate[i] === TARGET[i]) score++;
}
return score / TARGET.length;
}, []);
const mutate = useCallback((parent) => {
return parent
.split('')
.map((char, i) => {
if (Math.random() < MUTATION_RATE) {
return randomChar();
}
return char;
})
.join('');
}, []);
const createParticles = useCallback((count = 20) => {
const newParticles = [];
for (let i = 0; i < count; i++) {
newParticles.push({
id: Date.now() + i,
x: Math.random() * 100,
y: Math.random() * 100,
size: Math.random() * 4 + 2,
duration: Math.random() * 2 + 1,
delay: Math.random() * 0.5,
});
}
setParticles(prev => [...prev.slice(-50), ...newParticles]);
}, []);
const initializePopulation = useCallback(() => {
populationRef.current = Array.from({ length: POPULATION_SIZE }, () => createRandomString());
}, [createRandomString]);
const evolve = useCallback(() => {
const population = populationRef.current;
// Find the best candidate
let best = population[0];
let bestFit = calculateFitness(best);
for (const candidate of population) {
const fit = calculateFitness(candidate);
if (fit > bestFit) {
bestFit = fit;
best = candidate;
}
}
// Create new population from mutations of the best
populationRef.current = Array.from({ length: POPULATION_SIZE }, () => mutate(best));
return { best, fitness: bestFit };
}, [calculateFitness, mutate]);
const step = useCallback(() => {
const { best, fitness: currentFitness } = evolve();
setGeneration(prev => prev + 1);
setBestCandidate(best);
setFitness(currentFitness);
if (generation % 5 === 0) {
setHistory(prev => [...prev.slice(-20), { gen: generation, str: best, fit: currentFitness }]);
}
if (currentFitness > 0.8) {
createParticles(Math.floor(currentFitness * 30));
}
if (best === TARGET) {
setIsRunning(false);
setIsComplete(true);
createParticles(100);
return true;
}
return false;
}, [evolve, generation, createParticles]);
const run = useCallback(() => {
if (!isRunning) return;
const complete = step();
if (!complete) {
animationRef.current = requestAnimationFrame(run);
}
}, [isRunning, step]);
useEffect(() => {
if (isRunning) {
animationRef.current = requestAnimationFrame(run);
}
return () => {
if (animationRef.current) {
cancelAnimationFrame(animationRef.current);
}
};
}, [isRunning, run]);
const handleStart = () => {
if (isComplete || generation === 0) {
initializePopulation();
setGeneration(0);
setBestCandidate(createRandomString());
setFitness(0);
setHistory([]);
setIsComplete(false);
setParticles([]);
}
setIsRunning(true);
};
const handlePause = () => {
setIsRunning(false);
};
const handleReset = () => {
setIsRunning(false);
setIsComplete(false);
setGeneration(0);
setBestCandidate('');
setFitness(0);
setHistory([]);
setParticles([]);
populationRef.current = [];
};
const renderCharacter = (char, index) => {
const isMatch = TARGET[index] === char;
const targetChar = TARGET[index];
return (
<span
key={index}
className="inline-block relative"
style={{
color: isMatch ? '#00ff88' : '#ff6b6b',
textShadow: isMatch
? '0 0 20px #00ff88, 0 0 40px #00ff88, 0 0 60px #00ff8855'
: '0 0 10px #ff6b6b55',
transform: isMatch ? 'scale(1.1)' : 'scale(1)',
transition: 'all 0.15s cubic-bezier(0.34, 1.56, 0.64, 1)',
animation: isMatch ? 'pulse 0.5s ease-out' : 'none',
}}
>
{char}
{!isMatch && (
<span
className="absolute -top-6 left-1/2 -translate-x-1/2 text-xs opacity-30"
style={{ color: '#888' }}
>
{targetChar}
</span>
)}
</span>
);
};
return (
<div className="min-h-screen bg-[#0a0a0f] text-white overflow-hidden relative" style={{
fontFamily: "'JetBrains Mono', 'Fira Code', monospace",
}}>
{/* Background Grid */}
<div
className="fixed inset-0 opacity-10"
style={{
backgroundImage: `
linear-gradient(rgba(0,255,136,0.1) 1px, transparent 1px),
linear-gradient(90deg, rgba(0,255,136,0.1) 1px, transparent 1px)
`,
backgroundSize: '50px 50px',
}}
/>
{/* Animated Gradient Orbs */}
<div className="fixed inset-0 overflow-hidden pointer-events-none">
<div
className="absolute w-96 h-96 rounded-full blur-3xl opacity-20"
style={{
background: 'radial-gradient(circle, #00ff88 0%, transparent 70%)',
top: '20%',
left: '10%',
animation: 'float 8s ease-in-out infinite',
}}
/>
<div
className="absolute w-80 h-80 rounded-full blur-3xl opacity-15"
style={{
background: 'radial-gradient(circle, #6366f1 0%, transparent 70%)',
bottom: '20%',
right: '15%',
animation: 'float 10s ease-in-out infinite reverse',
}}
/>
</div>
{/* Particles */}
{particles.map(p => (
<div
key={p.id}
className="absolute rounded-full pointer-events-none"
style={{
left: `${p.x}%`,
top: `${p.y}%`,
width: p.size,
height: p.size,
background: '#00ff88',
boxShadow: '0 0 10px #00ff88, 0 0 20px #00ff88',
animation: `particle ${p.duration}s ease-out ${p.delay}s forwards`,
opacity: 0,
}}
/>
))}
{/* Main Content */}
<div className="relative z-10 max-w-5xl mx-auto px-8 py-12">
{/* Header */}
<header className="text-center mb-16">
<h1
className="text-6xl font-bold tracking-tighter mb-4"
style={{
background: 'linear-gradient(135deg, #00ff88 0%, #00d4ff 50%, #6366f1 100%)',
WebkitBackgroundClip: 'text',
WebkitTextFillColor: 'transparent',
textShadow: '0 0 60px rgba(0,255,136,0.3)',
}}
>
WEASEL PROGRAM
</h1>
<p className="text-gray-500 text-lg tracking-widest uppercase">
Dawkins' Cumulative Selection Demonstration
</p>
<div className="mt-4 h-px w-48 mx-auto bg-gradient-to-r from-transparent via-[#00ff88] to-transparent" />
</header>
{/* Target Display */}
<div className="mb-12 p-8 rounded-2xl border border-[#1a1a2e] bg-[#0d0d15]/80 backdrop-blur-xl">
<div className="text-xs text-gray-600 uppercase tracking-widest mb-3">Target Phrase</div>
<div
className="text-2xl tracking-[0.3em] text-center py-4"
style={{ color: '#00ff88', textShadow: '0 0 30px rgba(0,255,136,0.5)' }}
>
{TARGET}
</div>
</div>
{/* Current Evolution Display */}
<div
className="mb-12 p-10 rounded-3xl relative overflow-hidden"
style={{
background: 'linear-gradient(135deg, #0d0d15 0%, #1a1a2e 100%)',
border: '1px solid rgba(0,255,136,0.2)',
boxShadow: isComplete
? '0 0 60px rgba(0,255,136,0.3), inset 0 0 60px rgba(0,255,136,0.05)'
: '0 0 40px rgba(0,0,0,0.5)',
}}
>
{isComplete && (
<div className="absolute inset-0 bg-gradient-to-r from-[#00ff88]/10 via-transparent to-[#00ff88]/10 animate-pulse" />
)}
<div className="text-xs text-gray-600 uppercase tracking-widest mb-6 text-center">
Current Best Candidate
</div>
<div className="text-3xl tracking-[0.25em] text-center font-bold min-h-[3rem] flex justify-center items-center flex-wrap gap-1">
{bestCandidate ? (
bestCandidate.split('').map((char, i) => renderCharacter(char, i))
) : (
<span className="text-gray-700 animate-pulse">AWAITING EVOLUTION...</span>
)}
</div>
{/* Progress Bar */}
<div className="mt-8 relative">
<div className="h-2 bg-[#1a1a2e] rounded-full overflow-hidden">
<div
className="h-full rounded-full transition-all duration-300 ease-out"
style={{
width: `${fitness * 100}%`,
background: 'linear-gradient(90deg, #00ff88 0%, #00d4ff 100%)',
boxShadow: '0 0 20px #00ff88',
}}
/>
</div>
<div className="flex justify-between mt-2 text-xs text-gray-600">
<span>0%</span>
<span
className="text-lg font-bold"
style={{
color: fitness > 0.9 ? '#00ff88' : fitness > 0.5 ? '#00d4ff' : '#6366f1',
textShadow: `0 0 20px ${fitness > 0.9 ? '#00ff88' : fitness > 0.5 ? '#00d4ff' : '#6366f1'}`,
}}
>
{(fitness * 100).toFixed(1)}%
</span>
<span>100%</span>
</div>
</div>
</div>
{/* Stats Grid */}
<div className="grid grid-cols-3 gap-6 mb-12">
{[
{ label: 'Generation', value: generation.toLocaleString(), color: '#6366f1' },
{ label: 'Matched', value: `${Math.round(fitness * TARGET.length)}/${TARGET.length}`, color: '#00d4ff' },
{ label: 'Population', value: POPULATION_SIZE, color: '#00ff88' },
].map((stat, i) => (
<div
key={stat.label}
className="p-6 rounded-xl text-center"
style={{
background: 'linear-gradient(135deg, #0d0d15 0%, #1a1a2e 100%)',
border: `1px solid ${stat.color}33`,
boxShadow: `0 0 30px ${stat.color}11`,
animation: `fadeIn 0.5s ease-out ${i * 0.1}s both`,
}}
>
<div className="text-xs text-gray-600 uppercase tracking-widest mb-2">{stat.label}</div>
<div
className="text-4xl font-bold"
style={{ color: stat.color, textShadow: `0 0 20px ${stat.color}55` }}
>
{stat.value}
</div>
</div>
))}
</div>
{/* Controls */}
<div className="flex justify-center gap-4 mb-12">
{!isRunning ? (
<button
onClick={handleStart}
className="px-10 py-4 rounded-xl font-bold uppercase tracking-wider transition-all duration-300 hover:scale-105"
style={{
background: 'linear-gradient(135deg, #00ff88 0%, #00d4ff 100%)',
color: '#0a0a0f',
boxShadow: '0 0 30px rgba(0,255,136,0.4), inset 0 0 20px rgba(255,255,255,0.2)',
}}
>
{isComplete ? '⟳ Restart' : generation > 0 ? '▶ Continue' : '▶ Start Evolution'}
</button>
) : (
<button
onClick={handlePause}
className="px-10 py-4 rounded-xl font-bold uppercase tracking-wider transition-all duration-300 hover:scale-105"
style={{
background: 'linear-gradient(135deg, #ff6b6b 0%, #ff8e53 100%)',
color: '#0a0a0f',
boxShadow: '0 0 30px rgba(255,107,107,0.4)',
}}
>
⏸ Pause
</button>
)}
<button
onClick={handleReset}
className="px-8 py-4 rounded-xl font-bold uppercase tracking-wider transition-all duration-300 hover:scale-105 border border-gray-700 hover:border-gray-500"
style={{
background: 'transparent',
color: '#888',
}}
>
↺ Reset
</button>
</div>
{/* Evolution History */}
{history.length > 0 && (
<div
className="p-8 rounded-2xl"
style={{
background: 'linear-gradient(135deg, #0d0d15 0%, #1a1a2e 100%)',
border: '1px solid #1a1a2e',
}}
>
<div className="text-xs text-gray-600 uppercase tracking-widest mb-6">Evolution History</div>
<div className="space-y-2 max-h-64 overflow-y-auto custom-scrollbar">
{history.map((h, i) => (
<div
key={i}
className="flex items-center gap-4 py-2 px-4 rounded-lg hover:bg-white/5 transition-colors"
style={{ animation: `slideIn 0.3s ease-out ${i * 0.02}s both` }}
>
<span className="text-xs text-gray-600 w-20">Gen {h.gen}</span>
<span className="flex-1 tracking-widest text-sm" style={{ color: '#888' }}>
{h.str}
</span>
<span
className="text-xs font-bold"
style={{ color: h.fit > 0.8 ? '#00ff88' : h.fit > 0.5 ? '#00d4ff' : '#6366f1' }}
>
{(h.fit * 100).toFixed(0)}%
</span>
</div>
))}
</div>
</div>
)}
{/* Footer */}
<footer className="mt-16 text-center text-gray-700 text-sm">
<p>Demonstrating cumulative selection vs single-step selection</p>
<p className="mt-1 text-gray-800">Richard Dawkins • The Blind Watchmaker (1986)</p>
</footer>
</div>
{/* Global Styles */}
<style>{`
@import url('https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;700&display=swap');
@keyframes float {
0%, 100% { transform: translateY(0) scale(1); }
50% { transform: translateY(-30px) scale(1.1); }
}
@keyframes pulse {
0% { transform: scale(1.1); }
50% { transform: scale(1.3); }
100% { transform: scale(1.1); }
}
@keyframes particle {
0% { opacity: 1; transform: translateY(0) scale(1); }
100% { opacity: 0; transform: translateY(-100px) scale(0); }
}
@keyframes fadeIn {
from { opacity: 0; transform: translateY(20px); }
to { opacity: 1; transform: translateY(0); }
}
@keyframes slideIn {
from { opacity: 0; transform: translateX(-20px); }
to { opacity: 1; transform: translateX(0); }
}
.custom-scrollbar::-webkit-scrollbar {
width: 6px;
}
.custom-scrollbar::-webkit-scrollbar-track {
background: #1a1a2e;
border-radius: 3px;
}
.custom-scrollbar::-webkit-scrollbar-thumb {
background: #00ff88;
border-radius: 3px;
}
.custom-scrollbar::-webkit-scrollbar-thumb:hover {
background: #00d4ff;
}
`}</style>
</div>
);
};
export default WeaselProgram;
参考文献
- Dawkins, R. (1996). The blind watchmaker: Why the evidence of evolution reveals a universe without design. WW Norton & Company.