Day 13 Part 1

After doing days 1-12, I’ve decided I should be recording my thought process while discovering solutions to the challenges. Seeing programming personalities online use this as a way to learn a new language, I decided to write these challenges in TypeScript. I use JavaScript a lot, but it’s always been with JQuery, so TypeScript isn’t new per-say and I’m not finding it that challenging. Next year will be different!


Summary

The core of this challenge is to find the “reflection” hidden in the pattern. Reflections can be vertical or horizontal. Add all the lines to the left of vertical reflections and add them to 100 multiplied by all the lines above horizontal reflections.

Example of a pattern:

1
2
3
4
5
6
7
#...##..#
#....#..#
..##..###
#####.##.
#####.##.
..##..###
#....#..#

Illustrated:

1
2
3
4
5
6
7
  #...##..#
2 #....#..#
1 ..##..###
\ #####.##.
/ #####.##.
1 ..##..###
2 #....#..#
Full challenge: Day 13

Horizontal Reflection Points

My original thinking was to break up this pattern into a nested array string[][], loop through each cell and checking the indexes that could possibly be reflections. Something like:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
const puzzle: string[][]; //puzzle input
for (let i = 0; i < puzzle.length; i++){
  let is_reflection: boolean = false;
  for (let j = 0; j < puzzle[i].length; j++){
    if (puzzle[i][j] == puzzle[i+1][j]){
      is_reflection = true;
    }else if (is_reflection){
      is_reflection = false;
    }
  };
};

I was thinking I could even check the vertical reflection at the same time as horizontal.

Upon writing this loop and realizing I’d have to write edge cases I decided to try doing a straight comparison of rows:

1
puzzle[3] == puzzle[4];

Which then turned into:

1
puzzle[3].join('') == puzzle[4].join('');

Realizing this is the path of least resistance, I ditched the nested array, string[][], idea and decided to keep it in the same format I read it in as; a normal array, string[].

My new reflection detection looked like this:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
function get_horizontal_reflections(contents: string[]): number[]{
  const reflections: number[] = [];
  for (let i = 0; i < contents.length; i++){
    // (skip the last row, [last+1] -> undefined)
    if (index != contents.length - 1 && contents[i] == contents[i+1]){
      reflections.push(i);
    }
  };
  return reflections;
}

I then decided, “Hey, I could use a map + filter instead!”

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
function get_horizontal_reflections(contents: string[]): number[]{
  return contents
    .map((ele, index) => {
      if (index != contents.length - 1 && ele === contents[index+1]){
        return index;
      }
      return -1;
    })
    .filter((ele) => ele != -1);
}

Vertical Reflection Points

With my wonderful, possibly not the most efficient but nice looking, map + filter combination I started to dread redoing this to check verticals reflections. My solution is to “rotate” the whole puzzle clockwise and feed it into the same get_horizontal_reflections function.

Note: If we rotated it counter clockwise than the “lines to the left of vertical reflections” would be at the bottom, clockwise they’re at the top the exact same number we need for the horizontal- less work!

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
function get_vertical(horizontal: string[]): string[]{
  let vertical: string[] = [];
  let vertical_count: number = -1;
  horizontal.forEach((row) => {
    row.split('').forEach((cell, index) => {
      if (vertical_count < index){
        vertical.push([cell]);
        vertical_count++;
      }else{
        vertical[index] = cell + vertical[index];
        
        // `cell + vertical[index]` turns:
        // [
        //   ['#']
        //   ['#']
        //   ['.']
        // ]
        // => '.##' (good!)
        //
        // vertical[index] + cell => '##.' (bad!)
        
      }
    });
  });
  return vertical;
}

Reflection Point Validation

Now that, my newly renamed, get_reflection_points() function can find both the horizontal and vertical->horizontal points, I need to make sure that this is actually correct.

My observations to get this done are:

  1. if the point is 3 then the actual reflection is between 3 and 4.
  2. if the point is 3 then there are 3 rows above the reflection point
  3. if we take the length of the pattern and subtract 2, we get the rows below the reflection point.

Using that my validation looks like this:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
function get_reflection_points(contents: string[]): number[]{
  const contents_len: number = contents.length;
  return contents
    .map((ele, index) => {
      if (index != contents.length - 1 && ele === contents[index+1]){
        // -1 to account for len-1 and -1 for reflect
        const rows_below: number = contents_len - index - 2;
        // grab the lowest one (lines above or lines below)
        const rows_to_check: number = index < rows_below ? index : rows_below;

        for (let i = 0; i < rows_to_check; i++){
          if (contents[index-(1+i)] !== contents[index+(2+i)]){
            return -1;
          }
        };
        return index;
      }
      return -1;
    })
    .filter((ele) => ele != -1);
}

Wrapping it Together

As noted above if the index is 3 then there are 3 rows above the reflection point, which is the number we need to find the answer. get_reflection_points() returns the index of all valid reflection points as a number array, number[], but it always seems to be 1 or 0.

A bare bones version of putting this together would look like this:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
const puzzle_list: string[][]; // filled from file

let horizontal: number = 0;
let vertical: number = 0;
puzzle_list.forEach((puzzle) => {
  let horiz: number[] = get_reflection_points(puzzle);
  if (horiz.length){ horizontal += horiz[0] }
  
  let vert: number[] = get_reflection_points(get_vertical(puzzle));
  if (vert.length){ vertical += vert[0] }
});

console.log(`Answer => ${vertical + (100 * horizontal)}`);

This is generally my solution. My actual version is slightly different as it uses interfaces to store puzzles with a puzzle number, has more validation and opportunities for meaningful debugging statements.

On to Part 2!

Post:
1/8
comments powered by Disqus