10
votes
Day 7: Amplification Circuit
Today's problem description: https://adventofcode.com/2019/day/7
Join the Tildes private leaderboard! You can do that on this page, by entering join code 730956-de85ce0c
.
Please post your solutions in your own top-level comment. Here's a template you can copy-paste into your comment to format it nicely, with the code collapsed by default inside an expandable section with syntax highlighting (you can replace python
with any of the "short names" listed in this page of supported languages):
<details>
<summary>Part 1</summary>
```python
Your code here.
```
</details>
Well, I was able to do Part 1 without too much modification to my int-puter. Part 2 is going to be a bit more work, but I think I know how to do it.
Part 1 - Python
Edit: Yeah, Part 2 is going to require not-insignificant rewrites of how my int-puter executes in order to account for the feedback
Edit 2: Yeah, Part 2 was super annoying, but it was more so because I had to figure out 1)What the question was specifically asking. It was a bit unclear about how the looping was supposed to work and that sent me down wrong path 2)Fucking iterators. God damn it
Part 2 - Still Python
Okay, so I had to convert my intputer to an actual class rather than just a bunch of variables in memory. This let me keep everything consistent but required me to write "self.something" approximately 300,000,000,000 times give or take.The other change I made allowed me to effectively 'pause' while waiting for input. When an intputer wants an input and doesn't have one, it'll exit but save it's current conditions.
So I looped through my intputers A-E feeding the output from one into the input of the next until I got an actual halt command. Then I finished the loop and output the result
Not really spoilers that I struggled with
It wants you to finish out the loop after one of the intputers halts.And make sure to remember to actually update your phase settings. I was using the old ones for a while and it caused some problems
Edit 3: These are really ramping up in difficulty. I'm thoroughly enjoying myself
It looks like our solutions for part 2 have some similarities and differences.
Discussion that may be spoiler for part 2
I think your approach of making each computer an object was the move. I kind of handled it by managing their memory and instruction pointers outside in an additional method, which works but looking at your solution my way feels a bit clunky.
I don't like today's challenge; I found the descriptions for part 2 very undetailed and confusing, but that might just be me. Here's my horrible solution for part 1:
Part 1
I'm still figuring out how to do part 2 in POSIX AWK...
EDIT: I finally finished part 2. It seems like it maybe could have been fun, but was ruined by the fact that I missed so many things because of the vague descriptions. Maybe I'll try it again next time with the right thing in mind from the start. Here's my even worse solution for part 2, which is extremely slow and may take a couple of minutes to finish (but works!):
Part 2
EDIT 2: Oh, apparently part 2's phase settings are just 5-9, not 0-9 like I thought. That one's on me, though. It's still slow with 5-9, but <3 seconds instead of ~3 minutes.
Evidently, I can't stop myself from sprinting through interesting problems once I become aware of them, so I'm going to give a brutal first pass of my code essentially verbatim from my initial solution and a revised second pass with despaghettified code (and possibly better solution ideas) after I take a little time away.
Hopefully this plan will make sense, though it might all just be a more glorified "messy code now, will edit later".
Brutal first pass (C++):
Part 1
My IntCode computer is the result of a previous brutal first pass and is thus almost hard coded.Luckily, the most one has to do here is store the original instructions and use appropriate data structures for input/output so that the resultant mess will spit out the right answer.
Also, C++ has
next_permutation
, which is a godsend for avoiding messy permutation code.Part 2
We need to keep the states of 5 computers at once and achieve this with (surprise!) 5 copies of the computer.Furthermore, I/O is achieved with 5 queues, where each computer reads from its own queue and outputs to the next computer's input queue.
Then, each computer's state is advanced one step at a time unless it need input when it has none, in which case that computer is temporarily halted.
The last output is tracked with a variable and is processed once all computers halt.
Again thanks to
next_permutation
, dealing with permutations requires just a few lines of number stuff and one for loop.Ambiguity Note: I might be reading this wrong, but the description first says that machine A takes its 5-9 number input immediately, but then goes on to say looping starts by feeding 0 into machine A.
In fact, Machine A accepts the 5-9 input first, 0 second, and then whatever comes out of machine E.
Second pass:
Moved the computer code its own class and compartmentalized many of the procedures reused in solutions.
It can definitely be further refined, but the main executed code to solve each of the problems is now much less ugly.
Part 1
The new computer code handles I/O with its own internal queues, so all the main procedure needs to do is fiddle with numbers a little and handle permutations.Part 2
The new code halts on its own and returns whether it was interrupted or terminated on its own, so the main procedure just handles I/O and tracking permutations.Isolated Computer Code
I rushed a bit and lost a bunch of time due to not correctly initializing my amplifiers in part 2. I had the actual feedback behavior correct for the longest time, so all my tests were passing, just the search part was setting them up wrong and causing each check to output the same (plausibly correct) result.
Day 7, Crystal
I went back and cleaned up a bit afterwards, to generalize running linked vms:
Day 7 - refactored
Ooh, my work a few days ago to make a nice module for the Intcode VM paid off. Especially the part where I made it support callbacks for IO and allowed those callbacks to suspend the VM. I didn't have to change my Intcode module for today's challenge.
I've only just started learning and using Rust for this year's Advent of Code. I've been really liking it. It slightly reminds me of C++ but without the constant horror that I've probably done everything wrong and that the code is just working by coincidence while full of memory leaks and corruption issues. I never run into mystery memory errors at runtime here, and I feel very confident that there aren't mystery issues like that in my code. The bodies of functions can get a little boilerplatey with going through various types, but it's checked for various kinds of correctness and often doesn't have too many free parameters so it doesn't bother me much.
I'm also a big fan of Rust's tooling. I don't have to put effort into build scripts, dependency management, or test configuration. One of my other favorite languages is Typescript, but I always feel a little uncomfortable while setting up a project for it like I'm going off the beaten path somehow. Rust's Cargo tool mostly works how I've expected.
Day 7
Intcode Module
Doing it in Swift this year. Link to day 7 in my repo
This took me a while. Spent the day doing some refactoring for the IntComputer. Also tried to fiddle around with parallel processing, but gave up on that for a while. Most of the work is done in the IntComputer.swift file:
IntComputer
This time the difference between parts was very large: part 1 was a breeze, but part 2 required much more.
My original Intcode accepted only a static list as input, and outputted similar list. And because everything is immutable in Elixir, linking the amplifiers together seemed very hard at first...
One possibility was to take snapshots of memory and pointer position, and change inputs in-between. However, in the end I finally got to really benefit from Elixir: I ran each amplifier in a separate process, and added ability to pass inputs and outputs through messages. Now I can do interactive IO, but the old tests from earlier days pass too!
The complete Intcode module
Part 1 and part 2
Oh, and the permutation function is from Rosetta Code.
This one was tough! I managed to complete it though. Not sure how many more of these I can get two stars on, since I do have real world responsibilities creeping up.
Parts 1 and 2, Python 3
Part 2 was the bulk of the work but I managed to accommodate it. I think the biggest things were fully understanding the initialization process and realizing that I could make blocking input. Once I had that I was able to manage them together and feed things around as needed. In retrospect I probably could have used some built in process and message passing tools but this was interesting so I don't regret it.
I'm pretty far behind, but I really enjoyed this one as it was ideally suited to Go's channel-based concurrency model. I got each amplifier series to run concurrently, and even within a single series had the individual amplifiers running in their own goroutines, passing inputs and outputs around via channels.
Some notes
I'm not including my intcode interpreter implementation because it's unchanged from day 5. All you really need to know is that
interpreter.New
takes a program ([]int
), an input device (func() int
), and an output device (func(int)
).I'm also omitting the code that loads the puzzle input from a file into a string as I keep that logic in a separate package that's used by all of my solutions.
If you want to view the code in GitHub, it's here. Go code is kind of rough to view in-browser—it uses tabs for indentation, and most browsers have default tab width set to 8 spaces. In GitHub you can set the tab width using the
?ts
query param. I set it to 2 in that link.Parts 1 and 2, Go