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.

コメントを残す

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