// Generates the ASCII hex map by painting province polygons, prints preview. const W = 27, H = 29; const grid = Array.from({ length: H }, () => Array(W).fill(".")); // point in polygon (hex-center space, col/row floats) function pip(x, y, poly) { let inside = false; for (let i = 0, j = poly.length - 1; i < poly.length; j = i++) { const [xi, yi] = poly[i], [xj, yj] = poly[j]; if ((yi > y) !== (yj > y) && x < ((xj - xi) * (y - yi)) / (yj - yi) + xi) inside = !inside; } return inside; } // painted in order; later paints overwrite const PAINTS = [ ["g", [[1,10],[8,9],[10,12],[10,15],[9,18],[8,22],[6,26],[3,26],[1,20],[0,14]]], ["n", [[6,11],[14,11],[16,13],[16,17],[14,21],[11,22],[8,20],[6,16]]], ["h", [[15,11],[21,11],[25,14],[24,19],[19,21],[15,18],[15,13]]], ["u", [[11,10],[17,10],[18,13],[16,16],[12,16],[10,13]]], ["x", [[15,9],[21,9],[22,12],[19,15],[16,14],[15,11]]], ["l", [[0,0],[8,0],[8,4],[6,6],[3,7],[0,6]]], ["o", [[4,4],[10,4],[11,7],[10,10],[7,11],[5,9],[3,7]]], ["s", [[7,7],[12,7],[13,10],[11,12],[8,12],[6,9]]], ["y", [[9,1],[17,1],[18,4],[16,6],[11,6],[9,4]]], ["j", [[9,4],[17,5],[17,8],[13,9],[10,7],[9,5]]], ["q", [[15,6],[22,6],[23,9],[20,12],[17,11],[15,9]]], ["a", [[11,8],[16,8],[17,11],[15,13],[11,13],[10,10]]], ]; for (const [ch, poly] of PAINTS) for (let r = 0; r < H; r++) for (let c = 0; c < W; c++) if (pip(c, r, poly)) grid[r][c] = ch; // mountain ranges (uppercase) — hand bands function band(pts, chs) { // mark cells near polyline for (let r = 0; r < H; r++) for (let c = 0; c < W; c++) { if (grid[r][c] === ".") continue; let d = 1e9; for (let i = 0; i < pts.length - 1; i++) { const [x1, y1] = pts[i], [x2, y2] = pts[i + 1]; const dx = x2 - x1, dy = y2 - y1; const t = Math.max(0, Math.min(1, ((c - x1) * dx + (r - y1) * dy) / (dx * dx + dy * dy))); d = Math.min(d, Math.hypot(c - (x1 + t * dx), r - (y1 + t * dy))); } if (d < 0.75 && chs.includes(grid[r][c])) grid[r][c] = grid[r][c].toUpperCase(); } } band([[9,4],[11,5],[11,7]], ["j","o"]); // Taihang band([[6,9],[9,10],[11,11]], ["o","s","g"]); // Qinling west band([[11,12],[13,12],[15,13]], ["s","u","n"]); // Funiu band([[2,12],[4,13],[6,15]], ["g"]); // Daba/Shu west band([[7,17],[8,20],[7,23]], ["g","n"]); // southern highlands band([[1,1],[3,3],[5,5]], ["l"]); // Hexi mountains band([[16,1],[17,3],[16,5]], ["y"]); // Yan mountains band([[19,6],[21,8]], ["q"]); // Shandong hills console.log(grid.map((row, r) => String(r).padStart(2) + " " + row.join("")).join("\n")); // ---------- bake ---------- const CITIES = [ ["ji","y",12,2],["beiping","y",15,2], ["ye","j",12,6],["nanpi","j",15,5], ["linzi","q",17,7],["beihai","q",21,8], ["puyang","a",12,9],["chenliu","a",14,10], ["luoyang","s",9,9],["hongnong","s",8,11], ["changan","o",7,8],["anding","o",5,6], ["wuwei","l",2,3],["tianshui","l",4,5], ["xiaopei","x",16,12],["xiapi","x",19,13], ["xuchang","u",14,12],["wan","u",12,14],["runan","u",15,14], ["shouchun","h",18,15],["jianye","h",21,14],["lujiang","h",18,17],["wu","h",21,17], ["xiangyang","n",10,14],["jiangling","n",11,17],["jiangxia","n",14,15],["changsha","n",11,20], ["hanzhong","g",4,11],["chengdu","g",3,15],["zitong","g",3,13],["jiangzhou","g",6,17],["yunnan","g",6,22], ]; function snap(prov, c, r) { let best = null, bd = 1e9; for (let rr = 0; rr < H; rr++) for (let cc = 0; cc < W; cc++) { if (grid[rr][cc].toLowerCase() !== prov) continue; const d = Math.hypot(cc - c, rr - r); if (d < bd) { bd = d; best = [cc, rr]; } } return best; } console.log("\n=== CITY COORDS ==="); const used = new Set(); for (const [id, p, c, r] of CITIES) { const [cc, rr] = snap(p, c, r); if (!cc && cc !== 0) { console.log(`!! ${id} NO TILE for ${p}`); continue; } if (used.has(cc+","+rr)) console.log(`!! overlap ${id} @${cc},${rr}`); used.add(cc+","+rr); console.log(`["${id}", "${cc}", "${rr}"],`); } const RIVERS = [ ["Yellow River", [[5,6],[6,7],[7,8],[8,8],[9,9],[10,9],[11,8],[12,7],[13,7],[14,8],[16,8],[18,8],[20,8]]], ["Yangtze", [[3,15],[4,15],[6,15],[8,15],[10,15],[12,15],[13,15],[15,15],[17,16],[19,16],[21,16],[23,17]]], ["Han", [[9,12],[9,13],[10,14]]], ["Huai", [[14,13],[16,13],[18,14],[19,15]]], ]; console.log("\n=== RIVERS ==="); for (const [name, pts] of RIVERS) { const out = pts.map(([c, r]) => { // nearest land tile let best = null, bd = 1e9; for (let rr = 0; rr < H; rr++) for (let cc = 0; cc < W; cc++) { if (grid[rr][cc] === ".") continue; const d = Math.hypot(cc - c, rr - r); if (d < bd) { bd = d; best = [cc, rr]; } } return best; }); console.log(`{ name: "${name}", pts: [${out.map(([c,r])=>`[${c},${r}]`).join(",")}] },`); } console.log("\n=== MAP_ROWS ==="); console.log("export const MAP_ROWS = ["); for (const row of grid) console.log(` "${row.join("")}",`); console.log("];");