import { rotate, trace, directions } from './model.js'; // A run only removes cars reachable in the current orientation. It never turns. export function currentDeparture(board, cars) { return cars.find(car => trace(board, car, cars).status === 'exit') || null; } export function drainOrientation(board, cars = board.cars) { let remaining = [...cars]; const departed = []; for (let car; (car = currentDeparture(board, remaining));) { departed.push(car.id); remaining = remaining.filter(c => c.id !== car.id); } return { remaining, departed }; } // Every edge is one player turn followed by a run. Search both directions to // measure actual player effort, rather than counting rotations in a car solver. export function rotationPlan(board, cars = board.cars) { const n = directions(board.kind).length; const initial = drainOrientation(board, cars); const queue = [{ orientation: 0, remaining: initial.remaining, turns: [], waves: [initial.departed.length] }]; const visited = new Set(); for (let head = 0; head < queue.length; head++) { const state = queue[head]; if (!state.remaining.length) return state; for (const turn of [-1, 1]) { const orientation = (state.orientation + turn + n) % n; const next = drainOrientation(rotate(board, orientation), state.remaining); const key = `${orientation}:${next.remaining.map(c => c.id).sort().join(',')}`; if (visited.has(key)) continue; visited.add(key); queue.push({ orientation, remaining: next.remaining, turns: [...state.turns, turn], waves: [...state.waves, next.departed.length] }); } } return null; } export const batchTitles = ['转向开路', '折返放行', '连续调度']; export const batchSeeds = { hex: [3, 14, 8], square: [6, 12, 19] };