12
votes
Day 8: Handheld Halting
Today's problem description: https://adventofcode.com/2020/day/8
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>
This is pretty similar to one of the recurring projects from last year where we had to build an int-puter that slowly got more complicated as the days went on. Personally I opted not to look at my old code, but I think a lot of last year's could be reusable. It's essentially a very primative processor at the assembly language level.
Day 8 Part A – Python
Basic processor has an instruction pointer that tracks what it's paying attention to. That will move around to different locations in memory. We also care about exactly one value, the accumulator, which can store numbers. Once that was working, I just added something to track which memory locations had been accessed already and told the processor to kill the process if it ever looped.
Day 8 Part B – Python
For this I took my original processor and turned it into a method. Then I ran it a bunch of times changing one line at at time.
Tips for Beginners
This is sort of how a processor works at a very low level. You want to read in an instruction one at a time, Do SOMETHING based on the code you get, and then move on. You'll need to define different actions for different opp codes.
If it's anything like last year, they'll add on to this before the month is over, so consider spending extra time to make sure you understand it and will be able to modify it in the future.
When you're changing the set of instructions for part B you need to make absolutely sure each test run only has 1 change. If you're not careful, you might end up accidentally applying multiple changes which will fuck up your results. Sometimes when you think you've copied data, you just actually just made a new variable that points to the exact same data. So if you change it, the changes will affect the original. There are ways around this, like in python you can copy a list by casting it as a list. For example in Python
list_b = list_a
would point to the same list. If you change one of them, the other will be affected.list_b = list(list_a)
would create a new list with a copy of the original data. So now you can change them independently of each other. This distinction is known as copy by reference vs copy by valueIf you want to look ahead (assuming this does get reused, which isn't certain) you might want to look at some of the problems and solutions from last year.
I totally freaked out on part 2 and wrote a crazy recursive solution--on seeing my placement and talking to friends, an iterative solution would have been much simpler to reason about. Great problem today though!
python
I love this kind of thing! I think Rust lends itself to building interpreters particularly well :).
lib.rs
main.rs
I believe I had really mediocre approaches which led me to make a lot of mistakes on my way to the solution, but I got the solution nevertheless! (That's my holiday spirit)
Repo link
Part 1
Here I edited the list's instruction itself to mark that it was already visited. I don't know what made me think of doing it this really odd way, but it messed me up a bit for part 2.Part 2
Kind of a mess, but it works. I ran into a problem when I used my part 1 `follow` and realized the program would stop running because it was reusing the same list of instructions that were renamed to mark they were visited on a previous iteration. So I went a simpler route and just marked a list instead for my visited instructions.I think last year ended up with too many hard problems, but this year so far is too easy...
Scheme
Python
Repo Link
Part 1
I took a more stateful and possibly more enterprisey approach with this problem and made a class. The solution is more verbose than the others here, but I hope the additional structure will make it easier to modify and extend for future use (should we come across another IntCode problem).
Part 2 (diff)
For the second part, I just brute-forced the solution by modifying each
nop
orjmp
instruction (one at a time) and checking if the console terminates. I'm just putting a diff here because the code is already pretty long, but the full source can be found in the Repo Link.I felt that I could try a Rust solution here, especially since it's getting to the interpreter bit. I'd appreciate feedback from the Rustaceans doing AoC, especially since I didn't want to leak an abstraction by having
Nop
have a value encoded, and not knowing how to use whatever Rust's equivalent ofyield return
from C# is. I feel like that may have slowed it down a bit in part 2 due to allocations.main.rs
Output
I wasn't aware of being able to use
Self
like that. I prefer less redundancy so I'll have to update my code to match this style.Awesome, thanks a lot.
Neat. I think I kind of went overboard with that since I had to use
iter()
for vectors and I got used to it, so I assumed that I'd have to do it for something as obvious aslines()
as well.It's not really a style thing; those were mostly leftovers from trying to fight the compiler with the whole
String
vs&str
issue. At some point when trying to fix that, I got several type inference errors, so I added the turbofishes to the code so that I could get better errors and sanity checks. (See also: my previousgenerate_alternative_inputs
function, which throws an error without the poor fish.)\o/
TIL about turbofish
I didn't need to add anything special for part 2 - if you were stopping execution when you saw an instruction twice (from part 1), you shouldn't have hit any infinite loops. I actually thought the problem was well structured to have you implement that part first.
Oh interesting, I completely missed that. Thanks a bunch!
It’s so cool to read other people’s rust solutions to the same problems every day. I completely forgot about implementing traits for my own structs! I’ll try and remember next time.
Mit scheme
Part 1 & 2
I used vectors instead of lists for my scheme solution; the
O(1)
access to elements makes it a lot more efficient than using lists.Rust
Virtual Machine
Main Code
Just a warning: If last year's problems are any indicator, this will turn into a fully featured virtual computer with if branching and whatnot. It likely will pay to start splitting this into multiple files and making it extensible.
Fun one! Though part 2 was a bit of a curveball...
Part 1
Part 2
JavaScript
Parts 1 + 2
So decided to switch things up a bit today and use Go instead of python. Got to use goroutines on the second part which was kind of nice. Parallelism ftw!
Anyway the second part to this was much trickier than the previous problems in my opinion.
Part 1
The code ends up longer mainly due to go being less expressive than python but using `go run` it still builds super fast and exectues quickly.Part 2
Go routines made it quite easy to evaluate all alternative paths in parallel. The main idea is that each time we encounter a jump or noop instruction (we spawn a copy that executes the alternate path). This only happens in the first evaluation since there could only ever be one modification as per the problem statement.Ruby
Part 1 was easy, Part 2 I stumbled around a bit on a shallow vs deep copy issue. Fixed that with a goofy hack (
JSON.parse(JSON.generate(...))
) for the quick and dirty solution, then went back and fixed it with the no-copy solution presented below.Virtual machine puzzles are often my favorites, I love the process of debugging my own interpreter and reverse engineering the subject code simultaneously, and building all the supporting pieces for I/O, debugger, graphics/visualization etc.
Part 1
Part 2
I got excited when I saw this one, since it looks similar to last years puzzle that built up over time, and also because I had an excuse to play with Julia's typing system! Julia doesn't have user defined objects, but it does have a robust (though largely optional) type system, and uses a multiple dispatch system to determine which function to run when given certain inputs. I maybe got a little excited to incorporate this stuff since I haven't gotten to play with it yet, but it actually created a very efficient solution and pushed me into a design pattern that I think I should be able to extend if this challenge becomes the basis for future challenges.
Part 1
Part 2 diff
I did have to make some modifications to the part 1 to add in support for things I wanted/needed in part 2, but I think it was worthwhile in the end.
PC vs Pi Performance
This one was crazy fast on my PC, and still pretty quick on the Pi.
This one was fun and familiar from last year, though I did last year's puzzles in Go. The approach is pretty different with Elixir but can be summarized as:
Parts 1 and 2, Elixir