import { hexagon, DIRS, key, rotateTracks } from './core.js'; const rotatePoint = ([q, r], turns) => { for (let i = 0; i < (turns % 6 + 6) % 6; i++) [q, r] = [-r || 0, q + r || 0]; return [q, r]; }; const direction = (a, b) => DIRS.findIndex(([q, r]) => b[0] - a[0] === q && b[1] - a[1] === r); function ring(radius, [q, r] = [0, 0]) { const points = []; let at = [q, r + radius]; for (let d = 0; d < 6; d++) for (let i = 0; i < radius; i++) { points.push(at); at = at.map((v, axis) => v + DIRS[(d + 5) % 6][axis]); } return points; } // Two nested petals per sector. Neighbouring outer petals cross without joining. const PETALS = [ring(2, [0, -3]), ring(1, [0, -3])]; const featured = []; function createFlower() { const cells = hexagon(5).map(c => ({ ...c, tracks: Array(6).fill(null) })); const board = new Map(cells.map(c => [key(c.q, c.r), c])); function pair(at, a, b) { const cell = board.get(key(...at)); if ((cell.tracks[a] !== null && cell.tracks[a] !== b) || (cell.tracks[b] !== null && cell.tracks[b] !== a)) throw new Error('Flower tracks conflict'); cell.tracks[a] = b; cell.tracks[b] = a; featured.push({ q: at[0], r: at[1], a, b }); } for (let petal = 0; petal < 6; petal++) { for (const shape of PETALS) { const loop = shape.map(pt => rotatePoint(pt, petal)); for (let i = 0; i < loop.length; i++) { const at = loop[i], a = direction(at, loop[(i + loop.length - 1) % loop.length]), b = direction(at, loop[(i + 1) % loop.length]); // Open each root: outer petal → inner petal → outward vein. if (i === 0) { pair(at, a, (3 + petal) % 6); pair(at, b, petal); } else pair(at, a, b); } } for (const distance of [3, 4, 5]) pair(rotatePoint([0, -distance], petal), petal, (petal + 3) % 6); } for (const a of [0, 2, 4]) pair([0, 0], a, a + 1); for (const radius of [1, 2, 3]) { const loop = ring(radius); for (let i = 0; i < loop.length; i++) pair(loop[i], direction(loop[i], loop[(i + loop.length - 1) % loop.length]), direction(loop[i], loop[(i + 1) % loop.length])); } // Fill the remaining ports in rotational orbits, preserving a regular pattern. const done = new Set(['0,0']); for (const cell of cells) { if (done.has(key(cell.q, cell.r))) continue; const free = cell.tracks.map((v, i) => v === null ? i : -1).filter(i => i >= 0); while (free.length) { const a = free.shift(), opposite = (a + 3) % 6; const b = free.splice(free.includes(opposite) ? free.indexOf(opposite) : 0, 1)[0]; cell.tracks[a] = b; cell.tracks[b] = a; } let tracks = [...cell.tracks]; for (let turn = 0; turn < 6; turn++) { const at = rotatePoint([cell.q, cell.r], turn), target = board.get(key(...at)); for (let d = 0; d < 6; d++) if (target.tracks[d] !== null && target.tracks[d] !== tracks[d]) throw new Error('Flower symmetry conflicts'); target.tracks = [...tracks]; done.add(key(...at)); tracks = rotateTracks(tracks); } } // Splice ALL three rings into the petal routes at their six radial crossings. // Re-pairing these same featured ports creates one woven, open network; no // emphasized ring is left as an ornamental loop that pieces cannot escape. for (const [distance, wiring] of [[1, [1, 0, 3, 2, 5, 4]], [2, [1, 0, 3, 2, 5, 4]], [3, [2, 5, 0, 4, 3, 1]]]) { let tracks = wiring; for (let turn = 0; turn < 6; turn++) { board.get(key(...rotatePoint([0, -distance], turn))).tracks = [...tracks]; tracks = rotateTracks(tracks); } } const tiles = Array.from({ length: 6 }, (_, i) => { const [q, r] = rotatePoint([0, -5], i); return { id: `petal-${i}`, q, r, dir: [0, 2].includes(i) ? i : (i + 3) % 6, color: i }; }); // Four interior departures introduce the weaving before the full-length runs. for (const [i, [q, r, dir]] of [[-2, 1, 1], [1, 0, 2], [0, -2, 2], [0, 2, 2]].entries()) { tiles.push({ id: `bloom-${i}`, q, r, dir, color: 6 + i }); } return { version: 1, id: 'study-flower-01', name: '花有出路 · 重瓣', rotationEnabled: true, cells, tiles, moveLimit: tiles.length, lesson: '先给出口让路,再解开花心中的棋子;转动底盘,把朝内的箭头接上另一片花瓣。', }; } export const FLOWER_LEVEL = createFlower(); export const FLOWER_PETALS = PETALS.flatMap(shape => Array.from({ length: 6 }, (_, i) => shape.map(pt => rotatePoint(pt, i)))); export function flowerTrackEmphasis(turns = 0) { const keys = new Set(featured.flatMap(p => { const [q, r] = rotatePoint([p.q, p.r], turns); return [p.a, p.b].map(d => `${q},${r}:${(d + turns % 6 + 6) % 6}`); })); return (cell, port) => keys.has(`${cell.q},${cell.r}:${port}`); }