Claude 4 Sonnetのコーディング能力を試す 5


Claude 4 が話題を呼んでいる.今回はClaude 3.7との比較でLangton’s Antのシミュレーションを行ってみた.

こちらも素晴らしいシミュレーターがポンだしで出せた.本当にこういった簡易的なシミュレーターはClaudeでポンだしで事足りる世界になってきた.

Langton's Antのシミュレーションを行うコードを書いて. 
Reactで動的に処理できるようにして. 
エラーが出ないように自己点検して.
一切の遠慮は不要です!!最高品質を追い求めて.
import React, { useState, useEffect, useRef, useCallback } from 'react';
import { Play, Pause, RotateCcw, Settings, Zap } from 'lucide-react';

const LangtonsAnt = () => {
  // グリッドサイズとセルサイズの設定
  const GRID_WIDTH = 120;
  const GRID_HEIGHT = 80;
  const CELL_SIZE = 6;
  
  // 方向の定義(北、東、南、西)
  const DIRECTIONS = [
    { dx: 0, dy: -1 }, // 北
    { dx: 1, dy: 0 },  // 東
    { dx: 0, dy: 1 },  // 南
    { dx: -1, dy: 0 }  // 西
  ];

  // 状態管理
  const [grid, setGrid] = useState(() => 
    Array(GRID_HEIGHT).fill().map(() => Array(GRID_WIDTH).fill(0))
  );
  const [ant, setAnt] = useState({
    x: Math.floor(GRID_WIDTH / 2),
    y: Math.floor(GRID_HEIGHT / 2),
    direction: 0 // 0=北, 1=東, 2=南, 3=西
  });
  const [isRunning, setIsRunning] = useState(false);
  const [speed, setSpeed] = useState(50);
  const [stepCount, setStepCount] = useState(0);
  const [showSettings, setShowSettings] = useState(false);
  
  const intervalRef = useRef(null);
  const canvasRef = useRef(null);

  // シミュレーションの1ステップを実行
  const step = useCallback(() => {
    setGrid(prevGrid => {
      const newGrid = prevGrid.map(row => [...row]);
      
      setAnt(prevAnt => {
        const { x, y, direction } = prevAnt;
        
        // 境界チェック
        if (x < 0 || x >= GRID_WIDTH || y < 0 || y >= GRID_HEIGHT) {
          return prevAnt;
        }
        
        const currentCell = newGrid[y][x];
        
        // Langton's Antのルール
        let newDirection;
        if (currentCell === 0) {
          // 白いセル: 右に90度回転
          newDirection = (direction + 1) % 4;
          newGrid[y][x] = 1; // セルを黒に変更
        } else {
          // 黒いセル: 左に90度回転
          newDirection = (direction + 3) % 4;
          newGrid[y][x] = 0; // セルを白に変更
        }
        
        // 新しい位置を計算
        const newX = x + DIRECTIONS[newDirection].dx;
        const newY = y + DIRECTIONS[newDirection].dy;
        
        return {
          x: newX,
          y: newY,
          direction: newDirection
        };
      });
      
      setStepCount(prev => prev + 1);
      return newGrid;
    });
  }, []);

  // キャンバス描画
  const draw = useCallback(() => {
    const canvas = canvasRef.current;
    if (!canvas) return;
    
    const ctx = canvas.getContext('2d');
    
    // キャンバスをクリア
    ctx.fillStyle = '#f8fafc';
    ctx.fillRect(0, 0, canvas.width, canvas.height);
    
    // グリッドを描画
    for (let y = 0; y < GRID_HEIGHT; y++) {
      for (let x = 0; x < GRID_WIDTH; x++) {
        if (grid[y][x] === 1) {
          ctx.fillStyle = '#1e293b';
          ctx.fillRect(x * CELL_SIZE, y * CELL_SIZE, CELL_SIZE, CELL_SIZE);
        }
      }
    }
    
    // アリを描画
    if (ant.x >= 0 && ant.x < GRID_WIDTH && ant.y >= 0 && ant.y < GRID_HEIGHT) {
      const centerX = ant.x * CELL_SIZE + CELL_SIZE / 2;
      const centerY = ant.y * CELL_SIZE + CELL_SIZE / 2;
      
      // アリの本体(円)
      ctx.fillStyle = '#dc2626';
      ctx.beginPath();
      ctx.arc(centerX, centerY, CELL_SIZE / 3, 0, 2 * Math.PI);
      ctx.fill();
      
      // 方向を示す三角形
      ctx.fillStyle = '#b91c1c';
      ctx.beginPath();
      
      const size = CELL_SIZE / 4;
      let angle = ant.direction * Math.PI / 2 - Math.PI / 2;
      
      const x1 = centerX + Math.cos(angle) * size;
      const y1 = centerY + Math.sin(angle) * size;
      const x2 = centerX + Math.cos(angle + 2.4) * size * 0.7;
      const y2 = centerY + Math.sin(angle + 2.4) * size * 0.7;
      const x3 = centerX + Math.cos(angle - 2.4) * size * 0.7;
      const y3 = centerY + Math.sin(angle - 2.4) * size * 0.7;
      
      ctx.moveTo(x1, y1);
      ctx.lineTo(x2, y2);
      ctx.lineTo(x3, y3);
      ctx.closePath();
      ctx.fill();
    }
  }, [grid, ant]);

  // アニメーションループ
  useEffect(() => {
    if (isRunning) {
      intervalRef.current = setInterval(() => {
        step();
      }, 101 - speed);
    } else {
      if (intervalRef.current) {
        clearInterval(intervalRef.current);
      }
    }
    
    return () => {
      if (intervalRef.current) {
        clearInterval(intervalRef.current);
      }
    };
  }, [isRunning, speed, step]);

  // 描画更新
  useEffect(() => {
    draw();
  }, [draw]);

  // リセット機能
  const reset = () => {
    setIsRunning(false);
    setGrid(Array(GRID_HEIGHT).fill().map(() => Array(GRID_WIDTH).fill(0)));
    setAnt({
      x: Math.floor(GRID_WIDTH / 2),
      y: Math.floor(GRID_HEIGHT / 2),
      direction: 0
    });
    setStepCount(0);
  };

  // 高速実行
  const fastForward = () => {
    for (let i = 0; i < 100; i++) {
      step();
    }
  };

  return (
    <div className="min-h-screen bg-gradient-to-br from-slate-900 via-blue-900 to-slate-800 p-6">
      <div className="max-w-6xl mx-auto">
        {/* ヘッダー */}
        <div className="text-center mb-8">
          <h1 className="text-4xl font-bold text-white mb-4 bg-gradient-to-r from-blue-400 to-purple-400 bg-clip-text text-transparent">
            Langton's Ant シミュレーター
          </h1>
          <p className="text-slate-300 text-lg">
            単純なルールから生まれる複雑なパターンを観察しよう
          </p>
        </div>

        {/* メインコンテンツ */}
        <div className="bg-white/10 backdrop-blur-lg rounded-2xl p-6 shadow-2xl border border-white/20">
          {/* 統計パネル */}
          <div className="grid grid-cols-1 md:grid-cols-3 gap-4 mb-6">
            <div className="bg-gradient-to-r from-blue-500/20 to-purple-500/20 rounded-lg p-4 text-center">
              <div className="text-2xl font-bold text-white">{stepCount.toLocaleString()}</div>
              <div className="text-slate-300">ステップ数</div>
            </div>
            <div className="bg-gradient-to-r from-green-500/20 to-blue-500/20 rounded-lg p-4 text-center">
              <div className="text-2xl font-bold text-white">({ant.x}, {ant.y})</div>
              <div className="text-slate-300">アリの位置</div>
            </div>
            <div className="bg-gradient-to-r from-purple-500/20 to-pink-500/20 rounded-lg p-4 text-center">
              <div className="text-2xl font-bold text-white">
                {['北', '東', '南', '西'][ant.direction]}
              </div>
              <div className="text-slate-300">方向</div>
            </div>
          </div>

          {/* キャンバス */}
          <div className="bg-white rounded-lg p-4 mb-6 shadow-inner">
            <canvas
              ref={canvasRef}
              width={GRID_WIDTH * CELL_SIZE}
              height={GRID_HEIGHT * CELL_SIZE}
              className="border border-slate-200 rounded mx-auto block"
              style={{ maxWidth: '100%', height: 'auto' }}
            />
          </div>

          {/* コントロールパネル */}
          <div className="flex flex-wrap gap-4 items-center justify-center">
            <button
              onClick={() => setIsRunning(!isRunning)}
              className={`flex items-center gap-2 px-6 py-3 rounded-lg font-semibold transition-all transform hover:scale-105 ${
                isRunning 
                  ? 'bg-red-500 hover:bg-red-600 text-white' 
                  : 'bg-green-500 hover:bg-green-600 text-white'
              }`}
            >
              {isRunning ? <Pause size={20} /> : <Play size={20} />}
              {isRunning ? '一時停止' : '開始'}
            </button>

            <button
              onClick={reset}
              className="flex items-center gap-2 px-6 py-3 bg-slate-600 hover:bg-slate-700 text-white rounded-lg font-semibold transition-all transform hover:scale-105"
            >
              <RotateCcw size={20} />
              リセット
            </button>

            <button
              onClick={fastForward}
              className="flex items-center gap-2 px-6 py-3 bg-yellow-500 hover:bg-yellow-600 text-white rounded-lg font-semibold transition-all transform hover:scale-105"
            >
              <Zap size={20} />
              高速実行
            </button>

            <button
              onClick={() => setShowSettings(!showSettings)}
              className="flex items-center gap-2 px-6 py-3 bg-blue-500 hover:bg-blue-600 text-white rounded-lg font-semibold transition-all transform hover:scale-105"
            >
              <Settings size={20} />
              設定
            </button>
          </div>

          {/* 設定パネル */}
          {showSettings && (
            <div className="mt-6 bg-black/20 rounded-lg p-6 backdrop-blur">
              <h3 className="text-xl font-semibold text-white mb-4">シミュレーション設定</h3>
              <div className="space-y-4">
                <div>
                  <label className="block text-slate-300 mb-2">
                    実行速度: {speed}%
                  </label>
                  <input
                    type="range"
                    min="1"
                    max="100"
                    value={speed}
                    onChange={(e) => setSpeed(parseInt(e.target.value))}
                    className="w-full h-2 bg-slate-600 rounded-lg appearance-none cursor-pointer slider"
                  />
                </div>
              </div>
            </div>
          )}

          {/* 説明パネル */}
          <div className="mt-6 bg-black/20 rounded-lg p-6 backdrop-blur">
            <h3 className="text-xl font-semibold text-white mb-4">Langton's Antとは?</h3>
            <div className="text-slate-300 space-y-2">
              <p>• 白いセルにいる場合:右に90度回転し、そのセルを黒く塗り、前進</p>
              <p>• 黒いセルにいる場合:左に90度回転し、そのセルを白く塗り、前進</p>
              <p>• この単純なルールから、最初は混沌としたパターンが生まれ、約10,000ステップ後に規則的な「ハイウェイ」パターンが出現します</p>
            </div>
          </div>
        </div>
      </div>
    </div>
  );
};

export default LangtonsAnt;

コメントを残す

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