Update python solutions
This commit is contained in:
parent
083e284d1a
commit
26a81da2f3
25 changed files with 438 additions and 661 deletions
|
@ -1,15 +1,8 @@
|
|||
from utils.parsing import ints
|
||||
from lib import *
|
||||
|
||||
input = read_input(2022, 1)
|
||||
|
||||
def part1(puzzle: str):
|
||||
return max(sum(ints(p)) for p in puzzle.split("\n\n"))
|
||||
nums = [sum(ints(p)) for p in input.split("\n\n")]
|
||||
|
||||
|
||||
def part2(puzzle: str):
|
||||
return sum(sorted([sum(ints(p)) for p in puzzle.split("\n\n")], reverse=True)[:3])
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
from aoc import run
|
||||
|
||||
run(2022, 1, part1, part2)
|
||||
print(max(nums))
|
||||
print(sum(sorted(nums, reverse=True)[:3]))
|
||||
|
|
|
@ -1,36 +1,29 @@
|
|||
def part1(puzzle: str):
|
||||
out = 0
|
||||
for line in puzzle.splitlines():
|
||||
a, b = line.split()
|
||||
a = ord(a) - ord("A")
|
||||
b = ord(b) - ord("X")
|
||||
w = (a - b) % 3
|
||||
if w == 0:
|
||||
out += 3 + b + 1
|
||||
elif w == 1:
|
||||
out += b + 1
|
||||
elif w == 2:
|
||||
out += 6 + b + 1
|
||||
return out
|
||||
from lib import *
|
||||
|
||||
input = read_input(2022, 2)
|
||||
|
||||
def part2(puzzle: str):
|
||||
out = 0
|
||||
for line in puzzle.splitlines():
|
||||
a, b = line.split()
|
||||
a = ord(a) - ord("A")
|
||||
b = ord(b) - ord("X")
|
||||
w = (a + b - 1) % 3
|
||||
if b == 0:
|
||||
out += w + 1
|
||||
elif b == 1:
|
||||
out += 3 + w + 1
|
||||
elif b == 2:
|
||||
out += 6 + w + 1
|
||||
return out
|
||||
part1 = 0
|
||||
part2 = 0
|
||||
for line in input.splitlines():
|
||||
a, b = line.split()
|
||||
a = ord(a) - ord("A")
|
||||
b = ord(b) - ord("X")
|
||||
|
||||
w = (a - b) % 3
|
||||
if w == 0:
|
||||
part1 += 3 + b + 1
|
||||
elif w == 1:
|
||||
part1 += b + 1
|
||||
elif w == 2:
|
||||
part1 += 6 + b + 1
|
||||
|
||||
if __name__ == "__main__":
|
||||
from aoc import run
|
||||
w = (a + b - 1) % 3
|
||||
if b == 0:
|
||||
part2 += w + 1
|
||||
elif b == 1:
|
||||
part2 += 3 + w + 1
|
||||
elif b == 2:
|
||||
part2 += 6 + w + 1
|
||||
|
||||
run(2022, 2, part1, part2)
|
||||
print(part1)
|
||||
print(part2)
|
||||
|
|
|
@ -1,19 +1,19 @@
|
|||
def part1(puzzle: str):
|
||||
return sum(
|
||||
from lib import *
|
||||
|
||||
input = read_input(2022, 3)
|
||||
|
||||
lines = input.splitlines()
|
||||
|
||||
print(
|
||||
sum(
|
||||
ord((x := ({*line[: len(line) // 2]} & {*line[len(line) // 2 :]}).pop()).upper()) - 64 + 26 * x.isupper()
|
||||
for line in puzzle.splitlines()
|
||||
for line in lines
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
def part2(puzzle: str):
|
||||
lines = puzzle.splitlines()
|
||||
return sum(
|
||||
print(
|
||||
sum(
|
||||
ord((x := ({*lines[i]} & {*lines[i + 1]} & {*lines[i + 2]}).pop()).upper()) - 64 + 26 * x.isupper()
|
||||
for i in range(0, len(lines), 3)
|
||||
)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
from aoc import run
|
||||
|
||||
run(2022, 3, part1, part2)
|
||||
)
|
||||
|
|
|
@ -1,15 +1,9 @@
|
|||
from utils.parsing import pints
|
||||
from lib import *
|
||||
|
||||
input = read_input(2022, 4)
|
||||
|
||||
lines = [*map(pints, input.splitlines())]
|
||||
|
||||
|
||||
def part1(puzzle: str):
|
||||
return sum(a <= c <= d <= b or c <= a <= b <= d for a, b, c, d in map(pints, puzzle.splitlines()))
|
||||
|
||||
|
||||
def part2(puzzle: str):
|
||||
return sum(d >= a and c <= b for a, b, c, d in map(pints, puzzle.splitlines()))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
from aoc import run
|
||||
|
||||
run(2022, 4, part1, part2)
|
||||
print(sum(a <= c <= d <= b or c <= a <= b <= d for a, b, c, d in lines))
|
||||
print(sum(d >= a and c <= b for a, b, c, d in lines))
|
||||
|
|
|
@ -1,36 +1,28 @@
|
|||
from utils.parsing import pints
|
||||
from lib import *
|
||||
|
||||
input = read_input(2022, 5)
|
||||
|
||||
initial, instructions = input.split("\n\n")
|
||||
nums, *initial = initial.splitlines()[::-1]
|
||||
|
||||
stacks = [[] for _ in pints(nums)]
|
||||
for line in initial:
|
||||
for i in range(0, len(line), 4):
|
||||
if line[i + 1] != " ":
|
||||
stacks[i // 4].append(line[i + 1])
|
||||
|
||||
instructions = [(cnt, i - 1, j - 1) for cnt, i, j in map(pints, instructions.splitlines())]
|
||||
|
||||
|
||||
def get_input(puzzle: str) -> tuple[list[list[str]], list[tuple[int, int, int]]]:
|
||||
initial, instructions = puzzle.split("\n\n")
|
||||
nums, *initial = initial.splitlines()[::-1]
|
||||
|
||||
stacks: list[list[str]] = [[] for _ in pints(nums)]
|
||||
for line in initial:
|
||||
for i in range(0, len(line), 4):
|
||||
if line[i + 1] != " ":
|
||||
stacks[i // 4].append(line[i + 1])
|
||||
|
||||
return stacks, [(cnt, i - 1, j - 1) for cnt, i, j in map(pints, instructions.splitlines())]
|
||||
_stacks = deepcopy(stacks)
|
||||
for cnt, i, j in instructions:
|
||||
for _ in range(cnt):
|
||||
stacks[j].append(stacks[i].pop())
|
||||
print("".join(s[-1] for s in stacks))
|
||||
|
||||
|
||||
def part1(puzzle: str):
|
||||
stacks, instructions = get_input(puzzle)
|
||||
for cnt, i, j in instructions:
|
||||
for _ in range(cnt):
|
||||
stacks[j].append(stacks[i].pop())
|
||||
return "".join(s[-1] for s in stacks)
|
||||
|
||||
|
||||
def part2(puzzle: str):
|
||||
stacks, instructions = get_input(puzzle)
|
||||
for cnt, i, j in instructions:
|
||||
stacks[j] += stacks[i][-cnt:]
|
||||
del stacks[i][-cnt:]
|
||||
return "".join(s[-1] for s in stacks)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
from aoc import run
|
||||
|
||||
run(2022, 5, part1, part2)
|
||||
stacks = _stacks
|
||||
for cnt, i, j in instructions:
|
||||
stacks[j] += stacks[i][-cnt:]
|
||||
del stacks[i][-cnt:]
|
||||
print("".join(s[-1] for s in stacks))
|
||||
|
|
|
@ -1,20 +1,15 @@
|
|||
def get_input(puzzle: str) -> str:
|
||||
return puzzle
|
||||
from lib import *
|
||||
|
||||
input = read_input(2022, 6)
|
||||
|
||||
|
||||
def part1(puzzle: str):
|
||||
for i in range(4, len(puzzle)):
|
||||
if len(set(puzzle[i - 4 : i])) == 4:
|
||||
return i
|
||||
for i in range(4, len(input)):
|
||||
if len(set(input[i - 4 : i])) == 4:
|
||||
print(i)
|
||||
break
|
||||
|
||||
|
||||
def part2(puzzle: str):
|
||||
for i in range(14, len(puzzle)):
|
||||
if len(set(puzzle[i - 14 : i])) == 14:
|
||||
return i
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
from aoc import run
|
||||
|
||||
run(2022, 6, part1, part2)
|
||||
for i in range(14, len(input)):
|
||||
if len(set(input[i - 14 : i])) == 14:
|
||||
print(i)
|
||||
break
|
||||
|
|
|
@ -1,53 +1,36 @@
|
|||
import re
|
||||
from lib import *
|
||||
|
||||
input = read_input(2022, 7)
|
||||
|
||||
sizes = {}
|
||||
seen = set()
|
||||
pwd = ()
|
||||
for line in input.splitlines():
|
||||
if match := re.match(r"^\$ cd (.+)$", line):
|
||||
d = match[1]
|
||||
if d == "/":
|
||||
pwd = ()
|
||||
elif d == "..":
|
||||
pwd = pwd[:-1]
|
||||
else:
|
||||
pwd = (*pwd, d)
|
||||
elif match := re.match(r"^(\d+) (.+)$", line):
|
||||
s = int(match[1])
|
||||
n = match[2]
|
||||
|
||||
if (pwd, n) in seen:
|
||||
continue
|
||||
seen.add((pwd, n))
|
||||
|
||||
d = pwd
|
||||
while True:
|
||||
sizes[d] = sizes.get(d, 0) + s
|
||||
if not d:
|
||||
break
|
||||
d = d[:-1]
|
||||
|
||||
|
||||
def get_input(puzzle: str) -> list[str]:
|
||||
return puzzle.splitlines()
|
||||
print(sum(s for s in sizes.values() if s <= 100000))
|
||||
|
||||
|
||||
def get_dir_sizes(lines: list[str]) -> dict[tuple[str, ...], int]:
|
||||
out = {}
|
||||
seen = set()
|
||||
pwd = ()
|
||||
for line in lines:
|
||||
if match := re.match(r"^\$ cd (.+)$", line):
|
||||
d = match[1]
|
||||
if d == "/":
|
||||
pwd = ()
|
||||
elif d == "..":
|
||||
pwd = pwd[:-1]
|
||||
else:
|
||||
pwd = (*pwd, d)
|
||||
elif match := re.match(r"^(\d+) (.+)$", line):
|
||||
s = int(match[1])
|
||||
n = match[2]
|
||||
|
||||
if (pwd, n) in seen:
|
||||
continue
|
||||
seen.add((pwd, n))
|
||||
|
||||
d = pwd
|
||||
while True:
|
||||
out[d] = out.get(d, 0) + s
|
||||
if not d:
|
||||
break
|
||||
d = d[:-1]
|
||||
|
||||
return out
|
||||
|
||||
|
||||
def part1(puzzle: str):
|
||||
sizes = get_dir_sizes(get_input(puzzle))
|
||||
return sum(s for s in sizes.values() if s <= 100000)
|
||||
|
||||
|
||||
def part2(puzzle: str):
|
||||
sizes = get_dir_sizes(get_input(puzzle))
|
||||
free = 70000000 - sizes[()]
|
||||
return min(s for s in sizes.values() if s >= 30000000 - free)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
from aoc import run
|
||||
|
||||
run(2022, 7, part1, part2)
|
||||
free = 70000000 - sizes[()]
|
||||
print(min(s for s in sizes.values() if s >= 30000000 - free))
|
||||
|
|
|
@ -1,51 +1,40 @@
|
|||
from utils.grid import NEIGH_DIRECT
|
||||
from lib import *
|
||||
|
||||
input = read_input(2022, 8)
|
||||
|
||||
grid = [[*map(int, line)] for line in input.splitlines()]
|
||||
|
||||
|
||||
def get_input(puzzle: str) -> list[list[int]]:
|
||||
return [[*map(int, line)] for line in puzzle.splitlines()]
|
||||
w, h = len(grid[0]), len(grid)
|
||||
visible = set()
|
||||
for dx, dy in NEIGH_DIRECT:
|
||||
for y in range(h):
|
||||
for x in range(w):
|
||||
i = y + dy
|
||||
j = x + dx
|
||||
while i in range(h) and j in range(w):
|
||||
if grid[i][j] >= grid[y][x]:
|
||||
break
|
||||
i += dy
|
||||
j += dx
|
||||
else:
|
||||
visible.add((x, y))
|
||||
print(len(visible))
|
||||
|
||||
|
||||
def part1(puzzle: str):
|
||||
grid = get_input(puzzle)
|
||||
w, h = len(grid[0]), len(grid)
|
||||
visible = set()
|
||||
for dx, dy in NEIGH_DIRECT:
|
||||
for y in range(h):
|
||||
for x in range(w):
|
||||
i = y + dy
|
||||
j = x + dx
|
||||
while i in range(h) and j in range(w):
|
||||
if grid[i][j] >= grid[y][x]:
|
||||
break
|
||||
i += dy
|
||||
j += dx
|
||||
else:
|
||||
visible.add((x, y))
|
||||
return len(visible)
|
||||
|
||||
|
||||
def part2(puzzle: str):
|
||||
grid = get_input(puzzle)
|
||||
out = 0
|
||||
for y in range(len(grid)):
|
||||
for x in range(len(grid[y])):
|
||||
d = 1
|
||||
for dx, dy in NEIGH_DIRECT:
|
||||
i = y + dy
|
||||
j = x + dx
|
||||
s = 0
|
||||
while i in range(len(grid)) and j in range(len(grid[0])):
|
||||
s += 1
|
||||
if grid[i][j] >= grid[y][x]:
|
||||
break
|
||||
i += dy
|
||||
j += dx
|
||||
d *= s
|
||||
out = max(out, d)
|
||||
return out
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
from aoc import run
|
||||
|
||||
run(2022, 8, part1, part2)
|
||||
out = 0
|
||||
for y in range(len(grid)):
|
||||
for x in range(len(grid[y])):
|
||||
d = 1
|
||||
for dx, dy in NEIGH_DIRECT:
|
||||
i = y + dy
|
||||
j = x + dx
|
||||
s = 0
|
||||
while i in range(len(grid)) and j in range(len(grid[0])):
|
||||
s += 1
|
||||
if grid[i][j] >= grid[y][x]:
|
||||
break
|
||||
i += dy
|
||||
j += dx
|
||||
d *= s
|
||||
out = max(out, d)
|
||||
print(out)
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
from utils.grid import NEIGH_DICT
|
||||
from lib import *
|
||||
|
||||
input = read_input(2022, 9)
|
||||
|
||||
def get_input(puzzle: str) -> list[tuple[int, int, int]]:
|
||||
return [(*NEIGH_DICT[line[0]], int(line[2:])) for line in puzzle.splitlines()]
|
||||
lines = [(*NEIGH_DICT[line[0]], int(line[2:])) for line in input.splitlines()]
|
||||
|
||||
|
||||
def solve(lines, n):
|
||||
|
@ -21,17 +21,5 @@ def solve(lines, n):
|
|||
return len(visited)
|
||||
|
||||
|
||||
def part1(puzzle: str):
|
||||
lines = get_input(puzzle)
|
||||
return solve(lines, 2)
|
||||
|
||||
|
||||
def part2(puzzle: str):
|
||||
lines = get_input(puzzle)
|
||||
return solve(lines, 10)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
from aoc import run
|
||||
|
||||
run(2022, 9, part1, part2)
|
||||
print(solve(lines, 2))
|
||||
print(solve(lines, 10))
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
from utils.parsing import ints, parse_ascii
|
||||
from lib import *
|
||||
|
||||
input = read_input(2022, 10)
|
||||
|
||||
instructions = [next(iter(ints(line)), None) for line in input.splitlines()]
|
||||
|
||||
|
||||
def get_input(puzzle: str) -> list[int | None]:
|
||||
return [next(iter(ints(line)), None) for line in puzzle.splitlines()]
|
||||
|
||||
|
||||
def execute(instructions: list[int | None]):
|
||||
def execute():
|
||||
x = 1
|
||||
i = 0
|
||||
for inst in instructions:
|
||||
|
@ -17,15 +17,5 @@ def execute(instructions: list[int | None]):
|
|||
x += inst
|
||||
|
||||
|
||||
def part1(puzzle: str):
|
||||
return sum((i + 1) * x for i, x in execute(get_input(puzzle)) if i % 40 == 19)
|
||||
|
||||
|
||||
def part2(puzzle: str):
|
||||
return parse_ascii({(i // 40, i % 40) for i, x in execute(get_input(puzzle)) if abs(i % 40 - x) <= 1})
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
from aoc import run
|
||||
|
||||
run(2022, 10, part1, part2)
|
||||
print(sum((i + 1) * x for i, x in execute() if i % 40 == 19))
|
||||
print(parse_ascii({(i // 40, i % 40) for i, x in execute() if abs(i % 40 - x) <= 1}))
|
||||
|
|
|
@ -1,20 +1,19 @@
|
|||
from utils.parsing import ints
|
||||
import math
|
||||
from lib import *
|
||||
|
||||
input = read_input(2022, 11)
|
||||
|
||||
|
||||
def get_input(puzzle: str) -> tuple[list[tuple[str, int, int, int]], list[list[int]]]:
|
||||
monkeys = []
|
||||
items = []
|
||||
for monkey in puzzle.split("\n\n"):
|
||||
lines = monkey.splitlines()
|
||||
starting = ints(lines[1])
|
||||
(op,) = lines[2].split(maxsplit=3)[3:]
|
||||
(test,) = ints(lines[3])
|
||||
(tt,) = ints(lines[4])
|
||||
(tf,) = ints(lines[5])
|
||||
monkeys.append((op, test, tt, tf))
|
||||
items.append(starting)
|
||||
return monkeys, items
|
||||
monkeys = []
|
||||
items = []
|
||||
for monkey in input.split("\n\n"):
|
||||
lines = monkey.splitlines()
|
||||
starting = ints(lines[1])
|
||||
(op,) = lines[2].split(maxsplit=3)[3:]
|
||||
(test,) = ints(lines[3])
|
||||
(tt,) = ints(lines[4])
|
||||
(tf,) = ints(lines[5])
|
||||
monkeys.append((op, test, tt, tf))
|
||||
items.append(starting)
|
||||
|
||||
|
||||
def simulate(monkeys, items, rounds, div3):
|
||||
|
@ -34,17 +33,5 @@ def simulate(monkeys, items, rounds, div3):
|
|||
return cnt[-1] * cnt[-2]
|
||||
|
||||
|
||||
def part1(puzzle: str):
|
||||
monkeys, items = get_input(puzzle)
|
||||
return simulate(monkeys, items, 20, True)
|
||||
|
||||
|
||||
def part2(puzzle: str):
|
||||
monkeys, items = get_input(puzzle)
|
||||
return simulate(monkeys, items, 10000, False)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
from aoc import run
|
||||
|
||||
run(2022, 11, part1, part2)
|
||||
print(simulate(monkeys, deepcopy(items), 20, True))
|
||||
print(simulate(monkeys, items, 10000, False))
|
||||
|
|
|
@ -1,21 +1,21 @@
|
|||
from utils.grid import get_neighbors
|
||||
from lib import *
|
||||
|
||||
input = read_input(2022, 12)
|
||||
|
||||
|
||||
def get_input(puzzle: str) -> tuple[tuple[int, int], tuple[int, int], list[list[int]]]:
|
||||
grid = []
|
||||
start = None
|
||||
end = None
|
||||
for i, line in enumerate(puzzle.splitlines()):
|
||||
grid.append([ord(c) - 97 if c not in "SE" else {"S": 0, "E": 25}[c] for c in line])
|
||||
if "S" in line:
|
||||
start = line.index("S"), i
|
||||
if "E" in line:
|
||||
end = line.index("E"), i
|
||||
assert start and end
|
||||
return start, end, grid
|
||||
grid = []
|
||||
start = None
|
||||
end = None
|
||||
for i, line in enumerate(input.splitlines()):
|
||||
grid.append([ord(c) - 97 if c not in "SE" else {"S": 0, "E": 25}[c] for c in line])
|
||||
if "S" in line:
|
||||
start = line.index("S"), i
|
||||
if "E" in line:
|
||||
end = line.index("E"), i
|
||||
assert start and end
|
||||
|
||||
|
||||
def bfs(start, grid, target, step):
|
||||
def bfs(start: tuple[int, int], grid, target, step):
|
||||
queue: list[tuple[int, int, int]] = [(0, *start)]
|
||||
visited = set()
|
||||
while queue:
|
||||
|
@ -33,17 +33,5 @@ def bfs(start, grid, target, step):
|
|||
queue.append((d + 1, p, q))
|
||||
|
||||
|
||||
def part1(puzzle: str):
|
||||
start, end, grid = get_input(puzzle)
|
||||
return bfs(start, grid, lambda x, y: (x, y) == end, lambda x, y, p, q: grid[q][p] - grid[y][x] <= 1)
|
||||
|
||||
|
||||
def part2(puzzle: str):
|
||||
_, end, grid = get_input(puzzle)
|
||||
return bfs(end, grid, lambda x, y: grid[y][x] == 0, lambda x, y, p, q: grid[y][x] - grid[q][p] <= 1)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
from aoc import run
|
||||
|
||||
run(2022, 12, part1, part2)
|
||||
print(bfs(start, grid, lambda x, y: (x, y) == end, lambda x, y, p, q: grid[q][p] - grid[y][x] <= 1))
|
||||
print(bfs(end, grid, lambda x, y: grid[y][x] == 0, lambda x, y, p, q: grid[y][x] - grid[q][p] <= 1))
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
def get_input(puzzle: str) -> list[tuple[list, list]]:
|
||||
out = []
|
||||
for pair in puzzle.split("\n\n"):
|
||||
out.append(tuple(map(eval, pair.splitlines())))
|
||||
return out
|
||||
from lib import *
|
||||
|
||||
input = read_input(2022, 13)
|
||||
|
||||
pairs = [tuple(map(eval, pair.splitlines())) for pair in input.split("\n\n")]
|
||||
|
||||
|
||||
def compare(a, b):
|
||||
|
@ -28,19 +28,10 @@ def compare(a, b):
|
|||
return None
|
||||
|
||||
|
||||
def part1(puzzle: str):
|
||||
return sum((i + 1) * (compare(a, b) is True) for i, (a, b) in enumerate(get_input(puzzle)))
|
||||
print(sum((i + 1) * (compare(a, b) is True) for i, (a, b) in enumerate(pairs)))
|
||||
|
||||
|
||||
def part2(puzzle: str):
|
||||
packets = [[[2]], [[6]]] + [x for a, b in get_input(puzzle) for x in [a, b]]
|
||||
|
||||
out = sum(compare(packet, packets[0]) is True for packet in packets) + 1
|
||||
out *= sum(compare(packet, packets[1]) is True for packet in packets) + 1
|
||||
return out
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
from aoc import run
|
||||
|
||||
run(2022, 13, part1, part2)
|
||||
packets = [[[2]], [[6]]] + [x for a, b in pairs for x in [a, b]]
|
||||
out = sum(compare(packet, packets[0]) is True for packet in packets) + 1
|
||||
out *= sum(compare(packet, packets[1]) is True for packet in packets) + 1
|
||||
print(out)
|
||||
|
|
|
@ -1,16 +1,15 @@
|
|||
from utils.grid import iter_line
|
||||
from utils.list import sliding_window
|
||||
from lib import *
|
||||
|
||||
input = read_input(2022, 14)
|
||||
|
||||
|
||||
def get_input(puzzle: str) -> tuple[set[tuple[int, int]], int]:
|
||||
rock = set()
|
||||
maxy = 0
|
||||
for line in puzzle.splitlines():
|
||||
for a, b in sliding_window(line.split(" -> ")):
|
||||
for x, y in iter_line(*map(int, a.split(",")), *map(int, b.split(","))):
|
||||
rock.add((x, y))
|
||||
maxy = max(y, maxy)
|
||||
return rock, maxy
|
||||
rock = set()
|
||||
maxy = 0
|
||||
for line in input.splitlines():
|
||||
for a, b in sliding_window(line.split(" -> ")):
|
||||
for x, y in iter_line(*map(int, a.split(",")), *map(int, b.split(","))):
|
||||
rock.add((x, y))
|
||||
maxy = max(y, maxy)
|
||||
|
||||
|
||||
def simulate(x, y, air, maxy):
|
||||
|
@ -28,23 +27,13 @@ def simulate(x, y, air, maxy):
|
|||
return None
|
||||
|
||||
|
||||
def part1(puzzle: str):
|
||||
rock, maxy = get_input(puzzle)
|
||||
sand = set()
|
||||
while s := simulate(500, 0, lambda x, y: (x, y) not in rock and (x, y) not in sand, maxy):
|
||||
sand.add(s)
|
||||
return len(sand)
|
||||
sand = set()
|
||||
while s := simulate(500, 0, lambda x, y: (x, y) not in rock and (x, y) not in sand, maxy):
|
||||
sand.add(s)
|
||||
print(len(sand))
|
||||
|
||||
|
||||
def part2(puzzle: str):
|
||||
rock, maxy = get_input(puzzle)
|
||||
sand = set()
|
||||
while (500, 0) not in sand:
|
||||
sand.add(simulate(500, 0, lambda x, y: y < maxy + 2 and (x, y) not in rock and (x, y) not in sand, maxy + 2))
|
||||
return len(sand)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
from aoc import run
|
||||
|
||||
run(2022, 14, part1, part2)
|
||||
sand = set()
|
||||
while (500, 0) not in sand:
|
||||
sand.add(simulate(500, 0, lambda x, y: y < maxy + 2 and (x, y) not in rock and (x, y) not in sand, maxy + 2))
|
||||
print(len(sand))
|
||||
|
|
|
@ -1,56 +1,44 @@
|
|||
from itertools import chain
|
||||
from utils.grid import iter_line
|
||||
from utils.parsing import ints
|
||||
from lib import *
|
||||
|
||||
input = read_input(2022, 15)
|
||||
|
||||
lines = [tuple(ints(line)) for line in input.splitlines()]
|
||||
|
||||
|
||||
def get_input(puzzle: str) -> list[tuple[int, int, int, int]]:
|
||||
return [tuple(ints(line)) for line in puzzle.splitlines()]
|
||||
ty = 2000000
|
||||
out = set()
|
||||
beacons = set()
|
||||
for sx, sy, bx, by in lines:
|
||||
d = abs(sx - bx) + abs(sy - by)
|
||||
a = sx - (d - abs(sy - ty))
|
||||
b = sx + (d - abs(sy - ty))
|
||||
out.update(range(a, b + 1))
|
||||
if by == ty:
|
||||
beacons.add(bx)
|
||||
print(len(out - beacons))
|
||||
|
||||
|
||||
def part1(puzzle: str):
|
||||
inp = get_input(puzzle)
|
||||
ty = 2000000
|
||||
out = set()
|
||||
beacons = set()
|
||||
for sx, sy, bx, by in inp:
|
||||
d = abs(sx - bx) + abs(sy - by)
|
||||
a = sx - (d - abs(sy - ty))
|
||||
b = sx + (d - abs(sy - ty))
|
||||
out.update(range(a, b + 1))
|
||||
if by == ty:
|
||||
beacons.add(bx)
|
||||
sensors = []
|
||||
minx = 1e1337
|
||||
miny = 1e1337
|
||||
maxx = -1e1337
|
||||
maxy = -1e1337
|
||||
for sx, sy, bx, by in lines:
|
||||
d = abs(sx - bx) + abs(sy - by)
|
||||
sensors.append((sx, sy, d))
|
||||
|
||||
return len(out - beacons)
|
||||
minx = min(sx, minx)
|
||||
miny = min(sy, miny)
|
||||
maxx = max(sx, maxx)
|
||||
maxy = max(sy, maxy)
|
||||
|
||||
|
||||
def part2(puzzle: str):
|
||||
inp = get_input(puzzle)
|
||||
sensors = []
|
||||
minx = 1e1337
|
||||
miny = 1e1337
|
||||
maxx = -1e1337
|
||||
maxy = -1e1337
|
||||
for sx, sy, bx, by in inp:
|
||||
d = abs(sx - bx) + abs(sy - by)
|
||||
sensors.append((sx, sy, d))
|
||||
|
||||
minx = min(sx, minx)
|
||||
miny = min(sy, miny)
|
||||
maxx = max(sx, maxx)
|
||||
maxy = max(sy, maxy)
|
||||
|
||||
for sx, sy, d in sensors:
|
||||
for x, y in chain(
|
||||
iter_line(sx, sy - d - 1, sx + d + 1, sy),
|
||||
iter_line(sx + d + 1, sy, sx, sy + d + 1),
|
||||
iter_line(sx, sy + d + 1, sx - d - 1, sy),
|
||||
iter_line(sx - d - 1, sy, sx, sy - d - 1),
|
||||
):
|
||||
if minx <= x <= maxx and miny <= y <= maxy and all(abs(x - a) + abs(y - b) > d for a, b, d in sensors):
|
||||
return x * 4000000 + y
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
from aoc import run
|
||||
|
||||
run(2022, 15, part1, part2)
|
||||
for sx, sy, d in sensors:
|
||||
for x, y in itertools.chain(
|
||||
iter_line(sx, sy - d - 1, sx + d + 1, sy),
|
||||
iter_line(sx + d + 1, sy, sx, sy + d + 1),
|
||||
iter_line(sx, sy + d + 1, sx - d - 1, sy),
|
||||
iter_line(sx - d - 1, sy, sx, sy - d - 1),
|
||||
):
|
||||
if minx <= x <= maxx and miny <= y <= maxy and all(abs(x - a) + abs(y - b) > d for a, b, d in sensors):
|
||||
print(x * 4000000 + y)
|
||||
break
|
||||
|
|
|
@ -1,83 +1,56 @@
|
|||
from functools import cache, reduce
|
||||
import re
|
||||
from lib import *
|
||||
|
||||
input = read_input(2022, 16)
|
||||
|
||||
n = len(input.splitlines())
|
||||
graph = [set() for _ in range(n)]
|
||||
rates = [0] * n
|
||||
names = {"AA": 0}
|
||||
|
||||
|
||||
def get_input(puzzle: str) -> tuple[list[int], list[set[int]]]:
|
||||
n = len(puzzle.splitlines())
|
||||
graph = [set() for _ in range(n)]
|
||||
rates = [0] * n
|
||||
names = {"AA": 0}
|
||||
|
||||
def name(n: str) -> int:
|
||||
return names.setdefault(n, len(names))
|
||||
|
||||
for line in puzzle.splitlines():
|
||||
v, r, n = re.match(r"^Valve (.+) has flow rate=(\d+); tunnels? leads? to valves? (.+)$", line).groups() # type: ignore
|
||||
v = name(v)
|
||||
r = int(r)
|
||||
n = list(map(name, n.split(", ")))
|
||||
rates[v] = r
|
||||
graph[v].update(n)
|
||||
return rates, graph
|
||||
def name(n: str) -> int:
|
||||
return names.setdefault(n, len(names))
|
||||
|
||||
|
||||
def part1(puzzle: str):
|
||||
rates, graph = get_input(puzzle)
|
||||
n = len(rates)
|
||||
dist = {i: {j: 0 if i == j else 1 if j in graph[i] else 1e1337 for j in range(n)} for i in range(n)}
|
||||
for k in range(n):
|
||||
for i in range(n):
|
||||
for j in range(n):
|
||||
dist[i][j] = min(dist[i][j], dist[i][k] + dist[k][j])
|
||||
|
||||
@cache
|
||||
def solve(p, time, closed):
|
||||
if time <= 0:
|
||||
return 0
|
||||
|
||||
out = 0
|
||||
for q in range(n):
|
||||
if closed & 1 << q == 0:
|
||||
continue
|
||||
t = time - dist[p][q] - 1
|
||||
out = max(out, solve(q, t, closed & ~(1 << q)) + rates[q] * t)
|
||||
return out
|
||||
|
||||
return solve(0, 30, reduce(lambda acc, x: acc | 1 << x, (i for i in range(n) if rates[i]), 0))
|
||||
for line in input.splitlines():
|
||||
v, r, n = re.match(r"^Valve (.+) has flow rate=(\d+); tunnels? leads? to valves? (.+)$", line).groups() # type: ignore
|
||||
v = name(v)
|
||||
r = int(r)
|
||||
n = list(map(name, n.split(", ")))
|
||||
rates[v] = r
|
||||
graph[v].update(n)
|
||||
|
||||
|
||||
def part2(puzzle: str):
|
||||
rates, graph = get_input(puzzle)
|
||||
n = len(rates)
|
||||
dist = {i: {j: 0 if i == j else 1 if j in graph[i] else 1e1337 for j in range(n)} for i in range(n)}
|
||||
for k in range(n):
|
||||
for i in range(n):
|
||||
for j in range(n):
|
||||
dist[i][j] = min(dist[i][j], dist[i][k] + dist[k][j])
|
||||
n = len(rates)
|
||||
dist = {i: {j: 0 if i == j else 1 if j in graph[i] else 1e1337 for j in range(n)} for i in range(n)}
|
||||
for k in range(n):
|
||||
for i in range(n):
|
||||
for j in range(n):
|
||||
dist[i][j] = min(dist[i][j], dist[i][k] + dist[k][j])
|
||||
|
||||
@cache
|
||||
def solve(p, time, closed):
|
||||
if time <= 0:
|
||||
return 0
|
||||
|
||||
out = 0
|
||||
for q in range(n):
|
||||
if closed & 1 << q == 0:
|
||||
continue
|
||||
t = time - dist[p][q] - 1
|
||||
out = max(out, solve(q, t, closed & ~(1 << q)) + rates[q] * t)
|
||||
return out
|
||||
@cache
|
||||
def solve(p, time, closed):
|
||||
if time <= 0:
|
||||
return 0
|
||||
|
||||
out = 0
|
||||
valves = [i for i in range(n) if rates[i]]
|
||||
for s in range(1 << len(valves)):
|
||||
a = solve(0, 26, reduce(lambda acc, x: acc | 1 << x, (j for i, j in enumerate(valves) if s & 1 << i), 0))
|
||||
b = solve(0, 26, reduce(lambda acc, x: acc | 1 << x, (j for i, j in enumerate(valves) if s & 1 << i == 0), 0))
|
||||
out = max(out, a + b)
|
||||
for q in range(n):
|
||||
if closed & 1 << q == 0:
|
||||
continue
|
||||
t = time - dist[p][q] - 1
|
||||
out = max(out, solve(q, t, closed & ~(1 << q)) + rates[q] * t)
|
||||
return out
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
from aoc import run
|
||||
print(solve(0, 30, reduce(lambda acc, x: acc | 1 << x, (i for i in range(n) if rates[i]), 0)))
|
||||
|
||||
run(2022, 16, part1, part2)
|
||||
|
||||
solve.cache_clear()
|
||||
out = 0
|
||||
valves = [i for i in range(n) if rates[i]]
|
||||
for s in range(1 << len(valves)):
|
||||
a = solve(0, 26, reduce(lambda acc, x: acc | 1 << x, (j for i, j in enumerate(valves) if s & 1 << i), 0))
|
||||
b = solve(0, 26, reduce(lambda acc, x: acc | 1 << x, (j for i, j in enumerate(valves) if s & 1 << i == 0), 0))
|
||||
out = max(out, a + b)
|
||||
print(out)
|
||||
|
|
|
@ -1,3 +1,7 @@
|
|||
from lib import *
|
||||
|
||||
input = read_input(2022, 17).strip()
|
||||
|
||||
SHAPES = [
|
||||
[[1, 1, 1, 1]],
|
||||
[[0, 1, 0], [1, 1, 1], [0, 1, 0]],
|
||||
|
@ -11,11 +15,7 @@ START_X = 2
|
|||
START_Y = 3
|
||||
|
||||
|
||||
def get_input(puzzle: str) -> str:
|
||||
return puzzle
|
||||
|
||||
|
||||
def part1(puzzle: str):
|
||||
def part1():
|
||||
grid = set()
|
||||
height = 0
|
||||
sh = 0
|
||||
|
@ -38,8 +38,8 @@ def part1(puzzle: str):
|
|||
x = START_X
|
||||
y = height + START_Y + len(s) - 1
|
||||
while True:
|
||||
d = {"<": -1, ">": 1}[puzzle[st]]
|
||||
st = (st + 1) % len(puzzle)
|
||||
d = {"<": -1, ">": 1}[input[st]]
|
||||
st = (st + 1) % len(input)
|
||||
if test(s, x + d, y):
|
||||
x += d
|
||||
if test(s, x, y - 1):
|
||||
|
@ -51,7 +51,7 @@ def part1(puzzle: str):
|
|||
return height
|
||||
|
||||
|
||||
def part2(puzzle: str):
|
||||
def part2():
|
||||
grid = set()
|
||||
height = 0
|
||||
st = 0
|
||||
|
@ -87,8 +87,8 @@ def part2(puzzle: str):
|
|||
x = START_X
|
||||
y = height + START_Y + len(s) - 1
|
||||
while True:
|
||||
d = {"<": -1, ">": 1}[puzzle[st]]
|
||||
st = (st + 1) % len(puzzle)
|
||||
d = {"<": -1, ">": 1}[input[st]]
|
||||
st = (st + 1) % len(input)
|
||||
if test(s, x + d, y):
|
||||
x += d
|
||||
if test(s, x, y - 1):
|
||||
|
@ -105,7 +105,5 @@ def part2(puzzle: str):
|
|||
return height
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
from aoc import run
|
||||
|
||||
run(2022, 17, part1, part2)
|
||||
print(part1())
|
||||
print(part2())
|
||||
|
|
|
@ -1,92 +1,62 @@
|
|||
from utils.parsing import ints
|
||||
from lib import *
|
||||
|
||||
input = read_input(2022, 18)
|
||||
|
||||
|
||||
def get_input(puzzle: str) -> list[tuple[int, int, int]]:
|
||||
return [(x, y, z) for x, y, z in map(ints, puzzle.splitlines())]
|
||||
cubes = [(x, y, z) for x, y, z in map(ints, input.splitlines())]
|
||||
|
||||
|
||||
def part1(puzzle: str):
|
||||
cubes = get_input(puzzle)
|
||||
out = 0
|
||||
for i, x in enumerate(cubes):
|
||||
out += 6
|
||||
for y in cubes[:i]:
|
||||
if abs(x[0] - y[0]) + abs(x[1] - y[1]) + abs(x[2] - y[2]) == 1:
|
||||
out -= 2
|
||||
return out
|
||||
out = 0
|
||||
for i, x in enumerate(cubes):
|
||||
out += 6
|
||||
for y in cubes[:i]:
|
||||
if abs(x[0] - y[0]) + abs(x[1] - y[1]) + abs(x[2] - y[2]) == 1:
|
||||
out -= 2
|
||||
print(out)
|
||||
|
||||
|
||||
def part2(puzzle: str):
|
||||
cubes = get_input(puzzle)
|
||||
out = 0
|
||||
(minx, miny, minz) = (maxx, maxy, maxz) = cubes[0]
|
||||
for i, x in enumerate(cubes):
|
||||
out += 6
|
||||
for y in cubes[:i]:
|
||||
if abs(x[0] - y[0]) + abs(x[1] - y[1]) + abs(x[2] - y[2]) == 1:
|
||||
out -= 2
|
||||
minx = min(x[0], minx)
|
||||
miny = min(x[1], miny)
|
||||
minz = min(x[2], minz)
|
||||
maxx = max(x[0], maxx)
|
||||
maxy = max(x[1], maxy)
|
||||
maxz = max(x[2], maxz)
|
||||
out = 0
|
||||
(minx, miny, minz) = (maxx, maxy, maxz) = cubes[0]
|
||||
for i, x in enumerate(cubes):
|
||||
out += 6
|
||||
for y in cubes[:i]:
|
||||
if abs(x[0] - y[0]) + abs(x[1] - y[1]) + abs(x[2] - y[2]) == 1:
|
||||
out -= 2
|
||||
minx = min(x[0], minx)
|
||||
miny = min(x[1], miny)
|
||||
minz = min(x[2], minz)
|
||||
maxx = max(x[0], maxx)
|
||||
maxy = max(x[1], maxy)
|
||||
maxz = max(x[2], maxz)
|
||||
|
||||
checked = set()
|
||||
candidates = [
|
||||
(i, j, k)
|
||||
for x, y, z in cubes
|
||||
for i, j, k in [
|
||||
(x, y, z - 1),
|
||||
(x, y, z + 1),
|
||||
(x, y - 1, z),
|
||||
(x, y + 1, z),
|
||||
(x - 1, y, z),
|
||||
(x + 1, y, z),
|
||||
]
|
||||
if (i, j, k) not in cubes
|
||||
]
|
||||
while candidates:
|
||||
queue = [candidates.pop()]
|
||||
if queue[0] in checked:
|
||||
checked = set()
|
||||
candidates = [
|
||||
(i, j, k)
|
||||
for x, y, z in cubes
|
||||
for i, j, k in [(x, y, z - 1), (x, y, z + 1), (x, y - 1, z), (x, y + 1, z), (x - 1, y, z), (x + 1, y, z)]
|
||||
if (i, j, k) not in cubes
|
||||
]
|
||||
while candidates:
|
||||
queue = [candidates.pop()]
|
||||
if queue[0] in checked:
|
||||
continue
|
||||
visited = set()
|
||||
while queue:
|
||||
x, y, z = queue.pop(0)
|
||||
if x not in range(minx, maxx + 1) or y not in range(miny, maxy + 1) or z not in range(minz, maxz + 1):
|
||||
break
|
||||
if (x, y, z) in visited:
|
||||
continue
|
||||
visited = set()
|
||||
while queue:
|
||||
x, y, z = queue.pop(0)
|
||||
if x not in range(minx, maxx + 1) or y not in range(miny, maxy + 1) or z not in range(minz, maxz + 1):
|
||||
break
|
||||
if (x, y, z) in visited:
|
||||
continue
|
||||
visited.add((x, y, z))
|
||||
for q in [
|
||||
(x, y, z - 1),
|
||||
(x, y, z + 1),
|
||||
(x, y - 1, z),
|
||||
(x, y + 1, z),
|
||||
(x - 1, y, z),
|
||||
(x + 1, y, z),
|
||||
]:
|
||||
if q not in cubes and q not in visited:
|
||||
queue.append(q)
|
||||
else:
|
||||
for x, y, z in visited:
|
||||
for q in [
|
||||
(x, y, z - 1),
|
||||
(x, y, z + 1),
|
||||
(x, y - 1, z),
|
||||
(x, y + 1, z),
|
||||
(x - 1, y, z),
|
||||
(x + 1, y, z),
|
||||
]:
|
||||
if q in cubes:
|
||||
out -= 1
|
||||
visited.add((x, y, z))
|
||||
for q in [(x, y, z - 1), (x, y, z + 1), (x, y - 1, z), (x, y + 1, z), (x - 1, y, z), (x + 1, y, z)]:
|
||||
if q not in cubes and q not in visited:
|
||||
queue.append(q)
|
||||
else:
|
||||
for x, y, z in visited:
|
||||
for q in [(x, y, z - 1), (x, y, z + 1), (x, y - 1, z), (x, y + 1, z), (x - 1, y, z), (x + 1, y, z)]:
|
||||
if q in cubes:
|
||||
out -= 1
|
||||
|
||||
checked.update(visited)
|
||||
checked.update(visited)
|
||||
|
||||
return out
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
from aoc import run
|
||||
|
||||
run(2022, 18, part1, part2)
|
||||
print(out)
|
||||
|
|
19
Python/justfile
Normal file
19
Python/justfile
Normal file
|
@ -0,0 +1,19 @@
|
|||
alias r := run
|
||||
alias t := test
|
||||
alias ty := test-year
|
||||
alias ta := test-all
|
||||
|
||||
_default:
|
||||
@just --list
|
||||
|
||||
run year day:
|
||||
python {{year}}/{{day}}.py
|
||||
|
||||
test year day:
|
||||
@diff <(just run {{year}} {{day}}) <(cat ../.cache/{{year}}/{{trim_start_match(day, "0")}}.{1,2})
|
||||
|
||||
test-year year:
|
||||
@set -e; for day in $(ls {{year}}); do just test {{year}} $(basename $day .py); done
|
||||
|
||||
test-all:
|
||||
@set -e; for year in *; do [[ -d $year ]] || continue; just test-year $year; done
|
|
@ -1,3 +1,39 @@
|
|||
from .graph import *
|
||||
from .grid import *
|
||||
from .lists import *
|
||||
from .math import *
|
||||
from .parsing import *
|
||||
|
||||
del graph, grid, lists, math, parsing
|
||||
|
||||
import ast
|
||||
import bisect
|
||||
import collections
|
||||
import functools
|
||||
import graphlib
|
||||
import heapq
|
||||
import io
|
||||
import itertools
|
||||
import json
|
||||
import math
|
||||
import operator
|
||||
import re
|
||||
import statistics
|
||||
from collections import Counter
|
||||
from copy import deepcopy
|
||||
from functools import cache, partial, reduce
|
||||
from heapq import heapify, heappop, heappush
|
||||
from pathlib import Path
|
||||
|
||||
import numpy as np
|
||||
import pyperclip
|
||||
|
||||
|
||||
def read_input(year: int, day: int) -> str:
|
||||
f = Path(__file__).parent / f"../../.cache/{year}/{day}"
|
||||
return f.read_text()
|
||||
|
||||
|
||||
def ans(answer):
|
||||
print(answer)
|
||||
pyperclip.copy(str(answer))
|
||||
|
|
|
@ -1,33 +0,0 @@
|
|||
import ast
|
||||
import bisect
|
||||
import collections
|
||||
import functools
|
||||
import graphlib
|
||||
import heapq
|
||||
import io
|
||||
import itertools
|
||||
import json
|
||||
import math
|
||||
import operator
|
||||
import re
|
||||
import statistics
|
||||
|
||||
import numpy as np
|
||||
|
||||
import pyperclip
|
||||
|
||||
from .graph import *
|
||||
from .grid import *
|
||||
from .list import *
|
||||
from .math import *
|
||||
from .parsing import *
|
||||
|
||||
heappush = heapq.heappush
|
||||
heappop = heapq.heappop
|
||||
reduce = functools.reduce
|
||||
Counter = collections.Counter
|
||||
|
||||
|
||||
def ans(answer):
|
||||
print(answer)
|
||||
pyperclip.copy(str(answer))
|
Loading…
Add table
Add a link
Reference in a new issue