chore: create new project structure and aoc.py runner script

This commit is contained in:
2025-08-04 16:23:06 +02:00
parent f76375d835
commit e2964c6c36
91 changed files with 177 additions and 113 deletions

View File

@@ -0,0 +1,46 @@
from itertools import islice
def window(seq, n=3):
"Returns a sliding window (of width n) over data from the iterable"
" s -> (s0,s1,...s[n-1]), (s1,s2,...,sn), ... "
it = iter(seq)
result = tuple(islice(it, n))
if len(result) == n:
yield result
for elem in it:
result = result[1:] + (elem,)
yield result
def part1(infile):
with open(infile) as f:
lines = f.readlines()
previous = int(lines[0])
i = 0
for line in lines[1:]:
if int(line) > previous:
i += 1
previous = int(line)
print("Part 1 ", i)
def part2(infile):
with open(infile) as f:
lines = f.readlines()
previous = None
i = 0
for w in window(lines):
measure = sum(int(x) for x in w)
if previous is not None and measure > previous:
i += 1
previous = measure
print("Part 2 ", i)
if __name__ == "__main__":
import sys
infile = sys.argv[1] if len(sys.argv) > 1 else "example.txt"
part1(infile)
part2(infile)

View File

@@ -0,0 +1,44 @@
def part1(inp):
horizontal_pos = 0
depth = 0
for line in inp:
command, amount = line.split(" ")
if command == "forward":
horizontal_pos += int(amount)
elif command == "up":
depth -= int(amount)
elif command == "down":
depth += int(amount)
#print(f"horizontal {horizontal_pos}, depth {depth}")
print("Part 1", horizontal_pos * depth)
def part2(inp):
horizontal_pos = 0
depth = 0
aim = 0
for line in inp:
command, amount = line.split(" ")
if command == "forward":
horizontal_pos += int(amount)
# It increases your depth by your aim multiplied by X.
depth += aim * int(amount)
elif command == "up":
aim -= int(amount)
elif command == "down":
aim += int(amount)
#print(f"horizontal {horizontal_pos}, depth {depth}")
print("Part 2", horizontal_pos * depth)
def main(input_file):
with open(input_file) as f:
entries = f.readlines()
part1(entries)
part2(entries)
if __name__ == "__main__":
import sys
infile = sys.argv[1] if len(sys.argv) > 1 else "example.txt"
main(infile)

View File

@@ -0,0 +1,69 @@
def calculate_gamma(inp):
gamma_rate = [0] * len(inp[0])
for line in inp:
for index, char in enumerate(line):
gamma_rate[index] += int(char)
gamma_rate = [0 if x < len(inp) // 2 else 1 for x in gamma_rate]
return gamma_rate
def part1(inp):
gamma = calculate_gamma(inp)
epsilon = [0 if x == 1 else 1 for x in gamma]
# power consumption = dec(gamma_rate) * dec(epsilon_rate)
power = int("".join(str(x) for x in gamma), 2) * int("".join(str(x) for x in epsilon), 2)
print("Part 1, power consumption : ", power)
def calculate_most_common(inp, pos):
sum = 0
for line in inp:
sum += int(line[pos])
return 0 if sum < len(inp) // 2 else 1
def filter_oxygen(inp, pos, most_common):
result = []
for line in inp:
if int(line[pos]) == most_common:
result.append(line)
return result
def oxygen_rating(inp):
result = inp[:]
for pos in range(len(inp[0])):
most_common = calculate_most_common(result, pos)
result = filter_oxygen(result, pos, most_common)
if len(result) == 1:
return result
def co2_rating(inp):
result = inp[:]
for pos in range(len(inp[0])):
least_common = 1 - calculate_most_common(result, pos)
result = filter_oxygen(result, pos, least_common)
if len(result) == 1:
return result
def part2(inp):
oxygen = oxygen_rating(inp)
co2 = co2_rating(inp)
res = int("".join(str(x) for x in oxygen), 2) * int("".join(str(x) for x in co2), 2)
print(f"Part 2 : {res}")
def main(input_file):
with open(input_file) as f:
entries = [x.rstrip() for x in f.readlines()]
part1(entries)
part2(entries)
if __name__ == "__main__":
import sys
infile = sys.argv[1] if len(sys.argv) > 1 else "example.txt"
main(infile)

View File

@@ -0,0 +1,99 @@
from dataclasses import dataclass
from collections import deque
@dataclass
class BingoItem:
value: int
marked: bool = False
def parse_grid(inp):
raw_grid = [inp.popleft() for _ in range(5)]
grid = [[BingoItem(int(y)) for y in x.rstrip().split(" ") if y != ''] for x in raw_grid]
return grid
def parse_grids(inp):
grids = []
while len(inp) >= 5:
grid = parse_grid(inp)
grids.append(grid)
try:
inp.popleft()
except IndexError:
break
return grids
def check_line_win(grid):
for line in grid:
if all(n.marked for n in line):
return True
return False
def check_column_win(grid):
for col_number in range(len(grid[0])):
column = [line[col_number] for line in grid]
if all(x.marked for x in column):
return True
return False
def calculate_score(grid, final_num):
unmarked = sum([sum([n.value for n in line if not n.marked]) for line in grid])
return final_num * unmarked
def print_green(text, end):
print(f"\033[1;32;40m{text}\033[0;37;40m", end=end)
def print_grid(grid):
for line in grid:
for col in line:
if col.marked:
print_green(f"{str(col.value).ljust(2)}", " ")
else:
print(f"{str(col.value).ljust(2)}", end=" ")
print()
print()
def play_bingo(numbers, grids):
winning_grids = []
for number in numbers:
print(number)
for grid in grids:
for line in grid:
for grid_number in line:
if grid_number.value == number:
grid_number.marked = True
for grid in grids:
win = [check_line_win(grid), check_column_win(grid)]
if any(win):
winning_grids.append((grid, number))
# the grid won, remove it from the game
grids.remove(grid)
first_winning_grid, number = winning_grids[0]
first_score = calculate_score(first_winning_grid, number)
print(f"Part 1, score = {first_score}")
last_winning_grid, number = winning_grids[-1]
last_score = calculate_score(last_winning_grid, number)
print(f"Part 2, score {last_score}")
def main(input_file):
with open(input_file) as f:
inp = deque(f.readlines())
numbers = [int(x) for x in inp.popleft().split(",")]
inp.popleft()
grids = parse_grids(inp)
play_bingo(numbers, grids)
if __name__ == "__main__":
import sys
infile = sys.argv[1] if len(sys.argv) > 1 else "example.txt"
main(infile)

View File

@@ -0,0 +1,41 @@
from collections import defaultdict
def main(infile):
points = defaultdict(int)
with open(infile) as f:
for line in f:
start, end = line.split(" -> ")
start_x, start_y = [int(x) for x in start.split(",")]
end_x, end_y = [int(x) for x in end.split(",")]
# column |
if start_x == end_x:
step = 1 if start_y < end_y else -1
for y in range(start_y, end_y + step, step):
points[(start_x, y)] = points[(start_x, y)] + 1
# line -
elif start_y == end_y:
step = 1 if start_x < end_x else -1
for x in range(start_x, end_x + step, step):
points[(x, start_y)] = points[(x, start_y)] + 1
# diagonal \
elif ((start_x < end_x and start_y > end_y)
or (start_x > end_x and start_y < end_y)):
step = 1 if start_y > end_y else -1
for dx in range(0, end_x - start_x + step, step):
points[(start_x + dx, start_y - dx)] = points[(start_x + dx, start_y + dx)] + 1
# diagonal /
elif ((start_x < end_x and start_y < end_y)
or (start_x > end_x and start_y > end_y)):
step = 1 if start_y < end_y else -1
for dx in range(0, end_x - start_x + step, step):
points[(start_x + dx, start_y + dx)] = points[(start_x + dx, start_y + dx)] + 1
res = len([x for x in points.values() if x >= 2])
print(res)
if __name__ == "__main__":
import sys
infile = sys.argv[1] if len(sys.argv) > 1 else "example.txt"
main(infile)

View File

@@ -0,0 +1,28 @@
from collections import defaultdict, Counter
def calculate_fishes(inp, days):
fishes = Counter(inp)
for day in range(days):
fishes_new = defaultdict(int)
for fish, cnt in fishes.items():
if fish == 0:
fishes_new[8] += cnt
fishes_new[6] += cnt
else:
fishes_new[fish - 1] += cnt
fishes = fishes_new
return sum(fishes.values())
def main(infile):
with open(infile) as f:
inp = [int(x) for x in f.readline().split(",")]
res = calculate_fishes(inp, 80)
print(f"Part 1, {res}")
res = calculate_fishes(inp, 256)
print(f"Part 2, {res}")
if __name__ == "__main__":
import sys
infile = sys.argv[1] if len(sys.argv) > 1 else "example.txt"
main(infile)

View File

@@ -0,0 +1,34 @@
total = 0
numbers = [0, 0, 0, 0, 0, 0, 0, 0, 0]
with open("input.txt") as f:
data = [int(x) for x in f.readline().split(',')]
for i in data:
if i == 0:
numbers[0] += 1
if i == 1:
numbers[1] += 1
if i == 2:
numbers[2] += 1
if i == 3:
numbers[3] += 1
if i == 4:
numbers[4] += 1
if i == 5:
numbers[5] += 1
if i == 6:
numbers[6] += 1
if i == 7:
numbers[7] += 1
if i == 8:
numbers[8] += 1
def rotate(l):
return l[1:] + l[:1]
for j in range(256):
numbers = rotate(numbers)
numbers[6] += numbers[8]
print(f'DAY {j+1} AMOUNT OF FISH: {sum(numbers)}')

View File

@@ -0,0 +1,35 @@
from collections import OrderedDict
def part1(crabs):
moves = OrderedDict()
for pos in range(min(crabs), max(crabs) + 1):
# calculate total fuel required to move to pos:
for crab in crabs:
fuel_cost = abs(pos - crab)
moves[pos] = moves.get(pos, 0) + fuel_cost
min_move = min(moves, key=moves.get)
print(f"Part 1, min move {min_move}, cost {moves[min_move]}")
def part2(crabs):
moves = OrderedDict()
for pos in range(min(crabs), max(crabs) + 1):
# calculate total fuel required to move to pos:
for crab in crabs:
dx = abs(pos - crab)
# S = (n+1)(u0 + un)/2
fuel_cost = dx * (dx+1)//2
moves[pos] = moves.get(pos, 0) + fuel_cost
min_move = min(moves, key=moves.get)
print(f"Part 1, min move {min_move}, cost {moves[min_move]}")
if __name__ == "__main__":
import sys
infile = sys.argv[1] if len(sys.argv) > 1 else "example.txt"
with open(infile) as f:
crabs = [int(x) for x in f.readline().split(",")]
part1(crabs)
part2(crabs)

View File

@@ -0,0 +1,48 @@
def part1(inp):
total = 0
for display in inp:
_, output_values = display
for value in output_values:
if len(value) in [2, 4, 3, 7]:
total += 1
print(f"Part 1 : {total}")
def part2(inp):
for display in inp:
patterns, values = display
patterns = sorted(patterns, key=lambda x: len(x))
print(patterns)
# easy
d1 = [x for x in patterns if len(x) == 2][0]
print("1", d1)
d4 = [x for x in patterns if len(x) == 4][0]
print("4", d4)
d7 = [x for x in patterns if len(x) == 3][0]
print("7", d7)
d8 = [x for x in patterns if len(x) == 7][0]
print("8", d8)
# 3 is the only digit that has all common segments with 1
breakpoint()
d3 = [x for x in patterns if set(d1).issubset(set(x)) and len(x) == 5][0]
print("3", d3)
break
def main(infile):
inp = []
with open(infile) as f:
for display in f:
display = display.rstrip().split(" | ")
signal_patterns = display[0].split(" ")
output_values = display[1].split(" ")
inp.append([signal_patterns, output_values])
part1(inp)
part2(inp)
if __name__ == "__main__":
import sys
infile = sys.argv[1] if len(sys.argv) > 1 else "example.txt"
main(infile)