You find yourselves on the roof of a top-secret Easter Bunny installation.
While The Historians do their thing, you take a look at the familiar huge antenna. Much to your surprise, it seems to have been reconfigured to emit a signal that makes people 0.1% more likely to buy Easter Bunny brand Imitation Mediocre Chocolate as a Christmas gift! Unthinkable!
Visit the website for the full story and puzzle description.
Continuing the steps I started yesterday, I extracted a common function (GetUniquePositions
) that takes a parameter to generate antinode positions, representing the difference between part one and part two.
getAntinodes
returns the antinode positions of srcAntenna on the dstAntenna side. It doesn’t need to be symmetric — i.e., it doesn’t have to return the antinodes on the srcAntenna side — because GetUniquePositions will call it with the parameters swapped as well. This allows us to handle only one direction at a time.
The generators are fairly straightforward: I simply take steps in the direction determined by the antennas, starting from the destination. Since I represented coordinates using complex numbers again, there’s no need for any special handling on the algebra side, regular + and - operations work.
The GetAntinodes
delegate is introduced only for documentation purposes. It looks better to my eyes than an ugly Func<>
with four type parameters would in GetUniquePositions
-s signature.
namespace AdventOfCode.Y2024.Day08;
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using System.Numerics;
using Map = System.Collections.Immutable.ImmutableDictionary<System.Numerics.Complex, char>;
[ProblemName("Resonant Collinearity")]
class Solution : Solver {
public object PartOne(string input) => GetUniquePositions(input, GetAntinodes1).Count();
public object PartTwo(string input) => GetUniquePositions(input, GetAntinodes2).Count();
HashSet<Complex> GetUniquePositions(string input, GetAntinodes getAntinodes) {
var map = GetMap(input);
var antennaLocations = (
from pos in map.Keys
where char.IsAsciiLetterOrDigit(map[pos])
select pos
).ToArray();
return (
from srcAntenna in antennaLocations
from dstAntenna in antennaLocations
where srcAntenna != dstAntenna && map[srcAntenna] == map[dstAntenna]
from antinode in getAntinodes(srcAntenna, dstAntenna, map)
select antinode
).ToHashSet();
}
// returns the antinode positions of srcAntenna on the dstAntenna side
delegate IEnumerable<Complex> GetAntinodes(Complex srcAntenna, Complex dstAntenna, Map map);
// in part 1 we just look at the immediate neighbour
IEnumerable<Complex> GetAntinodes1(Complex srcAntenna, Complex dstAntenna, Map map) {
var dir = dstAntenna - srcAntenna;
var antinote = dstAntenna + dir;
if (map.Keys.Contains(antinote)) {
yield return antinote;
}
}
// in part 2 this becomes a cycle, plus dstAntenna is also a valid position now
IEnumerable<Complex> GetAntinodes2(Complex srcAntenna, Complex dstAntenna, Map map) {
var dir = dstAntenna - srcAntenna;
var antinote = dstAntenna;
while (map.Keys.Contains(antinote)) {
yield return antinote;
antinote += dir;
}
}
// store the points in a dictionary so that we can iterate over them and
// to easily deal with points outside the area using GetValueOrDefault
Map GetMap(string input) {
var map = input.Split("\n");
return (
from y in Enumerable.Range(0, map.Length)
from x in Enumerable.Range(0, map[0].Length)
select new KeyValuePair<Complex, char>(x - y * Complex.ImaginaryOne, map[y][x])
).ToImmutableDictionary();
}
}
Please ☆ my repo if you like it!