aoc-2023/day_16/lightMap.ts
2024-12-09 22:41:02 +09:00

148 lines
3.5 KiB
TypeScript

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<LightDir, [number, number]>;
type LightDirSet = Set<LightDir>;
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<LightDir, LightDir>)[direction];
case "\\":
return ({
[UP]: LEFT,
[RIGHT]: DOWN,
[DOWN]: RIGHT,
[LEFT]: UP
} satisfies Record<LightDir, LightDir>)[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();
})
})
}