Day 6 already! Around a quarter of the way through Advent of Code!
For those of you that didn't like rectangular shapes on 2D grids a couple of days ago, have I got a treat for you! Now we're dealing with blobby, non-uniform shapes with possibly infinite area! Yay us!
I'm definitely not going to be able to complete this one tonight. I'll have to work on it tomorrow after a good night's sleep.
Happy plotting (X-Y plotting, I mean)!
Top comments (19)
Here is a quick and dirty (and slow!) Python solution. I will later translate it to Golang, but I doubt it will become any faster since it already uses dictionaries (hashmaps). What slows it down is probably iterating through each coordinate individually.
Python using a kd-tree.
Here is the Golang code. Even though it uses the exact same algorithm, it runs surprisingly faster!
From my github:
I'm starting to see what's going on here. The elves of 1518 have somehow procured a future book of classic programming algorithms. Today there was a little distraction in problem description around infinite spaces and equidistant points, but it boils down to a pixel fill algorithm. If you ever worked on a classic 1980s-90s style paint program you'll recognise it.
First let's make some geometric primitives:
For readable code we'll add some useful operations, using Kotlin's carefully limited operator overloading where appropriate:
We need a data model for our coordinates, and a parser. I know it's just two integers separated by a comma, but I hope by now you see what I mean by the One True Way of parsing text:
The last two lines make an infinite sequence of integers and zip it with the parsed coordinates so each coordinate gets a "name".
The problem talks about an infinite space, but the insight you need to solve it is that any area which reaches the edge of the "known" space is effectively infinite. The known space is the extent of the input coordinates in every direction, so let's compute that:
Breaking things down into tiny operations makes everything much easier to understand, means you're composing ideas at the same level of abstraction and frankly leaves fewer places for bugs to creep in.
We know the size of the space, now we need the area filling data model. A cell is either unclaimed, has a coordinate on it, is claimed by a single coordinate or is equidistant to two or more coordinates. It's a sum type!
And we represent the space with a simple two-dimensional array of these:
Now the fill algorithm. I've written this before, so I'm happy to say once it compiled this actually gave the correct result first time. The only change I had to make was to change it from stack-based recursion to a heap-based stack of pending operations to cope with the size of the main input data.
Things to note:
fill()
helper called itself recursively. Now it returns the next set of cells to fill, and the outer loop processes these.when
block looks at the current state of the cell and computes what to do. If the cell is unclaimed or closer to the current coordinate, we claim it. If it's the same distant it becomesEquidistant
. Some of the cases leave it alone.Part 1
The question in part 1 is to determine the largest finite area. We need a sum type!
To calculate the area for a coordinate ID I took a very simple approach:
This is not the most efficient form of the algorithm by any means. It will check the same cell many times during a pass. It runs in 1.5 seconds on my Thinkpad though.
Part 2
Part 2 was a bit disappointing as it didn't really build on the first much. I refactored the
Space
class to be generic in its content, so I could make a space of manhattan distances. Filling it was easy:Then the size of the area under the threshold is
space.count { d -> d < max }
I enjoyed this one a lot, which was pleasing after yesterday. Roll on the next...
PHP solution
Part 1 took me a while to figure out how to get rid of coordinates that have infinite area. Part 2 was straight forward loop.
Btw using
array_filter
was ~3x slower thanforeach
loop. PHP ¯\(ツ)/¯Part 1
Part2
readFile.php
Kotlin Solution
Part 1
This one wasn't too bad. I had a false start trying to flood-fill with multiple generators, and that got silly really quickly.
Then I realized I could just build a big array, chunk through item by item, remove any symbol on an edge, and then just count the places they reached.
Part 2
This is where I got stuck a bit trying to optimize instead of just letting it finish this code's 30 second cycle.
I'm not happy I couldn't figure out the math, but I'll post some pictures I've generated from my output in penance.
The promised images!
Sample Data
Part 1 - Answer
Part 2 - Answer
Answers
Part 1 - Answer
Part 2 - Answer
Annnnnd, by posting these pictures, I see that the optimization for part 2 is to bound yourself by the points. 10000 is a bit of a red herring.
EDIT: 2018-12-06T11:01:40-08:00
Found some problems with my image processing and fixed them.
You don't have to construct an array; you can just iterate over min(x)..max(x) and min(y)..max(y) and add + to the nearest input point.
Oh no, I've been affected by the bug in Day 6. My solution was correct, but the adventofcode.com didn't recognise it. I tested some of the solutions posted here and they gave the same output as mine. I'm not sure how to progress to Part 2, so only posting Part 1:
Oh no! That’s a bummer 😐
The worst for this day was understanding the instructions.
I just couldn't for the life of me understand what was being asked, until a friend helped me and then I was on my way.
Full disclosure: I was feeling pretty dumb, and almost gave up. I'm glad I didn't 🙏
Part2:
I want to come back to this exercise after Advent is over and refactor, because I think I could make my solution really nice now, but for right now, I'm delirious and need sleep. Here's what I've got. It's not pretty, but it gets stars and that's all that matters.
PHP
Weird fact I learned about PHP in this puzzle: if you try and splice a very large array into an already very large multidimensional array, at a certain point PHP will throw an overflow error, but it doesn't happen with array_pad?
Anyway for the first one, I ended up just manually increasing the value by which I was expanding the grid and seeing at what point the number I was getting stopped changing, since "is this infinite" is hard to check for. The second part, I checked all the edge spaces to see if any of them were part of the central area, and if they weren't then I knew I could stop.
Part 1:
Part 2:
This is my least favorite code I may have ever written. Need to refactor.
Part 1
part 2
I doubt this is the most performant solution, but it gets the job done for smaller sets of input data.