148 lines
3.5 KiB
TypeScript
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();
|
|
})
|
|
})
|
|
} |