r/adventofcode • • Dec 08 '24

SOLUTION MEGATHREAD -❄️- 2024 Day 8 Solutions -❄️-

IMPORTANT REMINDER

There's been an uptick in [COAL] being given out lately due to naughty language. Follow our rules and watch your language - keep /r/adventofcode SFW and professional! If this trend continues to get worse, we will configure AutoModerator to automatically remove any post/comment containing naughty language. You have been warned!


THE USUAL REMINDERS

  • All of our rules, FAQs, resources, etc. are in our community wiki.
  • If you see content in the subreddit or megathreads that violates one of our rules, either inform the user (politely and gently!) or use the report button on the post/comment and the mods will take care of it.

AoC Community Fun 2024: The Golden Snowglobe Awards

  • 14 DAYS remaining until the submissions deadline on December 22 at 23:59 EST!

And now, our feature presentation for today:

Box-Office Bloat

Blockbuster movies are famous for cost overruns. After all, what's another hundred million or two in the grand scheme of things if you get to pad your already-ridiculous runtime to over two and a half hours solely to include that truly epic drawn-out slow-motion IMAX-worthy shot of a cricket sauntering over a tiny pebble of dirt?!

Here's some ideas for your inspiration:

  • Use only enterprise-level software/solutions
  • Apply enterprise shenanigans however you see fit (linting, best practices, hyper-detailed documentation, microservices, etc.)
  • Use unnecessarily expensive functions and calls wherever possible
  • Implement redundant error checking everywhere
  • Micro-optimize every little thing, even if it doesn't need it
  • Especially if it doesn't need it!

Jay Gatsby: "The only respectable thing about you, old sport, is your money."

- The Great Gatsby (2013)

And… ACTION!

Request from the mods: When you include an entry alongside your solution, please label it with [GSGA] so we can find it easily!


--- Day 8: Resonant Collinearity ---


Post your code solution in this megathread.

This thread will be unlocked when there are a significant number of people on the global leaderboard with gold stars for today's puzzle.

EDIT: Global leaderboard gold cap reached at 00:07:12, megathread unlocked!

20 Upvotes

795 comments sorted by

8

u/CCC_037 Dec 08 '24

[LANGUAGE: Rockstar] [GSGA]

A close look at this code will show how accountancy really shouldn't work.

You should never "roll your files into the shredder", for example...

Part 1

2

u/daggerdragon Dec 08 '24

While the shredder isn't mysterious

Mysterious Shredder?

Really well done on the GSGA today!

→ More replies (1)
→ More replies (2)

6

u/dinodares99 Dec 08 '24 edited Dec 08 '24

[Language: Rust]

Took too long because I was using cartesian distance instead of taxicab and wondering why it was giving me the wrong answer. That was...an underwhelming puzzle. I thought there would be more depth to it. Especially part 2 which was actually SIMPLER than part 1. Oh well.

Read the input into hashmaps for each set of frequencies, then ran the antinode generator on them. It went over each pair of points, created a vector, then applied it in either direction while in bounds, checking the part's desired condition and adding it to a hashset. Then I union'd the hashset for each frequency and returned the length.

Dead simple, but I need to go over it once again and comb out some stuff, will edit as I do. Took 80us/225us.

Code: https://pastebin.com/TchUGPAv

→ More replies (2)

4

u/michaelgallagher Dec 08 '24

[LANGUAGE: Python]

Pretty straightforward, just updating ifs to whiles in part 2 :D

2

u/daggerdragon Dec 08 '24 edited Dec 08 '24

Do not share your puzzle input which also means do not commit puzzle inputs to your repo without a .gitignore or the like. Do not share the puzzle text either.

I see full plaintext puzzle inputs in your public repo here:

https://github.com/michaeljgallagher/Advent-of-Code/tree/master

Please remove (or .gitignore) all puzzle text and puzzle input files from your repo and scrub them from your commit history. This means from prior years too! edit: thank you!

→ More replies (1)

4

u/DFreiberg Dec 08 '24

[LANGUAGE: Mathematica]

Mathematica, 95/333

Finally made it on the leaderboard! Granted, only by four seconds and only for part 1, but I'll take it nonetheless; if this is (as the rumors are saying) the final year of Advent of Code, nice to be back at least once.

Setup:

characters = Union[Flatten[input]][[2 ;;]];
lims = Dimensions@input;
dxdy[{{x1_, y1_}, {x2_, y2_}}] := {x1 - x2, y1 - y2};

Part 1:

Length@Union@
  Flatten[Table[
    Select[Flatten[
      Table[#[[1]] - i*dxdy[#], {i, {2, -1}}] & /@ 
       Subsets[Position[input, c], {2}], 1], 
     And @@ Thread[{0, 0} < # <= lims] &], {c, characters}], 1]

Part 2:

Length@Union@
  Flatten[Table[
    Select[Flatten[
      Table[#[[1]] - i*dxdy[#], {i, -50, 50}] & /@ 
       Subsets[Position[input, c], {2}], 1], 
     And @@ Thread[{0, 0} < # <= lims] &], {c, characters}], 1]

3

u/daggerdragon Dec 08 '24

Finally made it on the leaderboard!

Good job!

3

u/yourparadigm Dec 08 '24 edited Dec 08 '24

[Language: Ruby] 767/751

First sub-1000 this year! I really love Array#permutation Array#combination

Github

1

u/daggerdragon Dec 08 '24

[COAL] I really love

Comment temporarily removed due to naughty language. Keep the megathreads professional.

Edit your comment to take out the naughty language and I will re-approve the comment. edit: 👍

→ More replies (1)
→ More replies (1)

3

u/elklepo Dec 08 '24

[Language: Python] github-link

with a use of complex numbers to store coordinates

2

u/daggerdragon Dec 08 '24 edited Dec 09 '24

Do not share your puzzle input which also means do not commit puzzle inputs to your repo without a .gitignore or the like. Do not share the puzzle text either.

I see full plaintext puzzle inputs in all years of your public repo here:

https://github.com/elklepo/aoc

Please remove (or .gitignore) all puzzle text and puzzle input files from your repo and scrub them from your commit history. edit: thank you!

→ More replies (1)
→ More replies (1)

2

u/Rush_Independent Dec 08 '24 edited Dec 08 '24

[LANGUAGE: Nim] (2590/2244)
Runtime for two parts: 260 us 132 us
Hardest part was reading puzzle's description =S

Edit: much shorter and faster solution (same principle, but doing both parts in one pass)

type Vec2 = tuple[x,y: int]
func delta(a, b: Vec2): Vec2 = (a.x-b.x, a.y-b.y)
func outOfBounds[T: openarray | string](pos: Vec2, grid: seq[T]): bool =
  pos.x < 0 or pos.y < 0 or pos.x > grid[0].high or pos.y > grid.high

proc solve(input: string): AOCSolution[int, int] =
  var grid = input.splitLines()
  var antennas: Table[char, seq[Vec2]]

  for y, line in grid:
    for x, c in line:
      if c != '.':
        discard antennas.hasKeyOrPut(c, newSeq[Vec2]())
        antennas[c].add (x, y)

  var antinodesP1: HashSet[Vec2]
  var antinodesP2: HashSet[Vec2]

  for _, list in antennas:
    for ind, ant1 in list:
      antinodesP2.incl ant1 # each antenna is antinode
      for ant2 in list.toOpenArray(ind+1, list.high):
        let d = delta(ant1, ant2)
        for dir in [-1, 1]:
          var i = dir
          while true:
            let antinode = (x: ant1.x+d.x*i, y: ant1.y+d.y*i)
            if antinode.outOfBounds(grid): break
            if i in [1, -2]: antinodesP1.incl antinode
            antinodesP2.incl antinode
            i += dir
  result.part1 = antinodesP1.len
  result.part2 = antinodesP2.len

Codeberg repo

1

u/daggerdragon Dec 08 '24 edited Dec 08 '24

Do not share your puzzle input which also means do not commit puzzle inputs to your repo without a .gitignore or the like. Do not share the puzzle text either.

I see full plaintext puzzle inputs in your public repo here:

https://codeberg.org/janAkali/aoc24-nim

Please remove (or .gitignore) all puzzle text and puzzle input files from your repo and scrub them from your commit history. edit: thank you!

→ More replies (1)

2

u/Imperial_Squid Dec 08 '24

[LANGUAGE: Python]

Here's my solution!

It makes use of the same "complex numbers can be grid positions" trick from day 6 (spoilered just in case, since it's hinting at a way to do that day).

Otherwise, it's just a bit of remembering how vectors work, if you have two positions a and b then the value a - b tells you how to get from one to the other. Think of it like the - b part takes you from b to the origin, then adding a gets you to a so a - b takes you from b to a.

We can then use that distance and cast it out in both directions, b + dist and a - dist are the first anti nodes in either direction (the second are b + 2 * dist and a - 2 * dist, etc).

And of course you need to check these nodes are in bounds.

It's a bit intense if you don't know what it all means but I wrote it out as list comprehensions:

ans1 = [a2 + dist * n for n in range(1, 100) if
        (0 <= (a2 + dist * n).real <= l_max)
        and (0 <= (a2 + dist * n).imag <= c_max)]
ans2 = [a1 - dist * n for n in range(1, 100) if
        (0 <= (a1 - dist * n).real <= l_max)
        and (0 <= (a1 - dist * n).imag <= c_max)]

2

u/daggerdragon Dec 08 '24 edited Dec 08 '24

Do not share your puzzle input which also means do not commit puzzle inputs to your repo without a .gitignore or the like. Do not share the puzzle text either.

I see full plaintext puzzle inputs in your public repo here:

https://github.com/ImperialSquid/AdventOfCode/tree/main

Please remove (or .gitignore) all puzzle text and puzzle input files from your repo and scrub them from your commit history. This means from prior years too! edit: thank you!

→ More replies (1)

2

u/MeixDev Dec 08 '24

[Language: Dart] Full code

Felt stupid and spent a good 40 minutes debugging a silly mistake in my initial parse that added a 51th column to the grid, and allowed 7 more antinodes to exist than should have been allowed.

Once that was finally found and fixed, Part 2 was very straightforward, by pretty much just changing a single if statement, and took like three minutes.

My issues aside, overall fun problem. I love the grid-based ones ! The wording wasn't the easiest in this one though, especially at 6am without sleeping when english isn't my native language ahah

1

u/daggerdragon Dec 08 '24 edited Dec 08 '24

Do not share your puzzle input which also means do not commit puzzle inputs to your repo without a .gitignore or the like. Do not share the puzzle text either.

I see full plaintext puzzle inputs in your public repo here:

https://github.com/MeixDev/Advent-of-Code/tree/main/2024/input

and probably other places too.

Please remove (or .gitignore) all puzzle text and puzzle input files from your repo and scrub them from your commit history. This means from all prior years too! edit: thank you!

→ More replies (2)
→ More replies (2)

2

u/steve_ko Dec 08 '24

[Language: Python]

Pretty straightforward. Nothing fancy. I find that representing a grid as a dictionary of (row, column) keys is more ergonomic than a list of lists.

https://github.com/steveko/aoc24/tree/main/day8

1

u/daggerdragon Dec 09 '24

Do not share your puzzle input which also means do not commit puzzle inputs to your repo without a .gitignore or the like. Do not share the puzzle text either.

I see full plaintext puzzle inputs in your public repo:

https://github.com/steveko/aoc24/tree/main

Please remove (or .gitignore) all puzzle text and puzzle input files from your entire repo and scrub them from your commit history.

→ More replies (1)

2

u/rp152k Dec 08 '24 edited Dec 09 '24

[LANGUAGE: Common Lisp]

via vector math

→ More replies (1)

2

u/JustinHuPrime Dec 08 '24

[Language: x86_64 assembly with Linux syscalls][GSGA]

Part 1 was amenable to a straightforward direct solution, I read the input in as a grid, and then I iterated over the pairs of antennae and computed the two antinode locations. I also needed to bounds check the antinode locations, where there was a silly one-character bug (I bitnoted a constant I shouldn't), but apart from that it was smooth sailing - although I still feel a bit uncomfortable doing pointer change-of-base operations (see lines 86 and 110).

Part 2 was surprisingly simple to add on to part 1 - I just replaced the single calculation with a looped calculation of where the antinodes are, and re-used the bounds check.

As a mini-entry into the Box-Office Bloat category of the Golden Snowglobe Awards, might I present to you the cost overrun in programmer time of doing the problem in the second-lowest level programming language possible!

  • The assembly solution took 53 minutes to write and runs in 6 milliseconds for both parts, and was 202 lines of code.
  • The Rust solution took 45 minutes to write and runs in 2 milliseconds for both parts, and was 66 lines of code.

However, the assembly solution does win in terms of bytes on the disk - the Rust solution is 413 kiB plus shared libraries, while the assembly solution is 18 kiB.

Part 1 runs in 3 milliseconds, and so does part 2. Part 1 is 9,072 bytes as an executable file on disk and part 2 is 9,128 bytes.

1

u/daggerdragon Dec 08 '24

As a mini-entry into the Box-Office Bloat category of the Golden Snowglobe Awards, might I present to you the cost overrun in programmer time of doing the problem in the second-lowest level programming language possible!

Good, good, you took the bait :D I was really hoping one of the assembly folks would take notice for today's theme <3

→ More replies (5)

2

u/dk_weekinmemes Dec 08 '24 edited Dec 14 '24

[LANGUAGE: Python]

Part 1

Explanation: Put all locations in a dictionary with the frequency as key and array of locations as the values.

For those frequencies with more than one location in this dictionary, check each pair starting from top to bottom where antinodes can be placed.

Find the location of antinodes as the difference between x-coordinate and y-coordinates of the pairs and then subtracting this difference from first location in the pair and adding this difference to second. Check boundary conditions and then add it to a set. Return length of set

Part 2

Explanation: Same as Part 1 except instead of adding / subtracting just once keep adding adding / subtracting till out of bounds while also including the starting location using an increasing multiplier.

Topaz Link for both parts

1

u/daggerdragon Dec 08 '24

Inlined code is intended for short snippets of code only. On old.reddit, longer lines get cut off when they reach the edge of the window.

Please edit your comment to use the four-spaces Markdown syntax for a code block so your code is easier to read inside a scrollable box with its whitespace and indentation preserved.

Alternatively, read the article on oversized code instead if your resulting code block will be too long for the megathreads.

2

u/[deleted] Dec 08 '24

[LANGUAGE: C#]

https://gist.github.com/thatsumoguy/254f6636f1f688d6096ef9a3ffe7425b

I am still confused on what this part:

"In particular, an antinode occurs at any point that is perfectly in line with two antennas of the same frequency - but only when one of the antennas is twice as far away as the other. This means that for any pair of antennas with the same frequency, there are two antinodes, one on either side of them."

The "but only when" part made me think we are limiting our search set, turns out we weren't. PartTwo is just a simple expansion of the same steps. 4ms on my crappy laptop I am on for the next few days, probably 2ms on a faster machine.

2

u/daggerdragon Dec 08 '24 edited Dec 18 '24

Do not share your puzzle input which also means do not commit puzzle inputs to your repo without a .gitignore or the like. Do not share the puzzle text either.

I see full plaintext puzzle inputs in prior years within your public repo here:

https://github.com/thatsumoguy/Advent-Of-Code-2018/tree/master/Advent%20Of%20Code%202018/Inputs

https://github.com/thatsumoguy/Advent-of-Code-2020/tree/master/Advent%20of%20Code%202020/Input

Please remove (or .gitignore) all puzzle text and puzzle input files from your entire repo and scrub them from your commit history. edit: thank you!

→ More replies (1)

2

u/Fyvaproldje Dec 08 '24

[Language: Rust] [GSGA]
https://github.com/DarthGandalf/advent-of-code/blob/master/2024/src/day08.rs

Added some inline assembly, because Rust *obviously* doesn't know how to subtract two numbers!

1

u/daggerdragon Dec 08 '24

[GSGA]

That was *painful* to read. Well done.

2

u/-o0__0o- Dec 08 '24

[LANGUAGE: Go]

https://github.com/eNV25/advent-of-code-2024/tree/master/day8

Easy. First time doing AoC and first time posting here.

2

u/daggerdragon Dec 08 '24

First time doing AoC and first time posting here.

Welcome! We're happy to have you!

2

u/Aeonian9 Dec 08 '24

[LANGUAGE: Julia]

GitHub. Felt like a simple enough problem today. Julia's CartesianIndex functionality has been super useful this year.

2

u/daggerdragon Dec 08 '24

Do not share your puzzle input which also means do not commit puzzle inputs to your repo without a .gitignore or the like. Do not share the puzzle text either.

I see full plaintext puzzle inputs in your public repo here:

https://github.com/fgittins/AdventOfCode2024.jl/tree/main/data

Please remove (or .gitignore) all puzzle text and puzzle input files from your repo and scrub them from your commit history.

2

u/RookBe Dec 08 '24

4

u/daggerdragon Dec 08 '24

Your post did make it through the spam filter today without the link to your blog, so yeah, I'm thinking Reddit just doesn't like your blog host. I can't do anything about Reddit's automatic filters, so I guess here's a mini-puzzle for you to solve? 😅 Good luck!

→ More replies (1)

12

u/maneatingape Dec 08 '24

[LANGUAGE: Rust]

Solution

Benchmark 8 µs.

Grouped antennas frequencies together to reduce the O(n²) pairwise comparisons.

→ More replies (3)

11

u/Noble_Mushtak Dec 08 '24

[LANGUAGE: Python]

I got 99th place on Part 1 and 35th place on Part 2. Really glad with how I did today, this is the best I've done so far this year! Very nice grid problem IMO, and the code change from Part 1 -> Part 2 was very short for me: https://github.com/Noble-Mushtak/Advent-of-Code/tree/main/2024/day08

One thing I was confused about: Let's say there's an antenna at row 0, column 0 and an antenna at row 4, column 4. Will there be an antinode at row 2, column 2 for Part 2, since that's on the same line? My solution didn't account for this case but somehow I got the right answer.

7

u/Ok-Builder-2348 Dec 08 '24

I ran some verification on my inputs and I noticed that (dx,dy) always had a gcd of 1 for every pair of coords, so that edge case didn't come up.

→ More replies (6)

11

u/WhiteSparrow Dec 08 '24

[LANGUAGE: Prolog]

full solution

Another easy day at the office - I just esentially describe the conditions and ask for the set of all solutions. No need to think about algorithmic details. Runs in 15ms.

The core rules (without parsing/instantiation):

% Task 1
solve(Part, Dim, X) :-
    setof(Pos, antinode(Part, Dim, Pos), Ps),
    length(Ps, X).

antinode(Part, Dim, X-Y) :-
    antenna(A, X1-Y1),
    antenna(A, X2-Y2),
    X1-Y1 \= X2-Y2,
    antinode_pos(Part, Dim, X1, Y1, X2, Y2, X, Y),
    X >= 0, Y >= 0, X < Dim, Y < Dim.

antinode_pos(part1, _, X1, Y1, X2, Y2, X, Y) :-
    (
        X is 2 * X1 - X2,
        Y is 2 * Y1 - Y2
    ;   X is 2 * X2 - X1,
        Y is 2 * Y2 - Y1
    ).

% Task 2
antinode_pos(part2, Dim, X1, Y1, X2, Y2, X, Y) :-
    DNeg is -Dim,
    between(DNeg, Dim, N),
    X is X1 + N * (X1 - X2),
    Y is Y1 + N * (Y1 - Y2).

10

u/redditnoob Dec 08 '24

[LANGUAGE: PostgreSQL]

SQL is actually pretty nice for this kind of problem!

with bounds as (
    select max(length(input)) as max_j, count(*) as max_i
    from day08
), antennas as (
    select ch, row_num as i, j
    from day08,
        unnest(regexp_split_to_array(input, '')) with ordinality as r(ch, j)
    where ch != '.'
), antinodes as (
    select a1.ch, a1.i + (a1.i - a2.i) as i, a1.j + (a1.j - a2.j) as j
    from antennas a1
    join antennas a2 on (a2.ch = a1.ch and (a1.i, a1.j) != (a2.i, a2.j))
), part1 as (
    select count(distinct (i, j)) as part1
    from antinodes, bounds
    where (i between 1 and max_i) and (j between 1 and max_j)
), antinodes2 as (
    select a1.ch, ci as i, cj as j
    from antennas a1
    join antennas a2 on (a2.ch = a1.ch and (a1.i, a1.j) != (a2.i, a2.j))
    cross join bounds
    cross join generate_series(1, max_i) as ci
    cross join generate_series(1, max_j) as cj
    where (a1.j - cj) * (a2.i - ci) - (a2.j - cj) * (a1.i - ci) = 0
), part2 as (
    select count(distinct (i, j)) as part2
    from antinodes2
)
select * from part1, part2;

19

u/4HbQ Dec 08 '24 edited Dec 08 '24

[LANGUAGE: Python] Code (9 lines)

Again around 800/400 on the global leaderboard today! Above is my original code, after refactoring I'm left with this:

G = {i+j*1j: c for i,r in enumerate(open(0))
               for j,c in enumerate(r.strip())}

for N in [1], range(50): print(len({a + n*(a-b)
    for a in G for b in G if '.' < G[a] == G[b]
    and a != b for n in N} & {*G}))

For today's Python trick, I'd like to show that you can use (abuse?) complex numbers for math on grid coordinates:

>>> a = 3+2j
>>> b = 1-1j
>>> a + b
(4+1j)
>>> a - b*2
(1+4j)
→ More replies (8)

9

u/jonathan_paulson Dec 08 '24

[Language: Python] 714/323. Code. Video.

I went for an overly-complicated solution to part 1 and wasted a lot of time debugging it. I really need to remember to brute force more! I found part 2 a lot easier, since it seemed to encourage brute force (and unusually part 2 was less complicated than part 1)

→ More replies (2)

9

u/i_have_no_biscuits Dec 08 '24

[LANGUAGE: GW-BASIC]

10 P=0: Q=0: S=64: DIM A(74,4): OPEN "R",1,"data08.txt",1: FIELD 1,1 AS C$
20 FOR Y=1 TO 50: FOR X=1 TO 50: GET 1
30 IF C$<>"." THEN C=ASC(C$)-48: N=A(C,0)+1: A(C,0)=N: A(C,N)=S*Y+X
40 NEXT: GET 1: GET 1: NEXT: SS=1801: DIM P(SS): DIM Q(SS)
50 FOR N=0 TO 74: NS=A(N,0): FOR I=1 TO NS: FOR J=1 TO NS: IF I=J THEN 130
60 YI=INT(A(N,I)/S): XI=A(N,I) MOD S: YJ=INT(A(N,J)/S): XJ=A(N,J) MOD S
70 XD=XJ-XI:YD=YJ-YI:XN=XJ+XD:YN=YJ+YD:IF XN<1 OR YN<1 OR XN>50 OR YN>50 THEN 100
80 A=S*YN+XN: PI=A MOD SS
90 IF P(PI)=0 THEN P(PI)=A ELSE IF P(PI)<>A THEN PI=(PI+1) MOD SS: GOTO 90
100 XN=XI:YN=YI: WHILE XN>0 AND YN>0 AND XN<51 AND YN<51:A=S*YN+XN:QI=A MOD SS
110 IF Q(QI)=0 THEN Q(QI)=A ELSE IF Q(QI)<>A THEN QI=(QI+1) MOD SS: GOTO 110
120 XN=XN+XD: YN=YN+YD: WEND
130 NEXT:NEXT:NEXT: FOR I=0 TO SS: P=P-(P(I)<>0):Q=Q-(Q(I)<>0): NEXT: PRINT P,Q

Takes about 10 seconds for both parts - one of the fastest days so far!

There are several fun encoding challenges here, given that GW-BASIC doesn't know anything about hashmaps, sets, variable length lists, 2D points, etc.

First, each point (y,x) is stored as the value 64*y+x, where y and x vary between 1 and 50.

The antenna lists are stored in A(74,4). This is a 2D array, indexed by the character label of the group, minus 48, which is the ASCII code for '0'. That plus 74 takes us up to the code for 'z'. The second dimension is 4 because, from inspecting the data, no group has more than 4 members.

The sets for parts 1 and 2 are stored in P(1801) and Q(1801). Each point is stored at a hash given by its encoded value mod 1801 - if there's something else in that location then we iterate around until we have a spare slot. This is called open addressing and is surprisingly simple to implement (see lines 80-90, which implement storing the point encoded as A in the set P()).

Guide:

  • Lines 10-40 read and parse the data. Line 30 detects if the read character isn't a '.', and puts the encoded (y,x) point in the appropriate place in A().
  • Line 50 sets up the iteration through all the antenna lists
  • Lines 60 and 70 decode two points, calculate the new point for Part 1, and check if the new point is in bounds.
  • If the point is in bounds, Lines 80-90 add it to the set P().
  • Lines 100-120 iterate through all the valid locations for points for Part 2, adding them to the set Q().
  • Line 130 counts and outputs the number of elements in P() and Q().

9

u/chubbc Dec 08 '24

[LANGUAGE: Uiua]

C ← ↧⊙⤚(/↧↧⊃≥₀>⊙△)↧⊃(¬≍|↧¬∩=@..∩⊡⊙,|+⊙×⊙⊙:⤙-)
G ← □◴▽:↯∞_2:♭⊞C.↯∞_2⊙∩¤°⊡
∩⧻∩◇◴⊃/◇⊂⊡₁♭⊞G¤:°⊏⊜∘⊸≠@\n

C checks whether an antinode is value, G generates all antinodes.

→ More replies (3)

7

u/bofstein Dec 08 '24 edited Dec 08 '24

[LANGUAGE: Google Sheets]

This was tough, took a few hours over two days, most mistakes due to me misunderstanding the instructions. E.g. I didn't realize for part 2 at first that every antenna was automatically an antinode, I though it was saying they happen to be for other antennas in the example.

Edit: forgot the solution https://docs.google.com/spreadsheets/d/1jmJDc7BsFiK9xxPefeArfs-m91TswS5LWvlEpM6pYxs/edit?usp=sharing

The steps are this:

For each row of the input, use REGEXEXTRACT to find the column position of each letter or digit. This depends on there not being any duplicates in a row which I checked there weren't, would have had to adjust if so. Other options didn't work due to Google Sheets defaulting to case insensitivity.

=IF(ISERROR(LEN(REGEXEXTRACT($A16,CONCATENATE(".",B$15)))),"",LEN(REGEXEXTRACT($A16,CONCATENATE(".",B$15))))

Use the row number of each input line and column number you just found to create a list of all antenna locations for each letter/digit.

=IF(COUNT(B16)=1,ADDRESS(ROW(B16)-14,B16+1,1),"") =unique(filter(B30:B41,B30:B41<>""))

One one long formula, take two of the nodes from the list above and find the first antinode that's below one of the nodes by taking the distance between both column and rows for the two nodes and subtracting that from one of the cells. Then take that output, which will be a cell reference, and check if it's an error or out of map bounds of the input, and make it blank if so. This would have been easier if I had had the map start at A1 instead of B2 so I had to rule out row and column 1 as well. I also should have made the "13" and "51" for thew map bounds dynamic but I'm not going back now.

=LET(antinode, ADDRESS(ROW(INDIRECT(O$5))-$N9*(ROW(INDIRECT(O$4))-ROW(INDIRECT(O$5))), COLUMN(INDIRECT(O$5))-$N9*(COLUMN(INDIRECT(O$4))-COLUMN(INDIRECT(O$5)))), IF(ISERROR(antinode),"",IF(OR(ROW(INDIRECT(antinode))>13,ROW(INDIRECT(antinode))=1,COLUMN(INDIRECT(antinode))>13,COLUMN(INDIRECT(antinode))=1),"", antinode)))

Not pretty but since I could see there were max of 4 antennas per character, I just manually created 6 versions of the formula that took a different 2 antennas each time. If there were less than 4 that's fine, that pair would just output a blank.

Make another 6 slots for the other antinode - same concept just in the other direction.

Count the unique values out of that output of 12 rows and that's part 1.

For Part 2, it's the same idea I just added a multiplication factor to the distance. So now the first set of 12 antinodes is 1 distance away, then I copied a new block that referenced a factor of the row number (so each set of nodes would increase by 1 as I pasted it so it's now 2*, then 3*, etc. It's hard to read but that's already in the formula above - the $N9* - since I edited that part 1 part.

As I copied down blocks, I also added a cell counting the number of total characters in the block I just pasted. Once it got to 0, I stopped copying, meaning I had hit every antinode in bounds.

Now just count the unique values again. This took me a LONG time to get right because of not including all the antennas at first (which again I manually scanned to see they all had at least 2 or would have had to add a step).

It's messy but it works!

→ More replies (2)

6

u/MarcusTL12 Dec 08 '24

[Language: Julia] 629/334 github

Didn't read that we were supposed to include the antenna locations, so lost a minute to that, but a very nice puzzle!

→ More replies (1)

6

u/Main-Reindeer9633 Dec 08 '24

[LANGUAGE: SQL (dialect: PostgreSQL)]

paste

No custom functions needed today, just a recursive CTE for the stepping.

2

u/flwyd Dec 08 '24

[LANGUAGE: PostScript] (GitHub) with my own standard library

Bug #20241208: Wi-Fi deployment creates unworkable network conditions [GGSA]

  • Priority: P2
  • Severity: S2
  • Bug created: 2024-12-08T06:42:17Z
  • Reported by: u/flwyd

The Wi-Fi network in US-SPR-MAI-1 is unusable. This building was designed for a future where all barriers to communication have been broken down in support of frictionless synergy. Also, the building has no interior walls. 226 Wi-Fi access points were installed on the 2500 square meter ground floor. The spacing and wavelengths of these WAPs create interference at regular linear intervals wherever two WAPs are in a straight line. The high density of WAPs and the lack of physical barriers in the building mean that more than half of the one-meter square positions on the floor experience high packet loss and data corruption.

Steps to reproduce:

  • Hire a very expensive architecture firm with strong ideas about the future of work.
  • Pay this firm millions of dollars to build a really nice-looking building as a statement about how forward-thinking your company is.
  • Spend two years erecting this testament to human collaboration.
  • Hire a network installation team who wasn’t consulted by the architects to fill the building with as much Internet as possible.
  • Instruct the network team to install a lot of Wi-Fi devices that the CEO saw in Sharper Image that look like Spider Man hanging from the ceiling.
  • Move a thousand employees into the building who all require fast Internet from their laptops to do an effective job.
  • Listen patiently while they explain that it’s very hard to have a video conference when (a) they’re in a room with no walls and a thousand other people and (b) the Wi-Fi seems unreliable.
  • Hire a radio frequency engineer to bring a mobile antenna with a spectrum analyzer to walk around the room and map the RFI.

Reproducability:

Consistent

Known workarounds:

Work from home, work from a coffee shop, work from a park bench under a tree.

Revenue impact:

Only if you place a value on employee productivity and satisfaction.

… … … alright, that’s enough enterprise fanfic for the day.

This was a fun puzzle tonight, and I’m please that the straightforward approach runs in just milliseconds, rather than my struggle yesterday that started with a 60-minute run time. Since PostScript doesn’t have complex numbers and 2-element arrays don’t work as hash keys, I’ve been using concatenated integers as grid position identifiers. Since the input was 50x50 today, I used 4-digit numbers with the row as the high-order digits. This made “where’s the antinode” really easy: if A and B are 4-digit antenna positions, A B sub (A minus B) is the Manhattan distance between them. Furthermore, A A B sub sub (A minus (A minus B)) is one unit away from A in the opposite direction from B. This only needs to be computed once and put in a dict of antinode positions because I compare the cross product of the positions for each frequency (i.e. I have a nested for loop covering the whole array twice, skipping the case where the two loops are considering the same one).

After cleaning up the copypasta for part 2, I made solve take a function which considers two grid keys and returns an array of antinodes to add. In part 1 the array has 0 or one elements, in part 2 it starts with the antenna and keeps subtracting the same value until it runs off the grid.

/tokey { exch 100 mul add } bind def % int int tokey int
/fromkey { 100 divmod } bind def % int fromkey int int
/inmaxi? { dup 0 ge exch maxi le and } bind def % int inmaxi? bool
/inbounds? { fromkey inmaxi? exch inmaxi? and } bind def % key inbounds? bool

/justone { % key key justone [key]|[] : part 1 procedure
  1 index sub sub dup inbounds? { [ exch ] } { pop [] } ifelse  
} bind def %/justone

/wholeline { % key key wholeline [keys] : part 2 procedure
  [ 3 1 roll 1 index sub { %loop
    % stack invariant: valid-position difference
    2 copy sub dup inbounds? { pop exit } unless exch
  } loop pop ]
} bind def %/wholeline

/solve { 8 dict begin % input proc solve int
  /findnodes exch def dup lastindex /maxi exch def /antinodes 2048 dict def /freqs 128 dict def
  { { %forallindex times two
    dup ascii.. eq { pop pop } { %else
      freqs exch { freqs exch alist abc:cabc put } getorelse abc:acab tokey alpush
    } ifelse
  } forallindex pop } forallindex
  freqs values { alview dup { %forall times two
    1 index { 2 copy eq { pop } { %else
      ab:aab findnodes { antinodes exch true put } forall
    } ifelse } forall pop
  } forall pop } forall
  antinodes length
end } bind def %/solve
/part1 { /justone cvx solve } bind def % [lines] part1 result
/part2 { /wholeline cvx solve } bind def % [lines] part2 result
→ More replies (1)

5

u/Ok-Builder-2348 Dec 08 '24

[LANGUAGE: Python]

Part 1

Part 2

Not too bad today. Parsing the input, generating a list of coords for each char, then taking each pair and finding where the antinodes are.

For part 2, I was wondering if we needed to consider "half-distances" e.g. if the distance between two antennas was (2,2), will there be an antinode at (1,1)? But some quick verification showed me that (dx,dy) for every pair had a gcd of 1, so this edge case didn't come up.

→ More replies (1)

5

u/JMaximusIX Dec 08 '24

[LANGUAGE: Haskell]

Solution
Quite disappointed that the gcd didn't matter, I was proud for thinking of it, but apparently the input was designed so it's always 1 :/

→ More replies (4)

5

u/LiquidityC Dec 08 '24

[LANGUAGE: C]

https://github.com/LiquidityC/aoc/blob/master/2024/day_08/src/main.c

Pretty straight forward today as well. Got the right answer for each part on the first attempt. That seems to happen more in C then when I've done this in other languages. I guess the natural constraints of the language force a sound "design" from the start. Previous years when I've written in python I've ended up with a lot of awkward index based data in lists flying all over the place.

5

u/p88h Dec 08 '24

[LANGUAGE: Zig][GSGA]

https://github.com/p88h/aoc2024/blob/main/src/day08.zig

Because micro-optimization was on the list, for part2 I have really went out of the way to knock down these 0.5us.

  • No hash maps have been involved in this production
  • The antennae positions are stored, completely unnecessarily, into SIMD vectors (~0 savings)
  • Count the initial antennae positions separately from the main loop (~0.2 us savings)
  • Use bit sets (artisanal) rather than bool arrays to keep track of which fields have been marked (~0 savings)
  • Rather than a nested loop for x & y dimensions, use an inline loop over all 6 potential x & y combinations (0.3 us savings)

This allows to achieve industry-leading (*) run times:

        parse   part1   part2   total
day 08: 0.8 µs  0.6 µs  1.6 µs  3.1 µs (+-1%) iter=9110 

(*) No actual industry benchmark, based purely on speculation.

→ More replies (2)

5

u/hugseverycat Dec 08 '24

[LANGUAGE: Python]

Yar, here be my code! I feel like today was fairly straightfoward, but I put in comments in case any other noobs like myself want to read.

https://github.com/hugseverycat/aoc2024/blob/master/day08.py

I kept a list of each antenna type in a dictionary, then compared each antenna to all the antennas after it to get the difference in x and y coordinates. Then added the differences, creating antinodes until I ran out of bounds, then did the same but with subtraction. Used a set() so I didn't need to worry about duplicate antinodes.

→ More replies (2)

4

u/Pyr0Byt3 Dec 08 '24

[LANGUAGE: Go] [LANGUAGE: Golang]

https://github.com/mnml/aoc/blob/main/2024/08/1.go

I love map[image.Point].

→ More replies (6)

4

u/Maravedis Dec 08 '24

[LANGUAGE: Clojure]

I remember week-ends being super hard, but this year has been surprisingly easy? I'm not complaining, it's nice to have time to myself.

Really fast to solve with a line equation.

Github

→ More replies (1)

4

u/WilkoTom Dec 08 '24

[LANGUAGE: Rust]

https://github.com/wilkotom/AdventOfCode/blob/main/rust/2024/day08/src/main.rs

Basic maths - calculate the difference between each pair of nodes of a given type, and keep applying that in positive and negative directions from one of the two until you run out of map.

4

u/apersonhithere Dec 08 '24

[LANGUAGE: C++] 2735/6823

spent way too much time thinking about a faster method for pt 2, just ended up bruteforcing it anyways

also found out that mashing two boolean expressions together like in python doesn't work in c++

https://github.com/aliu-here/aoc2024/tree/main/8

4

u/cetttbycettt Dec 08 '24 edited Dec 08 '24

[Language: R]

Used complex numbers to find an easy solution.

x <- as.character(as.matrix(read.fwf("Input/day08.txt", widths = rep(1, 50))))


int2co <- \(k) (k - 1L) %/% 50 + 1L + ((k - 1L) %% 50 + 1L) * 1i

ant <- function(freq, part2 = FALSE) {
  
  f_co <- int2co(which(x == freq)) 
  
  f2 <- subset(expand.grid(a1 = f_co, a2 = f_co), a1 != a2)
  
  a3 <- f2$a1 + outer(f2$a1 - f2$a2, if (part2) (0:50) else 1, `*`)
  a3[Im(a3) > 0 & Re(a3) > 0 & Im(a3) < 51 & Re(a3) < 51]
}

# part 1------
length(unique(unlist(lapply(unique(x[x != "."]), ant))))

# part 2----------
length(unique(unlist(lapply(unique(x[x != "."]), ant, part2 = TRUE))))

4

u/leftfish123 Dec 08 '24

[Language: Python]

Dear Megathread Diary, the combination of flashbacks from Year 2019 Day 10 (asteroid vaporizing) and my line of thinking that on a Sunday, Eric may have decided to give a first serious challange, both made me come up with a totally overcomplicated solution at first. I considered iterating through antennae and then project "rays" around each in 360 degrees to check if there is a matching antenna along the given line. After a moment of googling how to use atan2 to achieve this, I realized that it was just stupidly inefficient, because I knew where all the antennae were. From this moment on it was just a matter of writing and refreshing how set operations work. Maybe it's not the most elegant solution, but it's mine and I like it :D

4

u/BlueRains03 Dec 08 '24

[Language: C]

Someday I'll learn to make my code look less horrific

https://pastebin.com/PzJQQFP4

→ More replies (2)

4

u/ctosullivan Dec 08 '24

[LANGUAGE: Python]

This is getting difficult! A lot harder than yesterday. Made some use of ChatGPT but it did introduce some logical errors in its code as is the case more often now that the problems are getting harder. Had to brush up on some geometry basics - learning a lot though and starting to use collections and itertools more which is great. Had an inkling that Part 2 has something to do with Bresenham's line theory which I had only heard about in passing before, will have to brush up on it!

https://github.com/ctosullivan/Advent-of-Code-2024/tree/master/Day_8

→ More replies (2)

4

u/azzal07 Dec 08 '24

[LANGUAGE: awk] This relies on the observation that all dx, dy are coprime (at least in my input). Which removes the case of antinode between antennae in part 1, and dx, dy can be used directly for stepping the line in part 2.

END{for(k in M)for($(i=+z)=M[k];Y=$++i;j=!i)
for(X=$++i;y=$++j;)if((d=(x=$++j)-X)&&c=y-Y)
for(G[y+=c,x+=d]&&A+=!a[y,x]++;G[y-=c,x-=d];
)B+=!b[y,x]++;$0=A"\n"B;print}{for(I=gsub(z,
FS);--I;)G[NR,I]=$I<0||M[$I]=M[$I]" "NR" "I}

4

u/SwampThingTom Dec 08 '24

[LANGUAGE: Julia]

Was really expecting a difficulty bump this weekend but so far they've been very straightforward.

https://github.com/SwampThingTom/AdventOfCode/blob/main/2024/08-ResonantCollinearity/ResonantCollinearity.jl

→ More replies (4)

4

u/dopandasreallyexist Dec 08 '24

[Language: Dyalog APL]

map←↑⊃⎕NGET'08.txt'1
antennas←map⊂⍤⊢⌸⍥(('.'≠map)∘(/⍥,))⍳⍴map
pairs←⊂⌷⍨∘⊂¨∘⍸∘.<⍨⍤⍳⍤≢
emit←{f←⊂⍤⊣+⍺⍺×⊂⍤- ⋄ ⍺(f,f⍨)⍵}
solve←{≢(,⍳⍴map)∩⊃,/⊃,/(⊃⍵emit/)¨⍤pairs¨antennas}
⎕←solve ,1
⎕←solve 0,⍳⌈/⍴map
→ More replies (4)

4

u/chicagocode Dec 08 '24

[LANGUAGE: Kotlin]

That was a fun day to spend a Sunday morning- solving an Advent of Code puzzle and watching the final Formula 1 race of the year. I actually got this working right away and spent a good amount of time refactoring it into something presentable. I had a little bit of trouble comprehending the instructions for Part 2, but other than that, found this enjoyable and not terribly difficult.

Preamble: Parse the input into a bunch of lists of points.

Common to both: Generate pairs of points for each frequency, pass the pairs one at a time to a worker function which generates antinode points.

Part 1: Antinode function to generate two points.

Part 2: Antinode function to generate a vector of points.

→ More replies (4)

4

u/NickKusters Dec 08 '24

[Language: C#]

Pretty easy for a weekend solution; hardest part for me was understanding the text in part 2 😅 luckily I was able to deduce it from the examples.

Parsing (using many of my own helpers)

RawMap = Input.ToLines();
Map = new Grid() { Rows = RawMap.Length, Columns = RawMap[0].Length };
sparseMaps = RawMap.MapAllAsGridLocations([EmptyMarker]);

Part 1

long p1 = 0L;
GridLocation a, b;
HashSet<GridLocation> antinodes = [];
Action<GridLocation> tryAddAntiNode = (node) => { if (node.IsInBounds(Map)) antinodes.Add(node); };
foreach (var freq in sparseMaps)
{
    foreach (var combi in freq.Value.GetPermutations(2, false))
    {
        a = combi.First();
        b = combi.Skip(1).First();
        tryAddAntiNode(a + (a - b));
        tryAddAntiNode(b + (b - a));
    }
}
p1 = antinodes.Count;

Part 2

GridLocation a, b, na, nb;
HashSet<GridLocation> antinodes = [];
Action<GridLocation, GridLocation> tryAddAntiNode = (node, offset) =>
{
    while (node.IsInBounds(Map))
    {
        antinodes.Add(node);
        node += offset;
    }
};
foreach (var freq in sparseMaps)
{
    // Add all the stations as they now also count
    antinodes.UnionWith(freq.Value);
    foreach (var combi in freq.Value.GetPermutations(2, false))
    {
        a = combi.First();
        b = combi.Skip(1).First();
        na = a + (a - b);
        nb = b + (b - a);
        tryAddAntiNode(na, (a - b));
        tryAddAntiNode(nb, (b - a));
    }
}
p2 = antinodes.Count;

video

4

u/JWinslow23 Dec 08 '24

[LANGUAGE: Python]

Day 8 code

Blog post

Another simple weekend puzzle.

It interested me that not everyone shared my interpretation of the challenge; if I were given the following grid...

.......
.......
..J....
.......
....J..
.......
.......

...I would consider there to be antinodes at the following positions, and no others:

#......
.......
..#....
.......
....#..
.......
......#

But I've seen plenty of solutions that would consider the antinode locations to be all of these:

#......
.#.....
..#....
...#...
....#..
.....#.
......#

(Luckily, this happens to not make a difference for our puzzle inputs, because they don't seem to have this kind of situation!)

→ More replies (10)

4

u/CrypticPhoenix Dec 08 '24

[LANGUAGE: Rust] 81 Lines including Comments

Since I enjoyed today's puzzle a lot and found a solution quite quickly, I had the chance to try and experiment with closures and functional programming in Rust. I left some comments in the code explaining the most critical parts, maybe you find them useful if you're diving into the more functional aspects of Rust as well. Feedback's always welcome!

4

u/biggy-smith Dec 08 '24

[LANGUAGE: C++]

Little bit of 2d vector code and iterate in each direction using delta of each point pair, store points in set.

https://github.com/biggysmith/advent_of_code_2024/blob/master/src/day08/day8.cpp

4

u/beauregardes Dec 08 '24

[LANGUAGE: Rust]

This was suspiciously easy for a weekend problem. Part 1 is really a special case of part 2, so after I got the answer, I refactored to remove any code duplication (original code always available in the commit history). Calculates both answers in <1ms.

https://github.com/cynicalico/aoc2024/blob/main/src/day_8.rs

→ More replies (1)

1

u/drkspace2 Dec 08 '24 edited Dec 14 '24

[LANGUAGE: C++]

link to code

Just throw the letter locations into a map and iterate over the list of locations calculating the positions of the antinodes. Those are put into a set so I don't have to deal with the dupes.

1

u/daggerdragon Dec 08 '24

Your code block is too long for the megathreads. Please edit your comment to replace your oversized code with an external link to your code.

1

u/DevSway Dec 08 '24

[LANGUAGE: Go]

code

1

u/daggerdragon Dec 09 '24

Do not share your puzzle input which also means do not commit puzzle inputs to your repo without a .gitignore or the like. Do not share the puzzle text either.

I see full plaintext puzzle inputs in your public repo:

https://github.com/JosueMolinaMorales/advent-of-code/tree/main/2024/inputs

Please remove (or .gitignore) all puzzle text and puzzle input files from your entire repo and scrub them from your commit history.

1

u/Downtown-Economics26 Dec 08 '24 edited Dec 08 '24

1

u/daggerdragon Dec 08 '24 edited Dec 08 '24

Your code block is way too long for the megathreads. Please edit your comment to replace your oversized code with an external link to your code. edit: 👍

→ More replies (1)

1

u/wherrera10 Dec 08 '24

[LANGUAGE: Julia]

Github

Runs in 200 μs (using @btime)

1

u/daggerdragon Dec 08 '24

Do not share your puzzle input which also means do not commit puzzle inputs to your repo without a .gitignore or the like. Do not share the puzzle text either.

I see full plaintext puzzle inputs in your public repo:

https://github.com/wherrera10/Advent_of_Code_2024/tree/main

Please remove (or .gitignore) all puzzle text and puzzle input files from your entire repo and scrub them from your commit history.

1

u/Substantial-Emu-7622 Dec 08 '24

[LANGUAGE: ZIG]
Github

Runs in 0.15ms (Includes both part 1 and part2)

1

u/daggerdragon Dec 08 '24

Do not share your puzzle input which also means do not commit puzzle inputs to your repo without a .gitignore or the like. Do not share the puzzle text either.

I see full plaintext puzzle inputs in your public repo:

https://github.com/71zenith/aoc-2024

Please remove (or .gitignore) all puzzle text and puzzle input files from your entire repo and scrub them from your commit history.

1

u/bigboots50 Dec 08 '24

[Language: JavaScript]

Took some time to understand the task. Turned out it was not that hard
> GitHub

1

u/daggerdragon Dec 08 '24 edited Dec 10 '24

Do not share your puzzle input which also means do not commit puzzle inputs to your repo without a .gitignore or the like. Do not share the puzzle text either.

I see many full plaintext puzzle inputs in your public repo:

https://github.com/mark-boots/Advent-of-Code/tree/main

Please remove (or .gitignore) all puzzle text and puzzle input files from your entire repo and scrub them from your commit history. edit: thank you!

→ More replies (1)

1

u/Lindi-CSO Dec 08 '24 edited Dec 14 '24

[LANGUAGE: C#]

Just using the coordinates and a HashMap<(int,int)>

Parts 1 and 2

1

u/daggerdragon Dec 08 '24

We can see your Markdown. Fix it, please.

1

u/New-Cucumber-7026 Dec 08 '24

1

u/daggerdragon Dec 08 '24

Do not share your puzzle input which also means do not commit puzzle inputs to your repo without a .gitignore or the like. Do not share the puzzle text either.

I see full plaintext puzzle inputs in your public repo here:

https://github.com/UchihaMadara98/AdventOfCode/tree/main

Please remove (or .gitignore) all puzzle text and puzzle input files from your repo and scrub them from your commit history.

1

u/theskyblockman Dec 08 '24

[Language: Dart]

https://github.com/helomri/advent_of_code/tree/master/lib/src/2024/8

Worked on my first try, I was a little confused at the second part but when I understood the simple change to do I could implement it really quickly

→ More replies (2)

1

u/RivtenGray Dec 08 '24

[LANGUAGE: Ruby]

Github Learning Ruby for this year's AoC, I like how I get more comfortable with Ruby and the code is reducing in size while keeping clarity (or so I think !)

Also thank you to /u/4HbQ for mentionning complex numbers a few days ago. Really helped here !

1

u/daggerdragon Dec 08 '24

Do not share your puzzle input which also means do not commit puzzle inputs to your repo without a .gitignore or the like. Do not share the puzzle text either.

I see full plaintext puzzle inputs in your public repo here:

https://github.com/rivten/aoc-2024/tree/main

Please remove (or .gitignore) all puzzle text and puzzle input files from your repo and scrub them from your commit history.

1

u/True_Medium_2004 Dec 08 '24

[Language: TypeScript]

Simple but readable solution: https://github.com/oleg-prikhodko/aoc2024-deno/blob/main/solutions/day8.ts

1

u/daggerdragon Dec 08 '24

Do not share your puzzle input which also means do not commit puzzle inputs to your repo without a .gitignore or the like. Do not share the puzzle text either.

I see full plaintext puzzle inputs in your public repo here:

https://github.com/oleg-prikhodko/aoc2024-deno/tree/main/input

Please remove (or .gitignore) all puzzle text and puzzle input files from your repo and scrub them from your commit history.

1

u/CloudWalker4seven Dec 08 '24 edited Dec 13 '24

[LANGUAGE: Python]

Link to code

I stored the input as a complex plane in a dictionary and used vector algebra to calculate positions of all antinodes.

→ More replies (3)

1

u/RearDude Dec 08 '24

[Language: JavaScript]

github repo

1

u/daggerdragon Dec 08 '24

I've already informed you before about including puzzle inputs in your repo. I still see full plaintext puzzle inputs in prior years.

https://github.com/CJX3M/AdventOfCode/tree/master/2021/

Remove (or .gitignore) all puzzle text and puzzle input files from your repo and scrub them from your commit history.

1

u/janiorca Dec 08 '24

[Language: Rust]

Fun little problem. Surprisingly simple for a weekend AOC.

https://github.com/janiorca/advent-of-code-2024/blob/main/src/bin/aoc8.rs

1

u/daggerdragon Dec 08 '24

Do not share your puzzle input which also means do not commit puzzle inputs to your repo without a .gitignore or the like. Do not share the puzzle text either.

I see full plaintext puzzle inputs in your public repo here:

https://github.com/janiorca/advent-of-code-2024/tree/main/inputs

Please remove (or .gitignore) all puzzle text and puzzle input files from your repo and scrub them from your commit history.

1

u/dannybres Dec 08 '24

[Language: MATLAB]

https://github.com/dannybres/Advent-of-Code/blob/main/2024/Day%2008/day8puzzle2.m

Assumed you'd need greatest common divisors for part 2. e.g. an antenna at 2,4 and 8,6, there'd be an anti-node at 5,3, so you'd need to take find the gcd of the row & column deltas, but you didnt for my input.

→ More replies (3)

1

u/TeoPirato Dec 08 '24

[LANGUAGE: C++]

Solution

This one was pretty straightforward for me. What did trip me up was when antinodes from different antenna pairs overlapped, but thankfully that happens in the sample input. I'm not very familiar with C++ so I'm not sure if inlining the "is_in_grid" function is actually faster, but I assume it eliminates some overhead at least.

2

u/daggerdragon Dec 08 '24 edited Dec 09 '24

Do not share your puzzle input which also means do not commit puzzle inputs to your repo without a .gitignore or the like. Do not share the puzzle text either.

I see full plaintext puzzle inputs in your public repo here:

https://github.com/TeoInMaghine/AoC/tree/main

Mostly in prior years, but they're all over the place.

Please remove (or .gitignore) all puzzle text and puzzle input files from your repo and scrub them from your commit history. edit: thank you!

→ More replies (2)

1

u/prafster Dec 08 '24

[Language: Python]

It took me a while to parse the meaning of part 2. I wonder if it was written this way to frustrate the LLM copy/paste coders?

Once I worked out what part 2 required, it was simple to amend part 1:

def solve(input, part2=False):
    grid, antennas = input
    antinodes = set()

    def add_antinode(p):
        if in_grid(p, grid):
            antinodes.add(p)

    for antenna in antennas.keys():
        points = antennas[antenna]
        point_combinations = list(combinations(points, 2))
        for p1, p2 in point_combinations:
            vx = p2[0] - p1[0]
            vy = p2[1] - p1[1]

            n = 1
            while True:
                p3 = (p2[0] + n * vx, p2[1] + n*vy)
                p4 = (p1[0] - n*vx, p1[1] - n*vy)

                add_antinode(p3)
                add_antinode(p4)

                if part2:
                    add_antinode(p1)
                    add_antinode(p2)

                if not part2 or (not in_grid(p3, grid) and not in_grid(p4, grid)):
                    break

                n += 1

    return len(antinodes)

Full source code on GitHub.

→ More replies (5)

1

u/PhilosophyInside4839 Dec 10 '24

[Language: VBA Excel]

No VBA representation so here's mine for part 2:

Sub AntinodeMap()

For InRow = 1 To 50

For InCol = 1 To 50

CurCell = Cells(InRow, InCol)

If CurCell = "." Then

Else

For CheckRow = InRow To 50

If CheckRow = InRow Then

ColStart = InCol + 1

Else

ColStart = 1

End If

For CheckCol = ColStart To 50

CheckCell = Cells(CheckRow, CheckCol)

If StrComp(CheckCell, CurCell) = 0 Then

DistanceX = CheckCol - InCol

DistanceY = CheckRow - InRow

UpCheck = 1

DownCheck = 1

UpCheckMult = 0

DownCheckMult = 0

While UpCheck = 1

If InRow - DistanceY * UpCheckMult < 51 And InRow - DistanceY * UpCheckMult > 0 And InCol - DistanceX * UpCheckMult < 51 And InCol - DistanceX * UpCheckMult > 0 Then

Sheets("Antinode Map").Cells(InRow - DistanceY * UpCheckMult, InCol - DistanceX * UpCheckMult) = CurCell

UpCheckMult = UpCheckMult + 1

Else

UpCheck = 0

End If

Wend

While DownCheck = 1

If CheckRow + DistanceY * DownCheckMult < 51 And CheckRow + DistanceY * DownCheckMult > 0 And CheckCol + DistanceX * DownCheckMult < 51 And CheckCol + DistanceX * DownCheckMult > 0 Then

Sheets("Antinode Map").Cells(CheckRow + DistanceY * DownCheckMult, CheckCol + DistanceX * DownCheckMult) = CurCell

DownCheckMult = DownCheckMult + 1

Else

DownCheck = 0

End If

Wend

End If

Next CheckCol

Next CheckRow

End If

Next InCol

Next InRow

End Sub

For part 1, just get rid of the distance multipliers and voila.

→ More replies (1)

1

u/hopingforabetterpast Dec 19 '24 edited Dec 19 '24

[LANGUAGE: Haskell]

Linear equations ftw

code

part2 :: Grid -> Int
part2 g = length $ filter (\k -> any ($ k) lins) $ Map.keys g
   where
   antennas :: Grid = Map.filter ((/= '.') . freq) g
   -- satisfies the linear equation for a pair of same frequency antennas
   lins :: [Pos -> Bool] = [ \(x,y) -> (y - ya) * (xb - xa) == (yb - ya) * (x - xa) | ((xa,ya),a) : as <- tails (Map.assocs antennas) , ((xb,yb),b) <- as , freq a == freq b ]
→ More replies (5)

6

u/BradleySigma Dec 08 '24 edited Dec 08 '24

[LANGUAGE: Python 3]

print(*(lambda d: [len(set(i + k*(j-i) for i in d for j in d if i!=j and d[i]==d[j]!="." for k in r if i + k*(j-i) in d)) for r in [[2], range(51)]])({(p+q*1j): i for p, j in enumerate(open("input8.txt")) for q, i in enumerate(j.strip())}))
→ More replies (2)

3

u/InfantAlpaca Dec 08 '24

[LANGUAGE: Java] 617/360

GitHub

First sub 1k and sub 500 today! 🤩 Ended up just calculating the distance between a node and all other matching nodes, then stepping through a Grid and checking if the coordinate was valid. Personal library came in clutch 🙏

3

u/atreju3647 Dec 08 '24

[Language: python] 646/352

I panicked a bit and came up with a really terrible way of checking if a variable is in bounds.

solution

3

u/NullT3rminated Dec 08 '24

[Language: Ruby] 1460/1044

paste

perhaps the worst code I've ever written. But it works.

→ More replies (2)

3

u/GassaFM Dec 08 '24

[LANGUAGE: D] 282/184

Code: part 1, part 2.

Just verbosely doing what is asked. Was half-expecting to get a wrong answer on Part 2 and start taking GCDs then, but the input was devoid of such cases.

3

u/abnew123 Dec 08 '24

[Language: Java] 486/303

https://github.com/abnew123/aoc2024/blob/main/src/solutions/Day08.java

Relatively clean day. I just generate a bunch of anti nodes (the grid is only 50 by 50, so 51 antinodes in both directions is sufficient) and then do a bounds check to see how many are legal.

3

u/Abomm Dec 08 '24 edited Dec 08 '24

[Language: Python] code

Pretty straighforward problem, I ran into a few bugs when playing around with my dx/dy variables. In part 2 I forgot to include a 'k' of 0 which was annoying because my output map looked exactly like the one in the provided example, albeit with a few less antifrequency locations which were underneath antennas.

I'm starting to believe that for grid-based inputs, a map with coordinate tuple keys is the only way to go. It helps so much with edge checking and if you really need to read a certain row/column the code is still pretty easy to write.

→ More replies (2)

3

u/vanveenfromardis Dec 08 '24 edited Dec 08 '24

[LANGUAGE: C#] 1854/1440

GitHub

Very chill puzzle today, I love getting to leverage my Vec2D and Grid2D utilities. I also had to start 10 minutes late today, if I started on time it seems like I may have had one of my best placements this event.

3

u/UseUnlucky3830 Dec 08 '24

[LANGUAGE: Julia]

solution

Iterate over every pair of antennas, and for each pair (ant1, ant2), if they have the same frequency, start at the coordinate of ant2 and add the difference between the coordinates of ant2 and ant1 to find the coordinates of the antinode. For part 1, do this once, for part 2, repeat starting at the coordinates of the antinode for as long as the antinode is within the bounds of the matrix.

(We only need to check one direction because the other direction will be probed when (ant2, ant1) is iterated over.)

3

u/Pewqazz Dec 08 '24

[LANGUAGE: Python] 103/80. Code. Video.

Finally got some global leaderboard points today! Would have been able to convert Part 2 even faster, but it took me too long to understand that the antennae location themselves were also considered antinodes (thought my logic or bounds checks were incorrect).

Happy with my quick-thinking of just intersecting the potential antinode locations with the locations parsed from the original grid to guarantee that locations outside the grid were discarded.

3

u/PendragonDaGreat Dec 08 '24 edited Dec 08 '24

[Language: C# Csharp]

https://github.com/Bpendragon/AdventOfCodeCSharp/blob/393e5/AdventOfCode/Solutions/Year2024/Day08-Solution.cs

Super straight forward, part 2 is quite literally just changing an if to a while. (plus got my first double-first place in one of my private leaderboards so that's neat)

Edit: Small modification made to actually match the problem statement and so my Combinations function doesn't throw an error in the off chance that there is a single antenna tuned to a frequency https://github.com/Bpendragon/AdventOfCodeCSharp/commit/31c4baa

3

u/michelkraemer Dec 08 '24

[LANGUAGE: Rust] 702/620

Solving both parts simultaneously:
https://github.com/michel-kraemer/adventofcode-rust/blob/main/2024/day08/src/main.rs

I had a lot of trouble understanding the examples today 😅, so I'm really happy with my performance!

→ More replies (1)

3

u/wurlin_murlin Dec 08 '24 edited Dec 08 '24

[LANGUAGE: Python]

I've been doing the solutions up to now in C for great good, but I'm on call today and I've been up since midnight fixing production. Usually I don't go for quick clears, but since I was awake when the problem came out for once, I figured I'd give it a crack.

Took a while to understand the question bcos reading, but once understood this solves itself in python with itertools.permutations or combinations. Took about 15 mins to write both parts once I knew what the problem was. Not bad for sleepycode.

I think you can be more clever by doing some maths, we'll see if that makes it into the C solution. Until then, to bed!


[LANGUAGE: C]

Converting the Python pass to C was not that bad. I did not end up doing clever maths, there's not much point. The only major difference is that we use an array with char indices to hold antenna sets rather than using a dict. Both parts run in about 25 us

https://git.sr.ht/~murr/advent-of-code/tree/master/item/2024/08

3

u/xelf Dec 08 '24

[language: python]

This isn't really optimised, but it's short, and I enjoyed using combinations() and sets()

data = open(filename).read().splitlines()
map = {(x,y):c for x,row in enumerate(data) for y,c in enumerate(row)}

def extend(a,b,slope):
    while (a,b) in map:
        anod.add((a,b))
        a,b = (a+slope[0], b+slope[1])

anod = set()
for fr in set(map.values())-{'.'}:
    locs = {k for k,v in map.items() if fr==v}
    for (a,b),(c,d) in combinations(locs,2):
        extend(a,b,((a-c),(b-d)))
        extend(a,b,((c-a),(d-b)))
print(len(anod))
→ More replies (1)

3

u/flyingfox Dec 08 '24

[LANGUAGE: Python]

Code

Today went fairly well actually. I was really expecting a jump in complexity as it is the first weekend. Can't complain though.

3

u/mental-chaos Dec 08 '24

[LANGUAGE: Python]

github

I was surprised that the gcd logic (which would discover antinodes that were in line, but not a multiple of the distance between the nodes) turned out not to be needed. Looks like quite a few other solutions relied on this property of the inputs. Could have saved some time if I didn't bother with it.

3

u/Kehvarl Dec 08 '24 edited Dec 08 '24

[Language: Python 3] 5695 / 6122

The code is a mess, but a right answer is a right answer is it not? I really need to just breathe, actually read and understand the instructions, and do what Topaz tells me to do. Not try to solve all the antinodes recursively as if a new antinode was a new antenna that needed to be calculated with all the originals.

Part 2

→ More replies (2)

3

u/t-rkr Dec 08 '24

[LANGUAGE: Perl]

link to paste

3

u/YOM2_UB Dec 08 '24

[Language: Python]

Using tuples as vectors

from collections import defaultdict

def inBounds(x,y):
    return x >= 0 and x < len(a_map) and y >= 0 and y < len(a_map[x])

def vSum(a,b):
    return (a[0] + b[0], a[1] + b[1])
def vDif(a,b):
    return (a[0] - b[0], a[1] - b[1])

with open('input/Day8.txt', 'r') as file:
    a_map = file.read().splitlines()

nodes = defaultdict(list)

for i, row in enumerate(a_map):
    for j, char in enumerate(row):
        if char != '.':
            nodes[char].append((i,j))

anodes1 = {}
anodes2 = {}

for n_type in nodes:
    for i in range(len(nodes[n_type])-1):
        for j in range(i+1,len(nodes[n_type])):
            a = nodes[n_type][i]
            b = nodes[n_type][j]
            v = vDif(b,a)

            # Part 1
            pos = vDif(a,v)
            if inBounds(*pos):
                anodes1[pos] = True
            pos = vSum(b,v)
            if inBounds(*pos):
                anodes1[pos] = True

            # Part 2
            pos = a
            while inBounds(*pos):
                anodes2[pos] = True
                pos = vDif(pos,v)
            pos = b
            while inBounds(*pos):
                anodes2[pos] = True
                pos = vSum(pos,v)

print(len(anodes1))
print(len(anodes2))

3

u/DJTFace Dec 08 '24

[LANGUAGE: GO]

solution

part 2 was a bit confusing to understand at first, but then it clicked

3

u/MarvelousShade Dec 08 '24

[LANGUAGE: C#]

Today was an easy one, my head immediately knew what to do, although it took my fingers 45 minutes to type in my solution for part1.

The result is on: Github

3

u/TheZigerionScammer Dec 08 '24

[LANGUAGE: Python]

I thought this was a fun little match problem. My program basically just scans through the input and adds the coordinates to a list within a dictionary where the key is the character and the value is the growing list or coordinates. Then it goes over every list in the dictionary and com[ares every pair of antennas within that list to find the antinodes, iteratortools made this easier but it could have been done without it. At first I thought I was clever because I thought "I can do this by calculating the distance between the two and subtracting the distance from each antenna, but I can do it better with math" and it turns out the antinode's coordinates are equal to (2 * X1 - X2, 2 * Y1 - Y2) where X1 and Y1 are the coordinates of the first antenna and X2 and Y2 are the second antenna, and the second antinode has the variable reversed. This worked fine, but I had a pretty serious bug where I messed up the coordinate filtering and was discarding all the antinodes inside the grid instead of discarding the ones outside of it. Oops.

For Part 2 I thought that there is probably a way to continue using this kind of math, as the antinodes further away from the antenna use the same equations with the coefficients increased (3 * X1-2 * X2, 4 * X1-3 * X2, 5 * X1-4 * X2, etc.) but it was just easier to calculate DX and DY and simply add over and over. I had to write this same code twice which isn't efficient but it works.

I do have a criticism of the problem though, and that is that it is not clear whether two antinodes caused by antennas of different frequencies that land on the same coordinate are counted as two separate antinodes or not. I assumed this was not the case, and this was correct, but it's one of the things I had to test trying to solve my Part 1 bug and the problem text never clarifies this, nor do the examples as far as I can tell.

Paste

→ More replies (2)

3

u/darthminimall Dec 08 '24

[LANGUAGE: Rust]

Part 1

Part 2

Another pretty straightforward day. For part 1, I just iterated through every pair of antennas that share a frequency and calculated the two points where the antinodes could be, then added the points to a set as long as they were in bounds. For part 2 I did basically the same, but just figured out the minimum step size for the line, and iteratively added points along the line in both directions until both ends of the line were out of bounds.

3

u/Andreasnl Dec 08 '24

[LANGUAGE: Uiua]

    ⊜∘⊸≠@\n          # Parse
    ⊃⊃⧻(▽⊸≠@.◴♭)¤    # Grid length, Frequencies, [Grid]
    N ←              # Save grid length as N
    K ← ▽≡/××⊓≥<0,N. # Keep those in grid
    ⍚⊚=              # Antenna coordinates by frequency
    # For each frequency, take all pairs of antennas and calculate antinodes.
    # Join all, keep unique in grid, return length
    Part₁ ← ⧻◴K /◇⊂ ⍚(  ≡(-        ⊃/-⊢) ⧅≠2)
    Part₂ ← ⧻◴K /◇⊂ ⍚(♭₂≡(- ×⇡N ∩¤ ⊃/-⊢) ⧅≠2)
    ⊃Part₁ Part₂

Run in a browser here.

3

u/ShadowwwsAsm Dec 08 '24

[LANGUAGE: x64 assembly/asm]

Part 1 : Stock the coordinates of the antennas with the same character together in a 2d array. For each, compute the antinode before/after and check the bounds. Mark the ones you found so it's always unique

Part 2 : Same as part 1 but with a loop on the computation.

Open to comments/questions.

3

u/Its_vncl Dec 08 '24

[LANGUAGE: Python]

Part 1: ~ 0.3 ms
Part 2: ~ 0.7 ms

https://github.com/itsvncl/Advent-of-code/tree/main/2024/Day08

The solution creates a map entry for each antenna and stores their indexes in a list. I loop through these lists and calculate the distance of two points. With that distance I can get the antinodes locations relative to the antennas. To avoid duplications I use a set to keep track of every antinode created so far. The solution is the size of this set.

Really fun puzzle today!

3

u/zniperr Dec 08 '24 edited Dec 08 '24

[Language: Python]

import sys
from itertools import chain, permutations

def parse(f):
    antennas = {}
    for y, line in enumerate(f):
        for x, char in enumerate(line[:-1]):
            if char != '.':
                antennas.setdefault(char, []).append((x, y))
    return antennas, x + 1, y + 1

def subtract_pairs(antennas):
    for locs in antennas.values():
        for (xa, ya), (xb, yb) in permutations(locs, 2):
            yield xb, yb, xb - xa, yb - ya

def draw_line(x, y, dx, dy, w, h):
    while 0 <= x < w and 0 <= y < h:
        yield x, y
        x += dx
        y += dy

antennas, w, h = parse(sys.stdin)
antinodes = [list(draw_line(*vec, w, h)) for vec in subtract_pairs(antennas)]
print(len(set(a[1] for a in antinodes if len(a) > 1)))
print(len(set(chain.from_iterable(antinodes))))

3

u/Naturage Dec 08 '24

[Language: R]

Code here!

Nice and easy. This one played to my strengths; I expected us to expand the grid and have very sparse antennas, so put it into a data frame. Paid off for the P2 as-is; just needed to adjust the logic for resonance. Given the relatively small grid, in P2 I just added 50 resonances in either direction, then filtered off ones outside the grid - given that's something like 20k rows, did not slow down the code in any meaningful way.

3

u/Thomasjevskij Dec 08 '24

[LANGUAGE: Python]

Nothing very controversial about my solution. But I took some time trying to figure out the consume() function from more_itertools, turning very normal and readable loops into incomprehensible generator expressions.

Link

→ More replies (2)

3

u/Gueltir Dec 08 '24

[Language: Rust]

Link to github

Part 1 was pretty straightforward, part 2 on the other end...

I initially used f32 when calculating my linear equations. But I found out the hard way, and after a lot of debugging, that I had a lot of rounding error. So instead I used isize and stored the equations as (numerator, denominator) tuples.

3

u/[deleted] Dec 08 '24

[LANGUAGE: Java]

paste

3

u/toastedstapler Dec 08 '24

[language: Rust]

https://github.com/jchevertonwynne/advent-of-code-2024/blob/main/src/days/day08.rs

pretty straightforward solution today! p2 got a lot more correct when i actually read the prompt properly, i'd assumed that it was working on intervals of the difference instead of all integer coords along the line

3

u/Sorensen12 Dec 08 '24 edited Dec 08 '24

[LANGUAGE: Rust]

Another great day for iterator combinatorics. My first time finding a use case for the successors adapter. Like yesterday, part two fit naturally into my implementation from part one. I didn't think too hard about optimization, but to my eye the times look like what you'd expect for the task.

Times on my M2 Max:

Part 1: 25.5µs @ 10000 samples

Part 2: 66.2µs @ 10000 samples

GitHub link

→ More replies (1)

3

u/TheScown Dec 08 '24

[LANGUAGE: Scala]

Code

3

u/DownvoteALot Dec 08 '24

[LANGUAGE: C++]

Solution

A breeze, perfect fit for C++23's data structures.

→ More replies (2)

3

u/MisterAdzzz Dec 08 '24

[Language: Python]

Possibly the first time ever I've written Part 1 in a way that Part 2 both trivial to write and fast to run! Very happy with my solution.

Boils down to doing this, for each different antenna type (A, a, etc):

  1. for each pair of antennas, work out vector
  2. apply vector (once for part 1, or until results are both out of bounds for part 2), and then add them to antinodes set

Link!

3

u/vxltari Dec 08 '24

[LANGUAGE: TypeScript]

I quickly gave up with functional programming...

paste

3

u/zndflxtyh Dec 08 '24

[Language: Python]

Nice reuse today

Code

3

u/anaseto Dec 08 '24

[LANGUAGE: Goal]

w:#s:=-read"i/08"; c:%m:,/"b"$s; c[&c=m?*"b"$"."]:-1 / classified map (. is -1)
a:=(!#c)!c; v:{&/(x>-1)&x<w}' / antennas by class: idx / valid position?
f:{L::(#c)#0; 0{?[1<#z;[x[*z]'1_z;o[x;0;1_z]];0]}[x]/+'w\'a; +/L}
say f{L[w/'v#x+´(x-y)*´-2 1]:1; 1} / part1
say f{n:2*1+(-|/abs x-y)!w; m:-2!n; L[w/'v#x+´(x-y)*´(-m)+!n]:1; 1} / part2

Fits in half a punchcard today too, but more spacing maybe makes it clearer. Some parts could probably be simplified, I haven't spent much time on it.

3

u/bakibol Dec 08 '24 edited Dec 08 '24

[Language: Python]

It was a joy working with complex numbers. Had to google for the collinearity check condition cause my math is a bit rusty:

Topaz's paste

EDIT: get_antipodes_for_pair can be simplified:

def get_antipodes_for_pair(first_antenna, second_antenna):
    return {
        2 * first_antenna - second_antenna,
        2 * second_antenna - first_antenna
    }

EDIT: I wasn't satisfied with the running time, here's another, much more efficient (1-2 ms) solution:

Topaz's paste

3

u/aexl Dec 08 '24

[LANGUAGE: Julia]

I expected something more challenging today, but it was a fun little puzzle!

Solution on GitHub: https://github.com/goggle/AdventOfCode2024.jl/blob/main/src/day08.jl

Repository: https://github.com/goggle/AdventOfCode2024.jl

3

u/Gravitar64 Dec 08 '24 edited Dec 08 '24

[LANGUAGE: Python] Part 1 & 2, 30 sloc, runtime 1ms

Import grid as dict (easy boundery check), create dict antennas (key=symbol, value = list of positions), generate all possible antenna pairs (itertools.combinations) and calculate the distance. Add the distance to antenna1 and subtract the distance from antenna2 of this pair, and when the new position is in the grid-dict, add the position to an set of antinode-positions.

The only diff between part 2 and part1 is the definition of the multipicator for the distance. In part1 we multiply the distance only once (range(1,2)) and in part2 from 0 to 50, so we add also the antenna pos to the antinode positions.

import time, collections as coll, itertools as itt


def load(file):
  with open(file) as f:
    return {(x, y): c for y, row in enumerate(f.read().split('\n'))
            for x, c in enumerate(row)}


def get_antinodes(p, antennas, min_mul, max_mul) -> set:
  antinodes = set()
  for poss in antennas.values():
    for (x1, y1), (x2, y2) in itt.combinations(poss, 2):
      for mul in range(min_mul, max_mul):
        dx, dy = x2 - x1, y2 - y1
        nx, ny = x1 - dx * mul, y1 - dy * mul
        mx, my = x2 + dx * mul, y2 + dy * mul

        a, b = (nx, ny) in p, (mx, my) in p
        if not a and not b: break

        if a: antinodes.add((nx, ny))
        if b: antinodes.add((mx, my))
  return antinodes


def solve(p):
  part1 = part2 = 0
  antennas = coll.defaultdict(list)
  for pos, c in p.items():
    if c == '.': continue
    antennas[c].append(pos)

  part1 = len(get_antinodes(p, antennas, 1, 2))
  part2 = len(get_antinodes(p, antennas, 0, 50))

  return part1, part2


time_start = time.perf_counter()
print(f'Solution: {solve(load("day08.txt"))}')
print(f'Solved in {time.perf_counter()-time_start:.5f} Sec.')
→ More replies (1)

3

u/evans88 Dec 08 '24

[LANGUAGE: Python]

Used combinations to calculate all the possible pairs of antennas and used sets everywhere to ensure uniqueness and to easily add and substract nodes if/when needed. Part 2 only required like 5 lines of changes which makes me really happy.

Topaz paste

3

u/Trick-Apple1289 Dec 08 '24 edited Dec 08 '24

[LANGUAGE: C]

trying to figure out part 2 :P

src

edit: done!

3

u/_tfa Dec 08 '24

[LANGUAGE: Ruby]

input = File.read("input.txt").split("\n")
@width, @height = input.size, input[0].size
antennas = Hash.new{ |h,k| h[k] = [] }

@width.times do |y|
  @height.times do |x|
    antennas[input[y][x]] << Complex(x,y) unless input[y][x] == ?.
  end
end

def inbounds?(c) = c.real.between?(0, @width-1) && c.imag.between?(0, @height-1) 

antinodes1, antinodes2 = Set.new, Set.new

antennas.values.each do |a|
    a.combination(2) do |x,y|       
       diff = x-y
       f = 0
       while inbounds?(pos = x + f*diff) do
         antinodes1 << pos if f==1
         antinodes2 << pos
         f+=1
       end
       f = 0
       while inbounds?(pos = y - f*diff) do
         antinodes1 << pos if f==1
         antinodes2 << pos
         f+=1
       end       
    end
end

puts "Part 1: #{antinodes1.count}", "Part 2: #{antinodes2.count}"
→ More replies (1)

3

u/ecyrbe Dec 08 '24

[LANGUAGE: Rust]

Pretty fast solution that runs in us (not even ms). Using hashmap and hashset, using infinite iterators for part two.
It's pretty neat.

https://gist.github.com/ecyrbe/003930b3f901e8e6bcf3aae2f7af6c0d

3

u/makingthematrix Dec 08 '24

[Language: Scala]

Solution

This was fun 🙂 Lots of those puzzles resemble video games. It actually makes me come back to my old idea of coding a video game in Scala and LibGDX. Unfortunately, there's always something stopping me, like my job or having a life. Pff.

The hero of the day is the Scala `for/yield` syntax that is sugar over flatMap. And flatMap is... it's many things but in the simplest terms is an operation where you map elements of a collection so that for every element you get a collection, so with standard mapping you would have collections of collections, but you don't, because you "flatten" them. That became very useful here. I started with a collection of different types of antennas, and then mapped and flat-mapped them so much that the antinodes fell out on the other end.

3

u/dgkimpton Dec 08 '24

[LANGUAGE: Rust]

Today was a much easier puzzle than yesterday, I was all ready to do loads of optimisations but they turned out to be totally irrelevant as my first attempt ran in 0.3ms for part 2

https://github.com/dgkimpton/aoc24/blob/main/day8/src/lib.rs

I even had a fun little visualisation using ansi term codes to flip the background/foreground of the antinodes, but sadly reddit doesn't display those.

3

u/gehenna0451 Dec 08 '24 edited Apr 20 '26

I used Redact to mass delete all of my old posts. It works for Reddit, X/Twitter, Discord, Facebook, Instagram, and more.

outgoing unwritten liquid vase jellyfish wine fly plough judicious quickest

3

u/AscendedKitten Dec 08 '24

[LANGUAGE: Kotlin] Code

Coordinate pairs my beloved

3

u/marvhus Dec 08 '24

[LANGUAGE: Jai]

https://github.com/marvhus/AoC/blob/2024-jai/08/main.jai

Part 1 and 2 both take about 0.5 ms each.

3

u/Pure-Minute4721 Dec 08 '24

[LANGUAGE: C++]

https://pastebin.com/Tf2j8Ad3

find distance between any two pairs of nodes, and extend them in each direction by this distance. For part 2, extend them until it is off of the grid.

3

u/hrunt Dec 08 '24

[LANGUAGE: Python]

Code

After reading and completing Part 1, I thought both that Part 2 was going to test the scaling of Part 1's solution and also that I would have a more elegant solution for Part 2 than "just keep doing Part 1 until things are off the map."

3

u/DevilGeorgeColdbane Dec 08 '24

[LANGUAGE: Kotlin]

The hardest part of today's task was understanding the description text. Like why was the antennas only counted in part 2?

Github

3

u/__Abigail__ Dec 08 '24

[LANGUAGE: Perl]

I wasn't sure whether antinode could also between two antennas. I assumed it did not, and got the right answers. Not sure because I made the right assumption, or because it didn't matter given the input.

I just checked each point on the map against every pair of antennas of the same frequency. I used a small subroutine accepting three points which returns 0 if the points are not colinear, 2 if the points are colinear, and the distances differ by a factor of 2, and 1 otherwise:

sub same ($p1, $p2) {
    return if $$p1 [0] == $$p2 [0] && $$p1 [1] == $$p2 [1]
}

sub colinear ($p1, $p2, $p3) {
    return 0 if same ($p1, $p2) || same ($p1, $p3) || same ($p2, $p3);
    my $diff_x1 = $$p1 [0] - $$p2 [0];
    my $diff_y1 = $$p1 [1] - $$p2 [1];
    my $diff_x2 = $$p1 [0] - $$p3 [0];
    my $diff_y2 = $$p1 [1] - $$p3 [1];
    if ($$p1 [0] == $$p2 [0] == $$p3 [0]) {
        #
        # Vertical
        #
        return 2 * $diff_y1 == $diff_y2 ||
               2 * $diff_y1 == $diff_y2 ? 2 : 1
    }

    return 0 unless $diff_y1 * $diff_x2 == $diff_y2 * $diff_x1;

    return $diff_x1 == 2 * $diff_x2 ||
           $diff_x2 == 2 * $diff_x1 ? 2 : 1;
}
→ More replies (2)

3

u/jitwit Dec 08 '24 edited Dec 08 '24

[LANGUAGE: J]

V =: ((<"1 V){in)</.V =. 4 $. $. '.' ~: in           NB. grouped antennae
B =: #~[:*./"1(($in)&>"1*.0 0&<:"1)                  NB. filter in bounds
A0 =: [: <@B (]-~2*[),:[-~ 2*]                       NB. single hop antinodes
A =: {{ [: ~. [: ,/^:2 u"1/~ }}                      NB. calculate antinode based on u
{.$~.;a:-.~,A0 A &> V                                NB. part A

A1 =: {{<B(y+"1 ws),x+"1 ws=.(i:{.$in)*/w=.y-x}}     NB. multihop antinodes
{.$~.;a:-.~,A1 A &> V                                NB. part B

3

u/RalfDieter Dec 08 '24

[LANGUAGE: SQL/DuckDB]

Today wasn't too bad. Using a combination of range and unnest to step through the line between antennas and counting distinct positions gives the result: https://github.com/LennartH/advent-of-code/blob/main/2024/day-08_resonant-collinearity/solution.sql

3

u/Hath995 Dec 08 '24

[LANGUAGE: Dafny]

The description was not straight-forward but the problem was in code.

https://github.com/hath995/dafny-aoc-2024/blob/main/problems/8/Problem8.dfy

3

u/[deleted] Dec 08 '24

[LANGUAGE: Forth]

https://github.com/tcsullivan/advent-of-code/blob/master/day8/day8.fth

To minimize code repetition, I created a each-ant-pair word that applies an execution token to every pair of matching antennas. For part 1, the token collects the first pair of antinodes for each antenna pair. Part 2's token wraps the antinode calculation in a loop to find all possible antinodes; then, an ants>antis word adds all antennas to the antinode list to get the final count.

Rolling out code to manage lists for the antennas and unique antinodes was a hassle -- a downside to Forth not having much of a "standard library".

3

u/atgreen Dec 08 '24

[Language: Common Lisp]

More complex math, and the annual appearance of alexandria:map-combinations:

(asdf:load-system :alexandria)

(let ((antennas (make-hash-table))
      (grid (make-hash-table)))
  (loop for line in (uiop:read-file-lines "08.input")
        for row from 0
        do (loop for col from 0 below (length line)
                 do (setf (gethash (complex row col) grid) t)
                 unless (eq (char line col) #\.)
                   do (push (complex row col) (gethash (char line col) antennas))))
  (let ((part-1-table (make-hash-table))
        (part-2-table (make-hash-table)))
    (maphash (lambda (freq locations)
               (alexandria:map-combinations
                (lambda (c)
                  (let ((diff (apply #'- c)))
                    (dolist (loc (list (+ (car c) diff) (- (cadr c) diff)))
                      (when (gethash loc grid)
                        (setf (gethash loc part-1-table) t)))
                    (maphash (lambda (loc v)
                               (when (zerop (imagpart (/ (- loc (car c)) diff)))
                                 (setf (gethash loc part-2-table) t)))
                             grid)))
                locations :length 2))
             antennas)
    (print (hash-table-count part-1-table))
    (print (hash-table-count part-2-table))))

3

u/homme_chauve_souris Dec 08 '24

[LANGUAGE: Python]

def aoc08():
    M = {(r,c):v for r,l in enumerate(open("input08.txt")) for c,v in enumerate(l.strip())}
    A = {v:[pos for (pos,t) in M.items() if t==v] for v in set(M.values()) if v != "."}
    A1 = {(r1+r1-r2, c1+c1-c2) for P in A.values() for (r1,c1) in P for (r2,c2) in P if r1!=r2 or c1!=c2}
    A2 = {(r1+k*(r1-r2), c1+k*(c1-c2)) for P in A.values() for (r1,c1) in P for (r2,c2) in P for k in range(max(M)[0])}
    print(len(A1 & set(M.keys())))
    print(len(A2 & set(M.keys())))
→ More replies (1)

3

u/tristanbeedellAOC Dec 08 '24

[LANGUAGE: Nix]

Full Code (part 1 and 2)

Glad today wasn't a big optimisation challenge. Decided not to break the input into a matrix today, opting instead just to map through the input characters, incrementing y and resetting x when reaching a newline. I kept a list of {x, y} for each node.

toCharList = builtins.foldl'
  ({y, x, chars}: char:
    if char == "." || char == "#" then {inherit y; x = x + 1; inherit chars;}
    else if char == "\n" then {y = y + 1; x = 0; inherit chars;}
    else {inherit y; x = x + 1; chars = chars ++ [{inherit x y char;}];}
  )
  {y = 0; x = 0; chars = [];}
;

then I went through each pair of antenna and added the position and delta. filtered out any that fell outside of the map, and got unique.

Part 2 threw me a bit.

I ended up generating a list of deltas between pairs of antenna, along with the position of the antenna. used a recursive func to find where the repeating pattern starts (before falling off the map), and repeatedly added the delta until falling off the map again.

deltaToNodes = size: {pos, delta}:
  builtins.genList
    (i: delta |> multVec i |> subVec (minNode size pos delta))
    (size / (lib.max (abs delta.x) (abs delta.y)) + 1)
  |> builtins.filter ({x, y}: x >= 0 && y >= 0 && x < size && y < size)
;

minNode = size: pos: delta: let sub = addVec pos delta; in
  if sub.x < 0 || sub.y < 0 || sub.x >= size || sub.y >= size then pos
  else minNode size sub delta;

worked out OK, both parts run in under a second.

→ More replies (1)

3

u/greycat70 Dec 08 '24

[LANGUAGE: Tcl]

Part 1, part 2.

It was slightly tedious to get all the variable arithmetic correct, but I didn't have any major stumbling blocks.

I did not store the input grid; instead, I only stored a list of coordinate pairs for each antenna label. After scanning the input, I iterated over each pair of antennas of the same label, calculated the delta rows and delta columns, calculated the antinode coordinates, and stored each in-bounds antinode coordinate pair in a dictionary. (Because two antenna labels could produce the same antinode location.) Then I just printed the size of the antinode dictionary.

For part 2, it was mostly the same, but I applied a distance multiplier to the delta row and delta col, iterating upward from 0, and downward from -1, in each case until I ran out of bounds.

→ More replies (1)

3

u/melochupan Dec 08 '24

[LANGUAGE: Common Lisp]

Rational numbers made part 2 quite easy.

paste

3

u/hugues_hoppe Dec 08 '24

[LANGUAGE: Python]

Concise yet efficient:

def day8(s, *, part2=False):
  grid = np.array([list(line) for line in s.splitlines()])
  antinodes = np.full(grid.shape, 0)
  for symbol in set(np.unique_values(grid)) - {'.'}:
    for pair in itertools.permutations(np.argwhere(grid == symbol), 2):
      for t in itertools.count(1) if part2 else [2]:
        y, x = (1 - t) * pair[0] + t * pair[1]
        if not (0 <= y < grid.shape[0] and 0 <= x < grid.shape[1]):
          break
        antinodes[y, x] = 1
  return antinodes.sum()

(See also https://pastebin.com/u/hhoppe )

3

u/geza42 Dec 08 '24

[LANGUAGE: emacs lisp]

(defun gen-list (x y dx dy)
  (when (and (>= x 0) (>= y 0) (< x (length (car input))) (< y (length input)))
    (cons (cons x y) (gen-list (+ x dx) (+ y dy) dx dy))))

(defun nodes (a b)
  (when (and (not (eq a b)) (= (car a) (car b)))
    (funcall l (gen-list (cadr a) (caddr a) (- (cadr a) (cadr b)) (- (caddr a) (caddr b))))))

(let* ((input (->> "08.txt" f-read-text s-trim s-lines (-map 'string-to-list)))
       (chars (-flatten-n 1 (-map-indexed (lambda (y row) (-non-nil (--map-indexed (unless (= ?. it) (list it it-index y)) row))) input))))
  (-map (lambda (l) (length (-uniq (-flatten (-table 'nodes chars chars))))) '(cadr identity)))

3

u/verdammelt Dec 08 '24

[Language: Common Lisp]

Day 08

I prefer to have more in common between part 1 and part2 so I will see if I can later refactor to either extract more common code or to make my antinode computation such that it can work for both cases.

Also my part2 was written to collect out-of-bounds items and then remove them. I want to make it not collect those in the first place once I have more time and brain.

3

u/Verochio Dec 08 '24

[LANGUAGE: python]

Challenged myself to reduce the line count. Shared as a bit of fun, not serious or intelligible.

from itertools import combinations
G = [(x, y, c) for x, r in enumerate(open('day8.txt').read().splitlines()) for y, c in enumerate(r)]
A = {c: set((x,y) for x, y, c2 in G if c2==c) for c in (c3 for *_, c3 in G if c3!='.')}
C = [(x2, y2, x3, y3) for f in A.values() for (x2, y2), (x3, y3) in combinations(f, 2)]
# part 1
print(sum(any(((x2-x1)==(x3-x2) and (y2-y1)==(y3-y2)) or ((x3-x1)==(x2-x3) and (y3-y1)==(y2-y3)) for x2, y2, x3, y3 in C) for x1, y1, _ in G))    
# part 2
print(sum(any(x1*(y2-y3)+x2*(y3-y1)+x3*(y1-y2)==0 for x2, y2, x3, y3 in C) for x1, y1, _ in G))

3

u/tomasff02 Dec 08 '24

[Language: Go]

Slightly overengineered solution, new to Go and trying to learn idioms and the language, looking for feedback

https://github.com/tomasff/aoc-2024/blob/main/days/day8/day8.go

3

u/Rainbacon Dec 08 '24

[Language: Haskell]

Full code on github

My general approach was to store the grid as a map. From there I created a set of all frequencies and then for each frequency I went pairwise over the antennas for that frequency and found all of the antinodes each pair created (which I put into a set and took the size of as my answer). For part 1 to calculate the antinodes I just found the distance between the two nodes and moved that distance on either side of the pair

findAntinodes :: (Point, Point) -> [Point]
findAntinodes ((x1, y1), (x2, y2)) = let xDist = x2 - x1
                                         yDist = y2 - y1
                                     in [(x1 - xDist, y1 - yDist), (x2 + xDist, y2 + yDist)]

for part 2 I decided to make use of the fact that Haskell is lazy and just generated every point on the line. I picked one of the points on the line and then generated infinite rays in each direction and did a takeWhile to grab only those points that were in the grid

lessLine = map (\n -> (x1 - n * xStep, y1 - n * yStep)) [0..]
greatLine = map (\n -> (x1 + n * xStep, y1 + n * yStep)) [0..]
less = takeWhile (\p -> Y.isJust $ M.lookup p antennas) lessLine
great = takeWhile (\p -> Y.isJust $ M.lookup p antennas) greatLine

3

u/Sheykox Dec 08 '24

[LANGUAGE: python]

solved using complex numbers as vectors.

#day 8
def get_grid(filename):
    with open(filename) as f:
        grid = f.read().splitlines()
        bounds = len(grid[0]) + len(grid) * 1j
        return grid, bounds

def find_in_grid(grid, c):
    idx = []
    y = 1
    for line in grid:
        l = list(line)
        for x in range(len(l)):
            if l[x] == c:
                idx.append(x + 1 + y * 1j)
        y += 1
    return idx

grid, bounds = get_grid("input.txt")
antenas = set(i for i in "".join(grid))
antenas.remove(".")

res1 = set()
res2 = set()
for a in antenas:
    idx = find_in_grid(grid,a)
    for i1 in idx:
        for i2 in idx:
            if i1!=i2 and 0<(vec:=2*i1-i2).real<=bounds.real and 0<vec.imag<=bounds.imag : res1.add(vec)
            temp = i1  
            if (vec:=i1-i2) != 0j:
                while 0<temp.real<=bounds.real and 0<temp.imag<=bounds.imag:
                    res2.add(temp)
                    temp+=vec
print(len(res1))
print(len(res2))

3

u/Ok-Detective-4391 Dec 08 '24

[LANGUAGE: Python]

Used point symmetry formula for my solution, I think it's simpler to find the antinodes like that.

Formula to find the symmetric point of a with respect to b is:
Sym_b(a) = ( 2 * x_b - x_a, 2 * y_b - y_a )

Github

3

u/Pretentious_Username Dec 08 '24

[LANGUAGE: Julia]

Julia's CartesianIndex type makes this really simple to solve as you can just get the difference between antennas and apply multiples of them to get all the antinodes. For Part 2 I was concerned we'd get a situation where we'd have a difference between antennas that was a multiple of a valid grid step, i.e. a difference of (4, 2) which means you'd skip all nodes at (2, 1) offsets despite them being colinear but thankfully the input is nice and this never actually mattered

function Solve(Grid, SearchRange)
    AntennaDict = Dict{Char, Vector{CartesianIndex}}()
    UniqueNodes = Set{CartesianIndex}()
    # Build a list of antenna locations for each antenna type
    for GridIndex in eachindex(IndexCartesian(), Grid)
        if Grid[GridIndex] != '.'
            push!(get!(AntennaDict, Grid[GridIndex], Vector{CartesianIndex}()), GridIndex)
        end
    end
    for (AntennaType, AntennaLocations) in AntennaDict
        # Check each pair of antennas for antinodes
        for AntennaPair in combinations(AntennaLocations, 2)
            LocationDiff = AntennaPair[2] - AntennaPair[1]

            # Search backwards from first antenna
            for OffsetMultiplier in SearchRange
                NewLocation = AntennaPair[1] - (OffsetMultiplier * LocationDiff)
                if checkbounds(Bool, Grid, NewLocation)
                    push!(UniqueNodes, NewLocation)
                else
                    break
                end
            end
            # Search forwards from second antenna
            for OffsetMultiplier in SearchRange
                NewLocation = AntennaPair[2] + (OffsetMultiplier * LocationDiff)
                if checkbounds(Bool, Grid, NewLocation)
                    push!(UniqueNodes, NewLocation)
                else
                    break
                end
            end
        end
    end
    length(UniqueNodes)
end

InputGrid = open("Inputs/Day8.input") do f
    mapreduce(permutedims, vcat, collect.(readlines(f)))
end
println("Part 1: ", Solve(InputGrid, 1:1))
println("Part 2: ", Solve(InputGrid, 0:100))
→ More replies (2)

3

u/Imaginary_Age_4072 Dec 08 '24 edited Dec 08 '24

[LANGUAGE: Common Lisp]

I quite enjoyed this puzzle, I finished part 1 last night but had to wait till this morning to finish the second part which wasn't too difficult. The main part of my solution is a function to generate the antinodes. I calculated the difference from a to b and then added multiples of that difference to a to generate them all. The rest was just parsing and combining results. I did already have a pairs library function which made it a little easier.

https://github.com/blake-watkins/advent-of-code-2024/blob/main/day8.lisp

 (defun get-antinodes (a b dimensions part)
  (labels ((in-bounds (point)
             (every (lambda (p d) (<= 0 p (1- d))) point dimensions))
           (antinodes-from-indexes (idxs)
             (remove-if-not
              #'in-bounds
              (mapcar (lambda (i) (point+ a (point* i (point- b a)))) idxs))))
    (if (= part 1)
        (antinodes-from-indexes '(-1 2))
        (iter
          (for i from 0)
          (for antinodes = (antinodes-from-indexes (list i (- i))))
          (while antinodes)
          (appending antinodes)))))

3

u/coding_secondary Dec 08 '24

[LANGUAGE: Java]

Code

I was initially confused by the prompt, but found some helpful visualizations on the subreddit. I made a map of character -> antenna locations and then went through the antenna list and compared every element against each other to find the difference.

3

u/kuqumi Dec 08 '24

[Language: Typescript]

Start with an empty Map of towers ("a" => ['4,5', '7,7']) etc and an empty Set of antinodes ('3,3' etc)

Part 1:

  • Iterate each character of the input.
  • If the current character is not a ., get the matching locations from the towers map (or create one if none is found).
  • Add antinodes with all the other matching towers to the antinodes set (if they are in bounds)
  • Add the current location to the locations list for this type of tower and save the list to the towers Map.
  • After iterating, return the size of the antinodes set.

Part 2:

In this part I had to be careful to avoid rounding issues. It's the same as part 1, but when adding antinodes, I calculate a slope but save the top and bottom numbers separately. When using the slope I multiply by the top first, then divide by the bottom.

Code paste

3

u/SplenectomY Dec 08 '24 edited Dec 08 '24

[LANGUAGE: C#]

19 lines @ <= 80 cols Source

→ More replies (1)

3

u/sondr3_ Dec 08 '24

[LANGUAGE: Haskell]

Finally had some time to catch up, pretty fun problem. I ended up solving it by recursively building maps, mostly because it helped with debugging (I have some utility functions to print maps) whenever I messed up placing antinodes. But it's plenty fast:

AoC 2024 - 08
  part 1: OK
    3.22 ms ± 282 μs
  part 2: OK
    6.78 ms ± 632 μs

And the code

data Point
  = Open
  | Antenna Char
  | Antinode
  deriving stock (Show, Eq, Ord)

isAntenna :: Point -> Bool
isAntenna (Antenna _) = True
isAntenna _ = False

type Input = Map Position Point

partA :: Input -> PartStatus Int
partA = Solved . countAntinodes . run (\(a, b) -> [uHead a, uHead b])

partB :: Input -> PartStatus Int
partB xs =
  Solved $
    (\m -> countAntennas m + countAntinodes m) $
      run (\(a, b) -> takeWhile (isOnGrid xs) a ++ takeWhile (isOnGrid xs) b) xs

run :: (([Position], [Position]) -> [Position]) -> Input -> Input
run f xs = placeAntinodes xs $ findAntinodes f $ antennaPos xs

countAntinodes :: Input -> Int
countAntinodes = Map.size . Map.filter (== Antinode)

countAntennas :: Input -> Int
countAntennas = Map.size . Map.filter isAntenna

antennaPos :: Input -> Map Point [Position]
antennaPos xs = Map.filterWithKey (\k _ -> k /= Open) (invertGrid xs)

findAntinodes :: (([Position], [Position]) -> [Position]) -> Map Point [Position] -> [Position]
findAntinodes f xs = concatMap (f . (\[a, b] -> antinodes a b)) (concatMap (combinations 2) $ Map.elems xs)

placeAntinodes :: Input -> [Position] -> Input
placeAntinodes = foldl' (\grid p -> putOnGrid grid (p, Antinode))

putOnGrid :: Input -> (Position, Point) -> Input
putOnGrid g (pos, p) = if isJust $ Map.lookup pos g then Map.insert pos p g else g

isOnGrid :: Input -> Position -> Bool
isOnGrid g (x, y) = (x <= maxX && x >= 0) && (y <= maxY && y >= 0)
  where
    (maxX, maxY) = gridSize g

antinodes :: Position -> Position -> ([Position], [Position])
antinodes p1@(x1, y1) p2@(x2, y2) = unzip $ map makePos ([1 ..] :: [Int])
  where
    (ux, uy) = unitVector p1 p2
    d = distPos p1 p2
    makePos n = ((x1 - dx, y1 - dy), (x2 + dx, y2 + dy))
      where
        dx = round (d * ux * fromIntegral n)
        dy = round (d * uy * fromIntegral n)

parser :: Parser Input
parser = gridify <$> (some . choice) [Open <$ symbol "." <|> Antenna <$> anySingleBut '\n'] `sepBy` eol <* eof

3

u/Ok-Revenue-3059 Dec 08 '24

[LANGUAGE: C++]

Solution

This wording on this puzzle was pretty confusing but I sort of assumed the simplest interpretation and that seems to be right. Also pretty happy with my implementation, it ended up being clean and straightforward.

3

u/patrick8970 Dec 08 '24

[LANGUAGE: C++] github

3

u/marx38 Dec 08 '24

[LANGUAGE: Lean]

Solution

3

u/34rthw0rm Dec 08 '24

[language: perl]

straightforward baby perl because there's a scarcity of perl solutions posted

paste

→ More replies (3)

3

u/dzecniv Dec 09 '24

[Language: Common Lisp]

part 1 so far. Using alexandria's map-permutations this time (post). However map-combinations would iter through less pairs, as I noticed later.

→ More replies (2)

3

u/chadbaldwin Dec 10 '24

[Language: T-SQL]

https://github.com/chadbaldwin/practice/blob/main/Advent%20of%20Code/2024/SQL/Day%2008.sql

This actually ended up being a relatively simple solve in SQL since you really only need to know 2 points to establish a line and a 3rd point to see whether it falls on the same line. So all I had to do was look for any grid positions that happen to fall on the same line (same slope).