Newer
Older
quiz2023 / sakasho.js
document.addEventListener('DOMContentLoaded', () => {
    // Leafletマップ初期化 (東京中心)
    const map = L.map('map').setView([35.6762, 139.6503], 13); // 中心座標とズームレベル
    L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
        attribution: '&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
    }).addTo(map);

    const gridSize = 10; // 10x10仮想グリッド
    let elevationGrid = []; // 標高データ (ランダム)
    let start = null;
    let goal = null;
    let pathLayer = null; // ルート表示レイヤー
    let startMarker = null;
    let goalMarker = null;

    // 標高データをランダム生成 (0-100)
    function generateElevation() {
        elevationGrid = [];
        for (let y = 0; y < gridSize; y++) {
            elevationGrid[y] = [];
            for (let x = 0; x < gridSize; x++) {
                elevationGrid[y][x] = Math.floor(Math.random() * 100); // ランダム標高
            }
        }
    }

    // 緯度経度をグリッドインデックスに変換
    function latLngToGrid(latLng) {
        const bounds = map.getBounds();
        const north = bounds.getNorth();
        const south = bounds.getSouth();
        const east = bounds.getEast();
        const west = bounds.getWest();

        const x = Math.floor(((latLng.lng - west) / (east - west)) * gridSize);
        const y = Math.floor(((north - latLng.lat) / (north - south)) * gridSize);

        return { x: Math.max(0, Math.min(gridSize - 1, x)), y: Math.max(0, Math.min(gridSize - 1, y)) };
    }

    // グリッドインデックスを緯度経度に変換
    function gridToLatLng(grid) {
        const bounds = map.getBounds();
        const north = bounds.getNorth();
        const south = bounds.getSouth();
        const east = bounds.getEast();
        const west = bounds.getWest();

        const lat = north - (grid.y / gridSize) * (north - south);
        const lng = west + (grid.x / gridSize) * (east - west);

        return L.latLng(lat, lng);
    }

    // A*パスファインディング (坂を考慮)
    function findPath(startGrid, goalGrid) {
        const openSet = [];
        const cameFrom = {};
        const gScore = {};
        const fScore = {};

        const getKey = (node) => `${node.x},${node.y}`;
        const heuristic = (a, b) => Math.abs(a.x - b.x) + Math.abs(a.y - b.y);

        openSet.push(startGrid);
        gScore[getKey(startGrid)] = 0;
        fScore[getKey(startGrid)] = heuristic(startGrid, goalGrid);

        while (openSet.length > 0) {
            openSet.sort((a, b) => fScore[getKey(a)] - fScore[getKey(b)]);
            const current = openSet.shift();

            if (current.x === goalGrid.x && current.y === goalGrid.y) {
                const path = [];
                let temp = current;
                while (temp) {
                    path.push(temp);
                    temp = cameFrom[getKey(temp)];
                }
                return path.reverse();
            }

            const neighbors = [
                {x: current.x + 1, y: current.y},
                {x: current.x - 1, y: current.y},
                {x: current.x, y: current.y + 1},
                {x: current.x, y: current.y - 1}
            ];

            for (let neighbor of neighbors) {
                if (neighbor.x < 0 || neighbor.x >= gridSize || neighbor.y < 0 || neighbor.y >= gridSize) continue;

                const elevDiff = Math.abs(elevationGrid[current.y][current.x] - elevationGrid[neighbor.y][neighbor.x]);
                const tentativeG = gScore[getKey(current)] + 1 + elevDiff * 0.5;

                if (!gScore[getKey(neighbor)] || tentativeG < gScore[getKey(neighbor)]) {
                    cameFrom[getKey(neighbor)] = current;
                    gScore[getKey(neighbor)] = tentativeG;
                    fScore[getKey(neighbor)] = tentativeG + heuristic(neighbor, goalGrid);
                    if (!openSet.some(n => n.x === neighbor.x && n.y === neighbor.y)) {
                        openSet.push(neighbor);
                    }
                }
            }
        }
        return [];
    }

    // ルートを描画
    function drawPath(path) {
        if (pathLayer) map.removeLayer(pathLayer);
        if (path.length === 0) return;

        const latLngs = path.map(grid => gridToLatLng(grid));
        pathLayer = L.polyline(latLngs, { color: 'blue', weight: 3 }).addTo(map);
    }

    // クリックイベント (地図上でポイント選択)
    map.on('click', (event) => {
        const grid = latLngToGrid(event.latlng);

        if (!start) {
            start = grid;
            if (startMarker) map.removeLayer(startMarker);
            startMarker = L.marker(event.latlng, { color: 'green' }).addTo(map);
        } else if (!goal) {
            goal = grid;
            if (goalMarker) map.removeLayer(goalMarker);
            goalMarker = L.marker(event.latlng, { color: 'red' }).addTo(map);

            const path = findPath(start, goal);
            drawPath(path);
        }
    });

    // リセットボタンイベント
    document.getElementById('resetButton').addEventListener('click', resetMap);

    // リセット
    function resetMap() {
        start = null;
        goal = null;
        if (startMarker) map.removeLayer(startMarker);
        if (goalMarker) map.removeLayer(goalMarker);
        if (pathLayer) map.removeLayer(pathLayer);
        generateElevation(); // 新しい標高生成
    }

    // 初期化
    generateElevation();
});