text: const { inputAsString, Point } = require("../common/aoc");
  
  const DIFFS = [
      [-1, -1],
      [-1, 0],
      [-1, 1],
      [0, -1],
      [0, 0],
      [0, 1],
      [1, -1],
      [1, 0],
      [1, 1],
  ];
  function parseInput(input) {
      let [enhancer, start] = input.split("\n\n");
      const pixels = start.split("\n").map((line) => Array.from(line));
      return { enhancer, pixels };
  }
  function blank(dim, on) {
      const char = on ? "#" : ".";
      const blank = new Array(dim);
      for (let i = 0; i < dim; i++) {
          blank[i] = Array.from(char.repeat(dim));
      }
      return blank;
  }
  function expand(grid, on) {
      const dim = grid.length + 2;
      const expanded = blank(dim, on);
      for (let r = 0; r < grid.length; r++) {
          let row = grid[r];
          for (let c = 0; c < row.length; c++) {
              expanded[r + 1][c + 1] = grid[r][c];
          }
      }
      return expanded;
  }
  /*
  The trick here, which I didn't realize until reading the forums,
  is that if the first character (000000000) in the enhancer is "#", 
  that means all the infinite pixels will turn on every other turn.
  So instead of assuming that the void is dark, we have to check the 
  modulus of the turn to see if the default value is "." or "#"
  */
  function enhance(enhancer, pixels, turns) {
      let enhanced;
      for (let i = 0; i < turns; i++) {
          const on = i % 2 && enhancer[0] === "#";
          pixels = expand(pixels, on);
          let dim = pixels.length;
          enhanced = blank(dim, on);
          for (let r = 0; r < dim; r++) {
              for (let c = 0; c < dim; c++) {
                  let bin = "";
                  for (const diff of DIFFS) {
                      let dr = r + diff[0];
                      let dc = c + diff[1];
                      if (!pixels[dr] || !pixels[dr][dc]) {
                          bin += on ? 1 : 0;
                      } else {
                          bin += pixels[dr][dc] === "#" ? 1 : 0;
                      }
                  }
                  const index = parseInt(bin, 2);
                  enhanced[r][c] = enhancer[index];
              }
          }
          pixels = enhanced;
      }
      return enhanced;
  }
  function countLights(pixels) {
      let lights = 0;
      pixels.forEach((row) =>
          row.forEach((pixel) => {
              if (pixel === "#") {
                  lights += 1;
              }
          })
      );
      return lights;
  }
  function solve(data) {
      const start = Date.now();
      let parsed = parseInput(data);
      let enhanced = enhance(parsed.enhancer, parsed.pixels, 2);
      let pt1 = countLights(enhanced);
  
      parsed = parseInput(data);
      enhanced = enhance(parsed.enhancer, parsed.pixels, 50);
      let pt2 = countLights(enhanced);
      return { pt1, pt2, time: Date.now() - start };
  }
  
  const solution = solve(inputAsString(20));
  console.log(`Day 20.1: ${solution.pt1}`);
  console.log(`Day 20.2: ${solution.pt2}`);
  console.log(`Day 20 Time: ${solution.time} ms\n`);
  
  module.exports = { solution, parseInput, enhance, countLights };
  