Reindeer Maze

Advent of Code 2024 [Day 16]

16-12-2024

It’s time again for the Reindeer Olympics! This year, the big event is the Reindeer Maze, where the Reindeer compete for the lowest score.

You and The Historians arrive to search for the Chief right as the event is about to start. It wouldn’t hurt to watch a little, right?

The Reindeer start on the Start Tile (marked S) facing East and need to reach the End Tile (marked E). They can move forward one tile at a time (increasing their score by 1 point), but never into a wall (#). They can also rotate clockwise or counterclockwise 90 degrees at a time (increasing their score by 1000 points).

To figure out the best place to sit, you start by grabbing a map (your puzzle input) from a nearby kiosk. For example:

###############
#.......#....E#
#.#.###.#.###.#
#.....#.#...#.#
#.###.#####.#.#
#.#.#.......#.#
#.#.#####.###.#
#...........#.#
###.#.#####.#.#
#...#.....#.#.#
#.#.#.###.#.#.#
#.....#...#.#.#
#.###.#.#.#.#.#
#S..#.....#...#
###############

There are many paths through this maze, but taking any of the best paths would incur a score of only 7036. This can be achieved by taking a total of 36 steps forward and turning 90 degrees a total of 7 times:

###############
#.......#....E#
#.#.###.#.###^#
#.....#.#...#^#
#.###.#####.#^#
#.#.#.......#^#
#.#.#####.###^#
#..>>>>>>>>v#^#
###^#.#####v#^#
#>>^#.....#v#^#
#^#.#.###.#v#^#
#^....#...#v#^#
#^###.#.#.#v#^#
#S..#.....#>>^#
###############

Here’s a second example:

#################
#...#...#...#..E#
#.#.#.#.#.#.#.#.#
#.#.#.#...#...#.#
#.#.#.#.###.#.#.#
#...#.#.#.....#.#
#.#.#.#.#.#####.#
#.#...#.#.#.....#
#.#.#####.#.###.#
#.#.#.......#...#
#.#.###.#####.###
#.#.#...#.....#.#
#.#.#.#####.###.#
#.#.#.........#.#
#.#.#.#########.#
#S#.............#
#################

In this maze, the best paths cost 11048 points; following one such path would look like this:

#################
#...#...#...#..E#
#.#.#.#.#.#.#.#^#
#.#.#.#...#...#^#
#.#.#.#.###.#.#^#
#>>v#.#.#.....#^#
#^#v#.#.#.#####^#
#^#v..#.#.#>>>>^#
#^#v#####.#^###.#
#^#v#..>>>>^#...#
#^#v###^#####.###
#^#v#>>^#.....#.#
#^#v#^#####.###.#
#^#v#^........#.#
#^#v#^#########.#
#S#>>^..........#
#################

Note that the path shown above includes one 90 degree turn as the very first move, rotating the Reindeer from facing East to facing North.

Analyze your map carefully. What is the lowest score a Reindeer could possibly get?

const dx = [1, 0, -1, 0];
const dy = [0, 1, 0, -1];

interface State {
  x: number;
  y: number;
  dir: number;
  score: number;
}

class PriorityQueue<T> {
  private items: [number, T][] = [];

  enqueue(item: T, priority: number) {
    this.items.push([priority, item]);
    this.items.sort((a, b) => a[0] - b[0]);
  }

  dequeue(): T | undefined {
    return this.items.shift()?.[1];
  }

  isEmpty(): boolean {
    return this.items.length === 0;
  }
}

function findStart(grid: string[]): [number, number] {
  for (let y = 0; y < grid.length; y++) {
    for (let x = 0; x < grid[y].length; x++) {
      if (grid[y][x] === 'S') return [x, y];
    }
  }
  throw new Error('Start position not found');
}

function findLowestScore(grid: string[]): number {
  const [startX, startY] = findStart(grid);
  const height = grid.length;
  const width = grid[0].length;

  const visited = new Set<string>();
  const queue = new PriorityQueue<State>();

  queue.enqueue({ x: startX, y: startY, dir: 0, score: 0 }, 0);

  while (!queue.isEmpty()) {
    const current = queue.dequeue()!;

    const stateKey = `${current.x},${current.y},${current.dir}`;
    if (visited.has(stateKey)) continue;
    visited.add(stateKey);

    if (grid[current.y][current.x] === 'E') {
      return current.score;
    }

    const newX = current.x + dx[current.dir];
    const newY = current.y + dy[current.dir];
    if (newX >= 0 && newX < width && newY >= 0 && newY < height && 
        grid[newY][newX] !== '#') {
      queue.enqueue({
        x: newX,
        y: newY,
        dir: current.dir,
        score: current.score + 1
      }, current.score + 1);
    }

    const leftDir = (current.dir + 3) % 4;
    queue.enqueue({
      x: current.x,
      y: current.y,
      dir: leftDir,
      score: current.score + 1000
    }, current.score + 1000);

    const rightDir = (current.dir + 1) % 4;
    queue.enqueue({
      x: current.x,
      y: current.y,
      dir: rightDir,
      score: current.score + 1000
    }, current.score + 1000);
  }

  return Infinity;
}

async function main() {
  const input = await Deno.readTextFile("input.txt");
  const grid = input.trim().split('\n');
  console.log(findLowestScore(grid));
}

main().catch(console.error);

Now that you know what the best paths look like, you can figure out the best spot to sit.

Every non-wall tile (S, ., or E) is equipped with places to sit along the edges of the tile. While determining which of these tiles would be the best spot to sit depends on a whole bunch of factors (how comfortable the seats are, how far away the bathrooms are, whether there’s a pillar blocking your view, etc.), the most important factor is whether the tile is on one of the best paths through the maze. If you sit somewhere else, you’d miss all the action!

So, you’ll need to determine which tiles are part of any best path through the maze, including the S and E tiles.

In the first example, there are 45 tiles (marked O) that are part of at least one of the various best paths through the maze:

###############
#.......#....O#
#.#.###.#.###O#
#.....#.#...#O#
#.###.#####.#O#
#.#.#.......#O#
#.#.#####.###O#
#..OOOOOOOOO#O#
###O#O#####O#O#
#OOO#O....#O#O#
#O#O#O###.#O#O#
#OOOOO#...#O#O#
#O###.#.#.#O#O#
#O..#.....#OOO#
###############

In the second example, there are 64 tiles that are part of at least one of the best paths:

#################
#...#...#...#..O#
#.#.#.#.#.#.#.#O#
#.#.#.#...#...#O#
#.#.#.#.###.#.#O#
#OOO#.#.#.....#O#
#O#O#.#.#.#####O#
#O#O..#.#.#OOOOO#
#O#O#####.#O###O#
#O#O#..OOOOO#OOO#
#O#O###O#####O###
#O#O#OOO#..OOO#.#
#O#O#O#####O###.#
#O#O#OOOOOOO..#.#
#O#O#O#########.#
#O#OOO..........#
#################

Analyze your map further. How many tiles are part of at least one of the best paths through the maze?

const dx = [1, 0, -1, 0];
const dy = [0, 1, 0, -1];

const directions = [
  { dx: 1, dy: 0, name: "E" },
  { dx: 0, dy: 1, name: "S" },
  { dx: -1, dy: 0, name: "W" },
  { dx: 0, dy: -1, name: "N" },
] as const;

interface State {
  x: number;
  y: number;
  dir: number;
  score: number;
}

class PriorityQueue<T> {
  private items: [number, T][] = [];

  enqueue(item: T, priority: number) {
    this.items.push([priority, item]);
    this.items.sort((a, b) => a[0] - b[0]);
  }

  dequeue(): T | undefined {
    return this.items.shift()?.[1];
  }

  isEmpty(): boolean {
    return this.items.length === 0;
  }
}

function findStart(grid: string[]): [number, number] {
  for (let y = 0; y < grid.length; y++) {
    for (let x = 0; x < grid[y].length; x++) {
      if (grid[y][x] === 'S') return [x, y];
    }
  }
  throw new Error('Start position not found');
}

function findLowestScore(grid: string[]): number {
  const [startX, startY] = findStart(grid);
  const height = grid.length;
  const width = grid[0].length;

  const visited = new Set<string>();
  const queue = new PriorityQueue<State>();

  queue.enqueue({ x: startX, y: startY, dir: 0, score: 0 }, 0);

  while (!queue.isEmpty()) {
    const current = queue.dequeue()!;

    const stateKey = `${current.x},${current.y},${current.dir}`;
    if (visited.has(stateKey)) continue;
    visited.add(stateKey);

    if (grid[current.y][current.x] === 'E') {
      return current.score;
    }

    const newX = current.x + dx[current.dir];
    const newY = current.y + dy[current.dir];
    if (newX >= 0 && newX < width && newY >= 0 && newY < height && 
        grid[newY][newX] !== '#') {
      queue.enqueue({
        x: newX,
        y: newY,
        dir: current.dir,
        score: current.score + 1
      }, current.score + 1);
    }

    const leftDir = (current.dir + 3) % 4;
    queue.enqueue({
      x: current.x,
      y: current.y,
      dir: leftDir,
      score: current.score + 1000
    }, current.score + 1000);

    const rightDir = (current.dir + 1) % 4;
    queue.enqueue({
      x: current.x,
      y: current.y,
      dir: rightDir,
      score: current.score + 1000
    }, current.score + 1000);
  }

  return Infinity;
}

function getTurnCost(currentDir: number, targetDir: number): number {
  const diff = Math.abs(currentDir - targetDir);
  return 1000 * Math.min(diff, 4 - diff);
}

function findOptimalPathTiles(grid: string[], minScore: number): number {
  const costs = new Map<string, number>();
  const optimalPaths = new Set<string>();
  const paths = new Map<string, string[]>();
  const start = findStart(grid);

  const queue: {
    position: State;
    path: string[];
  }[] = [];

  queue.push({
    position: {
      x: start[0],
      y: start[1],
      dir: 0,
      score: 0,
    },
    path: [`${start[0]},${start[1]}`],
  });

  while (queue.length > 0) {
    const { position, path } = queue.shift()!;
    const key = `${position.x},${position.y},${position.dir}`;

    if (position.score > minScore) continue;
    if (costs.has(key) && costs.get(key)! < position.score) continue;

    costs.set(key, position.score);
    paths.set(key, path);

    if (grid[position.y][position.x] == 'E') {
      path.forEach((pos) => optimalPaths.add(pos));
      continue;
    }

    directions.forEach((newDir, newDirIndex) => {
      const newX = position.x + newDir.dx;
      const newY = position.y + newDir.dy;

      if (
        newX < 0 || newY < 0 ||
        newX >= grid[0].length || newY >= grid.length ||
        grid[newY][newX] === "#"
      ) {
        return;
      }

      const moveCost = 1;
      const turnCost = getTurnCost(position.dir, newDirIndex);
      const totalCost = position.score + moveCost + turnCost;
      const newPos = `${newX},${newY}`;

      const newPath = [...path, newPos];
      const insertIndex = queue.findIndex(({ position }) => position.score > totalCost);
      if (insertIndex === -1) {
        queue.push({
          position: {
            x: newX,
            y: newY,
            dir: newDirIndex,
            score: totalCost,
          },
          path: newPath,
        });
      } else {
        queue.splice(insertIndex, 0, {
          position: {
            x: newX,
            y: newY,
            dir: newDirIndex,
            score: totalCost,
          },
          path: newPath,
        });
      }
    });
  }

  return optimalPaths.size;
}

async function main() {
  const input = await Deno.readTextFile("input.txt");
  const grid = input.trim().split('\n');
  const minScore = findLowestScore(grid);
  console.log(findOptimalPathTiles(grid, minScore));
}

main().catch(console.error);
Click to show the input
#############################################################################################################################################
#...........#.......#.........#.........#.........#.............#...........#...#.......#.....#.......#.......#.......#.....#.........#....E#
#.#########.#####.#.#######.#.###.#.###.#.#####.###.#.#########.#.#.#######.#.#.#####.#.###.#.#.#####.#.#.###.#####.#.#.###.#.#######.#.###.#
#.............#...#.........#...#.#.#.....#...#.#...#.#.......#...#.#.....#...#.......#...#.........#.#.#...#.......#...#...#...#...#.#...#.#
#########.###.#.###.#######.###.#.#.#.#####.#.#.#.###.#.#########.#.#.###.###########.###.#.#######.#.#####.###########.#.###.#.#.#.#.#.#.#.#
#.......#.#...#...#.#...#.....#.#.#.#.#...#.#...#...#.#.........#.#.#.....................#.....#...#...................#...#.#.#.#.#.#.#.#.#
#.#####.#.#.###.#.###.#.#.#.###.###.###.#.#########.#.#.#####.#.#.#.#.#.###.###.#.#.#.#.#.#.###.#.#.#####.#.#.#####.#.#.###.#.#.###.#.#.#.#.#
#...#.....#.#.#.#.....#.#.#...#...#.....#...........#.#.#...#.#.#.#.#.#...#...#.#.#.#.#...#...#.#.#...#...#.#.#...#...#.#.#.#.#...#...#.#.#.#
#.#.#######.#.#.###.#.#.###.#.###.#.#####.###########.#.###.#.###.#.#.#.#####.#.#.#.#.#########.#.#####.###.#.###.#####.#.#.#.###.#.###.#.#.#
#.#...#...#.#...#...#.#.............#...#...#.......#.#.....#...#...#...#.....#.#...#.......#...#...#...#...#.....#.........................#
#.###.#.###.#####.#.#.#.#.#.#.#.#####.#.#####.#####.#.#####.###.#.#####.#.###.#.#####.###.#.#.#####.#.###.###.###.#.###.#.###.#.#####.#####.#
#...#.#.#...#.....#.....#.#...#.#.....#.......#...#.#...#...#.#...#.............#.........#...#.....#.#...........#.#.....#...#.#.#...#...#.#
#####.#.#.###.#######.###.#.###.#.#############.###.###.#.###.#.#.#####.#.###.#.#.###.#########.#.#.#.#.#####.#####.#.#########.#.#.###.#.###
#.....#.#.#...#...........#...#...#...........#.....#...#.....#.#.....#.#...#.#.#.#...#.....#.#.#.....................#.........#.#.#...#...#
#.#####.#.#.#.###.###.#.###########.#######.###.#####.#######.#.#####.#####.###.###.#.###.#.#.#.#.###.#.#####.#######.###.#######.#.#.#####.#
#...#...#.#.#...#.#...#.#.........#.#.#...#.#...#.....#.....#.#.#...#.....#...#.....#.....#.#...#...#.#...#.......#...#...#...#.....#.#...#.#
#.#.###.#.#.###.#.#.#.#.#.#####.###.#.#.#.#.#.#######.#.#####.#.#.#####.#.###.#######.#####.#.#####.#.#.#.#.#####.#.###.###.#.#######.###.#.#
#.#.#...#.#.#...#.#.#...#.....#.......#.#...#.....#...#.......#.#.#.............#.#...#...#.#.#.....#.#.#.#.....#.#...#.....#.#...........#.#
#.#.#.###.#.#.#####.#####.#####.#####.#.###.#####.#.###########.#.#.###########.#.#.#.#.###.#.#.###.#.###.#.###.#.###.#######.#.###########.#
#.#.#.#...#.#...#...#.....#...#.#...#.#.#.#.#...#...#...........#.#.#.#...#...#.#...#.#.#...#.#...#.#...#.#...#.#...#.#.......#.....#.......#
###.#.#.###.###.#.###.#####.#.###.#.###.#.#.#.#.#####.#.#########.#.#.#.#.#.#.#.#.###.#.#.#######.#.###.#.###.#.###.#.#.#.#########.#.#####.#
#...#...#...#...#.....#...#.#.....#.....#...#.#.#.....#.#...#.....#...#.#...#.#.#...#.#.....#...#.#...#.#...#.#...#.#.#.#.#.........#.....#.#
#.#####.###.#.#########.#.#.#############.#####.#.#####.#.#.#####.###.#.#####.#.###.#.#####.#.#.#.###.#.###.#.###.###.#.#.#.#############.#.#
#.....#.......#.........#.#.#.....#.....#.#...#.......#...#...#...#...#...#...#...#.#.....#...#...#...#...#.#...#.....#.#.#.......#.....#...#
#####.#####.###.#########.#.#.###.#.#####.#.#.#.#############.#.###.#####.#.#####.#.#####.#######.#.#####.#.###.#######.#########.###.###.#.#
#.....#.#...............#.#.#.#...#...#...#...#.#.........#...#.......#...#.#.#...#.#...#.#.......#.#...#...#.#...#...........#.#.....#...#.#
#.#####.#.#.###.#.#####.#.#.#.#.###.#.#.###.#.###.#######.#.#########.#.###.#.#.###.#.#.#.#.#####.#.#.#.#####.###.#.#####.###.#.#####.#.#####
#...#.....#...#.#.#...#.#.#.#.#.#...#.#.#...#...#.....#.#...#.......#.#.#...#.#...#.#.#...#.....#.#.#.#.......#.......#...#.........#.#.....#
###.#####.#.#.#.#.#.###.#.#.#.#.#####.#.#.#.###.###.#.#.###.#.#####.###.#.###.###.#.#.#########.#.###.#######.#.#######.#############.#####.#
#...#...#...#.#.#.#...#.#...#.#.#.....#.#.....#...#.#.#.....#.#...#.#...#.......#.#.#.........#.#.#...#.....#.........#.....#...............#
#.###.#.###.#.#.#.#.#.#.#.###.#.#.#####.#.###.###.#.#.###.###.#.###.#.#####.#.###.###.#.###.###.#.#.#######.#.#######.#.###.#.###########.#.#
#.#...#.#...#.#.#.#...#...#...#.#.#...#.#...#.#.#.#.#...#.#.#.#...#.#.....#.#.#.......#...#.#...#...#.....#.#.....#...#...#.#.#.....#.....#.#
#.#.###.#.#.#.#.#.#.#######.###.#.#.#.#.#####.#.#.#####.#.#.#.###.#.###.#.#.#.#.#########.#.#.###.#####.#.#.###.###.#####.#.#.#.#.###.#####.#
#.#.#...#.....#.#...#.......#.#.#...#.#.....#.#...#...#.#...#.....#.#...#.#.#.#.#.........#.#...........#.#...#.....#...#.#.#.#.#.#...#.....#
#.#.#.###.#.###.###.#.#######.#.#####.#####.#.#.###.#.#.#######.#.#.#.#.#.#.#.#.#.#########.#.###########.###.#######.#.#.#.#.###.#.###.#####
#.#.#.#.......#.#...#.#.......#.....#.....#...#.#...#...#.......#...#.#...#.#.#.#.........#.......#.....#.............#...#.......#...#.....#
#.#.#.#.###.###.#.###.###.#########.###.#######.###.###.#.#######.#.#.#####.#.#.#####.#####.#.#####.###.###.#.###################.###.#####.#
#.#.#.#.#.#.....#...#...#.........#...#.........#...#.....#.....#.#.#.....#.#.......#.#.....#.#.....#...#.#.#.....#...#...#.....#.#...#...#.#
#.#.#.#.#.#.###.#######.#.###.###.###.#.#########.###.#####.###.#.#######.#.#########.#.###.###.#####.#.#.#.#####.#.#.#.#.#.###.#.#.###.#.#.#
#...#.#...#.#...#.....#.#.#...#.#.#.#.#.....#.....#...#...#.#.#.#.......#.#.......#...#...#.#...#...#.#.....#...#.#.#...#.#.#.....#...#.#...#
#.###.#.###.#####.###.#.###.###.#.#.#.#####.#.###.#.###.#.#.#.#.###.#####.#######.#.#####.#.#.#####.#.###.#.#.###.#######.#.#####.###.#.#####
#.#...#.....#.....#.#...#.......#...#.....#.#.#...#.....#...#.............#...#...#.#...#.#...#.....#...#.#.#.....#.......#...#...#...#.....#
###.#####.#.#.#####.#####.###.#.###.#####.#.###.#.#####.#.#######.###.#####.#.#.###.#.###.#.#.#.###.###.###.#####.#.#####.###.#.#.#.#.#####.#
#...#...............#.....#...#.#.....#...#.....#.#.....#.....#...#...#.#...#.#.......#.....#.#.#.#.#.#.#...#...#...#.....#...#.....#...#...#
#.#####.#####.#.#.#.#.#.#####.###.#####.#######.###.#########.###.#.###.#.#.#.#.#######.###.#.#.#.#.#.#.#.###.#.#######.###.###.#.###.###.###
#.....#.......#.#.#.#...#...#.#...#.....#.......#...#.......#...#.#.#.....#.#.....#.....#.#.#.#.#.#.....#.....#.......#...#.#...#...#...#...#
#.###.###.#######.#.#.###.#.#.#.###.#############.#########.###.###.#####.#.#######.#####.#.#.#.#.###.#.###.#########.#.###.#.#.#.#.#.#.###.#
#...#...#.#.....#.#.#.....#.....#...#.......#.....#...........#.....#...#.#...#.....#...#...#.#.#...#.#.....#.......#.#.#...#...#.#...#...#.#
#.#####.#.###.#.#.#.#######.#####.###.#####.#.#####.#####.###.#####.#.#.#####.#.###.#.#.#####.#.#.###.#######.#####.#.#.#.###.#.#.###.###.#.#
#.............#...#...#.....#.....#...#.....#.#.......#.#.....#.#.....#.....#.#.#...#.#...#.....#...#.......#...#.#.#.#.#.#.#.#.#.#.....#.#.#
###.#####.###########.#.###.#.#####.#.#.#####.#######.#.#####.#.#.#.#######.#.#.#####.###.#.#######.###.#####.#.#.#.#.###.#.#.#.#.#.#.###.#.#
#...#...#.#...#.....#...#.#.#.....#.#.#.....#.........#.......#...#.#.........#.........#...#...........#...#.#.#.#.#.....#...#.....#.....#.#
#.###.#.###.#.#.###.#####.#.###.#.#.#.#####.#.#.#########.#######.#.#.#######.###########.###.###.#.#####.#.###.#.#.###.###.#.#.#.###.###.#.#
#.#...#.#...#...#...#.#...#...#.#...#.....#.......#.....#.......#.#.#.........#.....#...#...#.#...#.#...#.#.....#...#...#...#.............#.#
#.#.###.#.#######.###.#.#.###.#.#.###.#.#######.#.#.###.#######.#.#.#####.#####.###.#.#.#.###.#.###.#.#.#.#########.#####.#.###.#.#.###.#.#.#
#.#...#...#.......#.#...#.........#.#.#.....#...#...#.#...#...#.#.#...#.#.#...#.#.#...#.#.#...#...#.#.#.#.........#.......#.#...#.#.#...#.#.#
#.###.#####.#####.#.#.#########.#.#.#.#####.#.#######.###.#.#.#.#.###.#.#.#.#.#.#.#####.###.#####.#.#.#.#########.#########.#.#.#.#.#.#####.#
#.#...#...#...#.....#...#.......#.#...#...#...#.....#...#.#.#.#.#...#.#...#.#...............#...#.#...#.#.#.....#.#.......#...#...#.#.......#
#.#.#####.###.#.#.#.###.#.#######.#.###.#.#######.#.#.#.#.###.#.###.#.#.###.#########.#######.#.#.#####.#.#.###.#.#.###.#######.#.#.###.#####
#.#.........#...#.#...#...#.#.....#...#.#.........#...#.#...#.........#.#...#.......#...#.....#.#.....#...#.#.....#...#.#...#.....#...#.#...#
#.#.#######.###.###.#.#####.#.#######.#.###############.###.#####.#######.#####.###.#.#.#.#####.#####.#####.#.#######.###.#.#.#.#####.#.#.#.#
#.#.......#...#.....#.........#.#.......#...#.....#.....#.#.....#.#.....#...#...#.....#.#.#...#.#...#.#.....#.......#.#...#...#.....#.#.#.#.#
#.#.#####.#.#######.#.#.#####.#.#.#######.#.#.#.###.#####.#####.#.#.###.###.#.#######.#.#.###.#.###.#.#.###.#####.#.#.#.#######.###.#.#.#.#.#
#.#.....#...#.......#.#.#.....#.#.#.#.....#...#.....#.........#.#.#...#.#...#.....#...#...#...#.....#.#...#.#.....#.#...#.....#...#...#.#.#.#
#.#.###.#####.#######.###.#####.#.#.#.#.#.#.#.#####.###.#.###.#.#.###.#.#.#######.#.#######.###.#####.#.#.###.#####.#.###.#.#.#.#.#####.#.#.#
#.....#...#.........#.....#...#.....#...#...#...#.....#.#.#...#.#.#...#.#.#.......#...#.........#.....#.#.....#...#.#...#.#...............#.#
###.#####.#.#####.#.#####.#.#.#######.#.###.###.###.#.#.#.#.#.#.###.###.#.#.#########.#.#####.#.#.#####.#########.#.###.#.###.#.#.###.###.###
#...#.............#.#...#...#.....#...#.#.#...#.....#...#...#.#...#.#.#.#.....#.....#.......#...#.#...#.#...#...#.#...#.#.#...#.#...#...#...#
#.#.#.#.###########.#.#.#########.#.#.#.#.###.#########.#.#.#.###.#.#.#.#.###.#.###.#####.#######.#.#.#.#.#.#.#.#.###.#.#.#.###.###.###.###.#
#.#.#...#.#.......#...#...#...#...#.#.#.#...#...#.........#.....#.#...#.....#.....#.#...#.#.......#.....#.#...#...#...#.#.........#.....#...#
#.#.###.#.#.#####.#######.#.###.###.#.#.###.###.#########.#######.###.#####.#######.#.#.###.#######.#####.#######.#.###.###.###.#####.###.#.#
#.....#...#...#...........#...#...#.#...#.....#.#.....#...#...#...#...#...#...#.....#.#.....#...#...#.#.....#.....#.#.#...#.#.......#...#.#.#
###.#.#.#.###.#############.#.###.#.#####.###.#.#.###.###.#.#.#.###.#####.###.#.###.#.#########.#.###.#.###.#.#####.#.###.###.#####.###.#.#.#
#.....#...#.#.......#.....#.#...#...#.....#...#...#...#...#.#.#.#...#.#...#.....#...#.....#.....#.#.#...#.#.#...#.#.#...#.....#...#.#.#...#.#
#.#.#.#.###.#######.#.###.#.###.#####.#####.#######.###.#.#.#.#.#.###.#.#############.###.#.#####.#.#.###.#.###.#.#.#.#########.###.#.#####.#
#.#...#.....#.......#.#.#...#.#.......#...#.....#...#...#...#...#...#.#.........................#.#...#...#.#...#.#.#.....#.........#.....#.#
#.#.#.#.###.#.#######.#.#####.###.#####.###.#.#.#.###.#########.###.#.#####################.###.#.#.#.#.###.#.###.#.#.#.###.#########.#.###.#
#...#...#...#.....#...#.........#...#.........#.#.#...........#.#...#.....#.......#.......#...#...#.....#...#.....#.#.#...#.#...#.#...#.....#
#.#.#.#.#.#######.#.###.###.###.###.###########.#.###.#.###.#.#.#.###.###.#####.#.#.#####.#.#####.#####.#.###.#####.#####.#.#.#.#.#.#########
#.............#...#.#.....#...#.#.#.......#.....#.....#.........#...#.#.........#...#...#.#.#...#.........#...#...#.....#.....#.#...#.......#
###.#.#######.#.###.#.#######.#.#.#######.#############.###########.#.#####.#########.#.#.###.#.###########.###.#.###.#.#.#####.#.#.#######.#
#.#.....#...#.#...#.#...#.....#.......#.#.#...................#...........#.#.....#...#.#.....#...#...#.......#.#...#.#.....#.#...#.....#...#
#.###.#.#.#.#.###.#.#####.###########.#.#.#.#.#.###############.#.###.###.###.###.#.#####.#######.#.#.#.###.###.###.#######.#.###.#####.#.#.#
#.....#.#.#.....#.#.......#.............#.#...#.#...#.....#.....#...#.........#...#...#.........#...#.#.#...#...#...#.......#...#.....#.#.#.#
###.#.#.#.#######.#.#.###.#.#.###########.#.#.#.#.#.#.###.#.#####.#.###.#######.###.#.#.#.#####.#####.###.#.#.###.###.#######.###.###.#.#.#.#
#...#.#.#...#.....#...#...#.#.........#...#.#...#.#.#.#.#...#.#...#.....#.....#...#.#...#.#...#.#...#...#.#.#...#...#...............#.#...#.#
#.###.#.###.#.#########.###.#####.#.#.#.#.#.###.###.#.#.#.###.#.###########.#####.#.#####.#.###.#.#.###.#.#.###.###.#.###.#.#####.#.#.#######
#...#.#.#...#.....#...#.#...#.....#.#.#.#.....#.....#...#...................#...#.#.#...#.#.....#.#...#...#.....#.#.#...#.#.....#.#.#.......#
#.#.###.#.#.#####.#.#.#.#.###.#####.#.#.#####.#.#######.#####.#######.#######.#.#.#.#.#.#.#######.###.#####.#.###.#.#####.#####.#.#########.#
#.#...#...#...#.#...#...#...#.#.....#.#...#...#.#.....#.#.......#...#.#...#...#.#.#.#.#.#...#.....#.......#.#...#.....#...#.....#.#.........#
#.###.#######.#.#######.#.#.#.#.#####.###.#.###.#.###.#.#.#####.#.#.###.#.#.###.#.#.#.#.###.#.#####.#####.#.#.#.#####.#.###.#####.#.#########
#.#...#...#.......#...#.#.#.#.......#.......#.....#...#...#...#...#...#.#.#...#...#...#.#...#.#...#...#...#.#.#...#.....#.#.........#.......#
###.###.#.#.#######.#.#.###.#######.#####.#.#.#####.#######.#.#######.#.#.###.#######.#.#.###.#.#####.#.###.#####.#.###.#.#.#.###.#####.###.#
#...#...#...#.......#...#...#.....#.......#.#.....#.........#.#...#...#.#.#...#.....#...#.....#.#.....#...#.#.....#.......#.#.........#.#...#
#.###.#######.###########.#.#.###.#########.#.#####.#.###.###.#.#.#.###.#.#.#.#.###.#.#########.#.#########.#.#####.#####.#.#.###.###.###.#.#
#.....#.......#...........#.#...#...........#.#.......#...#...#.#.#...#.#.#.#.#...#.#.#.........#...#.......#.....#.#.......#...#.#.#.....#.#
#.#####.###.#####.#####.#######.#############.#.#######.#.###.#.#####.#.#.#.#####.#.#.#.#######.###.#.#######.###.#.###.###.#.###.#.#######.#
#.....#...#.#...#...#.#...#.....#.............#.........#...#...#...#.#.#...#.....#.#.#.#.....#.#.........#...#...#...#.#...#.#...#.....#...#
#####.###.###.#.###.#.###.#.#####.#####.#.#.###############.#.###.#.#.#.#####.#####.#.#.#.#####.#.#######.###.#.#####.#.#.###.#.#.#####.#.###
#...#...#.....#.#.#.#.#...#...........#.#.#.............#...#.#...#...#.....#.....#.#...#.#...#.#...#...#...#.#.#.....#.#.#...#...#.....#...#
###.###.#######.#.#.#.#.#####.#######.#.#.#.###########.#.#####.#######.###.#####.#.#####.#.#.#.###.#.#.###.###.###.###.#.#.#####.#.#######.#
#.....#.....#...#.#.#.#...#...........#.#.#.#.....#...#...#.....#...#.#.#.#.......#...#.#...#.....#...#...#...#...#...#.#.#.#...#.#.#.......#
#.#######.#.#.###.#.#.###.#.#.###.#######.###.###.###.###.#.#####.#.#.#.#.###.#######.#.#.#############.#.###.#.#.#.#.###.#.#.###.#.#.#######
#.........#.#.#.......#.#...#...#.......#.#...#...#...#...#.......#.#.........#.....#.#...#...#.........#...#...#.#.#.....#...#.............#
#.#########.#.#######.#.#######.#######.#.#.###.###.###.###########.#########.#.#.###.#.###.#.#.#####.#.#.#.###.#.#####.###.###.#.#.#######.#
#.....#.....#.#...#...#.......#.#.....#.#...#...#...#...#.....#...#.....#...#.#.#.#...#.....#...#...#.#.#.#.#.#.#.....#...#.#...#.#.#.......#
#####.#.###.#.#.#.#.###.#####.#.#.###.#.#.###.###.#.#.###.#.#.###.###.#.#.###.#.###.###.#########.###.###.#.#.#.#####.###.###.###.#.#.#.###.#
#...#...........#.#.#...#.....#.#.#.#...#...#.#...#...#...#.#.....#.#.........#.#...#...#...#.....#.......#...#.#.....#.......#...#.........#
#.#.#####.#.#####.#.###.#.#####.#.#.#######.#.#######.#####.#####.#.#.###.#####.#.#.#.#.#.#.#.###.#.#######.###.#.#.#############.#.###.###.#
#.........#.#...#.#.#...#.#...#...#.......#.#...#...#.........#.....#.#...#.....#.#...#.#.#...#.....#.....#.#...#.#.#.....#.......#...#...#.#
#.#####.###.#.###.#.#.###.#.#.#.#####.###.#.###.#.#.#########.#.#.###.#.###.#####.#.#####.###.#.#.#.#.###.###.###.###.###.#####.#####.#.#.###
#...............#.#...#...#...#.......#...#...#...#...#.......#.#.....#.#...#.....#.#.......#.#.#.#.#.#.....#...#.....#.#.#...#.......#.#...#
#.#.#.###.###.#.#.#########.#.#########.#####.#######.#######.#.#.#####.#.#.#.#####.#.#######.#.###.#.#.###.###.###.###.#.#.#.#####.#######.#
#.#.#...#.#...#.#.#.......#.#.........#...#.......#.#.......#.#.#.#...#...#.#.#.....#.#.......#.....#.#...#...#...#.....#...#.....#.#.....#.#
#.#.#####.#.###.#.#.#####.#.#########.###.#.#####.#.#######.#.#.#.#.#.#.#####.#.#####.#.#######.#####.###.#.#####.#####.#########.#.#.###.#.#
#.#.#...........#...#...#...#.......#.#.#.#.#...#...#...#...#.......#...#...#.#.......#.#...#.....#.............#...#...#.........#...#.#.#.#
#.#.#.#.#.#####.#####.#######.#.#####.#.#.###.#.###.#.#.#.###.#######.#.#.#.#.#.#######.#.#.#######.#########.#####.#####.#############.#.#.#
#.#...#.#.#.......#...#.......#.#.......#.#...#.#.....#.#.#.#.#...#...#.....#.#.#.....#...#...#.....#...#...#.....#.#.....#.........#...#.#.#
#.###.#.#.#.#####.###.#.###.#.###.#####.#.#.###.#########.#.#.###.#.###.###.#.#.#.#####.#####.#.#####.#.#.#.#####.#.#.#####.#.#######.#.#.#.#
#.....#.#.#...#...#...#...#.#.#...#.........#.......#.....#.#.....#...#.#.#...#...#.........#.#.#...#.#...#...#...#.#.#...............#.....#
#.###.#.#.#.#.#.#.#.#####.#.###.###.###############.#.#####.###.#####.#.#.#######.#.#######.#.#.#.###.#######.#.#.#.#.#.#####.#########.###.#
#.....#...#.#...#.#.....#...#...#.....#...........#...#.......#.#.....#.#.......#.#.#...#.....#.#.#...#.......#.#.#...#.#...#.#...#.......#.#
#.###.#########.#.###.#.#.#.#.#.#######.#########.#.#######.###.#.#####.###.#####.#.#.#.#.#####.#.#.#####.#####.###.###.#.#.#.#.###.###.###.#
#.#.............#...#.#...#...........#.#.#.....#.#.#...#...#...#.......#...#.....#...#.#.#...#...#.....#...#.....#.....#.#.#...#...#...#...#
#.###.#.###.#######.#.#####.###.#####.#.#.#.###.#.###.#.#.###.###########.###.#########.###.#.#.#.#####.###.#####.#.#####.#.#####.#######.###
#...........#.......#.#.....#.#.....#...#.#...#.#.....#.#.....#...#.....#.....#.......#.#...#...#.....#.#.........#.#...#.#.....#.#.......#.#
#.###.#####.###.#.#.###.###.#.#.###.#####.#.###.#.#####.#.#####.#.#.###.#.#########.#.#.#.#########.###.#.#######.#.###.#.#####.#.#.#######.#
#.#...#.....#...#.#.....#...#.#.#.......#...#...#.....#.#.....#.#...#...#.#.......#.#.#.........#...#...#.#.....#.#.....#...#.....#.#.......#
#.#.#.#.#####.###.#######.###.#.#.###.###.###.#.#.#####.#####.#.#######.#.#.#.###.#.#.#########.#.#.#.#.###.###.#.#####.###.#.###.#.#.#.#####
#.#.....#.....#...#.......................................................#.#.#...#.#...#...#.....#.#.#.....#...#.#...#...............#.#...#
#.###.###.#####.###.#.###.#######.###.#.#.###.###.#.#####.#.#####.#########.#.###.#.###.#.#.###.#.###.#.#####.#####.#.###.###.#.###.#.###.#.#
#.........#.#.....#...#...#.....#.#...#...#...#...#...#...#...#...#.......#.#...#...#...#.....................#...............#.....#.....#.#
###.#######.#.###.#.#.#.###.###.#.#.#.#.###.#.#.#.#.#.#.#####.#.#######.#.###.#.#########.###.#.###.###.#.#####.#.#.#.###.#.#.###########.#.#
#...#.......................#.#.#.#.#.....#.............................#...#.#.......#...#.........#...#.#.....#...#...#...#...#.......#.#.#
#.#####.#######.#.###.#######.#.###.#.#.#.#.###.#.###.#####.#.###.#########.#####.###.#.###.###.###.#.#.#.#.###########.#####.#.#.#.###.###.#
#...........#...#...#.........#.....#...#.#.#...#.....#...#.#.......#.......#...#.#...#.#.......................#.....#...#...#.#.#.#.#.#...#
#.#.#.#.#.#.#.#####.###.#############.#.#.#.#.#####.###.#.#.#####.#.#.#######.#.###.#.#.#######.#.#####.#.#####.#.###.#.#.#.#.#.#.#.#.#.#.#.#
#.#.#.#...#...#...#.......................#.#...#...#...#.#.#.......#.........#.....#.#.....#...#.......#...#...#.....#.....#...#.#.#.#.#.#.#
#.#.#.#########.#.#.#.###.#.###.#####.#.#.#.###.#####.###.#.#.###.###################.#####.#.#.###########.#.#######.#.#####.#####.#.#.#.#.#
#S..#...........#.......#.....#...............#.........#...#.............................#...............#...........#.............#.....#.#
#############################################################################################################################################