알고리즘/프로그래머스

프로그래머스 [빛의 경로 사이클]

Pearan 2021. 9. 29. 12:09
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);
}