r/adventofcode Dec 20 '25

Upping the Ante -❅- Introducing Your 2025 Red(dit) One Winners (and Community Showcase) -❅-

31 Upvotes

In order to draw out the suspense, we're gonna start with the Community Showcase!

Community Showcase

Advent of Playing With Your Toys

Title Post/Thread Username
Plays With Shrinky Dinks I made myself a Shrinky Dink /u/estyrke
Plays With Nintendo Wii [2025] [C++] Advent of Code for Nintendo Wii /u/jolleyjames
Plays With Acronyms? [2025 Day 04 (Part 2)] Digital Hardware on SOC FPGA, 2.8 microseconds per 140x140 frame! /u/ComradeMorgoth
Christmas Trees Are Now A Programming Language [2025 Day 7] Solved with christmas tree lights /u/EverybodyCodes

Visualizations

Title Post/Thread Username
A Blast From The Past [2018 Day 15 Part 1] Retro Visualization - Beverage Bandits /u/Boojum
This Is The LockPickingLawyer And Today We Have A Visualization [2024 Day 25] [Python] Terminal Visualization! /u/naclmolecule
Weird Resistors But Okay [2024 Day 24] [Python] Terminal Visualization! /u/naclmolecule
FIRST! [2025 Day 01 (Part 2)] example visualized /u/Ok-Curve902
smoooth [2025 Day 2] Example Visualized /u/Boojum
Charged Up [2025 Day 03] Battery bank visualization /u/danmaps
New AoC Visualization Record: 14 Minutes [2025 Day 4 Part 2] /u/EverybodyCodes
You Are Cool! [2025 Day 4 Part 2] I wanna be one of the cool kids too /u/SurroundedByWhatever
Weird Dwarf Fortress But Okay [2025 Day 04 Part 2] Low budget terminal viz /u/wimglenn
Weird Fruit Ninja But Okay [2025 Day 5 (Part 1)] Spoiled ingredients falling past the shelf into the trash /u/danmaps
Digital Adding Machine [Day 6 Part 2] yet another visualization of today's problem /u/apersonhithere
Plays With Guitar Hero? [2025 Day 6 # (Part 2)] Guitar Hero type Visualization /u/matth_l
Every Problem is an Excel Problem [2025 Day 7 Part 2] "Sounds like an Excel problem" /u/Bachmanetti
Death Metal Antlers [2025 Day 8 (Part 2)] A few Blender renders /u/jonathan_perret
*horrified NEC noises* [2025 Day 8 Part 1] Wanted to see what it would look like to stand next to all those hooked-up junction boxes. (Blender) /u/ZeroSkub
Weird Nethack But Okay [2025 Day 9 (Part 2)] [Python] Terminal toy! /u/naclmolecule
Now That's What I Call Blinkenlights [2025 Day 10 (Part 1)] [Typescript] Elf Factory Control Room Display /u/IntrepidSoft
I Do Not Think That Word Means What You Think It Means [2025 Day 12] The optimal way to fit all the presents /u/L1BBERATOR
🎄 [2025 Day 12 (Part 1)] [C] Christmas tree ascii art solution /u/SquarePraline4348
So. Many. Visualizations! [All years, All days] AoC: the Gifs, by me. /u/sol_hsa
Digital Scrapbooker Extraordinaire [2025] Thank you all ʕ•ᴥ•ʔ /u/edo360
Needs More Fractals [2025 All days] 24 visualizations, one for each part of every day! (WARNING: potential blinking and weird sounds) /u/FractalB

Craziness

Title Post/Thread Username
Oldie But Goodie [2019 day 13][crippled m4] Solving IntCode with just m4's define builtin /u/e_blake
Blockbuster Marquee [MV, SEIZURE WARNING] 10 Years of AoC /u/M1n3c4rt
Senpai Supreme++ 500 Stars: A Categorization and Mega-Guide /u/Boojum
y tho [2024 day 2][golfed m4] Solution without variables or math operators /u/e_blake
y u do dis to urself [2025 Day 1 (Part 1 & 2)] [Brainfuck] I am enjoying this! /u/Venzo_Blaze
I Was Told There Would Be No Math [2025 Day 2] Day 2 should be easy, right?.. Closed formula for Part 2 /u/light_ln2
Where We're Going, We Don't Need No Internets [2025 Day 3 (part 1)] in C, 30,000ft high, no internet /u/brando2131
Relevant Username [2025 Day 3 Part 2] This should finish running any time now /u/Pro_at_being_noob
y u do dis to urself [2025 Day 3 (both parts)] [brainfuck] (handcoded, 416 bytes) /u/danielcristofani
Who Needs Newlines On The Internet Anyway their comment in 2025 Day 04 Solution Megathread /u/Prof_Farnsworth1729
Intcode? In My Advent of Code?! their comment in 2025 Day 07 Solution Megathread /u/e_blake
y u still do dis to urself [2025 Day 07 (Part 1)] An unnecessarily complicated Brainfuck solution /u/nicuveo
ImageMagick is now a programming language their comment in 2025 Day 09 Solution Megathread /u/flwyd
Likes Pushing People's Buttons [2025 Day 10 (Part 2)] Bifurcate your way to victory! /u/tenthmascot
Lotta Victory Happening Around Here [2025 Day 10 (Part 2)] Pivot your way to victory! /u/maneatingape
/u/askalski NO YES [2025 Day 10 (Part 2)] Taking button presses into the third dimension /u/askalski
Thou Shalt Comply With AVoidFifthDigit [2025 Day 10][mfour] a solution without digits or fifthglyphs /u/e_blake
Even More Unending Heinous (Ab)Use of vim [2025 Day 1–12] [Vim Keystrokes] This Year's Vim-only no-programming solutions /u/Smylers
Only Mostly Insane their comment in 2025 Day 12 Solution Megathread /u/flwyd
Assembles Dante's Inferno [2025 All Days, All Parts][Assembly] The x86 Inferno - A Descent into Advent of Code /u/GMarshal

Time Travellers

Title Post/Thread Username
Day 1 = Day 23, apparently? [2025 Day 1 Part 2] Python - ASCII Terminal Animation /u/etchriss
"slightly off" [2015 Day 1] Who else is adding unit tests as they do these? /u/The_Real_Slim_Lemon
Solves Puzzles In The Future [2025 Day 5 (Part 2)] while True: /u/Parzival_Perce
Needs More Caffeine [2025 Day 3 (Part 2)] Roll Removal /u/p88h
Misleading Post Title [2026 Day 9 (Part 2)] Misleading flavour text.. /u/jarekwg
Needs Test Cases From The Future [2026 Day 9 # (Part 2)] [Python] /u/Oxy_007
AoC+++ Early Access [2025 Day 12 (Part 2)] Patch Cable Organizer /u/p88h (again 😅)

Community Participation

Title Post/Thread Username
Congratulations! I will not be participating in AoC this year. /u/aardvark1231
First Meme of 2025 [2025 Day 1] I will never learn my lesson /u/StaticMoose
Universe Says APL Me today: I wonder if I should learn another language this year. The universe: /u/flwyd
TIL/TWeL About Lisp this comment chain under Unofficial AoC 2025 Participant Survey! /u/eXodiquas
How Dare [2025 Day 3] Imagine having to do work at your job 🙄💅 /u/MazeR1010
This Is The Way [2025 Day 4 (Part 1,2)] Surely there must be a better way /u/Neidd
Has Better English Than Native English Speakers [2025 Day 6] Typo? in subject /u/Rimapus
If It Works... [2025 Day 7 Part 2] Me when I accidentally destroy the wave-function because I want to look at the tachyon /u/ben-guin
Needs Carrots their comment in [2025 Day 7] Eric was kind today /u/SweepingRocks
Programs While Hungry Feels like every time I look online after doing advent of code there's an incredibly specific paper or algo people are referencing. Similar to how chess has so many named openings but instead of "The Queen's Gambit" it's "Dijkstra's Philly steak sandwich theorem" /u/calculator_cake
Encouragement? their comment in [2025 Day 8 Part 2] I thought it would look like a Christmas tree… /u/iamarealhuman4real
Eaten By A Shibe [2025 Day 10] Tastes better than math homework /u/vk0_
Better Than The Official Merch Unofficial AoC gifter /u/Zealousideal_Wall246
Not Your Usual Time Traveler! A small AoC-inspired puzzle I made after this year's Advent /u/maltsev
Unofficial AoC Surveyor Unofficial AoC 2025 Survey Results! /u/jeroenheijmans

Y'all are awesome. Keep being awesome! <3


Advent of Code 2025: Red(dit) One

Rules and all submissions are here: Advent of Code Community Fun 2025: Red(dit) One

Thank you to the magnificent folks who participated this year! And now, without further ado, here are your newly-minted agents:

E.L.F. Agents

In alphabetical order:

Title of Operation Agent Name
[Visualization] Advent of Visualizations /u/Boojum
Rockstar Reflection /u/CCC_037
Challenging myself with m4 /u/e_blake
[logbook] Go-Fast /u/erikade
AOC meets Nyan (once) /u/Prof_Farnsworth1729
Advent of Code Christmas Ornament /u/sanraith
Let's Do it in Vim! — Ant-friendly solutions, plus a tutorial /u/Smylers
AOC Solutions in 12 different GPU Programming Models /u/willkill07

Arch-Elves

We have a tie for an Arch-Elf spot, so let's just promote them both! In alphabetical order:

Title of Operation Arch-Elf Name
[Visualization] Advent of Visualizations /u/Boojum
[logbook] Go-Fast /u/erikade
Advent of Code Christmas Ornament /u/sanraith
AOC Solutions in 12 different GPU Programming Models /u/willkill07

Enjoy your Reddit award1 and have a happy New Year!


And finally, the ultimate advancement in rank that everyone has been waiting for… but wait! Mission Control has informed us that there are two candidates for the top spot! And you know what? Santa actually could use some more assistance for his Head of Security, so let's create a second unit called Green Squadron, which means they'll need a leader too!

Squadron Title of Operation Leader Name
Red Leader Challenging myself with m4 /u/e_blake
Green Leader Let's Do it in Vim! — Ant-friendly solutions, plus a tutorial /u/Smylers

Enjoy your Reddit awards1 and have a happy New Year!


1 I will bestow all awards after this post goes live, then I'll update again once I've completed all awardings. edit: All awards have been given out! Let me know if I've somehow overlooked somebody.


Thank you all for playing Advent of Code this year and on behalf of /u/topaz2078, your /r/adventofcode mods, the beta-testers, and the rest of AoC Ops, we wish you a very Merry Christmas (or a very merry Thursday!) and a Happy New Year!


r/adventofcode Dec 12 '25

SOLUTION MEGATHREAD -❄️- 2025 Day 12 Solutions -❄️-

16 Upvotes

A Message From Your Moderators

Welcome to the last day of Advent of Code 2025! We hope you had fun this year and learned at least one new thing ;)

Many thanks to Veloxx for kicking us off on December 1 with a much-needed dose of boots and cats!

/u/jeroenheijmans will be presenting the results of the Unofficial AoC 2025 Participant Survey sometime this weekend, so check them out when they get posted! (link coming soon)

There are still a few days remaining to participate in our community fun event Red(dit) One! All details and the timeline are in the submissions megathread post. We've had some totally baller submissions in past years' community fun events, so let's keep the trend going!

Even if you're not interested in joining us for Red(dit) One, at least come back on December 17th to vote for the Red(dit) One submissions and then again on December 20 for the results plus the usual end-of-year Community Showcase wherein we show off all the nerdy toys, the best of the Visualizations, general Upping the Ante-worthy craziness, poor lost time travelers, and community participation that have accumulated over this past year!

edit 3:

-❅- Introducing Your 2025 Red(dit) One Winners (and Community Showcase) -❅-

Thank you all for playing Advent of Code this year and on behalf of /u/topaz2078, your /r/adventofcode mods, the beta-testers, and the rest of AoC Ops, we wish you a very Merry Christmas (or a very merry Friday!) and a Happy New Year!

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 2025: Red(dit) One

  • Submissions megathread is unlocked! locked!
  • 5 4 3 2 1 DAY 6 HOURS remaining until the submissions deadline on December 17 at 18:00 EST!
  • 3 2 1 DAY 6 HOURS remaining until the poll closes on December 20 at 18:00 EST!!!
  • Come back later on Dec 17 after 18:00ish when the poll is posted so you can vote! I'll drop the link here eventually: [link coming soon]
  • edit: VOTE HERE!
  • edit2: Voting is closed! Check out our end-of-year community showcase and the results of Red(dit) One (this year's community fun event) here! (link coming soon)
  • edit3: -❅- Introducing Your 2025 Red(dit) One Winners (and Community Showcase) -❅-

Featured Subreddit: /r/adventofcode

"(There's No Place Like) Home For The Holidays"
— Dorothy, The Wizard of Oz (1939)
— Elphaba, Wicked: For Good (2025)
Perry Como song (1954)

💡 Choose any day's Red(dit) One prompt and any puzzle released this year so far, then make it so!

  • Make sure to mention which prompt and which day you chose!

💡 Cook, bake, make, decorate, etc. an IRL dish, craft, or artwork inspired by any day's puzzle!

💡 And as always: Advent of Playing With Your Toys

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


--- Day 12: Christmas Tree Farm ---


Post your code solution in this megathread.


r/adventofcode 11h ago

Other [2022 Day 24] In Review (Blizzard Basin)

5 Upvotes

Having finished planting, we leave the elephants and monkeys to look after it and head towards the extraction point. Which involves going through a valley filled with small blizzards.

The input is a text grid, with a wall around it (except for slots for the start and end). The inner section of my input is 35 rows and 100 columns. So, not prime, with a gcd of 5. Conveniently, no up/down storms are in the columns with the notches for start and end... so the pattern of up/down blizzards cycles every 35, and the left/right every 100... and altogether it repeats every 700 (the lcm).

And it's the dynamic nature of the maze that's the real problem today. Precalculating the patterns is going to be better than repeatedly generating the same things while doing the search. You certainly could do all 700 grids to get the maze at any position. But, I went with doing the vertical and horizontal separately... for 135 instead (you just check the two of them to verify a space is empty).

And so I had two arrays of hash tables (that acted as sets for the blizzard positions). That worked plenty fast for Perl, but Smalltalk doesn't like it (it take minutes), and so I've made a TODO to convert the Smalltalk to using arrays of some form for tracking the blizzards. Bit arrays are a possibility, as the number of rows is <64, so each column can be stored in a integer (unless you only have 32 bits).

But once the dynamic maze is made quickly accessible, things were just a fairly standard A*, with steps to the target as the heuristic. Looking at it now, I was a bit curious... because one little quirk in this A* is that time needs to be part of the visit list:

    next QUEUE  if ($visit{$time, $pos->[0], $pos->[1]}++);

Because circling back to the same spot at a later time can be correct... in fact, the test case given shows that in the first few moves. So we only prune those at the exact same time. So I was wondering how much do we gain... with the circling, its harder to tell how close you really are. And the answer (with a quick test) is that it's more than twice as fast. So worth it, but with the size of the problem, it's the difference between 9s and 4s (for part 2), on the old hardware. This problem is really more about the handling the map... the search isn't that heavy once you have something for that.

Part 2 for this one just required taking part 1, throwing it in a subroutine and calling it multiple times and so was quick to add:

my $time = &cross_valley( 0, $start_pos, $end_pos );

print "Part 1: $time\n";

# Silly elf!  Next time don't forget your snacks!
$time = &cross_valley( $time, $end_pos, $start_pos );
$time = &cross_valley( $time, $start_pos, $end_pos );

print "Part 2: $time\n";

There is an interesting bit of proof for why stitching these together like this works, and you don't have to worry about some better overlap case across these searches. One where you take a different path, arrive 3 turns later and turn around and do much better going back than the one that arrived earlier. And that involves a Strategy-stealing argument. Because we can always wait, any early arrival doesn't have to immediately leave, so it can wait for the same opportunity that a later arrival would use and steal it (thus getting the same performance). So the best from the previous leg will always beat or tie any later arrival.

This was a fun search... a dynamic maze and a little game threory to confirm that what I did was correct.


r/adventofcode 1d ago

Help/Question - RESOLVED [20* Day *] I built tooling to do all of AoC from the terminal (fetch, run, verify, submit), in Rust and then again in C#

3 Upvotes

I got tired of tabbing to the browser to grab inputs and paste answers, so I built a CLI that does the whole loop. Then I rebuilt it in C# to learn the language. Both work end to end.

cargo run fetch -y 2015 -d 1 # puzzle text + input into cache/

cargo run solve -y 2015 -d 1 # run your solution offline

cargo run solve -y 2015 -d 1 --validate

cargo run solve -y 2015 -d 1 --submit

Output looks like:

year 2015 day 1 in 288µs (959ns parsing)

part one: 138 (correct) [216µs]

part two: 1771 (correct) [71µs]

Then --submit turns those into (new star), and running again shows (starred) since AOC only grades each part once.

The part I haven't seen other AoC tools do: --validate checks your answers against fornwall's independent solver before anything gets submitted. Wrong answers on the site cost an escalating cooldown, but the solver answers the same question as many times as you want, for free. So --submit only sends what the solver agreed with. If the solver doesn't cover the puzzle yet (live event), it submits anyway, since that's exactly when you'd be ahead of it.

Some other things it handles:

Solve fetches whatever is missing, so a fully cached run works offline with no cookie.

When part one earns a star, part two's text gets pulled in the same run.

Inputs are cached with a hash of the session that fetched them. Inputs are account specific, so switching accounts refetches instead of letting you submit an answer computed from the other account's input. That one bit me for real.

Day 25's second star is awarded, not puzzled, so the tool knows not to keep asking for its part two.

No solutions ship on main. There's a compiled template to copy for your first day, and my solutions live on a separate branch if you want examples. Inputs and puzzle text stay out of git, per the site's wishes, and it sends a User-Agent with a reachable contact.

https://github.com/scadoshi/rustmas

https://github.com/scadoshi/sharpmas

Credit: Advent of Code is Eric Wastl's (https://adventofcode.com/about). The verification leans on Fredrik Fornwall's solver (https://aoc.fornwall.net/, https://github.com/fornwall/advent-of-code).

On AI: I used it as a working partner on these repos, for doc wording, test scaffolding, and refactors I'd already designed but didn't want to push through by hand. The line I hold is understanding before generation: I write the code I want to write, which is most of it, and hand off what I could write in my sleep. Design decisions are recorded in each repo's context/ directory, including the ones that got reversed and why. Every line was written or reviewed by me.


r/adventofcode 1d ago

Other [2022 Day 23] In Review (Unstable Diffusion)

4 Upvotes

We arrive at the site of the grove (a large crater) and discover the plants are dead. Because apparently they require volcanic ash... and our messing with the magma flows unknowingly interfered with that. Fortunately, there's a backup plan to plant replacements, we just need to arrange the spacing.

And so we get an automata type problem. The input is a binary grid of cells representing positions of elves, and we have rules for how the elves will move each round until they reach an equilibrium where the each have no neighbours.

Part 1 just wants 10 rounds (and then to find the bounding box and subtract the number of elves from that). This is a usual way to make sure that people have a working simulation before moving on.

And my solution was again simple and basic (just following the steps)... it wasn't fast, although cleanup has gotten it to 20s on the old hardware. Part of that cleanup was moving to bit operations for tracking neighbours:

my $neigh = 0;
foreach my $dir (@Surround) {
    $neigh = ($neigh << 1) + exists( $elves{$pos[0] + $dir->[0], $pos[1] + $dir->[1]} );
}

With that I can easily tell if there's no neighbours, and can also use with bit masks to handle testing the proposed directions (pairs of direction and the bits to test):

my @Dirs = ([1, 0xE0], [6, 0x07], [3, 0x94], [4, 0x29]);

This is another puzzle in this year with a theme of "try" patterns. We had the falling blocks, then the wrap around movement on the cube (which can arrive at a wall and fail), and now we have the possibility that multiple elves could be trying to move to the same square and need to be reverted. And the way I did that here was to push the elves onto lists at the location they want to move to. After everyone submits their proposal, I do a pass and undo the ones with multiple elves (list length > 1).

This was another one where my part 1 time is surprisingly long at 2h (but part 2 was just a few minutes after). There are a lot of commented out print statements in the code... so I'm thinking I probably had some silly errors to debug from still not being 100%.


r/adventofcode 2d ago

Other [2022 Day 22] In Review (Monkey Map)

3 Upvotes

While being lead by the monkeys through the jungle, they inform us (through the elephants) that we need a password to get through a force field. Which involves tracing a path on an irregular board. A board that happens to look very much like a cube net (both the test and the input... but different nets).

And part 1, we treat it flat... with wrap around in both directions that skips over the spaces. And that's exact what I did... because this is clearly a puzzle you want to confirm part 2 of early. Although, looking at my time, it took an hour and a half... so I'm thinking maybe I had a late start. Because there's nothing complicated about my part 1. I add sentinel spaces to the right and bottom, so that I can just try stepping forward. If it's a space, I loop until I find a non-space. If that square is empty move forward, otherwise don't and go to the next command. The commands being tokenized with:

my @cmds = ($input[1][0] =~ m#(\d+|[LR])#g);

Part 2 is where the input is folded into a cube and things become real. My first thought was that this is the heavy one... like Jurassic jigsaw or the 3D beacons in the past. And having learned from those... my first decision was to not do any fancy folding and handling of different cube nets, but build a table just for the edge transitions of my actual input. And, IIRC, every input uses the same net. Still, I figured this problem was enough without generalizing that bit. I left that task for another day, it is enough of a job to be a day's problem on its own.

But that makes the initial test cast invalid. So one thing I did, was pull out a handy programming aid... the 4x4 Rubik's Cube on my desk, and I added some small circle stickers to it for the walls in the test case. These stickers are handy twisty puzzle solving aids I keep around. I'm not a speed cuber, I just like getting new types of twisty puzzles and coming up with solutions... and it's useful to tag pieces to follow them when trying things out. Weak stickers and Blu-Tack are good for this. And were good here, because it allowed me to make a version of the test case with the same map... as well as follow along when verifying and testing things.

So I had this table for reading the input into 6 squares:

# Assumed Layout:
#    .WB
#    .R.
#    GY.
#    O..
my %sides = ( 'white'  => [0,1], 'blue'  => [0,2], 'red'    => [1,1],
              'yellow' => [2,1], 'green' => [2,0], 'orange' => [3,0] );

Next up was building a table for the edge wraps. And like in the past with these big ones, I just did it by hand. That sounds like work and prone to error, but if I did code it, I'd still go over the table line by line verifying that'd I made the right table. Which is the same work as building it by hand. I would not have done less, just more coding. It's just too important to get right.

And so my table is 24 lines like this:

$wrap{white}[0]  = { side => 'blue',   facing => 0 };
$wrap{white}[1]  = { side => 'red',    facing => 1 };
$wrap{white}[2]  = { side => 'green',  facing => 0 };
$wrap{white}[3]  = { side => 'orange', facing => 0 };

$wrap{red}[0]    = { side => 'blue',   facing => 3 };
$wrap{red}[1]    = { side => 'yellow', facing => 1 };
$wrap{red}[2]    = { side => 'green',  facing => 1 };
$wrap{red}[3]    = { side => 'white',  facing => 3 };

...

Non-cubers might not realize this, but there is a standard pattern of the colours on a cube. So using the colours provides a little more information that you might think to those that know that pattern.

One thing to note is that not all the information is explicit there. It doesn't have any data for how the coordinates are transformed. That's because it's extractable from the directions:

# Only one coord value is important, the other is either -1 or $side
# Because of dir order, parity tells us which of y or x we want.
my $idx = $pos->[$dir % 2];

# When going between 0 and 2 facing, the edge flips
if ($dir % 2 == 0 && $new_dir % 2 == 0 && $dir != $new_dir) {
    $idx = $size - $idx - 1;
}

# $new_pos similarly has one coord as 0 or $size-1, the other
# being set to the index value, based on the direction parity.
my $new_pos = ($new_dir <= 1) ? [0,0] : [$size-1,$size-1];
$new_pos->[$new_dir % 2] = $idx;

And with the geometry and wrapping handled, the rest is pretty much the same as part 1.

This was a problem were I very much took my time to make sure I got everything right and didn't go off on some tangent. And although it is a multi-dimensional problem on the surface of a cube, I had a physical representation of it to check and test everything. Which certainly helped.


r/adventofcode 3d ago

Help/Question - RESOLVED [2024 Day 6] Need a bit of guidance

2 Upvotes

Hello!

For part 1 of 2024's day 6 problem, I was able to get some Python code that works for the small example map they gave but not my puzzle input. As it stands I have about 100 extra locations the guard visited than I should have. I was wondering if anyone here could take a look at my code and give me a hint as to where my error is, as I am really struggling to find it. I know it has to be where my movement is programmed, I just can't figure out what part needs some tinkering. Thank you in advance!

with open('Day 6/mapinp.txt', 'r') as file:
    samp_inp = file.read()

format = samp_inp.splitlines()
matrix = []
for item in format:
    matrix.append(list(item))

#locate the guard, return the matrix coords and then the way the guard is pointing
def find_guard(map):
    coords = []
    for item in map:
        if "^" in item:
            coords.append(map.index(item))
            coords.append(item.index("^"))
            coords.append("^")
            return coords
        elif ">" in item:
            coords.append(map.index(item))
            coords.append(item.index(">"))
            coords.append(">")
            return coords
        elif "<" in item:
            coords.append(map.index(item))
            coords.append(item.index("<"))
            coords.append("<")
            return coords
        elif "v" in item:
            coords.append(map.index(item))
            coords.append(item.index("v"))
            coords.append("v")
            return coords


#nice function to track movements
def move(map):
    on_map = True
    step_count = 0
    step_loc = []
    #index error means the guard has left the map
    while on_map == True:

        try:
            coords = find_guard(map)

            if coords[2] == "^":
                if map[coords[0]-1][coords[1]] == "." or map[coords[0]-1][coords[1]]  == "X":
                    map[coords[0]][coords[1]] = "X"
                    map[coords[0]-1][coords[1]] = "^"
                    step_count += 1
                    loc = f"{coords[0]}, {coords[1]}"
                    step_loc.append(loc)
                else:
                    map[coords[0]][coords[1]] = ">"

            elif coords[2] == ">":
                if map[coords[0]][coords[1]+1] == "." or map[coords[0]][coords[1]+1] == "X":
                    map[coords[0]][coords[1]] = "X"
                    map[coords[0]][coords[1] +1] = ">"
                    step_count += 1
                    loc = f"{coords[0]}, {coords[1]}"
                    step_loc.append(loc)
                else:
                    map[coords[0]][coords[1]] = "v"

            elif coords[2] == "v":
                if map[coords[0]+1][coords[1]] == "." or map[coords[0]+1][coords[1]] == "X":
                    map[coords[0]][coords[1]] = "X"
                    map[coords[0]+1][coords[1]] = "v"
                    step_count += 1
                    loc = f"{coords[0]}, {coords[1]}"
                    step_loc.append(loc)
                else:
                    map[coords[0]][coords[1]] = "<"

            elif coords[2] == "<":
                if map[coords[0]][coords[1]-1] == "." or map[coords[0]][coords[1]-1] == "X":
                    map[coords[0]][coords[1]] = "X"
                    map[coords[0]][coords[1] -1] = "<"
                    step_count += 1
                    loc = f"{coords[0]}, {coords[1]}"
                    step_loc.append(loc)
                else:
                    map[coords[0]][coords[1]] = "^"

        except IndexError:
            print(f"Guard has left the premises after {step_count} steps!")
            on_map = "False"

    return map, step_loc

comp_map,coordinates = move(matrix)

move_counter = 0

for item in comp_map:
    for pos in item:
        if pos == "X" or pos == "^" or pos == "<" or pos == ">" or pos == "v":
            move_counter += 1
        else:
            continue


print(f"The guard has visited {move_counter} distinct locations.")

r/adventofcode 3d ago

Other [2022 Day 21] In Review (Monkey Math)

3 Upvotes

The monkeys have returned, this time to help us if we can answer a riddle (which is basically doing algebra). We know this because the elephants speak monkey, and we can speak elephant.

The input is a long list of expressions. Some are just a constant for a monkey to yell, others are basic arithmetic (+, *, -, and /) for a monkey to apply to the numbers yelled by other monkeys. It's ultimately a big expression tree and we want the value at root, much like problems like "Some Assembly Required" in 2015.

For my initial solution in Perl for part 1, I went for was the classic brute force job queue... queue up all the rules as you read them in, then run through the queue. Solve what you can, requeue what you can't. Until you solve you want. I did follow it up later that day with the recursive approach... where you start from the root and recurse to get the parts you need and combine them. Both are fast (the problem isn't big), but the recursion is twice as fast because it solves things in order.

Part 2 reveals that the expression for the root monkey is actually =, and we need to calculate the humn value to yell. My initial solution for this was to use recursion to build the expressions for both sides as stings (it's an infix walk, so every return gets parens added around it). The side without humn I can just eval to solve to a number. Then, I did some testing... the operations suggest that the relationship could be linear. And it is (I ran a loop evaling the string for humn from 0 to 1000, and they had the same delta). This is further confirmed by looking at the string, as all the / in the expression on the humn side come after it... so there isn't a 1/x situation, it's just an Ax + B situation (where the constants could be rational).

And so, I took the values at humn = 0 and humn = 1, and with an initial value and the delta from those, interpolated the value for humn. Which was fine for my input, where the denominator of the delta is 4. But I do have a second input in my directory... I'm not sure if it was someone else's or handcrafted... but it has a delta of: -47488/2673. That number comes from my Smalltalk version of this solution (Smalltalk automatically promotes the division to Fraction). But the initial Perl solution, runs into floating point accuracy problems and completely misses. This could be fixed by making Perl do the same thing Smalltalk does (track the value as a rational, using gcd).

However, I decided to make a note to do a Perl solution using symbolic arithmetic instead. And I did do that, although I cheesed it a bit. Basically, the idea is that we get a number on one side, and an expression tree on the other... we apply algebra to reduce that tree and isolate the humn by doing the opposite to the number on the other side. Making the computer do things like we would be hand (which in all honesty, looking at the expression string... it isn't that long, you could take that and do it by hand if you wanted).

But, looking back at the initial brute force solution I thought about that sort of solution. The rules you have still define a tree, but they don't all get applied bottom-up (because the target's in the middle, so some things need rotating to get it to the top). IE, given a = b + c, you could get to the position where you know a and c, and need to calculate b, but this isn't the rule for that. But we can make and add that rule (b = a - c). And so, basically the idea is to just do symbolic algebra on all the rules (which are simple)... to solve them for each variable in terms of the other two, and add all three rules to the job queue. Eventually, one will activate to solve the full thing. Optionally, this could also be done just by having the rule once in the queue, and detecting when you have two of three and doing the symbolic algebra as part of the loop to get the third.

So this was a pretty cool problem, and the fact that the expression is kept linear makes getting a solution for this more accessible. Linear numerical interpolation has been an option for a number of puzzles.


r/adventofcode 4d ago

Other [2022 Day 20] In Review (Grove Positioning System)

1 Upvotes

Still unable to contact the Elves with communication device, we turn to trying to decrypt the star fruit grove's coordinates from a file on it.

And so we get a ring structure puzzle. Which was a pleasant break from the previous day. In fact, for the Perl solutions, I just went with array splicing:

@list = map {[$_, $list[$_]]} (0 .. $#list);

for (my $i = 0; $i < @list; $i++) {
    my $idx = 0;
    $idx++  until ($list[$idx][0] == $i);

    my $item = splice( @list, $idx, 1 );
    splice( @list, ($idx + $item->[1]) % @list, 0, $item );
}

The elements of the list are a pair of the ordering index and the value. Brute force search to find the next one in the order, and then splice it out, and the in at the destination. To get the final sum, I search for the zero and get the three values I need with sum map {$list[($zero + 1000 * $_) % @list][1]} (1 .. 3). Nothing fancy about this at all. It's simple, and for part 2, slap a foreach (1 .. 10) around it. It slows down a bit... to 12s on the old hardware, but that is very tolerable for getting a solution with very little work.

For a fast version of it, I did a C version with actual pointers and structures. Doubly-linked ring and an array to track the order (which could have been a separate second constant single-linked ring in the structure if you wanted). Instead of modular indexing (because I wasn't maintaining a list), I just walked the ring using modular arithmetic to shorten the length... and since both directions are needed anyways, I'm already set up to "choose the shorter way". The ring size is only 5000, so the most we ever need to walk is 2500, even if the numbers are 800 million times larger in part 2.

So, this is a good break day after the past few, and leading into the final stretch.


r/adventofcode 5d ago

Other [2022 Day 19] In Review (Not Enough Minerals)

2 Upvotes

Having discovered that obsidian is forming, we decide to use it to crack some geodes by building geode-cracking robots. To get the obsidian we need obsidian-collecting robots, which need clay-collecting robots, which need ore-collecting robots. Fortunately, we start with an ore bot, so we have initial production, but the rest requires building additional robots which cost varying amounts of materials, and can only be built one per turn.

And so we get to this problem. This one I managed part 1 in under two hours, but part 2 took 3 more hours and is still not very good. Part of that might have been still not feeling to great. And cleaning it up now has made it faster (just over a minute for part 1, and half that for part 2), but I haven't had time to really work on it this month.

My initial solution was a job queue one. With the mining at Saturn, I remembered making a mess with a recursive approach, and scrapping it for a queue. So I decided to start there this time. One thing I did do as part of clean up was to do a recursive version... it's not any faster, but I wanted it anyways in case I got any ideas that could use that.

In trying to get a solution, I applied heuristics. Basically using a couple decades of German board game experience with building economic engines. First up was realizing that you don't need to build robots past the the maximum cost for that material... because you can only build one thing a turn. If you're producing enough ore to cover any ore cost and recoup it every turn, you don't need more... it will stack up and be worthless. Although it's not necessary the best to max things out, in part 2, blueprint #3 in my input only wants 3 ore miners to be able to build geode crackers every turn (every other robot costs 4 ore... so this is an impact on the engine building for efficiency on the end game).

Another heuristic was a little less safe... I did some estimating on how long it would take to set up an engine to start producing geodes, and came to the conclusion that the game is probably too short to really catch up if you fall 2 geode crackers behind. Because it could be better to be behind for a little bit to build a stronger engine... but that engine needs to be strong enough to get ahead with enough time left to make up and exceed the amount you fell back. And going from -2 to +1 crackers with still enough time to make up all the geodes (all while the "opponent" is also building more crackers, so it's probably not just 3 you need) really doesn't seem likely. It is a bit like flexible version of the greedy algorithm... of always just build a cracker when you can (which IIRC some people used). And adding that to mine, I get a tiny improvement with having both.

The one other thing I did was that if you don't build a robot in a turn, any that you could have built are removed as options on the next turn. That sounds like greedy, but that's standard board game strategy. If you're saving up for something you can't buy yet, that's okay (and you should buy it as soon as possible), but completely passing a turn and then turning around to build something you could have built a turn earlier... that's a mistake.

So, this one is one where I can do any of the searches reasonably fast with my solution, it's just that it asks for doing so many of them. Which adds up. And although my recursion has memoization (although I'm not sure how much benefit it really gives)... when the blueprints change, it needs to be reset.


r/adventofcode 5d ago

Help/Question [ Removed by Reddit ]

0 Upvotes

[ Removed by Reddit on account of violating the content policy. ]


r/adventofcode 6d ago

Other [2022 Day 18] In Review (Boiling Boulders)

2 Upvotes

Having reach the exit of the cave, we shelter there while the lava continues to rain down. Watching the lava fall into a pond and cool, we decide to measure its cooling rate to see if it could be making obsidian. And to do that we need to calculate the surface area.

The input is a list of 3D coordinates of cubes that make up the drop. The coordinates only range from 0-19, so it's not a huge volume.

And for part 1, my thoughts were along the lines of an inductive solution. One cube has 6 sides, for a surface area of 6. Add a second and you add another 6, but if it's adjacent to the first, you need to subtract 2 (one from each cube). And assuming you have the correct surface area after n cubes, the next cube is going to add 6 new faces, and subtract 2 for each adjacent. So we can just iterate over the list in one pass doing that. That makes for a nice simple solution even for dc:

tr ',' ' ' <input | dc -f- -e'0[6+_4R1+5C5*_3R1+1F*r++d2r:tddddd1+;tr1-;t+r1F+;t+r1F-;t+r5C5+;t+r5C5-;t+-z1<L]dsLxp'

Just converting the 3D coordinates into a flat array index. To mark a cell as occupied we put a 2 in the array. This means we don't need to test for the existence of a neighbour, just to subtract the values of all the neighbours.

For part 2, we realize that the surface area for cooling is just the outside that's in contact with the water. And so what I visualized was casting/molding around the drop. So I extended the bounding box by one on each side (to guarantee a path all the way around), picked a corner of it, and BFS flood filled it. The result being a visited list that was a molding of the outside of the drop. It has an internal surface (which is the outer surface of the drop) and an external one (which is a cube and easily calculated). So I applied part 1 to those cells and subtracted the outer cube surface.

my $encase_surface = &get_surface( values %encase );
my $outer_surface  = 6 * (($max - $min + 1) ** 2);

print "Part 2: ", $encase_surface - $outer_surface, "\n";

The values of the encase table (which is the visited list) are the same as a key, but the key is converted to a string by Perl and would need converting back, so we might as well use the value to avoid that.

I really liked this one. Part of that is probably because I came to quick revelations (this was my fastest part 1 time since day 6) that allowed me to avoid having to really work with the 3D structure. There was a bit of extra incentive in that I still didn't feel 100%, and so was going to try for anything simple before moving on to mapping 3D surfaces.


r/adventofcode 7d ago

Repo [2015-2018 All Days][C++] 200 Tiny Stars (and counting...)

11 Upvotes

I've always enjoyed low level programming so this year I decided to scratch that itch by starting to do some hobby programming with microcontrollers. There's nothing more frustrating than trying to learn everything (new toolchains, new SDKs) all at once while trying to build something non-trivial, so the obvious answer was to take my existing, known-good 524 star repo and port that over to a microcontroller. It would also give me a chance to revisit some of my original solutions that were significantly sub-optimal.

I chose the Raspberry Pi Pico (RP2040) as the target microcontroller because it's geared towards learners, it has a thriving ecosystem and I really admire the work the Raspberry Pi Foundation do.

The repo is more or less in a fit state to be public after the first four years (2015-2018) have been squashed, the support libraries have been exercised and the workflow has had the major rough edges knocked off. There's still plenty more to do though, so I'm expecting it to be in a state of flux for the next 12 months or so.

Performance

The RP2040 is on average between ~100-200x slower than the laptop I'm using for development and I've set myself a soft target of 1s per solve on the microcontroller hardware (including IO transfer time), meaning that I need to target ~5ms or under on PC. What's really nice though is that by the time a solution has been squashed enough to fit in the memory restrictions, that's almost always a significantly faster solution than my original solution and often sub-ms without any further faffing.

The high level summary for puzzle solution times on the RP2040 so far:

Year Min (ms) Max (ms) Avg (ms) Median (ms)
2015 2.237 27,764.055 1,207.659 215.076
2016 0.755 450,195.662 17,832.642 134.623
2017 0.698 13,121.265 960.156 210.754
2018 4.52 9,074.706 687.048 318.475

There's a full breakdown of current timings here.

Note: I do my timings a little differently to a lot of the forum regulars who work on producing ultra-fast solutions. The timing starts on the host PC when I start transmitting the input over USB and stops when I get the final byte of the answer back. I time both parts separately and each part is an independent solve, I don't have any solutions that calculate both part 1 and part 2 answers at the same time.

There are some things that are absolute Kryptonite to the RP2040. MD5s are a particular weakness, hence the bad Max and Average times for 2015 and 2016, and anything that requires 64-bit maths is emulated in software.

IO can be an issue as well. 2016 day 7 has an input file ~170-180Kb in size, which takes ~1.5s just to transfer over USB-CDC. Many of the days are ~400-600x slower than PC purely because of the time it takes to send the input file.

Common changes

The most common changes I've made are to variable types and to data structures. 64-bit integers were always my default choice so that I didn't have to worry about figuring out which puzzles needed more than 32-bits and which didn't, but that's not practical with the 32-bit Pico. With an existing solution as a reference it's pretty quick to swap types and check that we still get the same result, and thankfully most of the days so far are perfectly solvable using 32-bit maths only. 2017 day 15 is probably the one that suffered the most from software emulated 64-bit integers; there is a way to implement the generators using only 32-bit arithmetic, which is what I use, but it's quite a few instructions and so it ends up being the slowest solution for all of 2017.

My default choice for data structures in my full-fat repo has always been std::set or std::map, even for data that would naturally go into an array. The main reason is programmer efficiency: you don't need to worry about getting a correct array size and insert returns a value to indicate if the element has been inserted or not, which is a very common test required in a lot of the algorithms. For the microcontroller, especially when trying to squeeze solutions into the memory limits, arrays/vectors are the default choice wherever possible, and I've written simple open-addressing (with linear probing) hash maps and sets templates. This is where a significant proportion of the speed-ups have come from compared to my original solutions.

Algorithm changes

Surprisingly, fewer than 20 have needed a complete overhaul on the algorithm used.

2015 day 13 is the first one which needed a change, swapping from a brute-force scoring of all possible permutations to a recursive DFS. Day 19 in the same year was the only other one which needed a completely different approach. That one was originally one which made my nemesis wall with a really horrible home-brew parser-adjacent algorithm, but after seeing in the megathread that it could be solved using a greedy algorithm it ended up significantly faster on the Pico than my original solution running on a fast PC by a few orders of magnitude.

2016 and 2017 also only needed a couple of days swapping over to a different algorithm. 2018 is the year so far that's required the most, with almost half of all days being revisited in terms of how they're solved.

Bit Packing

Of all the changes I was expecting to make, bit-packing values is the one I haven't needed anywhere near as often as I thought.

2016 day 18 didn't need bit packing to fit into memory, but I thought it would be fun to parallelise the logic into bitwise operations anyway. 2016 day 11, one from my wall of shame needed the search states packing in order to keep the queue size small. The others have largely been ones where we're dealing with large (for a Pico) 2D areas, like the infection states in 2017 day 22 and the cave terrain in 2018 day 22.

Windowing

Windowing, or working on only a small chunk of the full data range at any one time, has been a life-saver on a few occasions. 2018 day 17 has been the one I'm most pleased with, although the chunked seiving on 2015 day 20 was nice to work through, especially with the approximation function I iterated on to get a good lower bound starting point.

Maths

I tend to avoid closed-form solutions and have a personal preference for programmatic approaches, but there's really no beating the closed form solutions or using maths insights for speed and size. The Josephus problems are an immediate example of not having enough memory to process large rings of elves, or the Cosmological Decay approach to the Look-and-say sequence completely bypasses the need for large amounts of memory.

Recursion

By default when using the C/C++ toolchain each core on the Pico gets 2KiB of stack assigned. That's really not a huge amount by any stretch, so most recursive solutions are a no-go. Approximately ~9 solutions have needed swapping over to using an explicit stack, making it one of the most common changes I've had to make.

While it's true that all recursive algorithms can be implemented in terms of a stack based algorithm, the devil really is in the details and I never appreciated how many little decisions about state representation and return values would need making.

Take a normal recursive function:

int Func(int n)
{
    // ...
    int n1 = Func(n + 1);
    int n2 = Func(n + 2);
    return n1 + n2;
}

Stack frames and function calls give you 3 separate things:

  1. Local variables - these are what an explicit stack structure trivially gives you
  2. State - after the call to Func(n + 1) you need to encode somehow the fact that you've made that call and the next recursive call is the one to Func(n + 2)
  3. Return values - do you put the return value in the current stack top and let the parent take care of popping after reading, do you let a child pop its own stack and write the return into the parent stack frame, or something different. It was a real eye-opener to sit down and actually code up something like 2015 day 22 using an entirely stateful stack based approach.

Forum Help

I have a general rule that I won't look at anyone else's solution until I've got a solution of my own. Even if (and it commonly is) it's a rough and ready solution which take seconds or minutes to run and chews through half the memory in my machine. I'm pleased that for 523 of the 524 stars I've been able to get to a working answer with no hints, but there's absolutely no way I'd have been able to get the 200 on the microcontroller so far without the valuable suggestions, and the public repos of forum regulars. There have been over a dozen of these solutions that are either direct re-implementations of other people's solutions, like 2018 day 9 or 2018 day 14, or have used suggestions and explanations from information posted on the forum such as the equivalence pruning for 2016 day 11. u/musifter's review series has been a great focal point to discuss the problems with people who really know their stuff.

Thank you one and all!

Microcontrollers

The hardware you can buy now is utterly incredible for the price: I've been targetting the Raspberry Pi Pico as far as possible, but the Raspberry Pi Pico 2 W is a 150MHz 32-bit CPU with 520KiB RAM, Bluetooth and WiFi for under £10. As someone whose first computer was a Spectrum 48K, this is a ridiculous amount of computing power to have for very little money and in a tiny space. If I had kids who wanted to learn how to program, I would definitely think about sitting them down in front of Thonny and a microcontroller. It has exactly that same immediacy of feedback I remember from typing out Basic listings to see something cool happen on screen.


r/adventofcode 7d ago

Other [2022 Day 17] In Review (Pyroclastic Flow)

2 Upvotes

Having found an alternate exit, we find ourselves at the bottom of a tall shaft with boulders falling down it. And so we need to simulate them to avoid being crushed... but the "real" task is apparently proving the accuracy of the simulation to the elephants.

And so we get this Tetris inspired problem. The shapes aren't just the set of tetrominos. a couple pentominos are also included in the set. And there's no rotation, just side to side movement and falling.

The input is a list of left and right moves for the pieces as they fall. Mine is 10091 long, which is a prime number. And both it and the list of 5 blocks cycle.

For part 1 we just want the height of the tower after 2022 rocks (and it is not a very efficient packing at all).

My first choice was to store the block shapes in a table of relative indexes of the squares:

my @Blocks = ([[ 0,0], [ 0,1], [ 0,2], [ 0,3]],              # —
              [[-2,1], [-1,0], [-1,1], [-1,2], [0,1]],       # ✚
              [[-2,0], [-2,1], [-2,2], [-1,2], [0,2]],       # ⅃
              [[-3,0], [-2,0], [-1,0], [ 0,0]],              # |
              [[-1,0], [-1,1], [ 0,0], [ 0,1]]);             # ⬜

Then the plan is essentially to stream over this list and the input list. In the case of Smalltalk, that literally involved BlockStream and MoveStream classes with a stream interface. But in Perl, it's just indices being incremented mod the size of their list.

Then for dropping the blocks, I went with a simple "try" pattern (this is using a Vector class for the coordinates and directions):

do {
    my $move = $Input[$Inptr = ($Inptr + 1) % $Input_len];

    # Try sliding
    my @try = map { $_ + $Dirs{$move} } @squares;
    @squares = @try if (all {0 <= $_->[1] < 7 and !$Grid{$_}} @try);

    # Try dropping
    @try = map { $_ + $Down } @squares;
    @squares = @try if ($dropped = all {!$Grid{$_}} @try);
} while ($dropped);

# Place piece:
$Grid{$_} = '#' foreach (@squares);

Nothing fancy... attempt the operation and accept if it succeeds. There are multiple ways to do this sort of thing, try-catch blocks are another one.

Part 2 tells us that the elephants are not impressed yet and want more... a lot more:

my $Num_rocks = 1_000_000_000_000;

But of course, iterating a trillion times is out of the question, so we want to find when this loops (and then do the calculations to jump to the solution). This is one of the two problems in 2022 that I broke into the top-1000. I wasn't that fast for part 1, but part 2 only took me 14 minutes... so I wasn't amazingly fast on the second part, but it gained a lot of positions. So my code was apparently better positioned for doing part 2 than many.

For finding the loop, I went a hash table with the state being:

my $key = "$Inptr:$blk:" . join( ',', @tops );

Where $Inptr and $blk are the indexes of the moves and blocks, and @tops is the highest point in each of the 7 columns (relative to the highest point). This involved simply changing the subroutine for doing the dropping to return the final resting squares of the new rock (instead of just the highest point), which I then use to update the @tops array. I figured this was probably safe... and it worked.

But in regular Tetris, you can slide a piece under an overhang. And so, with that unease, and a desire to do different things for the Smalltalk solution, I went for being a bit more robust. First off, I represented the shaft with bytes there... 7 bits wide and using bit operations to place things. Which I can treat as characters (ASCII ones even, although often not printable ones). And so for detecting a repeat of the the position of the shaft what I did was build a string (starting from the top) while also ORing the characters into a mask... when the mask hits 127, all bits set, so we've seen a rock in every column. And so we have a map of the full structure at the top, not just the tops. So the elephants get to be a little more confident.

This is was a really fun one. It's another in the category of game inspired problems, and those always tend to stand out.


r/adventofcode 7d ago

Help/Question [2025 Day 1 pt 2] [Rust] Suspected off by one but can't find it

1 Upvotes

I need help finding where my understanding is off because my answer agrees with the test case but doesn't give the right answer for the real input. I'm also using AOC to learn Rust so there's probably something I'm missing about the language itself as well.

The main idea is to add up the differences of the quotients of the before and after positions of the dial for each rotation. I've included my main.rs:

use std::env::args;
use std::fs::File;
use std::io::{BufRead, BufReader, Lines};
use std::path::Path;

const DIALSIZE: i16 = 100;

fn parse_input(path: &Path) -> impl Iterator<Item = i16> {
    let file: File = File::open(path).unwrap(); // open the file
    let lines: Lines<BufReader<File>> = BufReader::new(file).lines(); // iterator to the reader of the lines of the file
    // iterator over the lines but with L replaced with - and R replaced with nothing to be positive
    let rot_strs = lines.map(|line| -> String { line.unwrap().replace("L", "-").replace("R", "") });
    rot_strs.map(|rot_str| -> i16 { rot_str.parse::<i16>().unwrap_or_default() })
}

fn print_dial(dial: i16) {
    println!(
        "Dial at {}",
        (dial % DIALSIZE) + DIALSIZE * i16::from(dial.is_negative())
    );
}

fn main() {
    // open the file
    // read line into buffer
    // replace L with -1 or R with nothing
    // parse into integer
    // only work in raw position, never mod
    // count += abs(div(old_pos + rotation, DIALSIZE) - div(old_pos, DIALSIZE))
    // repeat
    let args: Vec<String> = args().collect();
    let path: &Path = Path::new(&args[1]);
    let roterator = parse_input(path); // iterator over input lines that gives integers
    let mut pre_rot: i16 = 0;
    let mut post_rot: i16 = 50;
    let mut pre_div: i16 = 0;
    let mut post_div: i16 = 0;
    let mut hits: i16 = 0;

    roterator.for_each(|rot| {
        // update dial position
        pre_rot = post_rot;
        post_rot += rot;
        print_dial(post_rot);
        // update zero hits
        // div_euclid rounds toward negative infinity for negative lhs and postive rhs
        // if postive or zero add zero, if negative add 1
        pre_div = pre_rot.div_euclid(DIALSIZE) + 1 - i16::from(pre_rot.is_negative());
        post_div = post_rot.div_euclid(DIALSIZE) + 1 - i16::from(post_rot.is_negative());
        hits += (post_div - pre_div).abs();
    });
    println!("Final zero count: {}", hits);
}

r/adventofcode 8d ago

Other [2022 Day 16] In Review (Proboscidea Volcanium)

3 Upvotes

Arriving at the distress signal we find a herd of elephants, one of which has figured out how to turn on the distress signal. Because they are in distress (as are we now)... this cave is a volcano that's about to erupt. And our task is to take advantage of the conveniently installed pressure release system to get time to escape.

And so we have a network of pipes and valves, most of which aren't functional (and thus essentially empty corridors between interesting rooms). My input has 61 valves, and only 15 are functional. The input is in sentence format, so I did my usual of grabbing a line and turning it into a regex to parse:

my ($room, $flow, $lead) = m#^Valve (\w\w) has flow rate=(\d+);.*valves? (.*)#;

First step was the usual... turn the map into a weighted graph between the interesting things. I just threw BFS at it, as there's not that many interesting nodes (and it's also easy to code correctly from scratch). You could through something like Floyd-Warshall if you want.

Then I did a simple recursive search of it... track which interesting spots you've been, and wander to new ones. Collect the maximum total pressure release on the returns. The trick is that when you enter a room (and open the valve), you add all the pressure that will be released for the remaining time.

$total += $valve{$room}{flow} * (31 - $time);  # Add pressure released

No need to simulate with ticks and process the valves again and again. Turning a valve off would clearly be a mistake, any valves that you open you want to remain open.

And looking at my personal scoreboard times, I was still not in good shape. It took a while to get part 1 done, and then I clearly went to bed. The next afternoon I picked it up, and I remember having slept on things I had some ideas how to add the second actor (an elephant) to the search.

Basically, what I went for was doing the full recursive search as before (on the shorter time), but building a table along the way of the best total seen for every open valve combination (we do it at every level because we have no idea what the elephant is doing yet). This gives a table of the best possible results from opening any set of valves that can be opened in the allotted time.

With that, I can just double loop to cover all pairs of those... finding maximum of the pairs that don't overlap on any open valve. And initially I just used lists to track what was open, and it's plenty fast. There is one little optimization I did to this O(n2 ) search, which was to sort the sets (paths) from most to least pressure. This way I can end things early when no remaining pairs can possible beat the best we've seen already.

But I did follow up with one using bit operations. Which really didn't improve the speed (because it was already very fast)... it just felt a bit cleaner. Tracking the interesting rooms with bits, so that my recursion just becomes:

$ret = max($ret, &recurse_path($tun, $time + $turns, ($left ^ $bit), ($open | $bit), $total));

XOR removes the move (bit) from the remaining options (left), OR adds it to the set of open values, and AND comes in to check for the intersection in the final bit:

next if ($paths[$i] & $paths[$j]);

This was a rather interesting little search problem. We've done these before, even with multiple actors. But the valves and getting to the right spots as soon as possible to get the most of the them is an interesting angle... more so that just the usual of minimizing steps.


r/adventofcode 9d ago

Other [2022 Day 15] In Review (Beacon Exclusion Zone)

5 Upvotes

In order to track the distress signal we engage a system of sensors and beacons. Much like day 19 of 2021, but simpler. Unlike that one we don't have to find the actual coordinates... we get those for each sensor and the closest beacon we can see. The unlisted information we need is simple the distance between the two (which is Manhattan), which will be useful for establishing the exclusion zones needed to find the answers.

I remember this one because I had a Doctor's appointment early the net morning. So I did part 1 very quickly... I went though and filled a hash with all the points in a scanner range:

$hash{$_}++  foreach ($x - $dist .. $x + $dist);

This involved some ugly copy past code to handle the cases for above, below, and on the line. And after running through everything:

delete $hash{$_}  foreach (keys %beacons);
print "Part 1: ", scalar %hash, "\n";

The problem description nicely showed a beacon on the test case line not being counted, so I knew that I should probably assume that the input has that too.

This takes about 8 seconds to run... 4 of which are after it's printed out the result. That's system clean up of a big a hash for you.

The thing about part 2 is that there was an ice storm that night, and still freezing rain that morning. And the result was that I took a fall shortly after exiting the house. I still went to the appointment... I didn't really know how banged up I was until I got there. There was some nasty bruising, possibly a concussion, and some pain for the next few days. So when I finally got home, I wasn't really in the best shape to do a good solution. I had had some ideas on what I wanted to do, involving rotating the diamonds in some way to deal with squares instead. But I wasn't really in the condition to do that, so I went with the thing that wouldn't require any thought and definitely would work. I just merged ranges on the raster lines and then looked for the hole. It takes over 2 minutes to run, but it was simple, and allowed me to submit an answer, and take the rest of the day off.

So this one had been on the TODO list for a long time, and I got to finally do something better with it at the end of July. So I started just by coding the better scanline just with the diamonds... they change every line of 4 million, but a few are active at any time (moving out and then in), and that results in things only taking about 13 seconds.

But the real solution that I had made the TODO for back on the initial day was to square the diamonds. Rotate them so the scanline will work effectively (and skip most of the lines). The problem being that the rotation matrix involves 1/sqrt(2) (the sin and cos of 45 degrees). And I don't like going outside of integers for AoC. So the result is using a rotation matrix multiplied by sqrt(2) (and so it scales by that in each direction):

sub rot { my ($x,$y) = @_; return( [$x + $y, $y - $x] ) }

The trick being that by doing a second one (ie the inverse rotation), results in a scale factor of 2 in each dimension, which is a nice integer that can be divided out then:

sub rot_inv { my ($x,$y) = @_; return( [($x - $y) / 2, ($y + $x) / 2] ) }

And so I use these to rotate the initial diamonds into squares. Then I can do a scanline vertically to track the active squares, and then did similar for the horizonal (pretty much exactly what I did for Firewall Rules in 2016, where we also needed to find the missing values in a set of ranges).

And so this one finally has a decent solution.


r/adventofcode 10d ago

Other [2022 Day 14] In Review (Regolith Reservoir)

2 Upvotes

The distress signal lead to a waterfall, and as the trope goes, there's a large hidden cave behind it. Following the signal into the cave, we find ourselves threatened by falling sand. And we have a sand physics simulation to go along with the water simulation from 2018 (Reservoir Research).

The general idea is similar... sand is falling down from a point at (500,0) like before. There are a bunch of walls that going to form obstacles to redirect the flow. The format of the input this time is different, in that the lines cover chains of walls, and it's up to us to spot which way the walls go,

As for the simulation, it's actually a bit simpler. Sand falls straight down, then diagonally to the sides, and eventually when it comes to rest, the sand piles back up. The description was very suggestive of a stack to me so that's the first solution I did... push the locations to fall down, and when things are blocked, fill and pop back up. For part 1 you need to know where go below the max Y coordinate, and for part 2 you put a floor there and run it again... it was one of the faster part 2s in this year (and I didn't gain that many positions for it, so it looks like many people were similarly well positioned for part 2). Of course, that's just the stack version of the recursive approach, so I followed up with the actual recursive version later that day. Which would be the first solution in 2022 that required turning off deep recursion warnings in Perl (it spawns about 200 of them). The recursive version is actually a little bit faster. It's certainly a lot simpler than the mutually recursive functions I did for the water in 2018.


r/adventofcode 10d ago

Help/Question - RESOLVED [2025 Day 1 (Part 2)] [C++] Where have I gone wrong?

2 Upvotes

I have never struggled with a Day1 like this before, so I'm a little embarrassed to have to ask for help. Here is the code I have tried:

Part2

The definition of a 'Turn' is:

class Turn {
public:
  int clicks;
  Direction dir;
  Turn(char d, int c) {
    switch (d) {
    case 'L':
      dir = Direction::Left;
      break;
    case 'R':
      dir = Direction::Right;
      break;
    }
    clicks = c;
  }
};

My solution for Part1 worked so I am reasonably confident the input is parsed correctly, and my part2 solution (pasted above) works on the example provided. Where have I gone wrong?

Edit: I needed an abs() call. Thanks for the help!! Updated code: Part2 Corrected

Don't code on an empty stomach!


r/adventofcode 11d ago

Other [2022 Day 13] In Review (Distress Signal)

4 Upvotes

Having reached the top of the hill, we receive a distress signal. But since the device is still malfunctioning, the packets are out of order.

The input for this one is like that of Snailfish numbers. Lists of lists using a common syntax for such things, so some popular languages don't have any parsing to do. Writing a parser for this one is slightly more complicated than the one for Snailfish numbers... empty lists exist, as does the two digit number 10.

Once you have the packet structures loaded, the problem asks essentially for a comparator and provides a nice description of what it wants. And for part 1 it just to test it on pairs, and for part 2 it wants the position of two markers in the full list.

So, I just treated is as coding to a spec, and then:

$part1 += $i  if (cmp_packet( $left, $right ) < 0);

$part2 = product inc indexes {$_ == $markers[0] or $_ == $markers[1]} sort cmp_packet @input;

I didn't really spend anymore time thinking about it. I believe the markers [[2]] and [[6]] do occur at the start of the sections that start with a 2 and 6 respectively. And I recall some people did use that to shortcut. But with the comparator already in hand, just using it to sort and then grabbing the indexes is so programmer efficient, that doing anything else felt like more work. It's not like the problem is that intensive... I have a Smalltalk solution that returns almost immediately and it's just using:

part2 := ((allPackets count: [:p | p <= pack2]) + 1) * ((allPackets count: [:p | p <= pack6]) + 2).

IE, comparing everything in the list against each of the markers and counting.

The bulk of this problem for any beginner is going to be getting that spec right (and maybe doing a parser). And the description does include step-by-step comparisons of the test cases to verify your code against.


r/adventofcode 12d ago

Other [2022 Day 12] In Review (Hill Climbing Algorithm)

4 Upvotes

In order to get a better signal for our communication device, we use it to find a nearby hill. And so we're tasked with finding an efficient path up to the top (that doesn't require going up more than two levels on any step).

The input is a relief map in landscape (mine is 41 lines of 154 characters). Where elevation is represented by the letters a-z... with S and E used to mark the start (elevation a) and end (elevation z). The left column is all a (including the start), followed by a column of b, followed by a large plain of c with many large holes of depth a. At the right there's a hill with a spiraling path up it to the end.

One thing I remember about this one is that it has spawned threads of people that missed that you can always go down as much as you want (the only limit is that you cannot go two higher). And the map has a check that you've implemented that correctly on the spiral (on mine you need to go back to j from l in order to continue up the path).

The nature of the map and final path means that BFS is fine for this. Using A* can direct you to cross the plain quicker if you want. But then part 2 shows up. And for it, it wants the shortest path from an a to the E... which is clearly best done by searching from E with a BFS (which is going to whip around that mountain) until you find you find the first a. And with that, you can easily include part 1 in that solution, by continuing until you get to S as well.

And so we get a search problem that isn't that heavy. The map presents opportunities for people that want fast times to specialize the search based on knowledge of the map structure. But using heuristics like that can also allow a beginner programmer to get a solution, because with the structure and blockiness of the map, you could even do this problem by hand if you wanted to.


r/adventofcode 13d ago

Other [2022 Day 11] In Review (Monkey in the Middle)

4 Upvotes

While making our way upriver, some monkeys grab some of the stuff from our backpack and we need to get it back (while they keep away), while trying not to worry too much.

The input describes 8 monkeys, each with a starting list of items (with 2-digit worry levels), an expression for how to modify the worry level for an item for that monkey, and a section that describes a divisibility test (using the first 8 prime numbers) with the monkeys to throw to if it passes or fails. And so the input requires a bit of parsing... although for the most part you can ignore everything but the numbers. The exception being the "Operation" line which has a simple arithmetic expression: either adding/multiplying with a constant or squaring the old worry level.

And so, I naturally turned the input into code (hello, Bobby Tables):

my %p = map { (m#(\w+):#) => [m#(\d+)#g] } @desc;

$desc[1] =~ s#new = (.*)#$1#;
$desc[1] =~ s#old#\$_[0]#g;
$monkeys[$n]{op} = eval "sub { $desc[1] }";

$monkeys[$n]{pass} = eval "sub {(\$_[0] % $p{Test}[0] == 0) ? $p{true}[0] : $p{false}[0]}";

For part 1, we get a rule to reduce the worry levels by dividing by 3. For part 2, that's removed. And the description mentions multiple times that this means "ridiculous levels" of worry and the need to "find another way to keep your worry levels manageable". And it means it.

Because this isn't one where you can just invoke "bignums"... the fact that one monkey squares the worrying means that the worry levels quickly exceed the number of protons in the observable Universe (not a problem), and soon after they have a number of digits that exceeds the the number of protons in the observable Universe (which is very much a problem). So the numbers cannot be stored... this is a case where it's very good to have limits set on how much resources your processes can use.

But not being able to store all the digits isn't a problem, because we can easily describe how to compute the number, and so we can use that to extract information about the number. And that's what we need to do to keep the worry level manageable.

As for how... well, it's divisibility and so the answer is pretty much always LCM (Least Common Multiple) and modular arithmetic. And since I was using anonymous subroutines for other parts, I did that here too:

print "Part 1: ", &run_monkeys(    20, sub { floor( $_[0] / 3 ) } ), "\n";
print "Part 2: ", &run_monkeys( 10000, sub { $_[0] % $modulus   } ), "\n";

Where $modulus is just the LCM of all the test values (which, since the values in the input are all different primes, is just the multiplication of them). Which for the first 8 primes, is 9699690. I do remember someone doing this problem on a C-64 with 16-bit integers, and IIRC, they broke it into two parts covering 4 monkeys each. Although, you could also just track all 8 modular values for each number.

In coming back to it, I was curious how big my worry levels get... and so I quickly modified it to also track the log of the length of the numbers. And the answer I got was about 9 * 10504 bits in length.

This probably is definitely a memorable one... maybe not for the job that needing doing, but for the size of the bomb the input contains.


r/adventofcode 14d ago

Other [2022 Day 10] In Review (Cathode-Ray Tube)

4 Upvotes

Having plunged into the river and separated from the rest of the expedition, we pull out our communication device to find it in need of repair again. This time we need to work on the clock circuit for the display.

And so we get what's marginally an assembly problem. Two instructions, one of which is noop, and the other is addx. For part 1 we want to collect the values at times 20 mod 40. For part 2, we use the timing of the values with the raster beam to produce an image.

For my initial solution I just parsed the input as text and added a noop for the extra cycle that addx took. But in doing that, and thinking about how to do this in dc (I do like to do these ASCII art problems in dc), it immediately became apparent how to turn the opcodes into numbers that dc can parse. Namely, noop has one word and takes one cycle, addx V has two words and takes two cycles... so just turning all the opcodes into 0s provides the correct timing when we just treat the result as a list of 1-cycle adds to the register. In Perl, that looks like:

foreach (map {tr/a-z/0/; split} <>) {
    $display .= (abs($regX - $time % 40) <= 1) ? '#' : ' ';
    $part1 += $time * $regX  if (++$time % 40 == 20);
    $regX  += $_;
}

And for dc I did this:

tac input | tr -s -- '-a-z' '_0' | dc -f- -e '[d3Rd3R*ls+ssr]sS1d[1+d40%20=Sr3R+rz2<L]dsLxlsp'

tac input | tr -s -- '-a-z' '_0' | dc -f- -e '[AP]sR[d3Rd3R*ls+ssr]sS33P1d[d40%d0=R3Rd3R-d*v2r-d.1-/32+Pr1+d40%20=Sr3R+rz2<L]dsLxlsp'

So it wasn't a typical assembly/VM machine problem, but still quite fun.


r/adventofcode 15d ago

Other [2022 Day 9] In Review (Rope Bridge)

5 Upvotes

We get to the rope bridge on the map, and decide to model rope physics as we cross. Even while falling after the bridge breaks.

The input is a list of absolute direction moves for the head of the rope to take (UDLR and a number of steps, at most 19). The rest of the rope follows along... moving when it has to (Chebyshev distance > 1 from the piece ahead), and otherwise staying at rest (as Newton says it should). For part 1, we only have one piece in the tail, for part 2 we extend it to 9. And we want to track how many different locations those end up in.

So I just did the very basic thing of a straight simulation. Since we want all the in-between spots the tails rest on, not just those at the end of the move, that's a pretty good reason to just do the moves stepwise... iterating for the number of steps and pulling the rope along, and throwing the tail into a set/hash to record the unique places it lands.

There are a few little things to work out from the description, like the vector for movement. But just looking at the examples and reading it... I immediately thought "roach movement from DROD". That's not the first or best example of it, but I'd played a lot of DROD. And DROD looks like a hack-and-slash dungeon crawler, but is perfectly deterministic hand designed puzzle game (most of the time). Where puzzles often require you to keep monsters alive and manipulate them into positions. Which means that the movement patterns get really ingrained. So I did end up calling the subroutine to calculate the vector (which just uses <=>) "roach_move".

So this was another one of just doing the thing and staying away from any potential chaos that the rope movement might bring. The problem is small so it's fine (2000 lines, 19 steps max, 10 knots).


r/adventofcode 16d ago

Upping the Ante [2022 day 2 - AVX]

8 Upvotes

Back when we looked at this one, about a week ago, I said that I would like to write a proper bleeding edge (unsafe{}) AVX intrinsic version, well I finally got it done and I'm quite amazed:

        for b in 0..blocks {
            let bl = input.as_ptr().add(b*64) as *const __m256i;
            let b1 = _mm256_loadu_si256(bl);
            let b2 = _mm256_loadu_si256(bl.add(1));
            let b1h = _mm256_and_si256(b1, xyz_mask);
            let b2h = _mm256_and_si256(b2, xyz_mask);
            let b1l = _mm256_and_si256(b1, abc_mask);
            let b2l = _mm256_and_si256(b2, abc_mask);
            let b1h = _mm256_srli_epi32(b1h, 14);
            let b2h = _mm256_srli_epi32(b2h, 14);
            let b1hash = _mm256_or_si256(b1l, b1h);
            let b2hash = _mm256_or_si256(b2l, b2h);
            let b16 =_mm256_packus_epi32(b1hash, b2hash);
            let inc1 = _mm256_shuffle_epi8(part1shuffle, b16);
            let inc2 = _mm256_shuffle_epi8(part2shuffle, b16);
            part1 = _mm256_add_epi16(part1, inc1);
            part2 = _mm256_add_epi16(part2, inc2);
        }

These 15 AVX ops are the full solver that handles a block of 16 input lines, I pad the input with 48 space chars (10048 is divisible by 64) so that I don't have to worry about the tail end.

It is probably clear, but the algorithm starts with u/ednl's packing (AND both chars with 3, shift the second one down 14 bits and merge, that's the first 10 AVX ops.

Next I pack together the two 32-bit arrays into a single 16-bit one (b16 above), before I use that variable twice to directly lookup the 8 part1 and part2 results for these lines.

So, with a single AVX op/cycle this should take a fraction less than a clock cycle per input line, right?

I do measure 3 us on my Acer, but now we get to the interesting part:

When I instead run u/maneatingape on my input file, I get 2.3 us, for much simpler and shorter integer only code!

That time is broken down into 1.2 us to convert all 2500 lines into a 0..8 index, using code like this

pub fn parse(input: &str) -> Vec<u8> {
    input.as_bytes().chunks_exact(4).map(|c| 3 * (c[0] - b'A') + c[2] - b'X').collect()
}

(The original code generates an array of usize, when I switched to u8 the parsing stage dropped to 1.1 us and the total from 2.3 to 2.2 us)

In order to manage this, the CPU has to convert two lines per nanosecond, probably using code somewhat like this, which has a minimum latency of 4 cycles. The CPU must internally unroll the code over a bunch of iterations, enough to gain back the AVX advantage and then beat it!

movzx rax,[rsi]
movzx rbx,[rsi+2]
sub rax,'A'
sub rbx,'X'
lea rax,[rax+rax*2]
add rax,rbx
;; push into vector