140 lines
3.8 KiB
Python
140 lines
3.8 KiB
Python
import re
|
|
from lib import *
|
|
|
|
EXAMPLE = """
|
|
467..114..
|
|
...*......
|
|
..35..633.
|
|
......#...
|
|
617*......
|
|
.....+.58.
|
|
..592.....
|
|
......755.
|
|
...$.*....
|
|
.664.598..
|
|
"""
|
|
|
|
def solve(i: Input, second=False):
|
|
"""
|
|
This is a new version I have implemented after the fact to test the
|
|
improvements to my library. My original versions are `solve1` and `solve2`.
|
|
"""
|
|
g = i.grid2()
|
|
if not second:
|
|
parts = g.find_not(NUMBERS + ".")
|
|
else:
|
|
parts = g.find("*")
|
|
|
|
res = 0
|
|
numbers = []
|
|
for p in parts:
|
|
numbers_gear = []
|
|
for n in g.neighbors_adj(p):
|
|
if g[n] in NUMBERS:
|
|
while n in g and g[n] in NUMBERS:
|
|
n = (n[0], n[1] - 1)
|
|
number = ""
|
|
n = (n[0], n[1] + 1)
|
|
start = n
|
|
while n in g and g[n] in NUMBERS:
|
|
number += g[n]
|
|
n = (n[0], n[1] + 1)
|
|
numbers.append((int(number), n[0], start))
|
|
numbers_gear.append(int(number))
|
|
numbers_gear = list(set(numbers_gear))
|
|
if len(numbers_gear) == 2:
|
|
res += numbers_gear[0] * numbers_gear[1]
|
|
|
|
if second:
|
|
return res
|
|
else:
|
|
return sum([n for n, _, _ in list(set(numbers))])
|
|
|
|
def clean(text: str) -> list[str]:
|
|
return list(filter(lambda l: l.strip() != "", text.splitlines()))
|
|
|
|
def is_adj_to_symbol(x, y, lines):
|
|
non_symbol = re.compile(r"[^0-9\.]")
|
|
for xo, yo in [(-1, -1), (-1, 0), (-1, 1), (0, -1), (0, 1), (1, -1), (1, 0), (1, 1)]:
|
|
try:
|
|
if x + xo < 0 or y + yo < 0:
|
|
continue
|
|
if non_symbol.match(lines[y + yo][x + xo]):
|
|
return True
|
|
except IndexError:
|
|
pass
|
|
return False
|
|
|
|
def solve1(lines: list[str]):
|
|
d = ""
|
|
adj_to_symbol = False
|
|
s = 0
|
|
for (y, line) in enumerate(lines):
|
|
for (x, c) in enumerate(line):
|
|
if c not in NUMBERS:
|
|
if len(d) > 0 and adj_to_symbol:
|
|
s += int(d)
|
|
d = ""
|
|
adj_to_symbol = False
|
|
else:
|
|
if is_adj_to_symbol(x, y, lines):
|
|
adj_to_symbol = True
|
|
d = d + c
|
|
return s
|
|
|
|
def get_entire_number(x, y, lines):
|
|
assert(lines[y][x] in NUMBERS)
|
|
x_start = x - 1
|
|
while lines[y][x_start] in NUMBERS and x_start >= 0:
|
|
x_start -= 1
|
|
x_start += 1
|
|
x_start_orig = x_start
|
|
r = ""
|
|
while x_start < len(lines[0]) and lines[y][x_start] in NUMBERS:
|
|
r += lines[y][x_start]
|
|
x_start += 1
|
|
return x_start_orig, int(r)
|
|
|
|
def find_sourrounding_numbers(x, y, lines):
|
|
full_coords = []
|
|
numbers = []
|
|
for xo, yo in [(-1, -1), (0, -1), (1, -1), (-1, 0), (1, 0), (-1, 1), (0, 1), (1, 1)]:
|
|
new_x = x + xo
|
|
new_y = y + yo
|
|
if new_x < 0 or new_y < 0 or new_x >= len(lines[0]) or new_y >= len(lines):
|
|
continue
|
|
if lines[new_y][new_x] in NUMBERS:
|
|
x_start_orig, n = get_entire_number(new_x, new_y, lines)
|
|
if (x_start_orig, new_y, n) not in full_coords:
|
|
numbers.append(n)
|
|
full_coords.append((x_start_orig, y + yo, n))
|
|
return numbers
|
|
|
|
def solve2(lines: list[str]):
|
|
s = 0
|
|
for (y, line) in enumerate(lines):
|
|
for (x, c) in enumerate(line):
|
|
if c == '*':
|
|
numbers = find_sourrounding_numbers(x, y, lines)
|
|
if len(numbers) == 2:
|
|
# print(numbers)
|
|
s += numbers[0] * numbers[1]
|
|
return s
|
|
|
|
def main():
|
|
input = Input(EXAMPLE)
|
|
print("Example 1:", solve(input))
|
|
|
|
input = Input("i3.txt")
|
|
print("Solution 1:", solve(input))
|
|
|
|
input = Input(EXAMPLE)
|
|
print("Example 2:", solve(input, True))
|
|
|
|
input = Input("i3.txt")
|
|
print("Solution 2:", solve(input, True))
|
|
return
|
|
|
|
if __name__ == "__main__":
|
|
main()
|