Exemplo n.º 1
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
Exemplo n.º 2
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)
Exemplo n.º 3
0
    def __init__(self, data, part="a"):
        grid = ZGrid(data, on=".", off="#")
        pos = ("@", )
        if part == "b":
            [z0] = [z for z, v in grid.items() if v == "@"]
            if grid.count_near(z0, val=".", n=8, default=".") == 8:
                pos = ("@0", "@1", "@2", "@3")
                for z in grid.near(z0, n=5):
                    grid[z] = grid.off
                for p, dz in zip(pos, [-1 - 1j, 1 - 1j, 1 + 1j, -1 + 1j]):
                    grid[z0 + dz] = p
        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("nothing possible to do")
            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)
Exemplo n.º 4
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
Exemplo n.º 5
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()
Exemplo n.º 6
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
Exemplo n.º 7
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
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
Exemplo n.º 9
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()))
Exemplo n.º 10
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("#"))
Exemplo n.º 11
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())
Exemplo n.º 12
0
    def check(y):
        z = left_edge_of_beam(y, gradient_estimate, beam)
        val = beam(z + d * ZGrid.NE)
        print(f"y={y}", "wide" if val else "narrow")
        return val

    bisect = Bisect(check, lo=d, hi=hi)
    print("bisecting...")
    y = bisect.run() + 1
    z = left_edge_of_beam(y, gradient_estimate, beam) + d * ZGrid.N
    return z


if __name__ == "__main__":
    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

    print("part a", sum(grid.values()))
Exemplo n.º 13
0
def test_calibrate():
    assert q17.calibration(ZGrid(test_calibration)) == 76
Exemplo n.º 14
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
Exemplo n.º 15
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]))
Exemplo n.º 16
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)
Exemplo n.º 17
0
"""
--- Day 3: Toboggan Trajectory ---
https://adventofcode.com/2020/day/3
"""
from math import prod
from aocd import data
from aoc_wim.zgrid import ZGrid

grid = ZGrid(data)
w, h = grid.width, grid.height
dzs = {}.fromkeys([1 + 1j, 3 + 1j, 5 + 1j, 7 + 1j, 1 + 2j], 0)
for dz in dzs:
    z = grid.top_left
    while z.imag < h:
        dzs[dz] += grid[complex(z.real % w, z.imag)] == "#"
        z += dz

print("part a:", dzs[3 + 1j])
print("part b:", prod(dzs.values()))
Exemplo n.º 18
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)
Exemplo n.º 19
0
  #......CJ                       NM..#...#  
  ###.#.#                           #.###.#  
RE....#.#                           #......RF
  ###.###        X   X       L      #.#.#.#  
  #.....#        F   Q       P      #.#.#.#  
  ###.###########.###.#######.#########.###  
  #.....#...#.....#.......#...#.....#.#...#  
  #####.#.###.#######.#######.###.###.#.#.#  
  #.......#.......#.#.#.#.#...#...#...#.#.#  
  #####.###.#####.#.#.#.#.###.###.#.###.###  
  #.......#.....#.#...#...............#...#  
  #############.#.#.###.###################  
               A O F   N                     
               A A D   M                     """

grid = ZGrid(data, on=".", off="#")
h, w = np.array(grid).shape
dzs = [-1j, 1, 1j, -1]

# parse the warps
outside = {}
inside = {}
for z, glyph in grid.items():
    if glyph in string.ascii_uppercase:
        for dz in dzs:
            if grid.get(z + dz) == ".":
                zp = z + dz  # actual position of portal
                name = glyph + grid.get(z - dz)  # add other letter
                if 3 < z.real < w - 3 and 3 < z.imag < h - 3:
                    side = inside
                else:
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
Exemplo n.º 21
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"))
Exemplo n.º 22
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
Exemplo n.º 23
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]
Exemplo n.º 24
0
def new_empty_grid():
    grid = ZGrid(".....\n" * 5)
    del grid[2 + 2j]
    return grid
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()