Claude 4 Sonnetのコーディング能力を試す
Claude 3.7 Sonnetのコーディング能力を試していたらClaude 4.0 が発表された.
そこで今回はその比較をすべく,以前と同じ内容のプロンプトを4.0Sonnetに打ち込んでみた.
すると,Claude 3.7 Sonnetよりも工夫された出力を返してくれた.
ライフゲームのシミュレーションを行うコードを書いて.
Reactで動的に処理できるようにして.
エラーが出ないように自己点検して.
import React, { useState, useEffect, useCallback, useRef } from 'react';
import { Play, Pause, RotateCcw, Square, Grid3X3 } from 'lucide-react';
const GRID_SIZE = 30;
const CELL_SIZE = 12;
// 空のグリッドを作成
const createEmptyGrid = () => {
return Array(GRID_SIZE).fill().map(() => Array(GRID_SIZE).fill(false));
};
// セルの近傍の生きているセル数を数える
const countNeighbors = (grid, x, y) => {
let count = 0;
const directions = [
[-1, -1], [-1, 0], [-1, 1],
[0, -1], [0, 1],
[1, -1], [1, 0], [1, 1]
];
for (const [dx, dy] of directions) {
const newX = x + dx;
const newY = y + dy;
if (newX >= 0 && newX < GRID_SIZE && newY >= 0 && newY < GRID_SIZE) {
if (grid[newX][newY]) {
count++;
}
}
}
return count;
};
// 次の世代を計算
const getNextGeneration = (currentGrid) => {
const newGrid = createEmptyGrid();
for (let i = 0; i < GRID_SIZE; i++) {
for (let j = 0; j < GRID_SIZE; j++) {
const neighbors = countNeighbors(currentGrid, i, j);
const isAlive = currentGrid[i][j];
if (isAlive) {
// 生きているセルの場合
newGrid[i][j] = neighbors === 2 || neighbors === 3;
} else {
// 死んでいるセルの場合
newGrid[i][j] = neighbors === 3;
}
}
}
return newGrid;
};
// よく知られたパターンを生成
const generatePattern = (pattern) => {
const grid = createEmptyGrid();
const centerX = Math.floor(GRID_SIZE / 2);
const centerY = Math.floor(GRID_SIZE / 2);
switch (pattern) {
case 'glider':
const glider = [[0, 1], [1, 2], [2, 0], [2, 1], [2, 2]];
glider.forEach(([x, y]) => {
if (centerX + x < GRID_SIZE && centerY + y < GRID_SIZE) {
grid[centerX + x][centerY + y] = true;
}
});
break;
case 'blinker':
for (let i = -1; i <= 1; i++) {
if (centerX + i >= 0 && centerX + i < GRID_SIZE) {
grid[centerX + i][centerY] = true;
}
}
break;
case 'block':
for (let i = 0; i < 2; i++) {
for (let j = 0; j < 2; j++) {
if (centerX + i < GRID_SIZE && centerY + j < GRID_SIZE) {
grid[centerX + i][centerY + j] = true;
}
}
}
break;
default:
// ランダムパターン
for (let i = 0; i < GRID_SIZE; i++) {
for (let j = 0; j < GRID_SIZE; j++) {
grid[i][j] = Math.random() < 0.3;
}
}
}
return grid;
};
const GameOfLife = () => {
const [grid, setGrid] = useState(() => createEmptyGrid());
const [isRunning, setIsRunning] = useState(false);
const [generation, setGeneration] = useState(0);
const [speed, setSpeed] = useState(200);
const intervalRef = useRef(null);
// シミュレーションの実行
const runSimulation = useCallback(() => {
if (!isRunning) return;
setGrid(currentGrid => {
const newGrid = getNextGeneration(currentGrid);
setGeneration(gen => gen + 1);
return newGrid;
});
}, [isRunning]);
// インターバルの管理
useEffect(() => {
if (isRunning) {
intervalRef.current = setInterval(runSimulation, speed);
} else {
if (intervalRef.current) {
clearInterval(intervalRef.current);
intervalRef.current = null;
}
}
return () => {
if (intervalRef.current) {
clearInterval(intervalRef.current);
}
};
}, [isRunning, speed, runSimulation]);
// セルのクリック処理
const toggleCell = (x, y) => {
if (isRunning) return;
setGrid(currentGrid => {
const newGrid = currentGrid.map(row => [...row]);
newGrid[x][y] = !newGrid[x][y];
return newGrid;
});
};
// コントロール関数
const startStop = () => {
setIsRunning(!isRunning);
};
const reset = () => {
setIsRunning(false);
setGrid(createEmptyGrid());
setGeneration(0);
};
const loadPattern = (pattern) => {
if (isRunning) return;
setGrid(generatePattern(pattern));
setGeneration(0);
};
const nextStep = () => {
if (isRunning) return;
setGrid(currentGrid => getNextGeneration(currentGrid));
setGeneration(gen => gen + 1);
};
return (
<div className="p-6 bg-gray-100 min-h-screen">
<div className="max-w-4xl mx-auto">
<h1 className="text-3xl font-bold text-center mb-6 text-gray-800">
Conway's Game of Life
</h1>
{/* コントロールパネル */}
<div className="bg-white rounded-lg shadow-md p-4 mb-6">
<div className="flex flex-wrap items-center justify-center gap-4 mb-4">
<button
onClick={startStop}
className={`flex items-center gap-2 px-4 py-2 rounded font-medium transition-colors ${
isRunning
? 'bg-red-500 hover:bg-red-600 text-white'
: 'bg-green-500 hover:bg-green-600 text-white'
}`}
>
{isRunning ? <Pause size={16} /> : <Play size={16} />}
{isRunning ? '停止' : '開始'}
</button>
<button
onClick={nextStep}
disabled={isRunning}
className="flex items-center gap-2 px-4 py-2 bg-blue-500 hover:bg-blue-600 disabled:bg-gray-300 text-white rounded font-medium transition-colors"
>
<Grid3X3 size={16} />
次の世代
</button>
<button
onClick={reset}
className="flex items-center gap-2 px-4 py-2 bg-gray-500 hover:bg-gray-600 text-white rounded font-medium transition-colors"
>
<RotateCcw size={16} />
リセット
</button>
</div>
{/* パターン選択 */}
<div className="flex flex-wrap items-center justify-center gap-2 mb-4">
<span className="font-medium text-gray-700">パターン:</span>
<button
onClick={() => loadPattern('random')}
disabled={isRunning}
className="px-3 py-1 bg-purple-500 hover:bg-purple-600 disabled:bg-gray-300 text-white rounded text-sm transition-colors"
>
ランダム
</button>
<button
onClick={() => loadPattern('glider')}
disabled={isRunning}
className="px-3 py-1 bg-purple-500 hover:bg-purple-600 disabled:bg-gray-300 text-white rounded text-sm transition-colors"
>
グライダー
</button>
<button
onClick={() => loadPattern('blinker')}
disabled={isRunning}
className="px-3 py-1 bg-purple-500 hover:bg-purple-600 disabled:bg-gray-300 text-white rounded text-sm transition-colors"
>
ブリンカー
</button>
<button
onClick={() => loadPattern('block')}
disabled={isRunning}
className="px-3 py-1 bg-purple-500 hover:bg-purple-600 disabled:bg-gray-300 text-white rounded text-sm transition-colors"
>
ブロック
</button>
</div>
{/* スピード調整 */}
<div className="flex items-center justify-center gap-4">
<label className="font-medium text-gray-700">速度:</label>
<input
type="range"
min="50"
max="1000"
value={speed}
onChange={(e) => setSpeed(parseInt(e.target.value))}
className="w-32"
/>
<span className="text-sm text-gray-600">{speed}ms</span>
</div>
</div>
{/* 情報表示 */}
<div className="text-center mb-4">
<span className="text-lg font-semibold text-gray-700">
世代: {generation}
</span>
</div>
{/* ゲームグリッド */}
<div className="flex justify-center">
<div
className="inline-block bg-white p-4 rounded-lg shadow-md"
style={{
display: 'grid',
gridTemplateColumns: `repeat(${GRID_SIZE}, ${CELL_SIZE}px)`,
gap: '1px',
backgroundColor: '#e5e7eb'
}}
>
{grid.map((row, i) =>
row.map((cell, j) => (
<div
key={`${i}-${j}`}
className={`cursor-pointer transition-colors duration-100 ${
cell ? 'bg-blue-600 hover:bg-blue-700' : 'bg-white hover:bg-gray-100'
}`}
style={{
width: `${CELL_SIZE}px`,
height: `${CELL_SIZE}px`
}}
onClick={() => toggleCell(i, j)}
/>
))
)}
</div>
</div>
{/* 説明 */}
<div className="mt-6 bg-white rounded-lg shadow-md p-4">
<h3 className="font-bold mb-2 text-gray-800">遊び方:</h3>
<ul className="text-sm text-gray-600 space-y-1">
<li>• セルをクリックして生きているセル(青)と死んでいるセル(白)を切り替えます</li>
<li>• 「開始」ボタンでシミュレーションを開始/停止します</li>
<li>• 「次の世代」ボタンで1ステップずつ進めます</li>
<li>• パターンボタンで有名な図形を配置できます</li>
<li>• スライダーでシミュレーションの速度を調整できます</li>
</ul>
<h3 className="font-bold mt-4 mb-2 text-gray-800">ルール:</h3>
<ul className="text-sm text-gray-600 space-y-1">
<li>• 生きているセルは2または3の近傍を持つ場合、次の世代でも生存</li>
<li>• 死んでいるセルは3の近傍を持つ場合、次の世代で誕生</li>
<li>• その他の場合は死滅または死んだまま</li>
</ul>
</div>
</div>
</div>
);
};
export default GameOfLife;
