16
votes
Day 7: No Space Left On Device
Today's problem description: https://adventofcode.com/2022/day/7
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>
Here is my Python solution. A hacky use of the
os.path
modulecame in handy.
Part 1
Part 2
The aimless trawling in the input file is quite relatable—almost looks like my
.bash_history
at times!This was a fun one. There doesn't seem to be any trickery like repeated
ls
invocations in the same directory, so I took quite a few shortcuts in parsing. :PPart 1
Part 2
The challenges are getting more interesting now. I love these ones where we have to build up some kind of world representation, rather than just do arbitrary data/list transformations on the input lines.
I'm using Github Copilot more, and it's amazing. I'm having fun figuring out how to best use it. I manually set up tests for the sample data, defined a few interfaces, wrote out the type declarations for some helper functions (
parse()
,translateParsedLinesToFilesystem()
andcalculateSizesOfAllDirectories()
), and then Github Copilot implemented those functions fully. Because the sample data test cases were in the file, Copilot saw the data and was able to write code for parsing the sample data into the data structures I defined! I did have to make some corrections to thetranslateParsedLinesToFilesystem()
function: in if-checks, it wrongly thought that my File and Directory interfaces had a type property. I realized it would be sensible to have them if someone / an AI reading the code assumed they would be there, and then I had to go through and add the type property to each spot a File or Directory object was created.Then I wrote the
part1()
andpart2()
functions manually. I just had to use the helper functions I just made.Typescript (Deno)
I've written a lot of tree and graph traversal code and yet, AoC coding vs. normal is identical real life hurdles vs. QWOP.
Data Structures
Part 1
Part 2
Speedrun
Both parts takes about 2.6ms.
As usual build up the data structures once and use it for both parts
Attempt 1: ~70% elapsed time
Breakdown:
~250 μs: read file
~ 1 ms: build the data structure
~270 μs: part 1
~230 μs: part 2
Note: The example listing and my input (and being AoC I suspect everyone's input) are both a recursive dfs of the disk.
I'm using Rust this year, and trying to keep it
std
-only throughout the month. I've been doing a pretty good job of avoiding allocation so far, I think, but not today.Part 1
Part 2
Python
Yeah, it was harder for me: I donʼt like trees; and at begining I had problem with electricity at the morning. Iʼll try to make the code cleaner but it kinda work, so for now itʼs fine.
Yikes, after yesterday's cakewalk this one was considerably more aggravating!
Here's my TypeScript solution
Part 1
Part 2
Today felt... hacky, again. Lots of mutation of state going on, to set everything up for the calculations at the end.
After solving, I realized how to better reuse the solver from part 1 for part 2, so I rewrote the bits I had.
Input-"sanitizer"
I figured I'd just build a tree of inodes while parsing.
With that done, I calculated all the sizes, recursively:
size-calculations
The "solver" can then go through the tree and find all the directories that match some criterion:
Solver
At this point, parts one and two are just a few lines of code:
Both Parts
Well, today was definitely not a good day to try an object oriented approach while attempting to continue forcing static typing into Python, and you can see that in how long and clunky my code is. But, it works, and I'm going to keep telling myself that that is the only thing that matters.
Parts 1 and 2, Python
Elixir
A challenging puzzle for a functional language that only provides immutable data structures!
I used a fun data structure (design pattern? 🤷) called a zipper, which provides an efficient and purely functional way to navigate and manipulate a deeply nested data structure.
The basic idea of it is that as you descend into the sub-nodes, parent nodes that have been traversed get removed from the structure and added to a stack of "context" structs. This way, all edits happen to the "root" node, and the "root" changes as you move around. When it's time to go back up a level, the parent node gets rebuilt from the stored context, and the node we were previously editing gets added as a child of the reconstructed parent. This can be repeated all the way back to the root node, at which point you've put the whole structure back together.
Directory struct
A simple struct to store a directory node. The struct originally had more fields (I also had a separate struct for the file entries) since I didn't know what part 2 might ask for, but I've pared everything down to just what was ultimately needed to solve both parts.
Zipper implementation
The typespecs kind of clutter things up, but they might be helpful to explain what's going on?
Solution
I kept track of each directory's size along the way, so combined with the zipper approach I think my solution is pretty efficient. Each part runs in 575 - 615 μs (and they each start from the string input—would be faster if I could reuse the parsed tree but my solution-runner utilities don't allow that).
This one was a pain in my butt (I just finished it last night), but I think that was mostly down to the fact that I'm just learning nim and I wasn't understanding refs correctly at first.
nim (both parts)