summaryrefslogtreecommitdiff
path: root/2024/day12/solve.py
blob: e7a6b7b95a930118bc8b7c28071ed6214d36ee96 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
from typing import Optional
from functools import cmp_to_key


def chart_region(plots: list[list[Optional[str]]], current_pos: tuple[int, int],
                 charted_positions: list[tuple[int, int]], fence_positions: list[list[tuple[int, int]]]) -> tuple[int, int]:
    charted_positions.append(current_pos)
    current_plot = plots[current_pos[0]][current_pos[1]]

    plot_count = 1
    fence_count = 0

    row_cnt = len(data)
    col_cnt = len(data[0])
    for dir_idx, direct in enumerate(((-1, 0), (1, 0), (0, -1), (0, 1))):
        new_pos = (current_pos[0] + direct[0], current_pos[1] + direct[1])
        if 0 <= new_pos[0] < row_cnt and 0 <= new_pos[1] < col_cnt and plots[new_pos[0]][new_pos[1]] == current_plot:
            if new_pos not in charted_positions:
                result = chart_region(plots, new_pos, charted_positions, fence_positions)
                plot_count += result[0]
                fence_count += result[1]
        else:
            fence_positions[dir_idx].append(current_pos)
            fence_count += 1

    return plot_count, fence_count


def compare_row_first(item1: tuple[int, int], item2: tuple[int, int]):
    row_diff = item1[0] - item2[0]
    if row_diff != 0:
        return row_diff

    return item1[1] - item2[1]


def compare_col_first(item1: tuple[int, int], item2: tuple[int, int]):
    col_diff = item1[1] - item2[1]
    if col_diff != 0:
        return col_diff

    return item1[0] - item2[0]


def calculate_discount(fence_positions: list[list[tuple[int, int]]]) -> int:
    adjacents = 0
    for charted_positions in fence_positions:
        charted_positions.sort(key=cmp_to_key(compare_row_first))
        last_pos = None
        for current_pos in charted_positions:
            if last_pos is not None and last_pos[0] == current_pos[0] and last_pos[1] + 1 == current_pos[1]:
                adjacents += 1

            last_pos = current_pos

        charted_positions.sort(key=cmp_to_key(compare_col_first))
        last_pos = None
        for current_pos in charted_positions:
            if last_pos is not None and last_pos[1] == current_pos[1] and last_pos[0] + 1 == current_pos[0]:
                adjacents += 1

            last_pos = current_pos

    return adjacents


use_discount = True  # for part two

data = []
with open("input") as f:
    for line in f:
        data.append(list(line.strip()))

row_count = len(data)
col_count = len(data[0])

final_sum = 0

for row in range(row_count):
    for col in range(col_count):
        if data[row][col] is not None:
            charted_pos: list[tuple[int, int]] = []
            fence_pos: list[list[tuple[int, int]]] = [[], [], [], []]
            chart_result = chart_region(data, (row, col), charted_pos, fence_pos)
            fences = chart_result[1]
            if use_discount:
                fences -= calculate_discount(fence_pos)
            final_sum += chart_result[0] * fences
            for pos in charted_pos:
                data[pos[0]][pos[1]] = None

print(final_sum)