enum Direction {
LEFT = 0,
UP = 1,
RIGHT = 2,
DOWN = 3
}
class NodeCircle {
mark: string;
visitDirection: Set<Direction>;
constructor(mark: string) {
this.mark = mark;
this.visitDirection = new Set<Direction>();
}
isVisit(dir: Direction): boolean {
return this.visitDirection.has(dir);
}
visit(dir: Direction): void {
this.visitDirection.add(dir);
}
}
class Position {
posX: number;
posY: number;
private maxPosX: number;
private maxPosY: number;
constructor(posX: number, posY: number, maxPosX: number, maxPosY: number) {
this.posX = posX;
this.posY = posY;
this.maxPosX = maxPosX;
this.maxPosY = maxPosY;
}
update(dir: Direction): void {
if (dir === Direction.UP) {
this.posX--;
if (this.posX < 0) this.posX = this.maxPosX;
} else if (dir === Direction.LEFT) {
this.posY--;
if (this.posY < 0) this.posY = this.maxPosY;
} else if (dir === Direction.RIGHT) {
this.posY++;
if (this.posY > this.maxPosY) this.posY = 0;
} else {
// down
this.posX++;
if (this.posX > this.maxPosX) this.posX = 0;
}
}
}
class MovingLight {
moveCount: number;
private pos: Position;
private world: NodeCircle[][];
private dir: Direction;
private originPosX: number;
private originPosY: number;
private originDir: Direction;
constructor(pos: Position, dir: Direction, world: NodeCircle[][]) {
this.pos = pos;
this.dir = dir;
this.world = world;
this.moveCount = 0;
this.originPosX = pos.posX;
this.originPosY = pos.posY;
this.originDir = dir;
}
isStartPoint(): boolean {
if (
this.pos.posX === this.originPosX &&
this.pos.posY === this.originPosY &&
this.dir === this.originDir
) {
return true;
}
return false;
}
isAlreadyVisit() {
const node = this.world[this.pos.posX][this.pos.posY];
return node.isVisit(this.dir);
}
getNextDir(): Direction {
const node = this.world[this.pos.posX][this.pos.posY];
if (node.mark === "S") {
return this.dir;
} else if (node.mark === "R") {
const nextDir = this.dir + 1;
if (nextDir > 3) return 0;
return nextDir;
} else {
const nextDir = this.dir - 1;
if (nextDir < 0) return 3;
return nextDir;
}
}
move() {
this.moveCount++;
this.pos.update(this.dir);
this.dir = this.getNextDir();
}
process(): boolean {
while (true) {
this.move();
if (this.isStartPoint()) return true;
if (this.isAlreadyVisit()) return false;
const node = this.world[this.pos.posX][this.pos.posY];
node.visit(this.dir);
}
}
}
function comparerForAscendingOrder(a: number, b: number) {
if (a > b) return 1;
else if (a < b) return -1;
else return 0;
}
function makeWorldFromGrid(grid: string[]) {
const world: NodeCircle[][] = Array.from(
Array(grid.length),
() => new Array(grid[0].length)
);
for (let i = 0; i < grid.length; i++) {
for (let j = 0; j < grid[0].length; j++) {
world[i][j] = new NodeCircle(grid[i][j]);
}
}
return world;
}
function solution(grid: string[]) {
const result: number[] = [];
const world = makeWorldFromGrid(grid);
const gridMaxX = grid.length - 1;
const gridMaxY = grid[0].length - 1;
for (let i = 0; i <= gridMaxX; i++) {
for (let j = 0; j <= gridMaxY; j++) {
for (let k = 0; k < 4; k++) {
const startPos = new Position(i, j, gridMaxX, gridMaxY);
const light = new MovingLight(startPos, k, world);
if (light.process()) result.push(light.moveCount);
}
}
}
return result.sort(comparerForAscendingOrder);
}