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.

コメントを残す

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