Advent of Code 2024, Day 12

This was one of the rare days, maybe the first one this year, where my code ran bug-free and gave the correct answer right when I ran it for the first time.

The hardest part was to formalize the fences:

Before I abstracted the directions as tuples in part 2, I hard-coded them and had a lot of code duplication.

Part 1

import sys

def extension(lines, i, j, x, acc):
    if not 0 <= i < len(lines): return set()
    if not 0 <= j < len(lines[i]): return set()
    if lines[i][j] != x: return set()
    if (i, j) in acc: return set()
    acc.add((i, j))
    acc.update(extension(lines, i-1, j, x, acc))
    acc.update(extension(lines, i+1, j, x, acc))
    acc.update(extension(lines, i, j-1, x, acc))
    acc.update(extension(lines, i, j+1, x, acc))
    return acc

def price(region):
    area = len(region)
    perimeter = area * 4
    for i, j in region:
        if (i-1, j) in region: perimeter -= 1
        if (i+1, j) in region: perimeter -= 1
        if (i, j-1) in region: perimeter -= 1
        if (i, j+1) in region: perimeter -= 1
    return area * perimeter

with open(sys.argv[1]) as f:
    lines = f.readlines()
    lines = [line.strip() for line in lines]

s = 0
visited = set()
for i in range(len(lines)):
    for j in range(len(lines[i])):
        if (i, j) in visited: continue
        region = extension(lines, i, j, lines[i][j], set())
        s += price(region)
        visited.update(region)
print(s)

Part 2

import sys

def extension(lines, i, j, x, acc):
    if not 0 <= i < len(lines): return set()
    if not 0 <= j < len(lines[i]): return set()
    if lines[i][j] != x: return set()
    if (i, j) in acc: return set()
    acc.add((i, j))
    acc.update(extension(lines, i-1, j, x, acc))
    acc.update(extension(lines, i+1, j, x, acc))
    acc.update(extension(lines, i, j-1, x, acc))
    acc.update(extension(lines, i, j+1, x, acc))
    return acc

def price(region):
    area = len(region)
    facing = {}
    facing[(-1, 0)] = set()
    facing[(+1, 0)] = set()
    facing[(0, -1)] = set()
    facing[(0, +1)] = set()
    for (i, j) in region:
        for ((diri, dirj), tiles) in facing.items():
            if (i+diri, j+dirj) not in region:
                tiles.add((i, j))
    sides = 0
    for ((diri, dirj), tiles) in facing.items():
        while len(tiles) != 0:
            i, j = tiles.pop()
            n = 1
            while (i+n*dirj, j+n*diri) in tiles:
                tiles.remove((i+n*dirj, j+n*diri))
                n += 1
            n = 1
            while (i-n*dirj, j-n*diri) in tiles:
                tiles.remove((i-n*dirj, j-n*diri))
                n += 1
            sides += 1
    return area * sides

with open(sys.argv[1]) as f:
    lines = f.readlines()
    lines = [line.strip() for line in lines]

s = 0
visited = set()
for i in range(len(lines)):
    for j in range(len(lines[i])):
        if (i, j) in visited: continue
        region = extension(lines, i, j, lines[i][j], set())
        s += price(region)
        visited.update(region)
print(s)

2024-12-12

Proxy Information
Original URL
gemini://dkalak.de/aoc/12.gmi
Status Code
Success (20)
Meta
text/gemini; lang=en
Capsule Response Time
165.375995 milliseconds
Gemini-to-HTML Time
0.257963 milliseconds

This content has been proxied by September (ba2dc).