Claude 3.7 Sonnetのコーディング能力を試す 7
以前の投稿でClaude 3.7 Sonnetのコーディング能力を試した.
今回は引き続きReactを使ってコードを書いてもらい交通渋滞のシミュレーションを行ってみた.
すると今回は描画も挙動も素晴らしいものを出してくれた.
MASの発想で交通渋滞が起こる様子のシミュレーションを行うコードを書いて.
Reactで動的に処理できるようにして.
エラーが出ないように自己点検して.
import { useState, useEffect, useRef } from 'react';
// 車両エージェントの定義
class Vehicle {
constructor(id, x, lane, speed, maxSpeed, length) {
this.id = id;
this.x = x; // 道路上の位置
this.lane = lane; // 車線
this.speed = speed; // 現在の速度
this.maxSpeed = maxSpeed; // 最高速度
this.length = length; // 車両の長さ
this.color = this.getRandomColor();
this.braking = false; // ブレーキ状態
}
// ランダムな車両カラーを生成
getRandomColor() {
const colors = ['#e74c3c', '#3498db', '#2ecc71', '#f1c40f', '#9b59b6', '#1abc9c', '#34495e', '#e67e22'];
return colors[Math.floor(Math.random() * colors.length)];
}
// 前方の車両との距離を計算して速度を調整
update(vehicles, roadLength) {
// 安全距離 (車両の長さの2倍 + 速度に応じた距離)
const safeDistance = this.length * 2 + this.speed * 2;
// 同じ車線の前方車両を探す
let nearestFrontVehicle = null;
let minDistance = roadLength;
for (const vehicle of vehicles) {
if (vehicle.id === this.id || vehicle.lane !== this.lane) continue;
// 前方の車両かどうか判定(円環状の道路を考慮)
let distance = vehicle.x - this.x;
if (distance < 0) distance += roadLength;
if (distance > 0 && distance < minDistance) {
minDistance = distance;
nearestFrontVehicle = vehicle;
}
}
// 前方に車がいる場合
if (nearestFrontVehicle && minDistance < safeDistance) {
// 前方車両との距離に応じて減速
this.speed = Math.max(0, this.speed - 0.5);
this.braking = true;
} else {
// 安全距離が確保されている場合、徐々に加速
this.speed = Math.min(this.maxSpeed, this.speed + 0.1);
this.braking = false;
}
// 位置の更新(円環状道路)
this.x = (this.x + this.speed) % roadLength;
}
}
export default function TrafficSimulation() {
const [vehicles, setVehicles] = useState([]);
const [isRunning, setIsRunning] = useState(false);
const [vehicleCount, setVehicleCount] = useState(20);
const [roadLength, setRoadLength] = useState(1000);
const [density, setDensity] = useState(20); // 車両密度 (%)
const [speedVariation, setSpeedVariation] = useState(true); // 速度のばらつき
const animationRef = useRef(null);
const canvasRef = useRef(null);
// シミュレーション初期化
const initializeSimulation = () => {
const newVehicles = [];
const segmentLength = roadLength / vehicleCount;
// 車両の生成
for (let i = 0; i < vehicleCount; i++) {
// 密度パラメータに基づいて車両を配置するかどうかを決定
if (Math.random() * 100 > density) continue;
// 基本速度 + ばらつき (speedVariationがtrueの場合)
const baseSpeed = 2;
const speedVar = speedVariation ? Math.random() * 2 - 0.5 : 0;
const maxSpeed = baseSpeed + speedVar;
// 車両を生成(位置を少しランダムに散らす)
const position = i * segmentLength + Math.random() * (segmentLength * 0.5);
const lane = Math.floor(Math.random() * 2); // 2車線
newVehicles.push(new Vehicle(
i,
position % roadLength,
lane,
maxSpeed * 0.5, // 初期速度は最高速度の半分
maxSpeed,
20 // 車両の長さ
));
}
setVehicles(newVehicles);
};
// シミュレーションの更新
const updateSimulation = () => {
setVehicles(prevVehicles => {
const updatedVehicles = [...prevVehicles];
// すべての車両を更新
for (let vehicle of updatedVehicles) {
vehicle.update(updatedVehicles, roadLength);
}
return updatedVehicles;
});
// キャンバスの描画
renderCanvas();
// アニメーションフレームを要求
if (isRunning) {
animationRef.current = requestAnimationFrame(updateSimulation);
}
};
// キャンバスへの描画
const renderCanvas = () => {
const canvas = canvasRef.current;
if (!canvas) return;
const ctx = canvas.getContext('2d');
const canvasWidth = canvas.width;
const canvasHeight = canvas.height;
// キャンバスをクリア
ctx.clearRect(0, 0, canvasWidth, canvasHeight);
// 道路を描画
ctx.fillStyle = '#555';
ctx.fillRect(0, 0, canvasWidth, canvasHeight);
// 車線区分線を描画
ctx.strokeStyle = '#fff';
ctx.setLineDash([20, 20]);
ctx.beginPath();
ctx.moveTo(0, canvasHeight / 2);
ctx.lineTo(canvasWidth, canvasHeight / 2);
ctx.stroke();
ctx.setLineDash([]);
// 道路の外枠を描画
ctx.strokeStyle = '#fff';
ctx.lineWidth = 3;
ctx.strokeRect(0, 0, canvasWidth, canvasHeight);
// 車両を描画
for (const vehicle of vehicles) {
// 道路上の位置をキャンバス座標に変換
const x = (vehicle.x / roadLength) * canvasWidth;
const laneHeight = canvasHeight / 2;
const y = vehicle.lane * laneHeight + laneHeight / 2;
// 車両の描画
ctx.fillStyle = vehicle.color;
ctx.beginPath();
const vehicleWidth = 30;
const vehicleHeight = 15;
ctx.rect(x - vehicleWidth/2, y - vehicleHeight/2, vehicleWidth, vehicleHeight);
ctx.fill();
// ブレーキランプを描画
if (vehicle.braking) {
ctx.fillStyle = '#ff0000';
ctx.beginPath();
ctx.rect(x - vehicleWidth/2, y - vehicleHeight/2 + vehicleHeight - 3, vehicleWidth, 3);
ctx.fill();
}
}
};
// シミュレーション開始/停止
const toggleSimulation = () => {
setIsRunning(prev => !prev);
};
// パラメータ変更時の再初期化
useEffect(() => {
if (isRunning) {
setIsRunning(false);
cancelAnimationFrame(animationRef.current);
}
initializeSimulation();
}, [vehicleCount, roadLength, density, speedVariation]);
// シミュレーション実行状態の管理
useEffect(() => {
if (isRunning) {
animationRef.current = requestAnimationFrame(updateSimulation);
} else if (animationRef.current) {
cancelAnimationFrame(animationRef.current);
}
return () => {
if (animationRef.current) {
cancelAnimationFrame(animationRef.current);
}
};
}, [isRunning]);
// 初期化
useEffect(() => {
initializeSimulation();
// キャンバスのサイズ設定
const canvas = canvasRef.current;
if (canvas) {
canvas.width = 800;
canvas.height = 200;
renderCanvas();
}
}, []);
return (
<div className="flex flex-col items-center w-full max-w-4xl mx-auto p-4 bg-gray-100 rounded-lg">
<h1 className="text-2xl font-bold mb-4">交通渋滞MASシミュレーション</h1>
<div className="w-full mb-4 bg-white p-4 rounded shadow">
<canvas ref={canvasRef} className="w-full h-auto border border-gray-300"></canvas>
</div>
<div className="w-full grid grid-cols-1 md:grid-cols-2 gap-4 mb-4">
<div className="flex flex-col">
<label className="mb-1 font-medium">車両数: {vehicleCount}</label>
<input
type="range"
min="5"
max="50"
value={vehicleCount}
onChange={(e) => setVehicleCount(parseInt(e.target.value))}
className="w-full"
/>
</div>
<div className="flex flex-col">
<label className="mb-1 font-medium">車両密度: {density}%</label>
<input
type="range"
min="10"
max="100"
value={density}
onChange={(e) => setDensity(parseInt(e.target.value))}
className="w-full"
/>
</div>
</div>
<div className="w-full flex flex-col md:flex-row justify-between mb-4">
<div className="flex items-center mb-2 md:mb-0">
<input
type="checkbox"
id="speedVariation"
checked={speedVariation}
onChange={() => setSpeedVariation(!speedVariation)}
className="mr-2"
/>
<label htmlFor="speedVariation" className="font-medium">速度のばらつき</label>
</div>
<div className="flex space-x-4">
<button
onClick={toggleSimulation}
className={`px-4 py-2 rounded font-medium ${isRunning ? 'bg-red-500 text-white' : 'bg-green-500 text-white'}`}
>
{isRunning ? '停止' : '開始'}
</button>
<button
onClick={initializeSimulation}
className="px-4 py-2 bg-blue-500 text-white rounded font-medium"
disabled={isRunning}
>
リセット
</button>
</div>
</div>
<div className="w-full bg-white p-4 rounded shadow">
<h2 className="text-lg font-semibold mb-2">シミュレーション説明</h2>
<p className="mb-2">
このシミュレーションは、マルチエージェントシステム(MAS)の概念に基づく交通流シミュレーションです。
各車両はエージェントとして独立して行動し、周囲の状況に応じて速度を調整します。
</p>
<p className="mb-2">
車両は前方の車両との安全距離を保とうとし、前方車両が近すぎる場合は減速します。
このシンプルなルールから、実際の交通と同様の渋滞パターンが自然発生します。
</p>
<ul className="list-disc pl-5">
<li className="mb-1">赤いライトは車両がブレーキをかけていることを示します</li>
<li className="mb-1">密度を高くするほど渋滞が発生しやすくなります</li>
<li className="mb-1">速度のばらつきをオンにすると、車両ごとに最高速度が異なり、より自然な交通流になります</li>
</ul>
</div>
</div>
);
}
