// map validation script (node) import { MAP_ROWS, PROVINCE_LETTERS, CITY_DEFS, hexToWorld, RIVERS } from "../js/data.js"; const rows = MAP_ROWS; const H = rows.length; const at = (c, r) => (r < 0 || r >= H || c < 0 || c >= (rows[r]?.length ?? 0)) ? "." : rows[r][c]; const isLand = ch => ch !== "."; const provOf = ch => isLand(ch) ? ch.toLowerCase() : null; // neighbors odd-r offset function nbrs(c, r) { const odd = r % 2; return [[c-1,r],[c+1,r],[c+odd-1,r-1],[c+odd,r-1],[c+odd-1,r+1],[c+odd,r+1]]; } // contiguity per province for (const P of PROVINCE_LETTERS) { const cells = []; for (let r = 0; r < H; r++) for (let c = 0; c < (rows[r].length); c++) if (provOf(rows[r][c]) === P) cells.push([c, r]); if (!cells.length) { console.log(`!! province ${P} EMPTY`); continue; } const set = new Set(cells.map(([c, r]) => c + "," + r)); const seen = new Set([cells[0].join(",")]); const stack = [cells[0]]; while (stack.length) { const [c, r] = stack.pop(); for (const [nc, nr] of nbrs(c, r)) { if (provOf(at(nc, nr)) === P && !seen.has(nc + "," + nr)) { seen.add(nc + "," + nr); stack.push([nc, nr]); } } } console.log(`${set.size === seen.size ? "OK " : "SPLIT"} ${P}: ${cells.length} hexes${set.size !== seen.size ? " (" + (set.size - seen.size) + " orphan)" : ""}`); } // adjacency between provinces const adj = new Set(); for (let r = 0; r < H; r++) for (let c = 0; c < rows[r].length; c++) { const p = provOf(rows[r][c]); if (!p) continue; for (const [nc, nr] of nbrs(c, r)) { const q = provOf(at(nc, nr)); if (q && q !== p) adj.add([p, q].sort().join("")); } } console.log("adjacency:", [...adj].sort().join(" ")); // city checks let bad = 0; for (const [id, name, , prov, c, r] of CITY_DEFS) { const p = provOf(at(c, r)); if (p !== prov) { console.log(`CITY BAD: ${name} wants ${prov} but tile(${c},${r})='${at(c, r)}'`); bad++; } } // uniqueness of city tiles const tiles = new Set(); for (const [id, name, , , c, r] of CITY_DEFS) { const k = c + "," + r; if (tiles.has(k)) { console.log("CITY OVERLAP at", k); bad++; } tiles.add(k); } console.log(bad ? `${bad} city problems` : `all ${CITY_DEFS.length} cities valid & unique`); // river points on land? for (const rv of RIVERS) for (const [c, r] of rv.pts) if (!isLand(at(c, r))) console.log(`river ${rv.name} off-land at ${c},${r}`); // world extent let minX = 1e9, maxX = -1e9, minZ = 1e9, maxZ = -1e9; for (let r = 0; r < H; r++) for (let c = 0; c < rows[r].length; c++) if (isLand(rows[r][c])) { const [x, z] = hexToWorld(c, r); minX = Math.min(minX, x); maxX = Math.max(maxX, x); minZ = Math.min(minZ, z); maxZ = Math.max(maxZ, z); } console.log(`map world extent x:[${minX.toFixed(0)},${maxX.toFixed(0)}] z:[${minZ.toFixed(0)},${maxZ.toFixed(0)}]`);