AoC 2017 Day 6: Tightrope

Source: Memory Reallocation

Part 1: Start with n stacks of different sizes. Take the largest block and distribute its items starting with n+1 and looping around. How many iterations of this does it take before you see a state you’ve seen before?

read more...


AoC 2017 Day 3: Spiraly

Source: Spiral Memory

Part 1: Create a grid in a spiral pattern like so:

17 16 15 14 13 18 5 4 3 12 19 6 1 2 11 20 7 8 9 10 21 22 23—> …


> Given a specific index, what is the [[wiki:Manhattan Distance]]() from that point to the origin (`1`)?





This same sort of structure comes up from time to time in [Project Euler](/programming/sources/project-euler/) as well. I'm going to guess we'll see this again. Seems like a fine contender for a [library function](https://blog.jverkamp.com/2017/12/01/aoc-2017-library-functions/).

Specifically, we'll create a `SpiralGrid` class that can be used to automatically generate as large of a grid as we want lazily (it won't generate more of the grid until we need it).

```python
class SpiralGrid():
    '''
    Generate a spiral grid that looks like this:
    17  16  15  14  13
    18   5   4   3  12
    19   6   1   2  11
    20   7   8   9  10
    21  22  23---> ...

    The point (0, 0) is 1. x runs left to right, y from top to bottom. So the
    point 12 is at (2, -1).
    '''

    def __init__(self):
        self._indexes = {}
        self._points = {}

        def make_spiral():
            index = 1
            (x, y) = (0, 0)

            yield index, (x, y)

            # Build the layers outwards
            for layer in itertools.count(1):
                # Each layer starts by going right and down one (we'll go back up before yielding)
                x += 1
                y += 1

                # Go through the four sides, up then left then down then right
                # Repeat 2*layer times per side
                for xd, yd in [(0, -1), (-1, 0), (0, 1), (1, 0)]:
                    for step in range(2 * layer):
                        index += 1
                        x += xd
                        y += yd
                        yield index, (x, y)

        self._generator = make_spiral()

    def __getitem__(self, key):
        '''
        Given an index or point return the other.

        If we're given an integer, it's an index, return the point.
        If we're given a tuple, it's a point, return the index.

        Either way, generate as much data as we need and don't have.
        '''

        if isinstance(key, int):
            field = self._indexes
        elif isinstance(key, str):
            key = int(key)
            field = self._indexes
        elif isinstance(key, tuple) and len(key) == 2:
            field = self._points
        else:
            raise ValueError

        while key not in field:
            index, point = next(self._generator)
            self._indexes[index] = point
            self._points[point] = index

        return field[key]

The neat part here is using a generator to generate the coordinates of points. That way, we can run the generator until we find the point we’re looking for and cache any results for later.

read more...


AoC 2017: Library Functions

As mentioned in the main post, I’m structuring my solutions a bit differently this year. Rather than repeating the same relatively lengthy header in each function, we’re going to have a few shared files that can be imported or used for every problem.

read more...