type LightDir = 1 | 2 | 3 | 4; export const UP = 1 satisfies LightDir; export const DOWN = 2 satisfies LightDir; export const LEFT = 3 satisfies LightDir; export const RIGHT = 4 satisfies LightDir; const DirTable = { [UP]: [0, -1], [DOWN]: [0, 1], [LEFT]: [-1, 0], [RIGHT]: [1, 0], } satisfies Record; type LightDirSet = Set; type MapType = "." | "/" | "\\" | "|" | "-"; type MapInfo = { type: MapType; lightTransportation: LightDirSet; }; export function toLightMap(x: string): MapInfo[][] { return x.split("\n").map(x => x.trim().split("").map(x => { return { type: x as MapType, lightTransportation: new Set(), } as MapInfo; })); } export function toString(x: MapInfo[][]): string { return x.map(x => x.map(y => { if (y.type !== ".") { return y.type; } if (y.lightTransportation.size === 0) { return "."; } if (y.lightTransportation.size > 1) { return y.lightTransportation.size.toString(); } const dir = y.lightTransportation.values().next().value; switch (dir) { case UP: return "^"; case DOWN: return "v"; case LEFT: return "<"; case RIGHT: return ">"; } throw new Error(`unreachable! unknown dir ${dir}`); }).join("")).join("\n"); } function nextDirection(type: MapType, direction: LightDir): LightDirSet | LightDir { switch (type) { case ".": return direction; case "/": return ({ [UP]: RIGHT, [RIGHT]: UP, [DOWN]: LEFT, [LEFT]: DOWN } satisfies Record)[direction]; case "\\": return ({ [UP]: LEFT, [RIGHT]: DOWN, [DOWN]: RIGHT, [LEFT]: UP } satisfies Record)[direction]; // light spliter case "|": { if (direction === UP || direction === DOWN) { return direction; } else { return new Set([UP, DOWN]); } } // light splitter case "-": { if (direction === LEFT || direction === RIGHT) { return direction; } else { return new Set([LEFT, RIGHT]); } } default: throw new Error(`unreachable! unknown type ${type}`); } } export function lightTrasport(map: MapInfo[][], [x, y]: [number, number], direction: LightDir) { const [width, height] = [map[0].length, map.length]; for (; ;) { if (x < 0 || x >= width || y < 0 || y >= height) { return; } const info = map[y][x]; // if light is already on if (info.lightTransportation.has(direction)) { return; } // mark light direction info.lightTransportation.add(direction); // move to next position by direction const nextDir = nextDirection(info.type, direction); // if light is splited, go to each direction if (nextDir instanceof Set) { nextDir.forEach(dir => lightTrasport(map, [x, y], dir)); return; } // if light is not splited, go to next position const dirVec = DirTable[nextDir as keyof typeof DirTable]; [x, y] = [x + dirVec[0], y + dirVec[1]]; direction = nextDir; } } export function countPoweredTile(lightMap : MapInfo[][]){ let count = 0 lightMap.forEach(x => { x.forEach(y => { if (y.lightTransportation.size > 0) { count += 1 } }) }) return count } export function clearLight(lightMap : MapInfo[][]){ lightMap.forEach(x => { x.forEach(y => { y.lightTransportation.clear(); }) }) }