Claude 3.7 Sonnetのコーディング能力を試す 3
以前の投稿でClaude 3.7 Sonnetのコーディング能力を試した.今回はリッチなという文言を追加してみてやってみたが,やはりこの文言を入れるとClaudeはReactを組み合わせた素晴らしいものを作ってくれる.しかし,多少のバグが含まれている.
Langton's Antのシミュレーションを可能な限りリッチな表現を用いたコードで書いて.その過程をアニメーションとして出力できるようにして.
import { useState, useEffect, useRef } from 'react';
const LangtonAnt = () => {
// 定数の設定
const GRID_SIZE = 75;
const CELL_SIZE = 8;
const ANIMATION_SPEED = 10; // ミリ秒単位のフレームの間隔
const MAX_STEPS = 15000; // 最大ステップ数
// 色の設定
const COLORS = {
WHITE: '#ffffff',
BLACK: '#000000',
ANT: '#ff0000',
TRAIL: '#ffcccc',
GRID_LINE: '#dddddd',
BACKGROUND: '#f5f5f5'
};
// アリの向きの列挙型
const Direction = {
UP: 0,
RIGHT: 1,
DOWN: 2,
LEFT: 3
};
// 状態の管理
const [grid, setGrid] = useState(() => {
const initialGrid = Array(GRID_SIZE).fill(null).map(() => Array(GRID_SIZE).fill(0));
return initialGrid;
});
const [ant, setAnt] = useState({
x: Math.floor(GRID_SIZE / 2),
y: Math.floor(GRID_SIZE / 2),
direction: Direction.UP
});
const [isRunning, setIsRunning] = useState(false);
const [steps, setSteps] = useState(0);
const [speed, setSpeed] = useState(ANIMATION_SPEED);
const requestRef = useRef(null);
const canvasRef = useRef(null);
// アリの移動ロジック
const moveAnt = () => {
setSteps(prevSteps => prevSteps + 1);
setGrid(prevGrid => {
const newGrid = [...prevGrid];
const { x, y } = ant;
// 現在のセルの色を変更
newGrid[y][x] = newGrid[y][x] === 0 ? 1 : 0;
return newGrid;
});
setAnt(prevAnt => {
const { x, y, direction } = prevAnt;
const currentColor = grid[y][x];
// アリの向きを変更
let newDirection;
if (currentColor === 0) {
// 白いセルでは右に90度回転
newDirection = (direction + 1) % 4;
} else {
// 黒いセルでは左に90度回転
newDirection = (direction - 1 + 4) % 4;
}
// 新しい位置を計算
let newX = x;
let newY = y;
switch (newDirection) {
case Direction.UP:
newY = (y - 1 + GRID_SIZE) % GRID_SIZE;
break;
case Direction.RIGHT:
newX = (x + 1) % GRID_SIZE;
break;
case Direction.DOWN:
newY = (y + 1) % GRID_SIZE;
break;
case Direction.LEFT:
newX = (x - 1 + GRID_SIZE) % GRID_SIZE;
break;
}
return {
x: newX,
y: newY,
direction: newDirection
};
});
};
// キャンバスの描画
const draw = () => {
const canvas = canvasRef.current;
if (!canvas) return;
const ctx = canvas.getContext('2d');
const width = GRID_SIZE * CELL_SIZE;
const height = GRID_SIZE * CELL_SIZE;
// キャンバスをクリア
ctx.fillStyle = COLORS.BACKGROUND;
ctx.fillRect(0, 0, width, height);
// グリッドの描画
for (let y = 0; y < GRID_SIZE; y++) {
for (let x = 0; x < GRID_SIZE; x++) {
const cellX = x * CELL_SIZE;
const cellY = y * CELL_SIZE;
// セルの色を設定
ctx.fillStyle = grid[y][x] === 0 ? COLORS.WHITE : COLORS.BLACK;
ctx.fillRect(cellX, cellY, CELL_SIZE, CELL_SIZE);
// グリッド線の描画
ctx.strokeStyle = COLORS.GRID_LINE;
ctx.strokeRect(cellX, cellY, CELL_SIZE, CELL_SIZE);
}
}
// アリの描画
const antX = ant.x * CELL_SIZE;
const antY = ant.y * CELL_SIZE;
ctx.fillStyle = COLORS.ANT;
ctx.fillRect(antX, antY, CELL_SIZE, CELL_SIZE);
// アリの向きを表す三角形を描画
ctx.beginPath();
ctx.fillStyle = COLORS.WHITE;
switch (ant.direction) {
case Direction.UP:
ctx.moveTo(antX + CELL_SIZE / 2, antY);
ctx.lineTo(antX + CELL_SIZE, antY + CELL_SIZE);
ctx.lineTo(antX, antY + CELL_SIZE);
break;
case Direction.RIGHT:
ctx.moveTo(antX + CELL_SIZE, antY + CELL_SIZE / 2);
ctx.lineTo(antX, antY + CELL_SIZE);
ctx.lineTo(antX, antY);
break;
case Direction.DOWN:
ctx.moveTo(antX + CELL_SIZE / 2, antY + CELL_SIZE);
ctx.lineTo(antX, antY);
ctx.lineTo(antX + CELL_SIZE, antY);
break;
case Direction.LEFT:
ctx.moveTo(antX, antY + CELL_SIZE / 2);
ctx.lineTo(antX + CELL_SIZE, antY);
ctx.lineTo(antX + CELL_SIZE, antY + CELL_SIZE);
break;
}
ctx.closePath();
ctx.fill();
};
// アニメーションループ
const animate = () => {
if (steps >= MAX_STEPS) {
setIsRunning(false);
return;
}
moveAnt();
draw();
requestRef.current = setTimeout(() => {
if (isRunning) {
requestAnimationFrame(animate);
}
}, speed);
};
// アニメーションの開始/停止
const toggleSimulation = () => {
setIsRunning(!isRunning);
};
// リセット
const resetSimulation = () => {
setIsRunning(false);
setSteps(0);
setGrid(Array(GRID_SIZE).fill(null).map(() => Array(GRID_SIZE).fill(0)));
setAnt({
x: Math.floor(GRID_SIZE / 2),
y: Math.floor(GRID_SIZE / 2),
direction: Direction.UP
});
// キャンバスの再描画
if (canvasRef.current) {
const ctx = canvasRef.current.getContext('2d');
ctx.clearRect(0, 0, GRID_SIZE * CELL_SIZE, GRID_SIZE * CELL_SIZE);
draw();
}
};
// ステップ実行
const stepSimulation = () => {
if (steps < MAX_STEPS) {
moveAnt();
draw();
}
};
// 速度変更
const handleSpeedChange = (e) => {
setSpeed(parseInt(e.target.value));
};
// 初期化時の描画
useEffect(() => {
draw();
}, []);
// アニメーションの管理
useEffect(() => {
if (isRunning) {
requestAnimationFrame(animate);
}
return () => {
if (requestRef.current) {
clearTimeout(requestRef.current);
}
};
}, [isRunning, speed]);
// 描画の更新
useEffect(() => {
draw();
}, [grid, ant]);
return (
<div className="flex flex-col items-center p-4 bg-gray-100 rounded-lg">
<h1 className="text-2xl font-bold mb-4 text-gray-800">ラングトンのアリ シミュレーション</h1>
<div className="mb-4">
<canvas
ref={canvasRef}
width={GRID_SIZE * CELL_SIZE}
height={GRID_SIZE * CELL_SIZE}
className="border border-gray-300 bg-white"
/>
</div>
<div className="flex items-center justify-center space-x-2 mb-4">
<button
onClick={toggleSimulation}
className={`px-4 py-2 rounded-md ${isRunning ? 'bg-red-500 hover:bg-red-600' : 'bg-green-500 hover:bg-green-600'} text-white font-medium`}
>
{isRunning ? '停止' : '開始'}
</button>
<button
onClick={stepSimulation}
className="px-4 py-2 rounded-md bg-blue-500 hover:bg-blue-600 text-white font-medium"
disabled={isRunning}
>
ステップ実行
</button>
<button
onClick={resetSimulation}
className="px-4 py-2 rounded-md bg-gray-500 hover:bg-gray-600 text-white font-medium"
>
リセット
</button>
</div>
<div className="flex items-center justify-center space-x-2 mb-4">
<label htmlFor="speed" className="text-gray-700">速度:</label>
<input
id="speed"
type="range"
min="1"
max="100"
value={speed}
onChange={handleSpeedChange}
className="w-32"
/>
<span className="text-gray-700">{speed}ms</span>
</div>
<div className="flex items-center justify-center space-x-4">
<p className="text-gray-700">ステップ数: <strong>{steps}</strong></p>
<p className="text-gray-700">位置: <strong>({ant.x}, {ant.y})</strong></p>
</div>
<div className="mt-4 p-4 bg-white rounded-md shadow-md">
<h2 className="text-lg font-semibold mb-2 text-gray-800">ラングトンのアリのルール:</h2>
<ul className="list-disc pl-5 text-gray-700">
<li>白いセルにアリがいる場合、右に90度回転し、セルを黒に変え、前進する</li>
<li>黒いセルにアリがいる場合、左に90度回転し、セルを白に変え、前進する</li>
</ul>
</div>
</div>
);
};
export default LangtonAnt;
