mirror of
https://github.com/thib8956/advent-of-code.git
synced 2025-08-24 00:11:57 +00:00
chore: create new project structure and aoc.py runner script
This commit is contained in:
46
adventofcode/2021/day1/day1.py
Normal file
46
adventofcode/2021/day1/day1.py
Normal 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)
|
44
adventofcode/2021/day2/day2.py
Normal file
44
adventofcode/2021/day2/day2.py
Normal 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)
|
69
adventofcode/2021/day3/day3.py
Normal file
69
adventofcode/2021/day3/day3.py
Normal 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)
|
99
adventofcode/2021/day4/day4.py
Normal file
99
adventofcode/2021/day4/day4.py
Normal 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)
|
41
adventofcode/2021/day5/day5.py
Normal file
41
adventofcode/2021/day5/day5.py
Normal 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)
|
28
adventofcode/2021/day6/day6.py
Normal file
28
adventofcode/2021/day6/day6.py
Normal 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)
|
34
adventofcode/2021/day6/day6_2.py
Normal file
34
adventofcode/2021/day6/day6_2.py
Normal 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)}')
|
35
adventofcode/2021/day7/day7.py
Normal file
35
adventofcode/2021/day7/day7.py
Normal 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)
|
||||
|
48
adventofcode/2021/day8/day8.py
Normal file
48
adventofcode/2021/day8/day8.py
Normal 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)
|
Reference in New Issue
Block a user