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;
