Sunday, December 8, 2024

Advent of Code: Day 8

advent-of-code
Stephen Keane
Stephen Keane@skeane.io

Advent of Code: Day 8

Day8: Resonant Collinearity

Get ready to mess around with radio frequencies.
Where I finally learn what the heck "antinodes" are. Check out Resonant Collinearity for the day8 puzzle.

Part 1

The first thing that I did was look up antinodes on wikipedia. spoiler: It didn't help.

I think the biggest challenge in this puzzle is to understand exactly what's being done to create an antinode. I had to read the puzzle description a few times before I got it: we need to look at the slope between two points and then extend out to find the next point.

I spend most of my coding time, parsing the input so that I could look at each frequency line by line. Once that was done this one came together pretty quickly.

Part 1 Solution:

import itertools

testPath = './testInput.txt'
inputPath = './input.txt'
chosenPath = inputPath

def parseInput(path):
  frequencies = {}
  with open(path, 'r') as file:
      frequencies = {}
      strings = [line.strip() for line in file.readlines()]
      map = [list(string) for string in strings]
      for y, col in enumerate(map):
          for x, char in enumerate(col):
              if char != '.':
                  frequencies[char] = []
      for y, col in enumerate(map):
          for x, char in enumerate(col):
              if char != '.':
                  frequencies[char].append((x, y))
      return (frequencies, map)
  
input, map = parseInput(chosenPath)

def getAntiNodes(key, frequencies):
  print(f'key: {key} appears in map {len(frequencies)} times:  {frequencies}
')
  pairings = list(itertools.combinations(frequencies, 2))
  def coordIsOffMap(coord):
      x, y = coord
      return x < 0 or x >= len(map[0]) or y < 0 or y >= len(map)
  antinodes = []
  for pair in pairings:
      #print(f'pair: {pair[0]} and {pair[1]}')
      distanceBetweenY = (pair[1][1] - pair[0][1])
      distanceBetweenX = (pair[1][0] - pair[0][0])
      #print(f'distance between: {distanceBetweenX}, {distanceBetweenY}')
      antiNode1 = (pair[0][0] - distanceBetweenX, pair[0][1] - distanceBetweenY)
      antiNode2 = (pair[1][0] + distanceBetweenX, pair[1][1] + distanceBetweenY)
      #print(f'antiNode1 for {pair[0]}: {antiNode1}')
      #print(f'antiNode2 for {pair[1]}: {antiNode2}
')
      if not coordIsOffMap(antiNode1):
          antinodes.append(antiNode1)
      if not coordIsOffMap(antiNode2):
          antinodes.append(antiNode2)
  return antinodes
      
      
result = []
for key in input:
  antiNodesByKey = (getAntiNodes(key, input[key]))
  for coord in antiNodesByKey:
      if coord not in result:
          result.append(coord)

print(f'antiNodes: {len(result)}')

Part 2

I think the setup I did in the first part, helped me out quite a bit for part 2.

Instead of just appending the next node based on the slope, I had to keep on appending points until I fell off the map. Like the first part, this took me multiple reads of the puzzle description to understand the requirement.

I did get snagged a bit: because I had one sub-function that got both nodes, I ran into a problem when I tried to loop through the antinodes. each loop tried to figure out nodes for both pairs which led to an infinite loop. I had to refactor the function to get the antinodes for each pair separately.

for pair in pairings:
  appendAntiNode1(pair)
  appendAntiNode2(pair)

I made the adjustment to add discrete functions for each pair and then looped through each separately.

Once I did that, I still came up short. When I ran the function against the example given, I was still 3 nodes short on the solution. I reread the puzzle description once again and realized that the original nodes should also be included. I added a check to make sure that the original nodes were included in the antinodes array. That change got me the correct solution.

Part 2 Solution:

import itertools
# import sys
# limit = 10 ** 6
# sys.setrecursionlimit(limit)

testPath = './testInput.txt'
inputPath = './input.txt'
chosenPath = inputPath


def parseInput(path):
  frequencies = {}
  with open(path, 'r') as file:
      frequencies = {}
      strings = [line.strip() for line in file.readlines()]
      map = [list(string) for string in strings]
      for y, col in enumerate(map):
          for x, char in enumerate(col):
              if char != '.':
                  frequencies[char] = []
      for y, col in enumerate(map):
          for x, char in enumerate(col):
              if char != '.':
                  frequencies[char].append((x, y))
      return (frequencies, map)
  
input, map = parseInput(chosenPath)

def renderAntinodes(antinodes):
  for y, col in enumerate(map):
      for x, char in enumerate(col):
          if (x, y) in antinodes:
              print('#', end='')
          else:
              print(char, end='')
      print('')

def getAntiNodes(key, frequencies):
  print(f'key: {key} appears in map {len(frequencies)} times:  {frequencies}
')
  pairings = list(itertools.combinations(frequencies, 2))
  def coordIsOffMap(coord):
      x, y = coord
      return x < 0 or x >= len(map[0]) or y < 0 or y >= len(map)
  
  antinodes = []

  def appendAntiNode1(pair):
      if not antinodes.__contains__(pair[0]):
          antinodes.append(pair[0])
      if not antinodes.__contains__(pair[1]):
          antinodes.append(pair[1])
      distanceBetweenY = (pair[1][1] - pair[0][1])
      distanceBetweenX = (pair[1][0] - pair[0][0])
      
      def getAntiNode1(coord):
          result = (coord[0] - distanceBetweenX, coord[1] - distanceBetweenY)
          if not coordIsOffMap(result):
              return result
          
      antiNode1 = getAntiNode1(pair[0])

      if antiNode1:
          antinodes.append(antiNode1)
          appendAntiNode1((antiNode1, pair[0]))

  def appendAntiNode2(pair):
      distanceBetweenY = (pair[1][1] - pair[0][1])
      distanceBetweenX = (pair[1][0] - pair[0][0])

      def getAntiNode2(coord):
          result = (coord[0] + distanceBetweenX, coord[1] + distanceBetweenY)
          if not coordIsOffMap(result):
              return result
          
      antiNode2 = getAntiNode2(pair[1])

      if antiNode2:
          antinodes.append(antiNode2)
          newPair = (pair[1], antiNode2)
          appendAntiNode2(newPair)
  
  for pair in pairings:
      appendAntiNode1(pair)
      appendAntiNode2(pair)

  renderAntinodes(antinodes)
  return antinodes
      
      
result = []
for key in input:
  antiNodesByKey = (getAntiNodes(key, input[key]))
  for coord in antiNodesByKey:
      if coord not in result:
          result.append(coord)

print(f'antiNodes: {len(result)}')