Beispiel #1
0
 def __init__(self, data):
     self.grid = ZGrid({0j: "."})
     self.comp = IntComputer(data)
     self.comp.output = deque(maxlen=1)
     self.freezer = {0j: self.comp.freeze()}
     state0 = 0j
     AStar.__init__(self, state0, None)
Beispiel #2
0
def parsed(data):
    comp = IntComputer(data)
    comp.run()
    txt = "".join([chr(x) for x in reversed(comp.output)])
    grid = ZGrid(txt)
    grid.draw()
    return grid
Beispiel #3
0
 def __getitem__(self, item):
     if isinstance(item, np.ndarray) and item.ndim == 2:
         H, W = item.shape
         if (H, W) in known_dims:
             # looking for just one letter in a numpy array
             if item.dtype.name.startswith(
                     "int") or item.dtype.name == "bool":
                 item = [["#" if val else "." for val in row]
                         for row in item]
             item = "\n".join(["".join(row) for row in item])
         elif H in {h for (h, w) in known_dims}:
             widths = {w for (h, w) in known_dims if H == h}
             for w in sorted(widths, reverse=True):
                 n, rem = divmod(W, w)
                 if rem == 0:
                     # could be a word? retry OCR per character
                     chars = [item[:, w * i:w * (i + 1)] for i in range(n)]
                     txt = "".join([self[char] for char in chars])
                     return txt
     if isinstance(item, dict):
         grid = ZGrid(item)
         grid.translate({0: ".", 1: "#"})
         full = np.array(grid)
         full = full[:, 1:-2]  # TODO: template matching
         return self.__getitem__(full)
     if isinstance(item, np.ndarray):
         self.__missing__(array2txt(item))
     return super(DebugDict, self).__getitem__(item)
Beispiel #4
0
def test_path_compression():
    uncompressed_test_path = q17.get_path(ZGrid(test_path), compressed=False)
    assert uncompressed_test_path == "R,8,R,8,R,4,R,4,R,8,L,6,L,2,R,4,R,4,R,8,R,8,R,8,L,6,L,2"
    compressed = "\n".join([
        "A,B,C,B,A,C",
        "R,8,R,8",
        "R,4,R,4,R,8",
        "L,6,L,2",
    ])
    assert compressed in q17.compress(uncompressed_test_path)
Beispiel #5
0
def part_a(data):
    seen = set()
    grid = ZGrid(data)
    while True:
        k = tuple(grid.items())
        if k in seen:
            return sum(2**i for i, glyph in enumerate(grid.values())
                       if glyph == "#")
        seen.add(k)
        evolve_a(grid)
Beispiel #6
0
def evolve_b(grids):
    min_grid = min(grids)
    max_grid = max(grids)
    grids[min_grid - 1] = new_empty_grid()  # add new grid above
    grids[max_grid + 1] = new_empty_grid()  # add new grid below
    new_grids = {}
    for depth, grid in grids.items():
        d = {}
        for z0, glyph in grid.items():
            n = 0
            for z in grid.near(z0):
                if z == 2 + 2j:
                    if depth + 1 not in grids:
                        continue
                    grid_down = grids[depth + 1]
                    if z == z0 + ZGrid.down:
                        n += sum([grid_down[i] == "#" for i in range(5)])
                    elif z == z0 + ZGrid.left:
                        n += sum(
                            [grid_down[4 + i * 1j] == "#" for i in range(5)])
                    elif z == z0 + ZGrid.up:
                        n += sum([grid_down[i + 4j] == "#" for i in range(5)])
                    elif z == z0 + ZGrid.right:
                        n += sum([grid_down[i * 1j] == "#" for i in range(5)])
                elif z in grid:
                    n += grid[z] == "#"
                else:
                    if depth - 1 not in grids:
                        continue
                    grid_up = grids[depth - 1]
                    assert z not in grid
                    if z.imag < 0:
                        n += grid_up[2 + 1j] == "#"
                    if z.imag > 4:
                        n += grid_up[2 + 3j] == "#"
                    if z.real < 0:
                        n += grid_up[1 + 2j] == "#"
                    if z.real > 4:
                        n += grid_up[3 + 2j] == "#"
            if glyph == "#":
                if n == 1:
                    d[z0] = "#"
                else:
                    d[z0] = "."
            elif 1 <= n <= 2:
                d[z0] = "#"
            else:
                d[z0] = glyph
        new_grids[depth] = ZGrid(d)
    if "#" not in new_grids[min_grid - 1].values():
        del new_grids[min_grid - 1]
    if "#" not in new_grids[max_grid + 1].values():
        del new_grids[max_grid + 1]
    grids.clear()
    grids.update(new_grids)
Beispiel #7
0
def decode(keypad):
    keypad = ZGrid(keypad)
    z = keypad.z("5")
    code = ""
    for line in data.splitlines():
        for direction in line:
            dz = getattr(ZGrid, direction)
            if keypad.get(z + dz, " ").strip():
                z += dz
        code += keypad[z]
    return code
Beispiel #8
0
def part_b(data, t=200):
    zgrid = ZGrid(data)
    del zgrid[2 + 2j]
    grids = {0: zgrid}
    for i in range(t):
        evolve_b(grids)
    n_bugs = 0
    for depth, grid in sorted(grids.items()):
        # print(f"Depth {depth}:")
        # grid.draw(pretty=False, overlay={2+2j: "?"})
        n_bugs += grid.n_on
    return n_bugs
Beispiel #9
0
def launch(dx, dy, draw=False):
    x = y = y_max = 0
    xys = []
    while y >= y0 and x <= x1:
        x += dx
        y += dy
        xys.append((x, y))
        dx -= 1 if dx > 0 else -1 if dx < 0 else 0  # drag
        dy -= 1  # gravity
        y_max = max(y_max, y)
        if (x, y) in target_area:
            if draw:
                grid = ZGrid({0: "S"} | {complex(*z): "T" for z in target_area})
                grid.draw(flip="y", overlay={complex(*xy): "#" for xy in xys})
            return y_max
Beispiel #10
0
    def __init__(self, data, part="a"):
        grid = ZGrid(data, on=".", off="#")
        if part == "b":
            [z0] = [z for z, v in grid.items() if v == "@"]
            pos = ("@0", "@1", "@2", "@3")
            for dz in [0, -1j, 1, 1j, -1]:
                grid[z0 + dz] = grid.off
            for p, dz in zip(pos, [-1 - 1j, 1 - 1j, 1 + 1j, -1 + 1j]):
                grid[z0 + dz] = p
        else:
            pos = ("@", )
        graph = grid.graph(extra=frozenset(string.ascii_letters).union(pos))
        self.all_keys = {k for k in graph.extra if k in string.ascii_lowercase}

        # map of the other keys that must be acquired before each key is acquired
        self.obstructions = {k: set() for k in self.all_keys}
        # precached distances between points of interest
        self.kdist = {}
        for k0 in self.all_keys:
            for p in pos:
                try:
                    path = nx.shortest_path(graph, graph.extra[p],
                                            graph.extra[k0])
                except nx.NetworkXNoPath:
                    pass
                else:
                    break
            else:
                raise Exception
            self.kdist[k0, p] = self.kdist[p, k0] = len(path) - 1
            assert path[0] == graph.extra[p]
            assert path[-1] == graph.extra[k0]
            for p in path[1:-1]:
                if p in graph.extra.values():
                    k1 = grid[p].lower()
                    self.obstructions[k0].add(k1)

        for k1, k2 in combinations(self.all_keys, 2):
            z1 = graph.extra[k1]
            z2 = graph.extra[k2]
            try:
                d = nx.shortest_path_length(graph, z1, z2)
            except nx.NetworkXNoPath:
                continue
            self.kdist[k1, k2] = self.kdist[k2, k1] = d

        state0 = pos, frozenset()
        AStar.__init__(self, state0, target=None)
Beispiel #11
0
def evolve(grid, part="a"):
    result = ZGrid(grid.d.copy())
    max_occ = 4 if part == "a" else 5
    for z0, c0 in grid.items():
        if c0 == ".":
            continue  # floor
        if part == "a":
            n_occ = grid.count_near(z0, "#", n=8)
        else:
            n_occ = 0
            for dz in grid.near(0, n=8):
                z = z0
                val = "."
                while val == ".":
                    z += dz
                    val = grid.get(z)
                    n_occ += val == "#"
        if c0 == "L" and n_occ == 0:
            result[z0] = "#"
        if c0 == "#" and n_occ >= max_occ:
            result[z0] = "L"
    return result
Beispiel #12
0
 def __getitem__(self, item):
     if isinstance(item, np.ndarray) and item.ndim == 2:
         if item.dtype == "U1":
             item = (item == "#").astype(int)
         item = autocrop(item)
         txt = array2txt(item).replace("0", ".").replace("1", "#")
         if txt in known:
             return known[txt]
         H, W = item.shape
         for k, v in glyphs.items():
             h, w = v.shape
             if h == H and (item[:, :w] == v).all():
                 letter = known[k]
                 return letter + self.__getitem__(item[:, w:])
         item = txt  # trigger fallthrough to __missing__
     if isinstance(item, (dict, ZGrid)):
         grid = ZGrid(item)
         grid.translate({grid.off: 0, grid.on: 1})
         full = np.array(grid)
         full = autocrop(full)
         return self.__getitem__(full)
     if isinstance(item, np.ndarray):
         self.__missing__(array2txt(item))
     return super(DebugDict, self).__getitem__(item)
Beispiel #13
0
def test_pathological_example():
    grid = q17.parsed(test_pathological_intcode)
    assert ZGrid(test_pathological).d == grid.d
    compressed = q17.get_path(grid)
    assert compressed == test_pathological_path
Beispiel #14
0
from aocd import data


class WallMap:
    def __init__(self, fav_number=None):
        if fav_number is None:
            fav_number = int(data)
        self.fav_number = fav_number

    def __call__(self, z):
        if z.real < 0 or z.imag < 0:
            return "#"
        x, y = z.real, z.imag
        fz = x * x + 3 * x + 2 * x * y + y + y * y
        popcount = bin(int(fz) + self.fav_number).count("1")
        result = "#" if popcount % 2 else "."
        return result


z0 = 1 + 1j
target = 31 + 39j


if __name__ == "__main__":
    grid = ZGrid(WallMap(), on=".", off="#")
    depths = grid.bfs(target=target, z0=z0)
    print("part a:", depths[target])
    depths = grid.bfs(z0=z0, max_depth=50)
    print("part b:", len(depths))
    grid.draw_path(z=target, z0=z0)
Beispiel #15
0

class OutOfBeam(Exception):
    pass


@functools.lru_cache(maxsize=100**2)
def beam(z):
    comp = IntComputer(data, inputs=[int(z.imag), int(z.real)])
    comp.run(until=IntComputer.op_output)
    [result] = comp.output
    return result


print("populating 50x50 zgrid...")
grid = ZGrid()
x0 = 0
for y in range(50):
    on = False
    for x in range(x0, 50):
        z = x + y*1j
        val = grid[z] = beam(z)
        if not on and val:
            on = True
            x0 = x
            if x0:
                m = y / x0
        if on and not val:
            break
grid.draw()
print("part a", sum(grid.values()))
Beispiel #16
0
            n_occ = 0
            for dz in grid.near(0, n=8):
                z = z0
                val = "."
                while val == ".":
                    z += dz
                    val = grid.get(z)
                    n_occ += val == "#"
        if c0 == "L" and n_occ == 0:
            result[z0] = "#"
        if c0 == "#" and n_occ >= max_occ:
            result[z0] = "L"
    return result


for p in "ab":
    grid0 = ZGrid(data)
    while True:
        grid1 = evolve(grid0, part=p)
        grid1.draw(clear=True,
                   pretty=True,
                   transform={
                       ".": "  ",
                       "L": "💺",
                       "#": "🧘🏽"
                   })
        if grid1.d == grid0.d:
            break
        grid0 = grid1
    print("part", p, grid0.count("#"))
Beispiel #17
0
"""
--- Day 11: Dumbo Octopus ---
https://adventofcode.com/2021/day/11
"""
from aocd import data
from aoc_wim.zgrid import ZGrid

g = ZGrid(data, transform=int)
a = b = 0
while True:
    b += 1
    n = 0
    flash = []
    for z in g:
        if g[z] == 9:
            g[z] = 0
            flash.append(z)
        else:
            g[z] += 1
    while flash:
        z0 = flash.pop()
        n += 1
        for z in g.near(z0, n=8):
            if g.get(z):
                if g[z] == 9:
                    g[z] = 0
                    flash.append(z)
                else:
                    g[z] += 1
    if b <= 100:
        a += n
Beispiel #18
0
..#...
.#....
#.....
#.....
######


#####
#...#
#...#
#...#
#####
"""


glyphs = {g: np.array(ZGrid(g)) == "#" for g in glyphs.strip().split("\n\n\n")}
known = dict(zip(glyphs, "AABBCCEEFFGGHHHIIJJKKLLNOPPRRSUXZZ□"))
known[""] = ""


def autocrop(A):
    if A.dtype != int:
        cropped = autocrop((A == "#").astype(int))
        return np.where(cropped, "#", ".")
    on = np.argwhere(A)
    if not on.size:
        return A[:0, :0]
    r0, c0 = on.min(axis=0)
    r1, c1 = on.max(axis=0) + 1
    return A[r0:r1, c0:c1]
Beispiel #19
0
"""
--- Day 24: Air Duct Spelunking ---
https://adventofcode.com/2016/day/24
"""
from aoc_wim.zgrid import ZGrid
from itertools import combinations
from itertools import permutations
import networkx as nx
from aocd import data

grid = ZGrid(data, on=".", off="#")
graph = grid.graph(extra="0123456789")
distances = {}
for a, b in combinations(graph.extra, 2):
    d = nx.shortest_path_length(graph, graph.extra[a], graph.extra[b])
    distances[a, b] = distances[b, a] = d


def shortest_path(part="a"):
    paths = {}
    for nodes in permutations(graph.extra.keys() - {"0"}):
        path = "0" + "".join(nodes)
        if part == "b":
            path += "0"
        paths[path] = sum(distances[a, b] for a, b in zip(path, path[1:]))
    return min(paths.values())


print("part a:", shortest_path(part="a"))
print("part b:", shortest_path(part="b"))
Beispiel #20
0
"""
--- Day 9: Smoke Basin ---
https://adventofcode.com/2021/day/9
"""
from aocd import data
import networkx as nx
from aoc_wim.zgrid import ZGrid

g = ZGrid(data)
bs = sorted(nx.connected_components(g.graph(extra="012345678")), key=len)
print("part a:", sum(min([1 + int(g[z]) for z in b]) for b in bs))
print("part b:", len(bs[-1]) * len(bs[-2]) * len(bs[-3]))
def test_n_points_in_10x10():
    grid = ZGrid(minibeam)
    count = 0
    for z in zrange(0, 10 + 10j):
        count += grid.get(z) == "#"
    assert count == 27
Beispiel #22
0
def parsed(data, part="a"):
    grid = ZGrid(data)
    A = (np.array(grid) == "#").astype(int)
    if part == "b":
        A[0, 0] = A[0, -1] = A[-1, 0] = A[-1, -1] = 1
    return A
def test_square():
    grid = ZGrid(minibeam)
    expected = grid.z("O")
    grid.translate({".": 0, "O": 1, "#": 1})
    z = q19.locate_square(beam=grid.get, width=10, hi=grid.height)
    assert z == 25 + 20j == expected
Beispiel #24
0
def test_calibrate():
    assert q17.calibration(ZGrid(test_calibration)) == 76
Beispiel #25
0
def parsed(data):
    zs = ZGrid(data).z(val="#", first=False)
    xys = [(int(z.real), int(z.imag)) for z in zs]
    assert len(xys) == data.count("#")
    return xys
Beispiel #26
0
"""
--- Day 19: A Series of Tubes ---
https://adventofcode.com/2017/day/19
"""
from aocd import data
from aoc_wim.zgrid import ZGrid


grid = ZGrid(data)
z = data.splitlines()[0].index("|")
assert grid[z] == "|"
dz = ZGrid.down
letters = ""
n_steps = 0
while True:
    z += dz
    n_steps += 1
    a = grid.get(z, " ")
    if a == "+":
        dz *= ZGrid.turn_left
        if grid.get(z + dz, " ") == " ":
            dz *= ZGrid.turn_around
    elif a not in "-| ":
        letters += a
    elif a == " ":
        break

print("part a:", letters)
print("part b:", n_steps)
Beispiel #27
0
def new_empty_grid():
    grid = ZGrid(".....\n" * 5)
    del grid[2 + 2j]
    return grid
Beispiel #28
0
                grid[row, col] = tile
                del tiles[tile.id]
                break

full_rows = [
    np.hstack([grid[r, c].trimmed() for c in range(n)]) for r in range(n)
]
full_grid = np.vstack(full_rows)
big_tile = Tile(0, full_grid)

sea_monster_raw = """\
                  #
#    ##    ##    ###
 #  #  #  #  #  #
"""
sea_monster = (np.array(ZGrid(sea_monster_raw)) == "#").astype(int)
n = sea_monster.sum()
h, w = sea_monster.shape
H, W = big_tile.data.shape

n_monsters = 0
while n_monsters <= 0:
    big_tile.transform()
    n_monsters = 0
    for row in range(H - h):
        for col in range(W - w):
            section = big_tile.data[row:row + h, col:col + w]
            if (sea_monster * section).sum() == n:
                n_monsters += 1

print("part b:", big_tile.data.sum() - n_monsters * sea_monster.sum())
Beispiel #29
0
                        pathABC = pathABC[len(A):].lstrip(",")
                        compressed_pathABC += "A,"
                    if pathABC.startswith(B):
                        pathABC = pathABC[len(B):].lstrip(",")
                        compressed_pathABC += "B,"
                    if pathABC.startswith(C):
                        pathABC = pathABC[len(C):].lstrip(",")
                        compressed_pathABC += "C,"
                if not pathABC:
                    result = compressed_pathABC.rstrip(",")
                    if len(result) <= mem:
                        results.append("\n".join([result, A, B, C]))
    return results


assert calibration(ZGrid(test_calibration)) == 76
uncompressed_test_path = get_path(ZGrid(test_path), compressed=False)
assert uncompressed_test_path == "R,8,R,8,R,4,R,4,R,8,L,6,L,2,R,4,R,4,R,8,R,8,R,8,L,6,L,2"
assert """\
A,B,C,B,A,C
R,8,R,8
R,4,R,4,R,8
L,6,L,2""" in compress(uncompressed_test_path)

grid = parsed(data)
print("part a", calibration(grid))
comp = IntComputer(data)
comp.reg[0] = 2
path = get_path(grid)
comp.input_text(path)
comp.run()
def test_animate():
    A = np.full((3, 7), ".")
    for frame, line in zip(frames.split("\n\n"), data.splitlines()):
        A = q08.animate(A, line)
        expected = np.array(ZGrid(frame))
        assert (A == expected).all()