Spaces:
Running
Running
Add solution files
Browse filesThis view is limited to 50 files because it contains too many changes.
See raw diff
- day01/solution_claude-3-5-sonnet-20241022.py +46 -0
- day01/solution_gemini-1.5-pro.py +27 -0
- day01/solution_gpt-4o.py +35 -0
- day01/solution_jerpint.py +36 -0
- day02/solution_claude-3-5-sonnet-20241022.py +52 -0
- day02/solution_gemini-1.5-pro.py +45 -0
- day02/solution_gpt-4o.py +45 -0
- day02/solution_jerpint.py +51 -0
- day03/solution_claude-3-5-sonnet-20241022.py +53 -0
- day03/solution_gemini-1.5-pro.py +36 -0
- day03/solution_gpt-4o.py +56 -0
- day03/solution_jerpint.py +37 -0
- day04/solution_claude-3-5-sonnet-20241022.py +66 -0
- day04/solution_gemini-1.5-pro.py +52 -0
- day04/solution_gpt-4o.py +45 -0
- day04/solution_jerpint.py +75 -0
- day05/solution_claude-3-5-sonnet-20241022.py +102 -0
- day05/solution_gemini-1.5-pro.py +61 -0
- day05/solution_gpt-4o.py +77 -0
- day05/solution_jerpint.py +92 -0
- day06/solution_claude-3-5-sonnet-20241022.py +76 -0
- day06/solution_gemini-1.5-pro.py +67 -0
- day06/solution_gpt-4o.py +78 -0
- day06/solution_jerpint.py +205 -0
- day07/solution_claude-3-5-sonnet-20241022.py +57 -0
- day07/solution_gemini-1.5-pro.py +56 -0
- day07/solution_gpt-4o.py +34 -0
- day07/solution_jerpint.py +115 -0
- day08/solution_claude-3-5-sonnet-20241022.py +86 -0
- day08/solution_gemini-1.5-pro.py +48 -0
- day08/solution_gpt-4o.py +80 -0
- day08/solution_jerpint.py +142 -0
- day09/solution_claude-3-5-sonnet-20241022.py +99 -0
- day09/solution_gemini-1.5-pro.py +82 -0
- day09/solution_gpt-4o.py +80 -0
- day09/solution_jerpint.py +159 -0
- day10/solution_claude-3-5-sonnet-20241022.py +70 -0
- day10/solution_gemini-1.5-pro.py +72 -0
- day10/solution_gpt-4o.py +56 -0
- day10/solution_jerpint.py +106 -0
- day11/solution_claude-3-5-sonnet-20241022.py +41 -0
- day11/solution_gemini-1.5-pro.py +33 -0
- day11/solution_gpt-4o.py +35 -0
- day11/solution_jerpint.py +81 -0
- day12/solution_claude-3-5-sonnet-20241022.py +84 -0
- day12/solution_gemini-1.5-pro.py +86 -0
- day12/solution_gpt-4o.py +77 -0
- day12/solution_jerpint.py +176 -0
- day13/solution_claude-3-5-sonnet-20241022.py +105 -0
- day13/solution_gemini-1.5-pro.py +42 -0
day01/solution_claude-3-5-sonnet-20241022.py
ADDED
@@ -0,0 +1,46 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
def read_lists(filename):
|
2 |
+
left = []
|
3 |
+
right = []
|
4 |
+
with open(filename, 'r') as f:
|
5 |
+
for line in f:
|
6 |
+
l, r = line.strip().split()
|
7 |
+
left.append(int(l))
|
8 |
+
right.append(int(r))
|
9 |
+
return left, right
|
10 |
+
|
11 |
+
def calculate_distance(left, right):
|
12 |
+
# Sort both lists
|
13 |
+
sorted_left = sorted(left)
|
14 |
+
sorted_right = sorted(right)
|
15 |
+
|
16 |
+
# Calculate total distance
|
17 |
+
total_distance = 0
|
18 |
+
for l, r in zip(sorted_left, sorted_right):
|
19 |
+
total_distance += abs(l - r)
|
20 |
+
|
21 |
+
return total_distance
|
22 |
+
|
23 |
+
def calculate_similarity(left, right):
|
24 |
+
# Count occurrences in right list
|
25 |
+
right_counts = {}
|
26 |
+
for num in right:
|
27 |
+
right_counts[num] = right_counts.get(num, 0) + 1
|
28 |
+
|
29 |
+
# Calculate similarity score
|
30 |
+
total_similarity = 0
|
31 |
+
for num in left:
|
32 |
+
# Multiply number by its occurrences in right list
|
33 |
+
total_similarity += num * right_counts.get(num, 0)
|
34 |
+
|
35 |
+
return total_similarity
|
36 |
+
|
37 |
+
# Read input
|
38 |
+
left_list, right_list = read_lists("./input.txt")
|
39 |
+
|
40 |
+
# Part 1
|
41 |
+
distance = calculate_distance(left_list, right_list)
|
42 |
+
print(str(distance))
|
43 |
+
|
44 |
+
# Part 2
|
45 |
+
similarity = calculate_similarity(left_list, right_list)
|
46 |
+
print(str(similarity))
|
day01/solution_gemini-1.5-pro.py
ADDED
@@ -0,0 +1,27 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
def solve():
|
2 |
+
file = "./input.txt"
|
3 |
+
left_list = []
|
4 |
+
right_list = []
|
5 |
+
|
6 |
+
with open(file, 'r') as f:
|
7 |
+
for line in f:
|
8 |
+
left, right = map(int, line.strip().split())
|
9 |
+
left_list.append(left)
|
10 |
+
right_list.append(right)
|
11 |
+
|
12 |
+
# Part 1
|
13 |
+
left_list_sorted = sorted(left_list)
|
14 |
+
right_list_sorted = sorted(right_list)
|
15 |
+
total_distance = 0
|
16 |
+
for i in range(len(left_list)):
|
17 |
+
total_distance += abs(left_list_sorted[i] - right_list_sorted[i])
|
18 |
+
print(total_distance)
|
19 |
+
|
20 |
+
# Part 2
|
21 |
+
similarity_score = 0
|
22 |
+
for left_num in left_list:
|
23 |
+
count = right_list.count(left_num)
|
24 |
+
similarity_score += left_num * count
|
25 |
+
print(similarity_score)
|
26 |
+
|
27 |
+
solve()
|
day01/solution_gpt-4o.py
ADDED
@@ -0,0 +1,35 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
file = "input.txt"
|
2 |
+
|
3 |
+
def calculate_total_distance(left_list, right_list):
|
4 |
+
left_list.sort()
|
5 |
+
right_list.sort()
|
6 |
+
total_distance = sum(abs(l - r) for l, r in zip(left_list, right_list))
|
7 |
+
return total_distance
|
8 |
+
|
9 |
+
def calculate_similarity_score(left_list, right_list):
|
10 |
+
from collections import Counter
|
11 |
+
right_count = Counter(right_list)
|
12 |
+
similarity_score = sum(l * right_count[l] for l in left_list)
|
13 |
+
return similarity_score
|
14 |
+
|
15 |
+
def main():
|
16 |
+
with open(file, 'r') as f:
|
17 |
+
lines = f.readlines()
|
18 |
+
|
19 |
+
left_list = []
|
20 |
+
right_list = []
|
21 |
+
|
22 |
+
for line in lines:
|
23 |
+
left, right = map(int, line.split())
|
24 |
+
left_list.append(left)
|
25 |
+
right_list.append(right)
|
26 |
+
|
27 |
+
# Part 1: Calculate the total distance
|
28 |
+
total_distance = calculate_total_distance(left_list, right_list)
|
29 |
+
print(total_distance)
|
30 |
+
|
31 |
+
# Part 2: Calculate the similarity score
|
32 |
+
similarity_score = calculate_similarity_score(left_list, right_list)
|
33 |
+
print(similarity_score)
|
34 |
+
|
35 |
+
main()
|
day01/solution_jerpint.py
ADDED
@@ -0,0 +1,36 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
with open("input.txt") as f:
|
2 |
+
data = f.read()
|
3 |
+
|
4 |
+
# with open("test.txt") as f:
|
5 |
+
# data = f.read()
|
6 |
+
|
7 |
+
l1 = []
|
8 |
+
l2 = []
|
9 |
+
for line in data.split("\n"):
|
10 |
+
if len(line.split(" ")) > 1:
|
11 |
+
v1, v2 = line.split(" ")
|
12 |
+
l1.append(int(v1))
|
13 |
+
l2.append(int(v2))
|
14 |
+
|
15 |
+
total = 0
|
16 |
+
for n1, n2 in zip(sorted(l1), sorted(l2)):
|
17 |
+
total += abs(n2 - n1)
|
18 |
+
|
19 |
+
print(total)
|
20 |
+
|
21 |
+
# Problem 2
|
22 |
+
def get_counts(l):
|
23 |
+
counts = {}
|
24 |
+
for num in l:
|
25 |
+
counts[num] = counts.get(num, 0) + 1
|
26 |
+
return counts
|
27 |
+
|
28 |
+
|
29 |
+
c2 = get_counts(l2)
|
30 |
+
|
31 |
+
total = 0
|
32 |
+
for n in l1:
|
33 |
+
total += n * c2.get(n, 0)
|
34 |
+
|
35 |
+
print(total)
|
36 |
+
|
day02/solution_claude-3-5-sonnet-20241022.py
ADDED
@@ -0,0 +1,52 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
def is_safe_sequence(nums):
|
2 |
+
if len(nums) <= 1:
|
3 |
+
return True
|
4 |
+
|
5 |
+
# Check first difference to determine if we're expecting increasing or decreasing
|
6 |
+
diff = nums[1] - nums[0]
|
7 |
+
increasing = diff > 0
|
8 |
+
|
9 |
+
for i in range(len(nums)-1):
|
10 |
+
curr_diff = nums[i+1] - nums[i]
|
11 |
+
|
12 |
+
# Check if difference is between 1 and 3
|
13 |
+
if abs(curr_diff) < 1 or abs(curr_diff) > 3:
|
14 |
+
return False
|
15 |
+
|
16 |
+
# Check if direction matches
|
17 |
+
if increasing and curr_diff <= 0:
|
18 |
+
return False
|
19 |
+
if not increasing and curr_diff >= 0:
|
20 |
+
return False
|
21 |
+
|
22 |
+
return True
|
23 |
+
|
24 |
+
def can_be_safe_with_removal(nums):
|
25 |
+
if is_safe_sequence(nums):
|
26 |
+
return True
|
27 |
+
|
28 |
+
# Try removing each number
|
29 |
+
for i in range(len(nums)):
|
30 |
+
new_nums = nums[:i] + nums[i+1:]
|
31 |
+
if is_safe_sequence(new_nums):
|
32 |
+
return True
|
33 |
+
|
34 |
+
return False
|
35 |
+
|
36 |
+
# Read input
|
37 |
+
with open("input.txt", "r") as f:
|
38 |
+
lines = f.readlines()
|
39 |
+
|
40 |
+
# Parse numbers
|
41 |
+
sequences = []
|
42 |
+
for line in lines:
|
43 |
+
nums = [int(x) for x in line.strip().split()]
|
44 |
+
sequences.append(nums)
|
45 |
+
|
46 |
+
# Part 1: Count safe sequences
|
47 |
+
safe_count = sum(1 for seq in sequences if is_safe_sequence(seq))
|
48 |
+
print(str(safe_count))
|
49 |
+
|
50 |
+
# Part 2: Count sequences that can be made safe with one removal
|
51 |
+
safe_with_removal_count = sum(1 for seq in sequences if can_be_safe_with_removal(seq))
|
52 |
+
print(str(safe_with_removal_count))
|
day02/solution_gemini-1.5-pro.py
ADDED
@@ -0,0 +1,45 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
def is_safe(report):
|
2 |
+
increasing = all(report[i] <= report[i+1] for i in range(len(report)-1))
|
3 |
+
decreasing = all(report[i] >= report[i+1] for i in range(len(report)-1))
|
4 |
+
|
5 |
+
if not (increasing or decreasing):
|
6 |
+
return False
|
7 |
+
|
8 |
+
for i in range(len(report) - 1):
|
9 |
+
diff = abs(report[i] - report[i+1])
|
10 |
+
if not (1 <= diff <= 3):
|
11 |
+
return False
|
12 |
+
return True
|
13 |
+
|
14 |
+
def solve_part1(reports):
|
15 |
+
safe_count = 0
|
16 |
+
for report in reports:
|
17 |
+
if is_safe(report):
|
18 |
+
safe_count += 1
|
19 |
+
return safe_count
|
20 |
+
|
21 |
+
def solve_part2(reports):
|
22 |
+
safe_count = 0
|
23 |
+
for report in reports:
|
24 |
+
if is_safe(report):
|
25 |
+
safe_count += 1
|
26 |
+
continue
|
27 |
+
|
28 |
+
for i in range(len(report)):
|
29 |
+
temp_report = report[:i] + report[i+1:]
|
30 |
+
if is_safe(temp_report):
|
31 |
+
safe_count += 1
|
32 |
+
break
|
33 |
+
return safe_count
|
34 |
+
|
35 |
+
|
36 |
+
with open("./input.txt", "r") as f:
|
37 |
+
reports = []
|
38 |
+
for line in f:
|
39 |
+
reports.append(list(map(int, line.strip().split())))
|
40 |
+
|
41 |
+
result1 = solve_part1(reports)
|
42 |
+
print(result1)
|
43 |
+
|
44 |
+
result2 = solve_part2(reports)
|
45 |
+
print(result2)
|
day02/solution_gpt-4o.py
ADDED
@@ -0,0 +1,45 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
def is_safe(report):
|
2 |
+
increasing = True
|
3 |
+
decreasing = True
|
4 |
+
for i in range(len(report) - 1):
|
5 |
+
diff = report[i+1] - report[i]
|
6 |
+
if not (1 <= abs(diff) <= 3):
|
7 |
+
return False
|
8 |
+
if diff > 0:
|
9 |
+
decreasing = False
|
10 |
+
if diff < 0:
|
11 |
+
increasing = False
|
12 |
+
return increasing or decreasing
|
13 |
+
|
14 |
+
def is_safe_with_removal(report):
|
15 |
+
if is_safe(report):
|
16 |
+
return True
|
17 |
+
for i in range(len(report)):
|
18 |
+
modified_report = report[:i] + report[i+1:]
|
19 |
+
if is_safe(modified_report):
|
20 |
+
return True
|
21 |
+
return False
|
22 |
+
|
23 |
+
def count_safe_reports(file_path, part_two=False):
|
24 |
+
with open(file_path, 'r') as file:
|
25 |
+
reports = [list(map(int, line.split())) for line in file]
|
26 |
+
|
27 |
+
safe_count = 0
|
28 |
+
for report in reports:
|
29 |
+
if part_two:
|
30 |
+
if is_safe_with_removal(report):
|
31 |
+
safe_count += 1
|
32 |
+
else:
|
33 |
+
if is_safe(report):
|
34 |
+
safe_count += 1
|
35 |
+
return safe_count
|
36 |
+
|
37 |
+
file = "input.txt"
|
38 |
+
|
39 |
+
# Part One
|
40 |
+
result1 = count_safe_reports(file, part_two=False)
|
41 |
+
print(result1)
|
42 |
+
|
43 |
+
# Part Two
|
44 |
+
result2 = count_safe_reports(file, part_two=True)
|
45 |
+
print(result2)
|
day02/solution_jerpint.py
ADDED
@@ -0,0 +1,51 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# with open("test.txt") as f:
|
2 |
+
# inputs = f.readlines()
|
3 |
+
|
4 |
+
with open("input.txt") as f:
|
5 |
+
inputs = f.readlines()
|
6 |
+
|
7 |
+
def is_increasing_or_decreasing(data):
|
8 |
+
sorted_data = sorted(data)
|
9 |
+
if data[0] > data[-1]:
|
10 |
+
return data == sorted_data[::-1]
|
11 |
+
else:
|
12 |
+
return data == sorted_data
|
13 |
+
|
14 |
+
def adjacency_check(data):
|
15 |
+
for i in range(len(data)-1):
|
16 |
+
abs_diff = abs(data[i] - data[i+1])
|
17 |
+
if not (0 < abs_diff < 4):
|
18 |
+
return False
|
19 |
+
return True
|
20 |
+
|
21 |
+
|
22 |
+
def is_safe(data: list[str]) -> bool:
|
23 |
+
return is_increasing_or_decreasing(data) and adjacency_check(data)
|
24 |
+
|
25 |
+
total = 0
|
26 |
+
for line in inputs:
|
27 |
+
data = [int(i) for i in line.split(" ")]
|
28 |
+
if is_safe(data):
|
29 |
+
total += 1
|
30 |
+
|
31 |
+
print(total)
|
32 |
+
|
33 |
+
|
34 |
+
## Part 2
|
35 |
+
# ... just brute force it?
|
36 |
+
|
37 |
+
total = 0
|
38 |
+
for line in inputs:
|
39 |
+
data = [int(i) for i in line.split(" ")]
|
40 |
+
if is_safe(data):
|
41 |
+
total += 1
|
42 |
+
else:
|
43 |
+
for i in range(len(data)):
|
44 |
+
new_data = data.copy()
|
45 |
+
new_data.pop(i)
|
46 |
+
|
47 |
+
if is_safe(new_data):
|
48 |
+
total += 1
|
49 |
+
break
|
50 |
+
|
51 |
+
print(total)
|
day03/solution_claude-3-5-sonnet-20241022.py
ADDED
@@ -0,0 +1,53 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import re
|
2 |
+
|
3 |
+
def parse_multiplications(text):
|
4 |
+
# Find all valid mul(X,Y) patterns where X and Y are 1-3 digit numbers
|
5 |
+
pattern = r'mul\((\d{1,3}),(\d{1,3})\)'
|
6 |
+
matches = re.finditer(pattern, text)
|
7 |
+
total = 0
|
8 |
+
|
9 |
+
for match in matches:
|
10 |
+
x, y = int(match.group(1)), int(match.group(2))
|
11 |
+
total += x * y
|
12 |
+
|
13 |
+
return total
|
14 |
+
|
15 |
+
def parse_with_conditionals(text):
|
16 |
+
# Find all instructions in order
|
17 |
+
mul_pattern = r'mul\((\d{1,3}),(\d{1,3})\)'
|
18 |
+
do_pattern = r'do\(\)'
|
19 |
+
dont_pattern = r'don\'t\(\)'
|
20 |
+
|
21 |
+
# Combine all patterns
|
22 |
+
all_patterns = f'({mul_pattern}|{do_pattern}|{dont_pattern})'
|
23 |
+
matches = re.finditer(all_patterns, text)
|
24 |
+
|
25 |
+
enabled = True
|
26 |
+
total = 0
|
27 |
+
|
28 |
+
for match in matches:
|
29 |
+
full_match = match.group(0)
|
30 |
+
if full_match == 'do()':
|
31 |
+
enabled = True
|
32 |
+
elif full_match == "don't()":
|
33 |
+
enabled = False
|
34 |
+
else:
|
35 |
+
# It's a multiplication
|
36 |
+
if enabled:
|
37 |
+
x = int(match.group(2))
|
38 |
+
y = int(match.group(3))
|
39 |
+
total += x * y
|
40 |
+
|
41 |
+
return total
|
42 |
+
|
43 |
+
# Read input file
|
44 |
+
with open('./input.txt', 'r') as file:
|
45 |
+
content = file.read()
|
46 |
+
|
47 |
+
# Part 1
|
48 |
+
result1 = parse_multiplications(content)
|
49 |
+
print(str(result1))
|
50 |
+
|
51 |
+
# Part 2
|
52 |
+
result2 = parse_with_conditionals(content)
|
53 |
+
print(str(result2))
|
day03/solution_gemini-1.5-pro.py
ADDED
@@ -0,0 +1,36 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import re
|
2 |
+
|
3 |
+
file = "./input.txt"
|
4 |
+
|
5 |
+
def solve_part1(input_string):
|
6 |
+
pattern = r"mul\((\d{1,3}),(\d{1,3})\)"
|
7 |
+
matches = re.findall(pattern, input_string)
|
8 |
+
total = 0
|
9 |
+
for match in matches:
|
10 |
+
total += int(match[0]) * int(match[1])
|
11 |
+
return str(total)
|
12 |
+
|
13 |
+
def solve_part2(input_string):
|
14 |
+
pattern = r"mul\((\d{1,3}),(\d{1,3})\)|do\(\)|don't\(\)"
|
15 |
+
matches = re.findall(pattern, input_string)
|
16 |
+
total = 0
|
17 |
+
enabled = True
|
18 |
+
for match in matches:
|
19 |
+
if match[0] and match[1]:
|
20 |
+
if enabled:
|
21 |
+
total += int(match[0]) * int(match[1])
|
22 |
+
elif match[0:2] == "do":
|
23 |
+
enabled = True
|
24 |
+
elif match[0:5] == "don't":
|
25 |
+
enabled = False
|
26 |
+
return str(total)
|
27 |
+
|
28 |
+
|
29 |
+
with open(file, 'r') as f:
|
30 |
+
input_string = f.read()
|
31 |
+
|
32 |
+
result1 = solve_part1(input_string)
|
33 |
+
print(result1)
|
34 |
+
|
35 |
+
result2 = solve_part2(input_string)
|
36 |
+
print(result2)
|
day03/solution_gpt-4o.py
ADDED
@@ -0,0 +1,56 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import re
|
2 |
+
|
3 |
+
def parse_instructions(memory):
|
4 |
+
# Regular expression to find valid mul(X,Y) instructions
|
5 |
+
mul_pattern = re.compile(r'mul\((\d{1,3}),(\d{1,3})\)')
|
6 |
+
# Regular expression to find do() and don't() instructions
|
7 |
+
do_pattern = re.compile(r'do\(\)')
|
8 |
+
dont_pattern = re.compile(r"don't\(\)")
|
9 |
+
|
10 |
+
# Find all mul instructions
|
11 |
+
mul_matches = mul_pattern.finditer(memory)
|
12 |
+
|
13 |
+
# Find all do() and don't() instructions
|
14 |
+
do_matches = list(do_pattern.finditer(memory))
|
15 |
+
dont_matches = list(dont_pattern.finditer(memory))
|
16 |
+
|
17 |
+
# Sort do and don't matches by their start position
|
18 |
+
control_matches = sorted(do_matches + dont_matches, key=lambda m: m.start())
|
19 |
+
|
20 |
+
# Part 1: Sum of all valid mul instructions
|
21 |
+
part1_sum = 0
|
22 |
+
for match in mul_matches:
|
23 |
+
x, y = int(match.group(1)), int(match.group(2))
|
24 |
+
part1_sum += x * y
|
25 |
+
|
26 |
+
# Part 2: Sum of enabled mul instructions
|
27 |
+
part2_sum = 0
|
28 |
+
mul_matches = mul_pattern.finditer(memory) # Re-iterate over mul matches
|
29 |
+
enabled = True # Initially, mul instructions are enabled
|
30 |
+
control_index = 0
|
31 |
+
|
32 |
+
for match in mul_matches:
|
33 |
+
# Check if there are any control instructions before this mul
|
34 |
+
while control_index < len(control_matches) and control_matches[control_index].start() < match.start():
|
35 |
+
if control_matches[control_index].group() == 'do()':
|
36 |
+
enabled = True
|
37 |
+
elif control_matches[control_index].group() == "don't()":
|
38 |
+
enabled = False
|
39 |
+
control_index += 1
|
40 |
+
|
41 |
+
if enabled:
|
42 |
+
x, y = int(match.group(1)), int(match.group(2))
|
43 |
+
part2_sum += x * y
|
44 |
+
|
45 |
+
return part1_sum, part2_sum
|
46 |
+
|
47 |
+
# Read the input file
|
48 |
+
with open('input.txt', 'r') as file:
|
49 |
+
memory = file.read()
|
50 |
+
|
51 |
+
# Get the results for both parts
|
52 |
+
result1, result2 = parse_instructions(memory)
|
53 |
+
|
54 |
+
# Print the results
|
55 |
+
print(result1)
|
56 |
+
print(result2)
|
day03/solution_jerpint.py
ADDED
@@ -0,0 +1,37 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import re
|
2 |
+
with open("input.txt") as f:
|
3 |
+
data = f.read()
|
4 |
+
|
5 |
+
# with open("test2.txt") as f:
|
6 |
+
# data = f.read()
|
7 |
+
|
8 |
+
pattern = r"mul\([0-9]*,[0-9]*\)"
|
9 |
+
matches = re.findall(pattern, data)
|
10 |
+
|
11 |
+
total = 0
|
12 |
+
for match in matches:
|
13 |
+
d1, d2 = match[4:-1].split(",") # Drop 'mul(' and ')'
|
14 |
+
d1, d2 = int(d1), int(d2)
|
15 |
+
total += d1*d2
|
16 |
+
print(total)
|
17 |
+
|
18 |
+
|
19 |
+
## Part 2
|
20 |
+
|
21 |
+
pattern = r"don't\(\)|do\(\)|mul\([0-9]*,[0-9]*\)"
|
22 |
+
match_iters = re.finditer(pattern, data)
|
23 |
+
|
24 |
+
is_on = True
|
25 |
+
total = 0
|
26 |
+
for i in match_iters:
|
27 |
+
if i.group() == "don't()":
|
28 |
+
is_on = False
|
29 |
+
elif i.group() == "do()":
|
30 |
+
is_on = True
|
31 |
+
elif is_on:
|
32 |
+
d1, d2 = i.group()[4:-1].split(",") # Drop 'mul(' and ')'
|
33 |
+
d1, d2 = int(d1), int(d2)
|
34 |
+
total += d1*d2
|
35 |
+
|
36 |
+
|
37 |
+
print(total)
|
day04/solution_claude-3-5-sonnet-20241022.py
ADDED
@@ -0,0 +1,66 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
def read_grid(filename):
|
2 |
+
with open(filename, 'r') as f:
|
3 |
+
return [line.strip() for line in f.readlines()]
|
4 |
+
|
5 |
+
def get_word_at(grid, x, y, dx, dy, length):
|
6 |
+
if not (0 <= x + (length-1)*dx < len(grid[0]) and 0 <= y + (length-1)*dy < len(grid)):
|
7 |
+
return ""
|
8 |
+
return ''.join(grid[y + i*dy][x + i*dx] for i in range(length))
|
9 |
+
|
10 |
+
def count_xmas(grid):
|
11 |
+
directions = [
|
12 |
+
(1,0), (0,1), (1,1), (-1,1), # right, down, diagonal-right, diagonal-left
|
13 |
+
(-1,0), (0,-1), (-1,-1), (1,-1) # left, up, diagonal-left-up, diagonal-right-up
|
14 |
+
]
|
15 |
+
|
16 |
+
count = 0
|
17 |
+
for y in range(len(grid)):
|
18 |
+
for x in range(len(grid[0])):
|
19 |
+
for dx, dy in directions:
|
20 |
+
if get_word_at(grid, x, y, dx, dy, 4) == "XMAS":
|
21 |
+
count += 1
|
22 |
+
return count
|
23 |
+
|
24 |
+
def check_xmas_pattern(grid, x, y):
|
25 |
+
# Check both diagonals for MAS or SAM
|
26 |
+
patterns = 0
|
27 |
+
diagonals = [
|
28 |
+
[(x-1, y-1), (x, y), (x+1, y+1)], # top-left to bottom-right
|
29 |
+
[(x+1, y-1), (x, y), (x-1, y+1)] # top-right to bottom-left
|
30 |
+
]
|
31 |
+
|
32 |
+
def is_valid_pos(pos):
|
33 |
+
return 0 <= pos[0] < len(grid[0]) and 0 <= pos[1] < len(grid)
|
34 |
+
|
35 |
+
def get_string(positions):
|
36 |
+
if not all(is_valid_pos(pos) for pos in positions):
|
37 |
+
return ""
|
38 |
+
return ''.join(grid[y][x] for x, y in positions)
|
39 |
+
|
40 |
+
for d1 in diagonals:
|
41 |
+
for d2 in diagonals:
|
42 |
+
if d1 == d2:
|
43 |
+
continue
|
44 |
+
s1 = get_string(d1)
|
45 |
+
s2 = get_string(d2)
|
46 |
+
if ((s1 == "MAS" or s1 == "SAM") and
|
47 |
+
(s2 == "MAS" or s2 == "SAM")):
|
48 |
+
patterns += 1
|
49 |
+
|
50 |
+
return patterns // 2 # Each valid pattern is counted twice due to diagonal combinations
|
51 |
+
|
52 |
+
def count_xmas_patterns(grid):
|
53 |
+
total = 0
|
54 |
+
for y in range(len(grid)):
|
55 |
+
for x in range(len(grid[0])):
|
56 |
+
total += check_xmas_pattern(grid, x, y)
|
57 |
+
return total
|
58 |
+
|
59 |
+
# Read input and solve both parts
|
60 |
+
grid = read_grid("input.txt")
|
61 |
+
|
62 |
+
# Part 1
|
63 |
+
print(str(count_xmas(grid)))
|
64 |
+
|
65 |
+
# Part 2
|
66 |
+
print(str(count_xmas_patterns(grid)))
|
day04/solution_gemini-1.5-pro.py
ADDED
@@ -0,0 +1,52 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
def solve():
|
2 |
+
with open("./input.txt", "r") as f:
|
3 |
+
grid = [line.strip() for line in f]
|
4 |
+
|
5 |
+
rows = len(grid)
|
6 |
+
cols = len(grid[0])
|
7 |
+
|
8 |
+
def check_pattern(r, c, pattern):
|
9 |
+
for dr in [-1, 0, 1]:
|
10 |
+
for dc in [-1, 0, 1]:
|
11 |
+
if dr == 0 and dc == 0:
|
12 |
+
continue
|
13 |
+
found = True
|
14 |
+
for i in range(len(pattern)):
|
15 |
+
nr, nc = r + dr * i, c + dc * i
|
16 |
+
if 0 <= nr < rows and 0 <= nc < cols and grid[nr][nc] == pattern[i]:
|
17 |
+
continue
|
18 |
+
else:
|
19 |
+
found = False
|
20 |
+
break
|
21 |
+
if found:
|
22 |
+
return True
|
23 |
+
return False
|
24 |
+
|
25 |
+
# Part 1
|
26 |
+
count1 = 0
|
27 |
+
for r in range(rows):
|
28 |
+
for c in range(cols):
|
29 |
+
if check_pattern(r, c, "XMAS") or check_pattern(r, c, "SAMX"):
|
30 |
+
count1 += 1
|
31 |
+
print(count1)
|
32 |
+
|
33 |
+
# Part 2
|
34 |
+
count2 = 0
|
35 |
+
for r in range(rows):
|
36 |
+
for c in range(cols):
|
37 |
+
if (
|
38 |
+
1 <= r < rows - 1 and 1 <= c < cols - 1 and
|
39 |
+
(grid[r-1][c-1] == 'M' or grid[r-1][c-1] == 'S') and
|
40 |
+
(grid[r-1][c+1] == 'M' or grid[r-1][c+1] == 'S') and
|
41 |
+
(grid[r+1][c-1] == 'M' or grid[r+1][c-1] == 'S') and
|
42 |
+
(grid[r+1][c+1] == 'M' or grid[r+1][c+1] == 'S') and
|
43 |
+
(grid[r-1][c] == 'A' or grid[r-1][c] == 'A') and
|
44 |
+
(grid[r][c-1] == 'A' or grid[r][c-1] == 'A') and
|
45 |
+
(grid[r][c+1] == 'A' or grid[r][c+1] == 'A') and
|
46 |
+
(grid[r+1][c] == 'A' or grid[r+1][c] == 'A') and
|
47 |
+
(grid[r][c] == 'S' or grid[r][c] == 'M')
|
48 |
+
):
|
49 |
+
count2 += 1
|
50 |
+
print(count2)
|
51 |
+
|
52 |
+
solve()
|
day04/solution_gpt-4o.py
ADDED
@@ -0,0 +1,45 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
def read_input(file):
|
2 |
+
with open(file, 'r') as f:
|
3 |
+
return [line.strip() for line in f.readlines()]
|
4 |
+
|
5 |
+
def count_xmas(grid):
|
6 |
+
directions = [
|
7 |
+
(0, 1), (0, -1), (1, 0), (-1, 0), # right, left, down, up
|
8 |
+
(1, 1), (1, -1), (-1, 1), (-1, -1) # diagonals: down-right, down-left, up-right, up-left
|
9 |
+
]
|
10 |
+
word = "XMAS"
|
11 |
+
count = 0
|
12 |
+
rows, cols = len(grid), len(grid[0])
|
13 |
+
|
14 |
+
for r in range(rows):
|
15 |
+
for c in range(cols):
|
16 |
+
for dr, dc in directions:
|
17 |
+
if all(0 <= r + i * dr < rows and 0 <= c + i * dc < cols and grid[r + i * dr][c + i * dc] == word[i] for i in range(len(word))):
|
18 |
+
count += 1
|
19 |
+
return count
|
20 |
+
|
21 |
+
def count_x_mas(grid):
|
22 |
+
count = 0
|
23 |
+
rows, cols = len(grid), len(grid[0])
|
24 |
+
|
25 |
+
for r in range(1, rows - 1):
|
26 |
+
for c in range(1, cols - 1):
|
27 |
+
# Check for X-MAS pattern
|
28 |
+
if (grid[r-1][c-1] == 'M' and grid[r][c-1] == 'A' and grid[r+1][c-1] == 'S' and
|
29 |
+
grid[r-1][c+1] == 'M' and grid[r][c+1] == 'A' and grid[r+1][c+1] == 'S'):
|
30 |
+
count += 1
|
31 |
+
if (grid[r-1][c+1] == 'M' and grid[r][c+1] == 'A' and grid[r+1][c+1] == 'S' and
|
32 |
+
grid[r-1][c-1] == 'M' and grid[r][c-1] == 'A' and grid[r+1][c-1] == 'S'):
|
33 |
+
count += 1
|
34 |
+
return count
|
35 |
+
|
36 |
+
file = "input.txt"
|
37 |
+
grid = read_input(file)
|
38 |
+
|
39 |
+
# Part 1
|
40 |
+
result1 = count_xmas(grid)
|
41 |
+
print(result1)
|
42 |
+
|
43 |
+
# Part 2
|
44 |
+
result2 = count_x_mas(grid)
|
45 |
+
print(result2)
|
day04/solution_jerpint.py
ADDED
@@ -0,0 +1,75 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
with open("input.txt") as f:
|
2 |
+
raw_data = f.readlines()
|
3 |
+
|
4 |
+
data = []
|
5 |
+
|
6 |
+
for line in raw_data:
|
7 |
+
data.append(list(line.strip("\n")))
|
8 |
+
|
9 |
+
M = len(data)
|
10 |
+
N = len(data[0])
|
11 |
+
|
12 |
+
targets = ["XMAS", "SAMX"]
|
13 |
+
|
14 |
+
## Part 1
|
15 |
+
total = 0
|
16 |
+
for i in range(M):
|
17 |
+
for j in range(N):
|
18 |
+
|
19 |
+
if j < N - 3:
|
20 |
+
# Check horizontally
|
21 |
+
line = data[i]
|
22 |
+
if "".join(line[j:j+4]) in targets:
|
23 |
+
total += 1
|
24 |
+
|
25 |
+
if i < M - 3:
|
26 |
+
# Check vertically
|
27 |
+
to_check = [data[i+a][j] for a in range(4)]
|
28 |
+
if "".join(to_check) in targets:
|
29 |
+
total += 1
|
30 |
+
|
31 |
+
# Check diagonally
|
32 |
+
directions = [(0,0), (1,1),(2,2),(3,3)]
|
33 |
+
to_check = ""
|
34 |
+
for dx, dy in directions:
|
35 |
+
if (i+dx) in range(M) and (j+dy) in range(N):
|
36 |
+
to_check += data[i+dx][j+dy]
|
37 |
+
|
38 |
+
if to_check in targets:
|
39 |
+
total += 1
|
40 |
+
|
41 |
+
# Check other diagonal
|
42 |
+
directions = [(0,0), (-1,1),(-2, 2),(-3, 3)]
|
43 |
+
to_check = ""
|
44 |
+
for dx, dy in directions:
|
45 |
+
if (i+dx) in range(M) and (j+dy) in range(N):
|
46 |
+
to_check += data[i+dx][j+dy]
|
47 |
+
|
48 |
+
if to_check in targets:
|
49 |
+
total += 1
|
50 |
+
|
51 |
+
|
52 |
+
print(total)
|
53 |
+
|
54 |
+
|
55 |
+
## Part 2
|
56 |
+
targets = ["SAM", "MAS"]
|
57 |
+
total = 0
|
58 |
+
for i in range(M):
|
59 |
+
for j in range(N):
|
60 |
+
if data[i][j] == "A":
|
61 |
+
dir1 = [(-1,-1), (0,0), (1,1)]
|
62 |
+
to_check1 = ""
|
63 |
+
for dx, dy in dir1:
|
64 |
+
if (i+dx) in range(M) and (j+dy) in range(N):
|
65 |
+
to_check1 += data[i+dx][j+dy]
|
66 |
+
|
67 |
+
dir2 = [(1,-1), (0,0), (-1,1)]
|
68 |
+
to_check2 = ""
|
69 |
+
for dx, dy in dir2:
|
70 |
+
if (i+dx) in range(M) and (j+dy) in range(N):
|
71 |
+
to_check2 += data[i+dx][j+dy]
|
72 |
+
|
73 |
+
if to_check1 in targets and to_check2 in targets:
|
74 |
+
total += 1
|
75 |
+
print(total)
|
day05/solution_claude-3-5-sonnet-20241022.py
ADDED
@@ -0,0 +1,102 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from collections import defaultdict
|
2 |
+
import copy
|
3 |
+
|
4 |
+
def parse_input(filename):
|
5 |
+
rules = []
|
6 |
+
updates = []
|
7 |
+
with open(filename) as f:
|
8 |
+
lines = f.read().strip().split('\n')
|
9 |
+
|
10 |
+
# Find the empty line that separates rules and updates
|
11 |
+
separator = lines.index('')
|
12 |
+
|
13 |
+
# Parse rules
|
14 |
+
for line in lines[:separator]:
|
15 |
+
x, y = map(int, line.split('|'))
|
16 |
+
rules.append((x, y))
|
17 |
+
|
18 |
+
# Parse updates
|
19 |
+
for line in lines[separator+1:]:
|
20 |
+
update = list(map(int, line.split(',')))
|
21 |
+
updates.append(update)
|
22 |
+
|
23 |
+
return rules, updates
|
24 |
+
|
25 |
+
def build_graph(rules):
|
26 |
+
# Build adjacency list representation
|
27 |
+
graph = defaultdict(list)
|
28 |
+
for x, y in rules:
|
29 |
+
graph[x].append(y)
|
30 |
+
return graph
|
31 |
+
|
32 |
+
def is_valid_order(nums, graph):
|
33 |
+
# Check if current order satisfies all rules
|
34 |
+
num_set = set(nums)
|
35 |
+
for i, num in enumerate(nums):
|
36 |
+
for next_num in graph[num]:
|
37 |
+
if next_num in num_set:
|
38 |
+
# If there's a rule num->next_num, check if next_num appears after num
|
39 |
+
next_pos = nums.index(next_num)
|
40 |
+
if next_pos < i:
|
41 |
+
return False
|
42 |
+
return True
|
43 |
+
|
44 |
+
def topological_sort(nums, graph):
|
45 |
+
# Create a graph only with the numbers in the update
|
46 |
+
num_set = set(nums)
|
47 |
+
local_graph = defaultdict(list)
|
48 |
+
in_degree = defaultdict(int)
|
49 |
+
|
50 |
+
# Initialize in-degree for all numbers
|
51 |
+
for num in nums:
|
52 |
+
in_degree[num] = 0
|
53 |
+
|
54 |
+
# Build local graph and calculate in-degrees
|
55 |
+
for num in nums:
|
56 |
+
for next_num in graph[num]:
|
57 |
+
if next_num in num_set:
|
58 |
+
local_graph[num].append(next_num)
|
59 |
+
in_degree[next_num] += 1
|
60 |
+
|
61 |
+
# Find all nodes with in-degree 0
|
62 |
+
queue = [num for num in nums if in_degree[num] == 0]
|
63 |
+
result = []
|
64 |
+
|
65 |
+
while queue:
|
66 |
+
current = queue.pop(0)
|
67 |
+
result.append(current)
|
68 |
+
|
69 |
+
for next_num in local_graph[current]:
|
70 |
+
in_degree[next_num] -= 1
|
71 |
+
if in_degree[next_num] == 0:
|
72 |
+
queue.append(next_num)
|
73 |
+
|
74 |
+
return result
|
75 |
+
|
76 |
+
def get_middle_number(nums):
|
77 |
+
return nums[len(nums)//2]
|
78 |
+
|
79 |
+
# Read and parse input
|
80 |
+
rules, updates = parse_input("input.txt")
|
81 |
+
graph = build_graph(rules)
|
82 |
+
|
83 |
+
# Part 1: Sum middle numbers of correctly ordered updates
|
84 |
+
valid_sum = 0
|
85 |
+
invalid_updates = []
|
86 |
+
|
87 |
+
for update in updates:
|
88 |
+
if is_valid_order(update, graph):
|
89 |
+
valid_sum += get_middle_number(update)
|
90 |
+
else:
|
91 |
+
invalid_updates.append(update)
|
92 |
+
|
93 |
+
print(str(valid_sum))
|
94 |
+
|
95 |
+
# Part 2: Sum middle numbers of corrected invalid updates
|
96 |
+
invalid_sum = 0
|
97 |
+
|
98 |
+
for update in invalid_updates:
|
99 |
+
sorted_update = topological_sort(update, graph)
|
100 |
+
invalid_sum += get_middle_number(sorted_update)
|
101 |
+
|
102 |
+
print(str(invalid_sum))
|
day05/solution_gemini-1.5-pro.py
ADDED
@@ -0,0 +1,61 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from collections import defaultdict
|
2 |
+
|
3 |
+
def parse_input(file):
|
4 |
+
with open(file, 'r') as f:
|
5 |
+
lines = f.read().splitlines()
|
6 |
+
rules_raw, updates_raw = lines[:lines.index('')], lines[lines.index('') + 1:]
|
7 |
+
rules = {}
|
8 |
+
for rule in rules_raw:
|
9 |
+
a, b = map(int, rule.split('|'))
|
10 |
+
rules[(a, b)] = True
|
11 |
+
updates = []
|
12 |
+
for update in updates_raw:
|
13 |
+
updates.append(list(map(int, update.split(','))))
|
14 |
+
return rules, updates
|
15 |
+
|
16 |
+
def is_correct_order(update, rules):
|
17 |
+
for i in range(len(update)):
|
18 |
+
for j in range(i + 1, len(update)):
|
19 |
+
if (update[j], update[i]) in rules:
|
20 |
+
return False
|
21 |
+
return True
|
22 |
+
|
23 |
+
def get_middle_page(update):
|
24 |
+
return update[len(update) // 2]
|
25 |
+
|
26 |
+
def topological_sort(update, rules):
|
27 |
+
graph = defaultdict(list)
|
28 |
+
in_degree = defaultdict(int)
|
29 |
+
nodes = set(update)
|
30 |
+
for i in nodes:
|
31 |
+
for j in nodes:
|
32 |
+
if (i, j) in rules:
|
33 |
+
graph[i].append(j)
|
34 |
+
in_degree[j] += 1
|
35 |
+
|
36 |
+
queue = [node for node in nodes if in_degree[node] == 0]
|
37 |
+
result = []
|
38 |
+
while queue:
|
39 |
+
u = queue.pop(0)
|
40 |
+
result.append(u)
|
41 |
+
for v in graph[u]:
|
42 |
+
in_degree[v] -= 1
|
43 |
+
if in_degree[v] == 0:
|
44 |
+
queue.append(v)
|
45 |
+
return result
|
46 |
+
|
47 |
+
file = "./input.txt"
|
48 |
+
rules, updates = parse_input(file)
|
49 |
+
|
50 |
+
correct_sum = 0
|
51 |
+
incorrect_sum = 0
|
52 |
+
|
53 |
+
for update in updates:
|
54 |
+
if is_correct_order(update, rules):
|
55 |
+
correct_sum += get_middle_page(update)
|
56 |
+
else:
|
57 |
+
correct_update = topological_sort(update, rules)
|
58 |
+
incorrect_sum += get_middle_page(correct_update)
|
59 |
+
|
60 |
+
print(correct_sum)
|
61 |
+
print(incorrect_sum)
|
day05/solution_gpt-4o.py
ADDED
@@ -0,0 +1,77 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from collections import defaultdict, deque
|
2 |
+
|
3 |
+
def parse_input(file):
|
4 |
+
with open(file, 'r') as f:
|
5 |
+
lines = f.read().strip().split('\n')
|
6 |
+
|
7 |
+
# Separate rules and updates
|
8 |
+
rules = []
|
9 |
+
updates = []
|
10 |
+
is_update_section = False
|
11 |
+
|
12 |
+
for line in lines:
|
13 |
+
if '|' in line:
|
14 |
+
rules.append(tuple(map(int, line.split('|'))))
|
15 |
+
else:
|
16 |
+
updates.append(list(map(int, line.split(','))))
|
17 |
+
|
18 |
+
return rules, updates
|
19 |
+
|
20 |
+
def is_correct_order(update, rules):
|
21 |
+
# Create a map of page to its index in the update
|
22 |
+
index_map = {page: i for i, page in enumerate(update)}
|
23 |
+
|
24 |
+
for x, y in rules:
|
25 |
+
if x in index_map and y in index_map:
|
26 |
+
if index_map[x] > index_map[y]:
|
27 |
+
return False
|
28 |
+
return True
|
29 |
+
|
30 |
+
def find_middle_page(update):
|
31 |
+
n = len(update)
|
32 |
+
return update[n // 2]
|
33 |
+
|
34 |
+
def reorder_update(update, rules):
|
35 |
+
# Create a graph and in-degree count for topological sorting
|
36 |
+
graph = defaultdict(list)
|
37 |
+
in_degree = defaultdict(int)
|
38 |
+
|
39 |
+
# Only consider rules that involve pages in the current update
|
40 |
+
update_set = set(update)
|
41 |
+
for x, y in rules:
|
42 |
+
if x in update_set and y in update_set:
|
43 |
+
graph[x].append(y)
|
44 |
+
in_degree[y] += 1
|
45 |
+
|
46 |
+
# Initialize queue with nodes having zero in-degree
|
47 |
+
queue = deque([page for page in update if in_degree[page] == 0])
|
48 |
+
sorted_update = []
|
49 |
+
|
50 |
+
while queue:
|
51 |
+
node = queue.popleft()
|
52 |
+
sorted_update.append(node)
|
53 |
+
for neighbor in graph[node]:
|
54 |
+
in_degree[neighbor] -= 1
|
55 |
+
if in_degree[neighbor] == 0:
|
56 |
+
queue.append(neighbor)
|
57 |
+
|
58 |
+
return sorted_update
|
59 |
+
|
60 |
+
def main():
|
61 |
+
file = "input.txt"
|
62 |
+
rules, updates = parse_input(file)
|
63 |
+
|
64 |
+
correct_order_sum = 0
|
65 |
+
incorrect_order_sum = 0
|
66 |
+
|
67 |
+
for update in updates:
|
68 |
+
if is_correct_order(update, rules):
|
69 |
+
correct_order_sum += find_middle_page(update)
|
70 |
+
else:
|
71 |
+
reordered_update = reorder_update(update, rules)
|
72 |
+
incorrect_order_sum += find_middle_page(reordered_update)
|
73 |
+
|
74 |
+
print(correct_order_sum)
|
75 |
+
print(incorrect_order_sum)
|
76 |
+
|
77 |
+
main()
|
day05/solution_jerpint.py
ADDED
@@ -0,0 +1,92 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
with open("input.txt") as f:
|
2 |
+
data = f.readlines()
|
3 |
+
|
4 |
+
|
5 |
+
def check_rule(pages: list[str], rule):
|
6 |
+
page2idx = {p: idx for idx, p in enumerate(pages)}
|
7 |
+
r0, r1 = rule
|
8 |
+
idx0 = page2idx.get(r0)
|
9 |
+
idx1 = page2idx.get(r1)
|
10 |
+
|
11 |
+
if idx0 is None or idx1 is None:
|
12 |
+
return True # "pass"
|
13 |
+
|
14 |
+
return idx0 < idx1
|
15 |
+
|
16 |
+
|
17 |
+
def check_rules(pages, rules):
|
18 |
+
for rule in rules:
|
19 |
+
passes = check_rule(pages, rule)
|
20 |
+
|
21 |
+
if not passes:
|
22 |
+
return False
|
23 |
+
|
24 |
+
return True
|
25 |
+
|
26 |
+
|
27 |
+
rules = []
|
28 |
+
page_numbers = []
|
29 |
+
for line in data:
|
30 |
+
line = line.strip("\n")
|
31 |
+
if "|" in line:
|
32 |
+
r1, r2 = [int(r) for r in line.split("|")]
|
33 |
+
rules.append((r1, r2))
|
34 |
+
|
35 |
+
elif len(line) > 0:
|
36 |
+
page_numbers.append([int(r) for r in line.split(",")])
|
37 |
+
|
38 |
+
|
39 |
+
total = 0
|
40 |
+
for pages in page_numbers:
|
41 |
+
passes = check_rules(pages, rules)
|
42 |
+
|
43 |
+
if passes:
|
44 |
+
mid_idx = len(pages) // 2
|
45 |
+
total += pages[mid_idx]
|
46 |
+
|
47 |
+
print(total)
|
48 |
+
|
49 |
+
## Part 2
|
50 |
+
|
51 |
+
def return_failing_rule(pages, rules):
|
52 |
+
for rule in rules:
|
53 |
+
passes = check_rule(pages, rule)
|
54 |
+
|
55 |
+
if not passes:
|
56 |
+
return rule
|
57 |
+
|
58 |
+
return None
|
59 |
+
|
60 |
+
|
61 |
+
def update_pages(pages, failed_rule):
|
62 |
+
"Swap the pages given the failed rule"
|
63 |
+
page2idx = {p: idx for idx, p in enumerate(pages)}
|
64 |
+
r0, r1 = failed_rule
|
65 |
+
idx0 = page2idx.get(r0)
|
66 |
+
idx1 = page2idx.get(r1)
|
67 |
+
new_pages = pages.copy()
|
68 |
+
new_pages[idx0] = r1
|
69 |
+
new_pages[idx1] = r0
|
70 |
+
return new_pages
|
71 |
+
|
72 |
+
|
73 |
+
def fix_pages(pages, rules):
|
74 |
+
failed_rule = return_failing_rule(pages, rules)
|
75 |
+
while failed_rule:
|
76 |
+
pages = update_pages(pages, failed_rule)
|
77 |
+
failed_rule = return_failing_rule(pages, rules)
|
78 |
+
|
79 |
+
return pages
|
80 |
+
|
81 |
+
|
82 |
+
|
83 |
+
total = 0
|
84 |
+
for pages in page_numbers:
|
85 |
+
passes = check_rules(pages, rules)
|
86 |
+
|
87 |
+
if not passes:
|
88 |
+
new_pages = fix_pages(pages, rules)
|
89 |
+
mid_idx = len(pages) // 2
|
90 |
+
total += new_pages[mid_idx]
|
91 |
+
|
92 |
+
print(total)
|
day06/solution_claude-3-5-sonnet-20241022.py
ADDED
@@ -0,0 +1,76 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
def parse_input(file):
|
2 |
+
with open(file, 'r') as f:
|
3 |
+
return [list(line.strip()) for line in f.readlines()]
|
4 |
+
|
5 |
+
def get_start_position(grid):
|
6 |
+
for y in range(len(grid)):
|
7 |
+
for x in range(len(grid[0])):
|
8 |
+
if grid[y][x] == '^':
|
9 |
+
return (x, y, 0) # x, y, direction (0=up, 1=right, 2=down, 3=left)
|
10 |
+
return None
|
11 |
+
|
12 |
+
def is_valid_position(x, y, grid):
|
13 |
+
return 0 <= y < len(grid) and 0 <= x < len(grid[0])
|
14 |
+
|
15 |
+
def get_next_position(x, y, direction):
|
16 |
+
if direction == 0: # up
|
17 |
+
return (x, y-1)
|
18 |
+
elif direction == 1: # right
|
19 |
+
return (x+1, y)
|
20 |
+
elif direction == 2: # down
|
21 |
+
return (x, y+1)
|
22 |
+
else: # left
|
23 |
+
return (x-1, y)
|
24 |
+
|
25 |
+
def simulate_path(grid, start_pos=None, added_obstacle=None):
|
26 |
+
if start_pos is None:
|
27 |
+
start_pos = get_start_position(grid)
|
28 |
+
x, y, direction = start_pos
|
29 |
+
visited = set([(x, y)])
|
30 |
+
|
31 |
+
while True:
|
32 |
+
next_x, next_y = get_next_position(x, y, direction)
|
33 |
+
|
34 |
+
# Check if out of bounds
|
35 |
+
if not is_valid_position(next_x, next_y, grid):
|
36 |
+
return visited, False
|
37 |
+
|
38 |
+
# Check if obstacle ahead (including added obstacle)
|
39 |
+
if (grid[next_y][next_x] == '#' or (next_x, next_y) == added_obstacle):
|
40 |
+
direction = (direction + 1) % 4
|
41 |
+
else:
|
42 |
+
x, y = next_x, next_y
|
43 |
+
visited.add((x, y))
|
44 |
+
|
45 |
+
# Check for loop
|
46 |
+
if len(visited) > len(grid) * len(grid[0]) * 4:
|
47 |
+
return visited, True
|
48 |
+
|
49 |
+
def solve_part1(grid):
|
50 |
+
visited, _ = simulate_path(grid)
|
51 |
+
return len(visited)
|
52 |
+
|
53 |
+
def solve_part2(grid):
|
54 |
+
start_pos = get_start_position(grid)
|
55 |
+
loop_positions = 0
|
56 |
+
|
57 |
+
for y in range(len(grid)):
|
58 |
+
for x in range(len(grid[0])):
|
59 |
+
# Skip if not empty space or start position
|
60 |
+
if grid[y][x] != '.' or (x, y) == (start_pos[0], start_pos[1]):
|
61 |
+
continue
|
62 |
+
|
63 |
+
# Try adding obstacle here
|
64 |
+
_, creates_loop = simulate_path(grid, start_pos, (x, y))
|
65 |
+
if creates_loop:
|
66 |
+
loop_positions += 1
|
67 |
+
|
68 |
+
return loop_positions
|
69 |
+
|
70 |
+
def main():
|
71 |
+
grid = parse_input("input.txt")
|
72 |
+
print(str(solve_part1(grid)))
|
73 |
+
print(str(solve_part2(grid)))
|
74 |
+
|
75 |
+
if __name__ == "__main__":
|
76 |
+
main()
|
day06/solution_gemini-1.5-pro.py
ADDED
@@ -0,0 +1,67 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
def solve():
|
2 |
+
file = "input.txt"
|
3 |
+
grid = []
|
4 |
+
with open(file, 'r') as f:
|
5 |
+
for line in f:
|
6 |
+
grid.append(list(line.strip()))
|
7 |
+
|
8 |
+
rows = len(grid)
|
9 |
+
cols = len(grid[0])
|
10 |
+
|
11 |
+
def find_guard():
|
12 |
+
for r in range(rows):
|
13 |
+
for c in range(cols):
|
14 |
+
if grid[r][c] == '^':
|
15 |
+
return r, c, 0 # row, col, direction (0: up, 1: right, 2: down, 3: left)
|
16 |
+
|
17 |
+
def is_valid(r, c):
|
18 |
+
return 0 <= r < rows and 0 <= c < cols
|
19 |
+
|
20 |
+
def move_guard(r, c, direction):
|
21 |
+
dr = [-1, 0, 1, 0]
|
22 |
+
dc = [0, 1, 0, -1]
|
23 |
+
|
24 |
+
nr, nc = r + dr[direction], c + dc[direction]
|
25 |
+
if is_valid(nr, nc) and grid[nr][nc] != '#':
|
26 |
+
return nr, nc, direction
|
27 |
+
else:
|
28 |
+
return r, c, (direction + 1) % 4
|
29 |
+
|
30 |
+
# Part 1
|
31 |
+
visited = set()
|
32 |
+
r, c, direction = find_guard()
|
33 |
+
while is_valid(r, c):
|
34 |
+
visited.add((r, c))
|
35 |
+
r, c, direction = move_guard(r, c, direction)
|
36 |
+
print(len(visited))
|
37 |
+
|
38 |
+
|
39 |
+
# Part 2
|
40 |
+
def simulate_with_obstacle(obstacle_r, obstacle_c):
|
41 |
+
temp_grid = [row[:] for row in grid]
|
42 |
+
temp_grid[obstacle_r][obstacle_c] = '#'
|
43 |
+
visited = set()
|
44 |
+
r, c, direction = find_guard()
|
45 |
+
while (r,c) not in visited and is_valid(r,c):
|
46 |
+
visited.add((r,c))
|
47 |
+
|
48 |
+
dr = [-1, 0, 1, 0]
|
49 |
+
dc = [0, 1, 0, -1]
|
50 |
+
|
51 |
+
nr, nc = r + dr[direction], c + dc[direction]
|
52 |
+
if is_valid(nr, nc) and temp_grid[nr][nc] != '#':
|
53 |
+
r,c = nr,nc
|
54 |
+
else:
|
55 |
+
direction = (direction + 1) % 4
|
56 |
+
return (r,c) in visited
|
57 |
+
|
58 |
+
loop_count = 0
|
59 |
+
start_r, start_c, _ = find_guard()
|
60 |
+
for r in range(rows):
|
61 |
+
for c in range(cols):
|
62 |
+
if grid[r][c] == '.' and (r, c) != (start_r, start_c):
|
63 |
+
if simulate_with_obstacle(r, c):
|
64 |
+
loop_count += 1
|
65 |
+
print(loop_count)
|
66 |
+
|
67 |
+
solve()
|
day06/solution_gpt-4o.py
ADDED
@@ -0,0 +1,78 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
def parse_input(file):
|
2 |
+
with open(file, 'r') as f:
|
3 |
+
grid = [list(line.strip()) for line in f.readlines()]
|
4 |
+
return grid
|
5 |
+
|
6 |
+
def find_guard_start(grid):
|
7 |
+
directions = {'^': (0, -1), '>': (1, 0), 'v': (0, 1), '<': (-1, 0)}
|
8 |
+
for y, row in enumerate(grid):
|
9 |
+
for x, cell in enumerate(row):
|
10 |
+
if cell in directions:
|
11 |
+
return (x, y), directions[cell]
|
12 |
+
return None, None
|
13 |
+
|
14 |
+
def turn_right(direction):
|
15 |
+
right_turns = {(0, -1): (1, 0), (1, 0): (0, 1), (0, 1): (-1, 0), (-1, 0): (0, -1)}
|
16 |
+
return right_turns[direction]
|
17 |
+
|
18 |
+
def move(position, direction):
|
19 |
+
return position[0] + direction[0], position[1] + direction[1]
|
20 |
+
|
21 |
+
def is_within_bounds(position, grid):
|
22 |
+
x, y = position
|
23 |
+
return 0 <= x < len(grid[0]) and 0 <= y < len(grid)
|
24 |
+
|
25 |
+
def simulate_guard(grid):
|
26 |
+
start_pos, direction = find_guard_start(grid)
|
27 |
+
visited = set()
|
28 |
+
position = start_pos
|
29 |
+
|
30 |
+
while is_within_bounds(position, grid):
|
31 |
+
visited.add(position)
|
32 |
+
next_position = move(position, direction)
|
33 |
+
if is_within_bounds(next_position, grid) and grid[next_position[1]][next_position[0]] == '#':
|
34 |
+
direction = turn_right(direction)
|
35 |
+
else:
|
36 |
+
position = next_position
|
37 |
+
|
38 |
+
return visited
|
39 |
+
|
40 |
+
def simulate_with_obstruction(grid, obstruction_pos):
|
41 |
+
start_pos, direction = find_guard_start(grid)
|
42 |
+
visited = set()
|
43 |
+
position = start_pos
|
44 |
+
direction_map = {(-1, 0): '<', (0, -1): '^', (1, 0): '>', (0, 1): 'v'}
|
45 |
+
|
46 |
+
while is_within_bounds(position, grid):
|
47 |
+
if position == obstruction_pos:
|
48 |
+
return False # Obstruction is here, can't move
|
49 |
+
if (position, direction) in visited:
|
50 |
+
return True # Loop detected
|
51 |
+
visited.add((position, direction))
|
52 |
+
next_position = move(position, direction)
|
53 |
+
if is_within_bounds(next_position, grid) and (grid[next_position[1]][next_position[0]] == '#' or next_position == obstruction_pos):
|
54 |
+
direction = turn_right(direction)
|
55 |
+
else:
|
56 |
+
position = next_position
|
57 |
+
|
58 |
+
return False
|
59 |
+
|
60 |
+
def find_loop_positions(grid):
|
61 |
+
loop_positions = 0
|
62 |
+
for y, row in enumerate(grid):
|
63 |
+
for x, cell in enumerate(row):
|
64 |
+
if cell == '.' and (x, y) != find_guard_start(grid)[0]:
|
65 |
+
if simulate_with_obstruction(grid, (x, y)):
|
66 |
+
loop_positions += 1
|
67 |
+
return loop_positions
|
68 |
+
|
69 |
+
file = "input.txt"
|
70 |
+
grid = parse_input(file)
|
71 |
+
|
72 |
+
# Part 1
|
73 |
+
visited_positions = simulate_guard(grid)
|
74 |
+
print(len(visited_positions))
|
75 |
+
|
76 |
+
# Part 2
|
77 |
+
loop_positions = find_loop_positions(grid)
|
78 |
+
print(loop_positions)
|
day06/solution_jerpint.py
ADDED
@@ -0,0 +1,205 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from dataclasses import dataclass
|
2 |
+
|
3 |
+
file = "input.txt"
|
4 |
+
with open(file) as f:
|
5 |
+
data = f.readlines()
|
6 |
+
|
7 |
+
grid = []
|
8 |
+
for line in data:
|
9 |
+
line = line.strip("\n")
|
10 |
+
grid.append(list(line))
|
11 |
+
|
12 |
+
M = len(grid)
|
13 |
+
N = len(grid[0])
|
14 |
+
|
15 |
+
@dataclass
|
16 |
+
class Position:
|
17 |
+
i: int
|
18 |
+
j: int
|
19 |
+
direction: str
|
20 |
+
|
21 |
+
def rotate_90(self):
|
22 |
+
directions = ["^", ">", "v", "<"]
|
23 |
+
new_idx = (directions.index(self.direction) + 1) % len(directions)
|
24 |
+
self.direction = directions[new_idx]
|
25 |
+
|
26 |
+
|
27 |
+
def is_in_bounds(self, grid):
|
28 |
+
M = len(grid)
|
29 |
+
N = len(grid[0])
|
30 |
+
return self.i in range(M) and self.j in range(N)
|
31 |
+
|
32 |
+
|
33 |
+
def get_next_pos(position: Position):
|
34 |
+
i, j, direction = position.i, position.j, position.direction
|
35 |
+
if direction == "^":
|
36 |
+
# Up
|
37 |
+
i -= 1
|
38 |
+
|
39 |
+
elif direction == "v":
|
40 |
+
# Down
|
41 |
+
i += 1
|
42 |
+
|
43 |
+
elif direction == "<":
|
44 |
+
# Left
|
45 |
+
j -= 1
|
46 |
+
|
47 |
+
elif direction == ">":
|
48 |
+
# Right
|
49 |
+
j += 1
|
50 |
+
|
51 |
+
return Position(i, j, direction)
|
52 |
+
|
53 |
+
def get_start_pos(grid):
|
54 |
+
|
55 |
+
M = len(grid)
|
56 |
+
N = len(grid[0])
|
57 |
+
|
58 |
+
for i in range(M):
|
59 |
+
for j in range(N):
|
60 |
+
if grid[i][j] == "^":
|
61 |
+
return Position(i, j, "^")
|
62 |
+
|
63 |
+
|
64 |
+
def count_Xs(grid):
|
65 |
+
total = 0
|
66 |
+
for i in range(M):
|
67 |
+
for j in range(N):
|
68 |
+
if grid[i][j] == "X":
|
69 |
+
total += 1
|
70 |
+
return total
|
71 |
+
|
72 |
+
|
73 |
+
def pprint(grid, pos = None):
|
74 |
+
|
75 |
+
grid_copy = grid.copy()
|
76 |
+
|
77 |
+
if pos:
|
78 |
+
# Print the current position
|
79 |
+
grid_copy[pos.i][pos.j] = pos.direction
|
80 |
+
|
81 |
+
grid_str = ""
|
82 |
+
|
83 |
+
for line in grid_copy:
|
84 |
+
grid_str += "".join(line)
|
85 |
+
grid_str += "\n"
|
86 |
+
|
87 |
+
print("*"*20)
|
88 |
+
print()
|
89 |
+
print(grid_str)
|
90 |
+
print()
|
91 |
+
print("*"*20)
|
92 |
+
|
93 |
+
|
94 |
+
|
95 |
+
pos = get_start_pos(grid)
|
96 |
+
|
97 |
+
grid[pos.i][pos.j] = "X" # Mark starting point as visited
|
98 |
+
in_bounds = True
|
99 |
+
while in_bounds:
|
100 |
+
# pprint(grid, pos)
|
101 |
+
next_pos = get_next_pos(pos)
|
102 |
+
if next_pos.is_in_bounds(grid):
|
103 |
+
|
104 |
+
if grid[next_pos.i][next_pos.j] in [".", "X"]:
|
105 |
+
# Valid next mode, mark as visited and move
|
106 |
+
grid[pos.i][pos.j] = "X"
|
107 |
+
pos = next_pos
|
108 |
+
else:
|
109 |
+
# Otherwise, rotate
|
110 |
+
pos.rotate_90()
|
111 |
+
else:
|
112 |
+
# Out of bounds, game over
|
113 |
+
in_bounds = False
|
114 |
+
grid[pos.i][pos.j] = "X"
|
115 |
+
pos = None
|
116 |
+
|
117 |
+
# pprint(grid, pos)
|
118 |
+
print(count_Xs(grid))
|
119 |
+
|
120 |
+
|
121 |
+
### Part 2
|
122 |
+
|
123 |
+
prev_grid = grid.copy()
|
124 |
+
|
125 |
+
def load_grid(file):
|
126 |
+
with open(file) as f:
|
127 |
+
data = f.readlines()
|
128 |
+
|
129 |
+
grid = []
|
130 |
+
for line in data:
|
131 |
+
line = line.strip("\n")
|
132 |
+
grid.append(list(line))
|
133 |
+
|
134 |
+
return grid
|
135 |
+
|
136 |
+
|
137 |
+
def check_is_infinite(grid):
|
138 |
+
|
139 |
+
pos = get_start_pos(grid)
|
140 |
+
grid[pos.i][pos.j] = "X" # Mark starting point as visited
|
141 |
+
|
142 |
+
visited = set() # Keep track of positions and orientations
|
143 |
+
in_bounds = True
|
144 |
+
infinite_loop = False
|
145 |
+
while in_bounds and not infinite_loop:
|
146 |
+
# pprint(grid, pos)
|
147 |
+
next_pos = get_next_pos(pos)
|
148 |
+
if next_pos.is_in_bounds(grid):
|
149 |
+
|
150 |
+
if grid[next_pos.i][next_pos.j] in [".", "X"]:
|
151 |
+
# Valid next mode, mark as visited and move
|
152 |
+
grid[pos.i][pos.j] = "X"
|
153 |
+
pos = next_pos
|
154 |
+
else:
|
155 |
+
# Otherwise, rotate
|
156 |
+
pos.rotate_90()
|
157 |
+
|
158 |
+
|
159 |
+
set_pos = (pos.i, pos.j, pos.direction)
|
160 |
+
if set_pos in visited:
|
161 |
+
infinite_loop = True
|
162 |
+
else:
|
163 |
+
visited.add(set_pos)
|
164 |
+
else:
|
165 |
+
# Out of bounds, game over
|
166 |
+
in_bounds = False
|
167 |
+
grid[pos.i][pos.j] = "X"
|
168 |
+
pos = None
|
169 |
+
|
170 |
+
return infinite_loop
|
171 |
+
|
172 |
+
file = "input.txt"
|
173 |
+
|
174 |
+
# Load first to get stats
|
175 |
+
# grid = load_grid(file)
|
176 |
+
M = len(grid)
|
177 |
+
N = len(grid[0])
|
178 |
+
|
179 |
+
# This is a very brute-force solution, takes long to run...
|
180 |
+
# For every point, place an obstacle, run the game, check if it gets stuck in infinite loop or not
|
181 |
+
# Surely, there must be a quicker way, but it works
|
182 |
+
|
183 |
+
total = 0
|
184 |
+
for i in range(M):
|
185 |
+
for j in range(N):
|
186 |
+
|
187 |
+
# Reset grid
|
188 |
+
grid = load_grid(file)
|
189 |
+
start_pos = get_start_pos(grid)
|
190 |
+
|
191 |
+
|
192 |
+
if start_pos.i == i and start_pos.j == j:
|
193 |
+
# Can't set obstacle on starting point, ignore
|
194 |
+
continue
|
195 |
+
|
196 |
+
if prev_grid[i][j] != "X":
|
197 |
+
# It never passed by there, so we can ignore
|
198 |
+
continue
|
199 |
+
|
200 |
+
# print(i, j)
|
201 |
+
|
202 |
+
grid[i][j] = "O" # Set Obstacle
|
203 |
+
if check_is_infinite(grid):
|
204 |
+
total += 1
|
205 |
+
print(total)
|
day07/solution_claude-3-5-sonnet-20241022.py
ADDED
@@ -0,0 +1,57 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
def parse_input(filename):
|
2 |
+
equations = []
|
3 |
+
with open(filename, 'r') as f:
|
4 |
+
for line in f:
|
5 |
+
test_value, numbers = line.strip().split(': ')
|
6 |
+
test_value = int(test_value)
|
7 |
+
numbers = [int(x) for x in numbers.split()]
|
8 |
+
equations.append((test_value, numbers))
|
9 |
+
return equations
|
10 |
+
|
11 |
+
def evaluate(numbers, operators):
|
12 |
+
result = numbers[0]
|
13 |
+
for i, op in enumerate(operators):
|
14 |
+
if op == '+':
|
15 |
+
result += numbers[i + 1]
|
16 |
+
elif op == '*':
|
17 |
+
result *= numbers[i + 1]
|
18 |
+
elif op == '||':
|
19 |
+
result = int(str(result) + str(numbers[i + 1]))
|
20 |
+
return result
|
21 |
+
|
22 |
+
def try_all_combinations(test_value, numbers, operators_set):
|
23 |
+
if len(numbers) == 1:
|
24 |
+
return test_value == numbers[0]
|
25 |
+
|
26 |
+
n = len(numbers) - 1 # number of operators needed
|
27 |
+
for ops in itertools.product(operators_set, repeat=n):
|
28 |
+
if evaluate(numbers, ops) == test_value:
|
29 |
+
return True
|
30 |
+
return False
|
31 |
+
|
32 |
+
import itertools
|
33 |
+
|
34 |
+
def solve_part1(equations):
|
35 |
+
total = 0
|
36 |
+
operators = ['+', '*']
|
37 |
+
|
38 |
+
for test_value, numbers in equations:
|
39 |
+
if try_all_combinations(test_value, numbers, operators):
|
40 |
+
total += test_value
|
41 |
+
|
42 |
+
return str(total)
|
43 |
+
|
44 |
+
def solve_part2(equations):
|
45 |
+
total = 0
|
46 |
+
operators = ['+', '*', '||']
|
47 |
+
|
48 |
+
for test_value, numbers in equations:
|
49 |
+
if try_all_combinations(test_value, numbers, operators):
|
50 |
+
total += test_value
|
51 |
+
|
52 |
+
return str(total)
|
53 |
+
|
54 |
+
# Read input and solve both parts
|
55 |
+
equations = parse_input("./input.txt")
|
56 |
+
print(solve_part1(equations))
|
57 |
+
print(solve_part2(equations))
|
day07/solution_gemini-1.5-pro.py
ADDED
@@ -0,0 +1,56 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
def solve_part1(equation):
|
2 |
+
test_value, numbers_str = equation.split(":")
|
3 |
+
test_value = int(test_value)
|
4 |
+
numbers = [int(x) for x in numbers_str.split()]
|
5 |
+
|
6 |
+
def evaluate(index, current_value):
|
7 |
+
if index == len(numbers):
|
8 |
+
return current_value == test_value
|
9 |
+
|
10 |
+
if evaluate(index + 1, current_value + numbers[index]):
|
11 |
+
return True
|
12 |
+
if evaluate(index + 1, current_value * numbers[index]):
|
13 |
+
return True
|
14 |
+
return False
|
15 |
+
|
16 |
+
return evaluate(1, numbers[0])
|
17 |
+
|
18 |
+
def solve_part2(equation):
|
19 |
+
test_value, numbers_str = equation.split(":")
|
20 |
+
test_value = int(test_value)
|
21 |
+
numbers = [int(x) for x in numbers_str.split()]
|
22 |
+
|
23 |
+
def evaluate(index, current_value):
|
24 |
+
if index == len(numbers):
|
25 |
+
return current_value == test_value
|
26 |
+
|
27 |
+
if evaluate(index + 1, current_value + numbers[index]):
|
28 |
+
return True
|
29 |
+
if evaluate(index + 1, current_value * numbers[index]):
|
30 |
+
return True
|
31 |
+
if evaluate(index + 1, int(str(current_value) + str(numbers[index]))):
|
32 |
+
return True
|
33 |
+
|
34 |
+
return False
|
35 |
+
return evaluate(1, numbers[0])
|
36 |
+
|
37 |
+
|
38 |
+
with open("./input.txt") as f:
|
39 |
+
equations = f.readlines()
|
40 |
+
|
41 |
+
part1_sum = 0
|
42 |
+
for equation in equations:
|
43 |
+
if solve_part1(equation.strip()):
|
44 |
+
test_value, _ = equation.split(":")
|
45 |
+
part1_sum += int(test_value)
|
46 |
+
|
47 |
+
print(part1_sum)
|
48 |
+
|
49 |
+
|
50 |
+
part2_sum = 0
|
51 |
+
for equation in equations:
|
52 |
+
if solve_part2(equation.strip()):
|
53 |
+
test_value, _ = equation.split(":")
|
54 |
+
part2_sum += int(test_value)
|
55 |
+
|
56 |
+
print(part2_sum)
|
day07/solution_gpt-4o.py
ADDED
@@ -0,0 +1,34 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from itertools import product
|
2 |
+
|
3 |
+
def evaluate_expression(numbers, operators):
|
4 |
+
result = numbers[0]
|
5 |
+
for i, op in enumerate(operators):
|
6 |
+
if op == '+':
|
7 |
+
result += numbers[i + 1]
|
8 |
+
elif op == '*':
|
9 |
+
result *= numbers[i + 1]
|
10 |
+
elif op == '||':
|
11 |
+
result = int(str(result) + str(numbers[i + 1]))
|
12 |
+
return result
|
13 |
+
|
14 |
+
def is_valid_equation(test_value, numbers):
|
15 |
+
num_operators = len(numbers) - 1
|
16 |
+
for ops in product(['+', '*', '||'], repeat=num_operators):
|
17 |
+
if evaluate_expression(numbers, ops) == test_value:
|
18 |
+
return True
|
19 |
+
return False
|
20 |
+
|
21 |
+
def main():
|
22 |
+
total_calibration_result = 0
|
23 |
+
with open("input.txt", "r") as file:
|
24 |
+
for line in file:
|
25 |
+
test_value_str, numbers_str = line.split(':')
|
26 |
+
test_value = int(test_value_str.strip())
|
27 |
+
numbers = list(map(int, numbers_str.split()))
|
28 |
+
|
29 |
+
if is_valid_equation(test_value, numbers):
|
30 |
+
total_calibration_result += test_value
|
31 |
+
|
32 |
+
print(total_calibration_result)
|
33 |
+
|
34 |
+
main()
|
day07/solution_jerpint.py
ADDED
@@ -0,0 +1,115 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
def parse_data(file):
|
2 |
+
with open(file) as f:
|
3 |
+
raw_data = f.readlines()
|
4 |
+
|
5 |
+
# Read and parse the data
|
6 |
+
data = []
|
7 |
+
for line in raw_data:
|
8 |
+
line = line.strip("\n")
|
9 |
+
target, nums = line.split(":")
|
10 |
+
target = int(target)
|
11 |
+
nums = [int(num) for num in nums.split()]
|
12 |
+
data.append((target, nums))
|
13 |
+
return data
|
14 |
+
|
15 |
+
|
16 |
+
def compute(a, b, op):
|
17 |
+
"""Compute given the op"""
|
18 |
+
if op == "+":
|
19 |
+
return a + b
|
20 |
+
if op == "*":
|
21 |
+
return a * b
|
22 |
+
|
23 |
+
|
24 |
+
def check_seq(nums, target, seq):
|
25 |
+
total = nums[0]
|
26 |
+
for i in range(len(seq)):
|
27 |
+
b = nums[i+1]
|
28 |
+
op = seq[i]
|
29 |
+
|
30 |
+
total = compute(total, b, op)
|
31 |
+
|
32 |
+
if total > target:
|
33 |
+
# Dead-end
|
34 |
+
return -1
|
35 |
+
|
36 |
+
# Check that we equal target and use all nums
|
37 |
+
return total == target and len(seq) == len(nums) - 1
|
38 |
+
|
39 |
+
|
40 |
+
def bfs(target, nums, ops):
|
41 |
+
q = ops.copy()
|
42 |
+
dead_ends = set()
|
43 |
+
|
44 |
+
while len(q) > 0:
|
45 |
+
# print(q)
|
46 |
+
# print(dead_ends)
|
47 |
+
seq = q.pop(0)
|
48 |
+
|
49 |
+
if seq in dead_ends:
|
50 |
+
break
|
51 |
+
|
52 |
+
check = check_seq(nums, target, seq)
|
53 |
+
|
54 |
+
# print(nums, target, seq, check)
|
55 |
+
|
56 |
+
if check == -1:
|
57 |
+
dead_ends.add(seq)
|
58 |
+
continue
|
59 |
+
|
60 |
+
elif check == True:
|
61 |
+
return True
|
62 |
+
|
63 |
+
else:
|
64 |
+
if len(seq) < len(nums)-1:
|
65 |
+
for op in ops:
|
66 |
+
q.append(seq+op)
|
67 |
+
|
68 |
+
return False
|
69 |
+
|
70 |
+
data = parse_data(file="input.txt")
|
71 |
+
ops = ["+", "*"]
|
72 |
+
total = 0
|
73 |
+
for target, nums in data:
|
74 |
+
|
75 |
+
result = bfs(target, nums, ops)
|
76 |
+
# print("*"*20)
|
77 |
+
# print(target, nums)
|
78 |
+
# print("Result:", result)
|
79 |
+
# print("*"*20)
|
80 |
+
|
81 |
+
if result:
|
82 |
+
total += target
|
83 |
+
|
84 |
+
print(total)
|
85 |
+
|
86 |
+
## Part 2
|
87 |
+
|
88 |
+
def compute(a, b, op):
|
89 |
+
"""Compute given the op"""
|
90 |
+
if op == "+":
|
91 |
+
return a + b
|
92 |
+
elif op == "*":
|
93 |
+
return a * b
|
94 |
+
elif op == "|":
|
95 |
+
return int(str(a) + str(b))
|
96 |
+
else:
|
97 |
+
raise ValueError(f"Op {op} Unknown")
|
98 |
+
|
99 |
+
|
100 |
+
|
101 |
+
ops = ["+", "*", "|"]
|
102 |
+
data = parse_data(file="input.txt")
|
103 |
+
total = 0
|
104 |
+
for target, nums in data:
|
105 |
+
|
106 |
+
result = bfs(target, nums, ops)
|
107 |
+
# print("*"*20)
|
108 |
+
# print(target, nums)
|
109 |
+
# print("Result:", result)
|
110 |
+
# print("*"*20)
|
111 |
+
|
112 |
+
if result:
|
113 |
+
total += target
|
114 |
+
|
115 |
+
print(total)
|
day08/solution_claude-3-5-sonnet-20241022.py
ADDED
@@ -0,0 +1,86 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
def read_input(file_path):
|
2 |
+
with open(file_path, 'r') as f:
|
3 |
+
return [line.strip() for line in f.readlines()]
|
4 |
+
|
5 |
+
def find_antennas(grid):
|
6 |
+
antennas = {}
|
7 |
+
for y in range(len(grid)):
|
8 |
+
for x in range(len(grid[y])):
|
9 |
+
if grid[y][x] not in '.':
|
10 |
+
freq = grid[y][x]
|
11 |
+
if freq not in antennas:
|
12 |
+
antennas[freq] = []
|
13 |
+
antennas[freq].append((x, y))
|
14 |
+
return antennas
|
15 |
+
|
16 |
+
def is_collinear(p1, p2, p3):
|
17 |
+
x1, y1 = p1
|
18 |
+
x2, y2 = p2
|
19 |
+
x3, y3 = p3
|
20 |
+
return (y2-y1)*(x3-x1) == (y3-y1)*(x2-x1)
|
21 |
+
|
22 |
+
def distance_squared(p1, p2):
|
23 |
+
return (p2[0]-p1[0])**2 + (p2[1]-p1[1])**2
|
24 |
+
|
25 |
+
def find_antinodes_part1(grid, antennas):
|
26 |
+
antinodes = set()
|
27 |
+
height, width = len(grid), len(grid[0])
|
28 |
+
|
29 |
+
for freq, positions in antennas.items():
|
30 |
+
if len(positions) < 2:
|
31 |
+
continue
|
32 |
+
|
33 |
+
for i in range(len(positions)):
|
34 |
+
for j in range(i+1, len(positions)):
|
35 |
+
a1, a2 = positions[i], positions[j]
|
36 |
+
|
37 |
+
# Check all points in the grid
|
38 |
+
for y in range(height):
|
39 |
+
for x in range(width):
|
40 |
+
point = (x, y)
|
41 |
+
if point == a1 or point == a2:
|
42 |
+
continue
|
43 |
+
|
44 |
+
if is_collinear(a1, a2, point):
|
45 |
+
d1 = distance_squared(point, a1)
|
46 |
+
d2 = distance_squared(point, a2)
|
47 |
+
if d1 == 2*d2 or d2 == 2*d1:
|
48 |
+
antinodes.add(point)
|
49 |
+
|
50 |
+
return len(antinodes)
|
51 |
+
|
52 |
+
def find_antinodes_part2(grid, antennas):
|
53 |
+
antinodes = set()
|
54 |
+
height, width = len(grid), len(grid[0])
|
55 |
+
|
56 |
+
for freq, positions in antennas.items():
|
57 |
+
if len(positions) < 2:
|
58 |
+
continue
|
59 |
+
|
60 |
+
# Add antenna positions as antinodes if there are multiple antennas of same frequency
|
61 |
+
antinodes.update(positions)
|
62 |
+
|
63 |
+
# Check all points in the grid
|
64 |
+
for y in range(height):
|
65 |
+
for x in range(width):
|
66 |
+
point = (x, y)
|
67 |
+
|
68 |
+
# Count how many pairs of antennas this point is collinear with
|
69 |
+
for i in range(len(positions)):
|
70 |
+
for j in range(i+1, len(positions)):
|
71 |
+
if is_collinear(positions[i], positions[j], point):
|
72 |
+
antinodes.add(point)
|
73 |
+
|
74 |
+
return len(antinodes)
|
75 |
+
|
76 |
+
def solve(file_path):
|
77 |
+
grid = read_input(file_path)
|
78 |
+
antennas = find_antennas(grid)
|
79 |
+
|
80 |
+
part1 = find_antinodes_part1(grid, antennas)
|
81 |
+
part2 = find_antinodes_part2(grid, antennas)
|
82 |
+
|
83 |
+
print(str(part1))
|
84 |
+
print(str(part2))
|
85 |
+
|
86 |
+
solve("./input.txt")
|
day08/solution_gemini-1.5-pro.py
ADDED
@@ -0,0 +1,48 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
def solve():
|
2 |
+
with open("./input.txt", "r") as f:
|
3 |
+
grid = [list(line.strip()) for line in f]
|
4 |
+
|
5 |
+
rows = len(grid)
|
6 |
+
cols = len(grid[0])
|
7 |
+
|
8 |
+
def part1():
|
9 |
+
antinodes = set()
|
10 |
+
for r1 in range(rows):
|
11 |
+
for c1 in range(cols):
|
12 |
+
freq = grid[r1][c1]
|
13 |
+
for r2 in range(rows):
|
14 |
+
for c2 in range(cols):
|
15 |
+
if grid[r2][c2] == freq and (r1, c1) != (r2, c2):
|
16 |
+
dr = r2 - r1
|
17 |
+
dc = c2 - c1
|
18 |
+
|
19 |
+
r3 = r2 + dr
|
20 |
+
c3 = c2 + dc
|
21 |
+
if 0 <= r3 < rows and 0 <= c3 < cols:
|
22 |
+
antinodes.add((r3, c3))
|
23 |
+
|
24 |
+
r3 = r1 - dr
|
25 |
+
c3 = c1 - dc
|
26 |
+
if 0 <= r3 < rows and 0 <= c3 < cols:
|
27 |
+
antinodes.add((r3, c3))
|
28 |
+
return len(antinodes)
|
29 |
+
|
30 |
+
def part2():
|
31 |
+
antinodes = set()
|
32 |
+
for r1 in range(rows):
|
33 |
+
for c1 in range(cols):
|
34 |
+
freq = grid[r1][c1]
|
35 |
+
for r2 in range(rows):
|
36 |
+
for c2 in range(cols):
|
37 |
+
if grid[r2][c2] == freq and (r1, c1) != (r2, c2):
|
38 |
+
for r3 in range(rows):
|
39 |
+
for c3 in range(cols):
|
40 |
+
if (r3*(c2-c1) - c3*(r2-r1)) == (r1*(c2-c1) - c1*(r2-r1)):
|
41 |
+
antinodes.add((r3,c3))
|
42 |
+
return len(antinodes)
|
43 |
+
|
44 |
+
|
45 |
+
print(part1())
|
46 |
+
print(part2())
|
47 |
+
|
48 |
+
solve()
|
day08/solution_gpt-4o.py
ADDED
@@ -0,0 +1,80 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
def parse_input(file):
|
2 |
+
with open(file, 'r') as f:
|
3 |
+
grid = [line.strip() for line in f.readlines()]
|
4 |
+
return grid
|
5 |
+
|
6 |
+
def find_antennas(grid):
|
7 |
+
antennas = {}
|
8 |
+
for y, row in enumerate(grid):
|
9 |
+
for x, char in enumerate(row):
|
10 |
+
if char != '.':
|
11 |
+
if char not in antennas:
|
12 |
+
antennas[char] = []
|
13 |
+
antennas[char].append((x, y))
|
14 |
+
return antennas
|
15 |
+
|
16 |
+
def calculate_antinodes_part_one(antennas):
|
17 |
+
antinodes = set()
|
18 |
+
for freq, positions in antennas.items():
|
19 |
+
n = len(positions)
|
20 |
+
for i in range(n):
|
21 |
+
for j in range(i + 1, n):
|
22 |
+
x1, y1 = positions[i]
|
23 |
+
x2, y2 = positions[j]
|
24 |
+
# Check for antinodes on the line between (x1, y1) and (x2, y2)
|
25 |
+
if x1 == x2: # Vertical line
|
26 |
+
for y in range(min(y1, y2) - abs(y2 - y1), max(y1, y2) + abs(y2 - y1) + 1):
|
27 |
+
if y != y1 and y != y2:
|
28 |
+
antinodes.add((x1, y))
|
29 |
+
elif y1 == y2: # Horizontal line
|
30 |
+
for x in range(min(x1, x2) - abs(x2 - x1), max(x1, x2) + abs(x2 - x1) + 1):
|
31 |
+
if x != x1 and x != x2:
|
32 |
+
antinodes.add((x, y1))
|
33 |
+
else: # Diagonal line
|
34 |
+
dx = x2 - x1
|
35 |
+
dy = y2 - y1
|
36 |
+
for k in range(-max(abs(dx), abs(dy)), max(abs(dx), abs(dy)) + 1):
|
37 |
+
x = x1 + k * dx
|
38 |
+
y = y1 + k * dy
|
39 |
+
if (x, y) != (x1, y1) and (x, y) != (x2, y2):
|
40 |
+
antinodes.add((x, y))
|
41 |
+
return antinodes
|
42 |
+
|
43 |
+
def calculate_antinodes_part_two(antennas):
|
44 |
+
antinodes = set()
|
45 |
+
for freq, positions in antennas.items():
|
46 |
+
n = len(positions)
|
47 |
+
for i in range(n):
|
48 |
+
for j in range(i + 1, n):
|
49 |
+
x1, y1 = positions[i]
|
50 |
+
x2, y2 = positions[j]
|
51 |
+
# Mark all points in line with at least two antennas
|
52 |
+
if x1 == x2: # Vertical line
|
53 |
+
for y in range(min(y1, y2), max(y1, y2) + 1):
|
54 |
+
antinodes.add((x1, y))
|
55 |
+
elif y1 == y2: # Horizontal line
|
56 |
+
for x in range(min(x1, x2), max(x1, x2) + 1):
|
57 |
+
antinodes.add((x, y1))
|
58 |
+
else: # Diagonal line
|
59 |
+
dx = x2 - x1
|
60 |
+
dy = y2 - y1
|
61 |
+
for k in range(max(abs(dx), abs(dy)) + 1):
|
62 |
+
x = x1 + k * (dx // abs(dx))
|
63 |
+
y = y1 + k * (dy // abs(dy))
|
64 |
+
antinodes.add((x, y))
|
65 |
+
return antinodes
|
66 |
+
|
67 |
+
def main():
|
68 |
+
file = "input.txt"
|
69 |
+
grid = parse_input(file)
|
70 |
+
antennas = find_antennas(grid)
|
71 |
+
|
72 |
+
# Part One
|
73 |
+
antinodes_part_one = calculate_antinodes_part_one(antennas)
|
74 |
+
print(len(antinodes_part_one))
|
75 |
+
|
76 |
+
# Part Two
|
77 |
+
antinodes_part_two = calculate_antinodes_part_two(antennas)
|
78 |
+
print(len(antinodes_part_two))
|
79 |
+
|
80 |
+
main()
|
day08/solution_jerpint.py
ADDED
@@ -0,0 +1,142 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
def load_data(file):
|
2 |
+
with open(file) as f:
|
3 |
+
raw_data = f.readlines()
|
4 |
+
|
5 |
+
grid = []
|
6 |
+
for line in raw_data:
|
7 |
+
line = line.strip("\n")
|
8 |
+
grid.append(list(line))
|
9 |
+
return grid
|
10 |
+
|
11 |
+
|
12 |
+
def get_antennas(grid):
|
13 |
+
|
14 |
+
M = len(grid)
|
15 |
+
N = len(grid[0])
|
16 |
+
|
17 |
+
antennas = {}
|
18 |
+
for i in range(M):
|
19 |
+
for j in range(N):
|
20 |
+
if grid[i][j] != ".":
|
21 |
+
a = grid[i][j]
|
22 |
+
if antennas.get(a):
|
23 |
+
antennas[a].append((i,j))
|
24 |
+
else:
|
25 |
+
antennas[a] = [(i,j)]
|
26 |
+
return antennas
|
27 |
+
|
28 |
+
|
29 |
+
def get_next_nodes(a, b):
|
30 |
+
|
31 |
+
# Get direction vector
|
32 |
+
dx = b[0] - a[0]
|
33 |
+
dy = b[1] - a[1]
|
34 |
+
|
35 |
+
node_a = (a[0] - dx, a[1] - dy) # Subtract from first node
|
36 |
+
node_b = (b[0] + dx, b[1] + dy) # Add to second node
|
37 |
+
|
38 |
+
return node_a, node_b
|
39 |
+
|
40 |
+
|
41 |
+
def add_antinode(a, grid):
|
42 |
+
M = len(grid)
|
43 |
+
N = len(grid[0])
|
44 |
+
i,j = a
|
45 |
+
if i in range(M) and j in range(N):
|
46 |
+
grid[i][j] = '#'
|
47 |
+
|
48 |
+
|
49 |
+
def count_antinodes(grid):
|
50 |
+
M = len(grid)
|
51 |
+
N = len(grid[0])
|
52 |
+
|
53 |
+
total = 0
|
54 |
+
for i in range(M):
|
55 |
+
for j in range(N):
|
56 |
+
if grid[i][j] == '#':
|
57 |
+
total += 1
|
58 |
+
return total
|
59 |
+
|
60 |
+
|
61 |
+
def add_antinodes(a, b, grid):
|
62 |
+
next_nodes = get_next_nodes(a,b)
|
63 |
+
|
64 |
+
for node in next_nodes:
|
65 |
+
add_antinode(node, grid)
|
66 |
+
|
67 |
+
|
68 |
+
grid = load_data("input.txt")
|
69 |
+
antennas = get_antennas(grid)
|
70 |
+
|
71 |
+
for freq in antennas:
|
72 |
+
|
73 |
+
# Get all positions for a given freq.
|
74 |
+
positions = antennas[freq]
|
75 |
+
for i in range(len(positions)):
|
76 |
+
for j in range(i+1, len(positions)):
|
77 |
+
# For each pair of antennas, add the antinodes
|
78 |
+
a, b = positions[i], positions[j]
|
79 |
+
add_antinodes(a, b, grid)
|
80 |
+
|
81 |
+
print(count_antinodes(grid))
|
82 |
+
|
83 |
+
|
84 |
+
## Part 2
|
85 |
+
def get_direction_vector(a, b):
|
86 |
+
|
87 |
+
# Get direction vector
|
88 |
+
dx = b[0] - a[0]
|
89 |
+
dy = b[1] - a[1]
|
90 |
+
|
91 |
+
return dx, dy
|
92 |
+
|
93 |
+
|
94 |
+
def is_in_bounds(node, grid):
|
95 |
+
M = len(grid)
|
96 |
+
N = len(grid[0])
|
97 |
+
|
98 |
+
i, j = node
|
99 |
+
|
100 |
+
return i in range(M) and j in range(M)
|
101 |
+
|
102 |
+
def add_antinodes(a, b, grid):
|
103 |
+
dx, dy = get_direction_vector(a,b)
|
104 |
+
|
105 |
+
next_node = (a[0] - dx, a[1] - dy) # Subtract from first node
|
106 |
+
while is_in_bounds(next_node, grid):
|
107 |
+
add_antinode(next_node, grid)
|
108 |
+
next_node = (next_node[0] - dx, next_node[1] - dy)
|
109 |
+
|
110 |
+
next_node = (b[0] + dx, b[1] + dy) # Add to second node
|
111 |
+
while is_in_bounds(next_node, grid):
|
112 |
+
add_antinode(next_node, grid)
|
113 |
+
next_node = (next_node[0] + dx, next_node[1] + dy)
|
114 |
+
|
115 |
+
|
116 |
+
def count_antinodes(grid):
|
117 |
+
M = len(grid)
|
118 |
+
N = len(grid[0])
|
119 |
+
|
120 |
+
total = 0
|
121 |
+
for i in range(M):
|
122 |
+
for j in range(N):
|
123 |
+
if grid[i][j] != '.':
|
124 |
+
# Antennas count too
|
125 |
+
total += 1
|
126 |
+
return total
|
127 |
+
|
128 |
+
grid = load_data("input.txt")
|
129 |
+
antennas = get_antennas(grid)
|
130 |
+
|
131 |
+
|
132 |
+
for freq in antennas:
|
133 |
+
positions = antennas[freq]
|
134 |
+
for i in range(len(positions)):
|
135 |
+
for j in range(i+1, len(positions)):
|
136 |
+
a, b = positions[i], positions[j]
|
137 |
+
add_antinodes(a, b, grid)
|
138 |
+
print(count_antinodes(grid))
|
139 |
+
|
140 |
+
def pprint(grid):
|
141 |
+
grid_str = "\n".join(["".join(l) for l in grid])
|
142 |
+
print(grid_str)
|
day09/solution_claude-3-5-sonnet-20241022.py
ADDED
@@ -0,0 +1,99 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
def parse_input(s):
|
2 |
+
# Convert the input string into alternating lengths of files and spaces
|
3 |
+
lengths = [int(c) for c in s]
|
4 |
+
|
5 |
+
# Convert to blocks representation
|
6 |
+
blocks = []
|
7 |
+
file_id = 0
|
8 |
+
pos = 0
|
9 |
+
for i, length in enumerate(lengths):
|
10 |
+
if i % 2 == 0: # File
|
11 |
+
blocks.extend([file_id] * length)
|
12 |
+
file_id += 1
|
13 |
+
else: # Space
|
14 |
+
blocks.extend([-1] * length) # -1 represents free space
|
15 |
+
return blocks
|
16 |
+
|
17 |
+
def calculate_checksum(blocks):
|
18 |
+
checksum = 0
|
19 |
+
for pos, block in enumerate(blocks):
|
20 |
+
if block != -1: # Skip free space
|
21 |
+
checksum += pos * block
|
22 |
+
return checksum
|
23 |
+
|
24 |
+
def compact_blocks_part1(blocks):
|
25 |
+
result = blocks.copy()
|
26 |
+
|
27 |
+
while True:
|
28 |
+
# Find rightmost file block
|
29 |
+
right = len(result) - 1
|
30 |
+
while right >= 0 and result[right] == -1:
|
31 |
+
right -= 1
|
32 |
+
if right < 0:
|
33 |
+
break
|
34 |
+
|
35 |
+
# Find leftmost free space
|
36 |
+
left = 0
|
37 |
+
while left < len(result) and result[left] != -1:
|
38 |
+
left += 1
|
39 |
+
if left >= right:
|
40 |
+
break
|
41 |
+
|
42 |
+
# Move one block
|
43 |
+
result[left] = result[right]
|
44 |
+
result[right] = -1
|
45 |
+
|
46 |
+
return result
|
47 |
+
|
48 |
+
def compact_blocks_part2(blocks):
|
49 |
+
result = blocks.copy()
|
50 |
+
|
51 |
+
# Get unique file IDs in descending order
|
52 |
+
file_ids = sorted(set(x for x in result if x != -1), reverse=True)
|
53 |
+
|
54 |
+
for file_id in file_ids:
|
55 |
+
# Find all blocks of this file
|
56 |
+
file_blocks = []
|
57 |
+
start = None
|
58 |
+
for i, block in enumerate(result):
|
59 |
+
if block == file_id:
|
60 |
+
if start is None:
|
61 |
+
start = i
|
62 |
+
file_blocks.append(block)
|
63 |
+
elif start is not None and block != file_id:
|
64 |
+
break
|
65 |
+
|
66 |
+
if not file_blocks:
|
67 |
+
continue
|
68 |
+
|
69 |
+
# Remove the file from its current position
|
70 |
+
for i in range(start, start + len(file_blocks)):
|
71 |
+
result[i] = -1
|
72 |
+
|
73 |
+
# Find leftmost position where file can fit
|
74 |
+
pos = 0
|
75 |
+
while pos < len(result):
|
76 |
+
if all(result[i] == -1 for i in range(pos, pos + len(file_blocks)) if i < len(result)):
|
77 |
+
# Place file here
|
78 |
+
for i in range(len(file_blocks)):
|
79 |
+
result[pos + i] = file_id
|
80 |
+
break
|
81 |
+
pos += 1
|
82 |
+
|
83 |
+
return result
|
84 |
+
|
85 |
+
# Read input
|
86 |
+
with open("input.txt", "r") as f:
|
87 |
+
input_data = f.read().strip()
|
88 |
+
|
89 |
+
# Part 1
|
90 |
+
blocks = parse_input(input_data)
|
91 |
+
compacted = compact_blocks_part1(blocks)
|
92 |
+
checksum = calculate_checksum(compacted)
|
93 |
+
print(str(checksum))
|
94 |
+
|
95 |
+
# Part 2
|
96 |
+
blocks = parse_input(input_data)
|
97 |
+
compacted = compact_blocks_part2(blocks)
|
98 |
+
checksum = calculate_checksum(compacted)
|
99 |
+
print(str(checksum))
|
day09/solution_gemini-1.5-pro.py
ADDED
@@ -0,0 +1,82 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
file = "input.txt"
|
2 |
+
|
3 |
+
def solve_part1(disk_map):
|
4 |
+
disk_blocks = []
|
5 |
+
file_id = 0
|
6 |
+
is_file = True
|
7 |
+
for i in range(0, len(disk_map), 2):
|
8 |
+
file_size = int(disk_map[i])
|
9 |
+
free_size = int(disk_map[i+1])
|
10 |
+
|
11 |
+
disk_blocks.extend([file_id] * file_size)
|
12 |
+
disk_blocks.extend(["."] * free_size)
|
13 |
+
file_id += 1
|
14 |
+
|
15 |
+
disk_str = "".join(map(str,disk_blocks))
|
16 |
+
|
17 |
+
while "." in disk_str:
|
18 |
+
first_dot = disk_str.find(".")
|
19 |
+
last_file_block = disk_str.rfind(str(file_id - 1))
|
20 |
+
|
21 |
+
disk_list = list(disk_str)
|
22 |
+
disk_list[first_dot] = disk_list[last_file_block]
|
23 |
+
disk_list[last_file_block] = "."
|
24 |
+
disk_str = "".join(disk_list)
|
25 |
+
|
26 |
+
checksum = 0
|
27 |
+
for i in range(len(disk_str)):
|
28 |
+
if disk_str[i] != ".":
|
29 |
+
checksum += i * int(disk_str[i])
|
30 |
+
return checksum
|
31 |
+
|
32 |
+
def solve_part2(disk_map):
|
33 |
+
disk_blocks = []
|
34 |
+
file_sizes = {}
|
35 |
+
file_id = 0
|
36 |
+
is_file = True
|
37 |
+
for i in range(0, len(disk_map), 2):
|
38 |
+
file_size = int(disk_map[i])
|
39 |
+
free_size = int(disk_map[i+1])
|
40 |
+
file_sizes[file_id] = file_size
|
41 |
+
disk_blocks.extend([file_id] * file_size)
|
42 |
+
disk_blocks.extend(["."] * free_size)
|
43 |
+
file_id += 1
|
44 |
+
|
45 |
+
disk_str = "".join(map(str, disk_blocks))
|
46 |
+
|
47 |
+
for fid in range(file_id - 1, -1, -1):
|
48 |
+
file_size = file_sizes[fid]
|
49 |
+
first_occurrence = disk_str.find(str(fid))
|
50 |
+
free_space_size = 0
|
51 |
+
free_space_start = -1
|
52 |
+
|
53 |
+
for i in range(len(disk_str)):
|
54 |
+
if disk_str[i] == ".":
|
55 |
+
if free_space_start == -1:
|
56 |
+
free_space_start = i
|
57 |
+
free_space_size += 1
|
58 |
+
else:
|
59 |
+
if free_space_size >= file_size and free_space_start < first_occurrence:
|
60 |
+
disk_list = list(disk_str)
|
61 |
+
for j in range(file_size):
|
62 |
+
disk_list[free_space_start + j] = str(fid)
|
63 |
+
disk_list[first_occurrence + j] = "."
|
64 |
+
disk_str = "".join(disk_list)
|
65 |
+
break
|
66 |
+
free_space_start = -1
|
67 |
+
free_space_size = 0
|
68 |
+
|
69 |
+
checksum = 0
|
70 |
+
for i in range(len(disk_str)):
|
71 |
+
if disk_str[i] != ".":
|
72 |
+
checksum += i * int(disk_str[i])
|
73 |
+
return checksum
|
74 |
+
|
75 |
+
with open(file) as f:
|
76 |
+
disk_map = f.readline().strip()
|
77 |
+
|
78 |
+
result1 = solve_part1(disk_map)
|
79 |
+
print(result1)
|
80 |
+
|
81 |
+
result2 = solve_part2(disk_map)
|
82 |
+
print(result2)
|
day09/solution_gpt-4o.py
ADDED
@@ -0,0 +1,80 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
def parse_disk_map(disk_map):
|
2 |
+
disk = []
|
3 |
+
file_id = 0
|
4 |
+
for i in range(0, len(disk_map), 2):
|
5 |
+
file_length = int(disk_map[i])
|
6 |
+
free_space_length = int(disk_map[i+1])
|
7 |
+
disk.extend([str(file_id)] * file_length)
|
8 |
+
disk.extend(['.'] * free_space_length)
|
9 |
+
file_id += 1
|
10 |
+
return disk
|
11 |
+
|
12 |
+
def compact_disk_individual_blocks(disk):
|
13 |
+
# Move individual blocks to the leftmost free space
|
14 |
+
for i in range(len(disk)):
|
15 |
+
if disk[i] == '.':
|
16 |
+
# Find the next file block to move
|
17 |
+
for j in range(i + 1, len(disk)):
|
18 |
+
if disk[j] != '.':
|
19 |
+
# Move the block
|
20 |
+
disk[i], disk[j] = disk[j], disk[i]
|
21 |
+
break
|
22 |
+
return disk
|
23 |
+
|
24 |
+
def compact_disk_whole_files(disk):
|
25 |
+
# Move whole files to the leftmost free space
|
26 |
+
file_positions = []
|
27 |
+
current_pos = 0
|
28 |
+
while current_pos < len(disk):
|
29 |
+
if disk[current_pos] != '.':
|
30 |
+
file_id = disk[current_pos]
|
31 |
+
file_start = current_pos
|
32 |
+
while current_pos < len(disk) and disk[current_pos] == file_id:
|
33 |
+
current_pos += 1
|
34 |
+
file_positions.append((file_id, file_start, current_pos - file_start))
|
35 |
+
else:
|
36 |
+
current_pos += 1
|
37 |
+
|
38 |
+
# Move files in reverse order of file ID
|
39 |
+
for file_id, file_start, file_length in sorted(file_positions, key=lambda x: -int(x[0])):
|
40 |
+
# Find the leftmost free space that can fit the file
|
41 |
+
free_start = 0
|
42 |
+
while free_start < len(disk):
|
43 |
+
if disk[free_start] == '.':
|
44 |
+
free_end = free_start
|
45 |
+
while free_end < len(disk) and disk[free_end] == '.':
|
46 |
+
free_end += 1
|
47 |
+
if free_end - free_start >= file_length:
|
48 |
+
# Move the file
|
49 |
+
for i in range(file_length):
|
50 |
+
disk[free_start + i] = file_id
|
51 |
+
for i in range(file_length):
|
52 |
+
disk[file_start + i] = '.'
|
53 |
+
break
|
54 |
+
free_start = free_end + 1
|
55 |
+
return disk
|
56 |
+
|
57 |
+
def calculate_checksum(disk):
|
58 |
+
checksum = 0
|
59 |
+
for position, block in enumerate(disk):
|
60 |
+
if block != '.':
|
61 |
+
checksum += position * int(block)
|
62 |
+
return checksum
|
63 |
+
|
64 |
+
def main():
|
65 |
+
with open("input.txt", "r") as file:
|
66 |
+
disk_map = file.readline().strip()
|
67 |
+
|
68 |
+
# Part 1
|
69 |
+
disk = parse_disk_map(disk_map)
|
70 |
+
compacted_disk = compact_disk_individual_blocks(disk)
|
71 |
+
checksum_part1 = calculate_checksum(compacted_disk)
|
72 |
+
print(checksum_part1)
|
73 |
+
|
74 |
+
# Part 2
|
75 |
+
disk = parse_disk_map(disk_map)
|
76 |
+
compacted_disk = compact_disk_whole_files(disk)
|
77 |
+
checksum_part2 = calculate_checksum(compacted_disk)
|
78 |
+
print(checksum_part2)
|
79 |
+
|
80 |
+
main()
|
day09/solution_jerpint.py
ADDED
@@ -0,0 +1,159 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
def load_data(file):
|
2 |
+
with open(file) as f:
|
3 |
+
return f.read().strip("\n")
|
4 |
+
|
5 |
+
|
6 |
+
def generate_blocks(disk_map):
|
7 |
+
blocks = []
|
8 |
+
for idx, i in enumerate(disk_map):
|
9 |
+
|
10 |
+
if idx % 2:
|
11 |
+
blocks.extend( ["."]*int(i))
|
12 |
+
else:
|
13 |
+
blocks.extend( [str(idx // 2)] *int(i))
|
14 |
+
|
15 |
+
return blocks
|
16 |
+
|
17 |
+
|
18 |
+
def get_next_valid_block_from_end(end: int, blocks):
|
19 |
+
for j in range(end, -1, -1):
|
20 |
+
if blocks[j] != ".":
|
21 |
+
return j
|
22 |
+
|
23 |
+
|
24 |
+
def compact_blocks(blocks):
|
25 |
+
# blocks = list(blocks)
|
26 |
+
end = get_next_valid_block_from_end(len(blocks) - 1, blocks)
|
27 |
+
for idx, block in enumerate(blocks):
|
28 |
+
|
29 |
+
if end == idx:
|
30 |
+
break
|
31 |
+
|
32 |
+
if block == ".":
|
33 |
+
blocks[idx] = blocks[end]
|
34 |
+
blocks[end] = "."
|
35 |
+
end = get_next_valid_block_from_end(end, blocks)
|
36 |
+
|
37 |
+
# print("".join(blocks))
|
38 |
+
|
39 |
+
return blocks
|
40 |
+
|
41 |
+
def compute_checksum(blocks):
|
42 |
+
checksum = 0
|
43 |
+
for idx, num in enumerate(blocks):
|
44 |
+
if num == ".":
|
45 |
+
return checksum
|
46 |
+
checksum += idx*int(num)
|
47 |
+
return checksum
|
48 |
+
|
49 |
+
|
50 |
+
disk_map = load_data("input.txt")
|
51 |
+
blocks = generate_blocks(disk_map)
|
52 |
+
compact_blocks = compact_blocks(blocks)
|
53 |
+
print(compute_checksum(compact_blocks))
|
54 |
+
|
55 |
+
|
56 |
+
## Part two
|
57 |
+
|
58 |
+
def generate_blocks(disk_map):
|
59 |
+
blocks = []
|
60 |
+
for idx, i in enumerate(disk_map):
|
61 |
+
|
62 |
+
if idx % 2:
|
63 |
+
if int(i) > 0:
|
64 |
+
blocks.append( ["."]*int(i))
|
65 |
+
else:
|
66 |
+
blocks.append( [str(idx // 2)] *int(i))
|
67 |
+
|
68 |
+
return blocks
|
69 |
+
|
70 |
+
|
71 |
+
def get_free_space_map(blocks):
|
72 |
+
free_space_map = []
|
73 |
+
pos = 0
|
74 |
+
for idx, block in enumerate(blocks):
|
75 |
+
|
76 |
+
if block[0] == ".":
|
77 |
+
free_space_map.append((idx, len(block)))
|
78 |
+
|
79 |
+
pos += len(block)
|
80 |
+
|
81 |
+
return free_space_map
|
82 |
+
|
83 |
+
|
84 |
+
def get_next_valid_block_from_end(blocks):
|
85 |
+
for idx, block in enumerate(blocks[::-1]):
|
86 |
+
if block[0] != ".":
|
87 |
+
block_idx = len(blocks) - idx - 1
|
88 |
+
return block, block_idx
|
89 |
+
|
90 |
+
|
91 |
+
def pprint(blocks):
|
92 |
+
print("".join(["".join(b) for b in blocks]))
|
93 |
+
|
94 |
+
|
95 |
+
def move_block_at_fileid(blocks, file_id: int):
|
96 |
+
|
97 |
+
free_space_map = get_free_space_map(blocks)
|
98 |
+
# block = blocks[block_idx]
|
99 |
+
for idx, block in enumerate(blocks):
|
100 |
+
if block[0] != "." and block[0] == str(file_id):
|
101 |
+
block_idx = idx
|
102 |
+
break
|
103 |
+
|
104 |
+
K = len(block)
|
105 |
+
for free_block_idx, free_len in free_space_map:
|
106 |
+
|
107 |
+
if free_len > K and free_block_idx < block_idx:
|
108 |
+
# First condition means we have more space than needed
|
109 |
+
# Second condition ensures we Only move forward in queue, not backwards
|
110 |
+
blocks[free_block_idx] = ["."] * (free_len - len(block)) # Remaining free memory after insert
|
111 |
+
blocks.insert(free_block_idx, block) # Insert at index
|
112 |
+
blocks[block_idx + 1] = ["."] * K # Overwrite at end (+1 because we added a new index)
|
113 |
+
return True
|
114 |
+
|
115 |
+
elif free_len == K and free_block_idx < block_idx:
|
116 |
+
# First condition means we have just enough space
|
117 |
+
# Second condition ensures we Only move forward in queue, not backwards
|
118 |
+
blocks[free_block_idx] = block
|
119 |
+
blocks[block_idx] = ["."] * K
|
120 |
+
return True
|
121 |
+
|
122 |
+
return False
|
123 |
+
|
124 |
+
|
125 |
+
def compute_checksum(blocks):
|
126 |
+
blocks_ext = [b for block in blocks for b in block]
|
127 |
+
checksum = 0
|
128 |
+
for idx, num in enumerate(blocks_ext):
|
129 |
+
if num == ".":
|
130 |
+
continue
|
131 |
+
|
132 |
+
checksum += idx*int(num)
|
133 |
+
return checksum
|
134 |
+
|
135 |
+
|
136 |
+
def get_max_file_id(blocks):
|
137 |
+
for block in blocks[::-1]:
|
138 |
+
if block[0] != ".":
|
139 |
+
max_file_id = int(block[0])
|
140 |
+
return max_file_id
|
141 |
+
|
142 |
+
|
143 |
+
disk_map = load_data("input.txt")
|
144 |
+
blocks = generate_blocks(disk_map)
|
145 |
+
|
146 |
+
max_file_id = get_max_file_id(blocks)
|
147 |
+
# pprint(blocks)
|
148 |
+
for file_id in range(max_file_id, -1, -1):
|
149 |
+
|
150 |
+
# Progress-bar
|
151 |
+
# if file_id % 1000 == 0:
|
152 |
+
# print(file_id)
|
153 |
+
|
154 |
+
moved = move_block_at_fileid(blocks, file_id)
|
155 |
+
|
156 |
+
# if moved:
|
157 |
+
# pprint(blocks)
|
158 |
+
|
159 |
+
print(compute_checksum(blocks))
|
day10/solution_claude-3-5-sonnet-20241022.py
ADDED
@@ -0,0 +1,70 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from collections import defaultdict
|
2 |
+
from typing import List, Set, Tuple, Dict
|
3 |
+
|
4 |
+
def read_grid(filename: str) -> List[List[int]]:
|
5 |
+
with open(filename) as f:
|
6 |
+
return [[int(c) for c in line.strip()] for line in f]
|
7 |
+
|
8 |
+
def get_neighbors(grid: List[List[int]], x: int, y: int) -> List[Tuple[int, int]]:
|
9 |
+
directions = [(0, 1), (1, 0), (0, -1), (-1, 0)]
|
10 |
+
height, width = len(grid), len(grid[0])
|
11 |
+
neighbors = []
|
12 |
+
for dx, dy in directions:
|
13 |
+
nx, ny = x + dx, y + dy
|
14 |
+
if 0 <= nx < height and 0 <= ny < width:
|
15 |
+
neighbors.append((nx, ny))
|
16 |
+
return neighbors
|
17 |
+
|
18 |
+
def find_trailheads(grid: List[List[int]]) -> List[Tuple[int, int]]:
|
19 |
+
return [(i, j) for i in range(len(grid)) for j in range(len(grid[0])) if grid[i][j] == 0]
|
20 |
+
|
21 |
+
def count_reachable_nines(grid: List[List[int]], start: Tuple[int, int]) -> int:
|
22 |
+
def dfs(x: int, y: int, visited: Set[Tuple[int, int]]) -> Set[Tuple[int, int]]:
|
23 |
+
nines = set()
|
24 |
+
visited.add((x, y))
|
25 |
+
current_height = grid[x][y]
|
26 |
+
|
27 |
+
if current_height == 9:
|
28 |
+
nines.add((x, y))
|
29 |
+
|
30 |
+
for nx, ny in get_neighbors(grid, x, y):
|
31 |
+
if (nx, ny) not in visited and grid[nx][ny] == current_height + 1:
|
32 |
+
nines.update(dfs(nx, ny, visited))
|
33 |
+
|
34 |
+
return nines
|
35 |
+
|
36 |
+
return len(dfs(start[0], start[1], set()))
|
37 |
+
|
38 |
+
def count_distinct_paths(grid: List[List[int]], start: Tuple[int, int]) -> int:
|
39 |
+
memo: Dict[Tuple[int, int], int] = {}
|
40 |
+
|
41 |
+
def dfs(x: int, y: int) -> int:
|
42 |
+
if (x, y) in memo:
|
43 |
+
return memo[(x, y)]
|
44 |
+
|
45 |
+
if grid[x][y] == 9:
|
46 |
+
return 1
|
47 |
+
|
48 |
+
paths = 0
|
49 |
+
current_height = grid[x][y]
|
50 |
+
|
51 |
+
for nx, ny in get_neighbors(grid, x, y):
|
52 |
+
if grid[nx][ny] == current_height + 1:
|
53 |
+
paths += dfs(nx, ny)
|
54 |
+
|
55 |
+
memo[(x, y)] = paths
|
56 |
+
return paths
|
57 |
+
|
58 |
+
return dfs(start[0], start[1])
|
59 |
+
|
60 |
+
def solve_part1(grid: List[List[int]]) -> int:
|
61 |
+
trailheads = find_trailheads(grid)
|
62 |
+
return sum(count_reachable_nines(grid, start) for start in trailheads)
|
63 |
+
|
64 |
+
def solve_part2(grid: List[List[int]]) -> int:
|
65 |
+
trailheads = find_trailheads(grid)
|
66 |
+
return sum(count_distinct_paths(grid, start) for start in trailheads)
|
67 |
+
|
68 |
+
grid = read_grid("./input.txt")
|
69 |
+
print(str(solve_part1(grid)))
|
70 |
+
print(str(solve_part2(grid)))
|
day10/solution_gemini-1.5-pro.py
ADDED
@@ -0,0 +1,72 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from collections import deque
|
2 |
+
|
3 |
+
def solve():
|
4 |
+
file = "input.txt"
|
5 |
+
grid = []
|
6 |
+
with open(file, 'r') as f:
|
7 |
+
for line in f:
|
8 |
+
grid.append([int(x) for x in line.strip()])
|
9 |
+
|
10 |
+
rows = len(grid)
|
11 |
+
cols = len(grid[0])
|
12 |
+
|
13 |
+
def get_neighbors(r, c):
|
14 |
+
neighbors = []
|
15 |
+
for dr, dc in [(0, 1), (0, -1), (1, 0), (-1, 0)]:
|
16 |
+
nr, nc = r + dr, c + dc
|
17 |
+
if 0 <= nr < rows and 0 <= nc < cols:
|
18 |
+
neighbors.append((nr, nc))
|
19 |
+
return neighbors
|
20 |
+
|
21 |
+
def dfs_score(r, c):
|
22 |
+
score = 0
|
23 |
+
visited = set()
|
24 |
+
stack = [(r, c)]
|
25 |
+
|
26 |
+
while stack:
|
27 |
+
curr_r, curr_c = stack.pop()
|
28 |
+
if (curr_r, curr_c) in visited:
|
29 |
+
continue
|
30 |
+
visited.add((curr_r, curr_c))
|
31 |
+
|
32 |
+
if grid[curr_r][curr_c] == 9:
|
33 |
+
score += 1
|
34 |
+
continue
|
35 |
+
|
36 |
+
for nr, nc in get_neighbors(curr_r, curr_c):
|
37 |
+
if grid[nr][nc] == grid[curr_r][curr_c] + 1:
|
38 |
+
stack.append((nr, nc))
|
39 |
+
return score
|
40 |
+
|
41 |
+
def dfs_rating(r, c):
|
42 |
+
rating = 0
|
43 |
+
stack = [([(r, c)], set())]
|
44 |
+
|
45 |
+
while stack:
|
46 |
+
path, visited = stack.pop()
|
47 |
+
curr_r, curr_c = path[-1]
|
48 |
+
|
49 |
+
if grid[curr_r][curr_c] == 9:
|
50 |
+
rating += 1
|
51 |
+
continue
|
52 |
+
|
53 |
+
for nr, nc in get_neighbors(curr_r, curr_c):
|
54 |
+
if grid[nr][nc] == grid[curr_r][curr_c] + 1 and (nr, nc) not in visited:
|
55 |
+
new_path = path + [(nr, nc)]
|
56 |
+
new_visited = set(visited)
|
57 |
+
new_visited.add((nr,nc))
|
58 |
+
stack.append((new_path, new_visited))
|
59 |
+
return rating
|
60 |
+
|
61 |
+
total_score = 0
|
62 |
+
total_rating = 0
|
63 |
+
for r in range(rows):
|
64 |
+
for c in range(cols):
|
65 |
+
if grid[r][c] == 0:
|
66 |
+
total_score += dfs_score(r, c)
|
67 |
+
total_rating += dfs_rating(r, c)
|
68 |
+
|
69 |
+
print(total_score)
|
70 |
+
print(total_rating)
|
71 |
+
|
72 |
+
solve()
|
day10/solution_gpt-4o.py
ADDED
@@ -0,0 +1,56 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
def read_map(file):
|
2 |
+
with open(file, 'r') as f:
|
3 |
+
return [list(map(int, line.strip())) for line in f]
|
4 |
+
|
5 |
+
def find_trailheads(map_data):
|
6 |
+
trailheads = []
|
7 |
+
for r in range(len(map_data)):
|
8 |
+
for c in range(len(map_data[0])):
|
9 |
+
if map_data[r][c] == 0:
|
10 |
+
trailheads.append((r, c))
|
11 |
+
return trailheads
|
12 |
+
|
13 |
+
def is_valid_move(map_data, r, c, current_height):
|
14 |
+
return 0 <= r < len(map_data) and 0 <= c < len(map_data[0]) and map_data[r][c] == current_height + 1
|
15 |
+
|
16 |
+
def explore_trails(map_data, start):
|
17 |
+
stack = [(start, [start])]
|
18 |
+
reached_nines = set()
|
19 |
+
distinct_paths = set()
|
20 |
+
|
21 |
+
while stack:
|
22 |
+
(r, c), path = stack.pop()
|
23 |
+
current_height = map_data[r][c]
|
24 |
+
|
25 |
+
if current_height == 9:
|
26 |
+
reached_nines.add((r, c))
|
27 |
+
distinct_paths.add(tuple(path))
|
28 |
+
continue
|
29 |
+
|
30 |
+
for dr, dc in [(-1, 0), (1, 0), (0, -1), (0, 1)]:
|
31 |
+
nr, nc = r + dr, c + dc
|
32 |
+
if is_valid_move(map_data, nr, nc, current_height):
|
33 |
+
stack.append(((nr, nc), path + [(nr, nc)]))
|
34 |
+
|
35 |
+
return len(reached_nines), len(distinct_paths)
|
36 |
+
|
37 |
+
def calculate_scores_and_ratings(map_data):
|
38 |
+
trailheads = find_trailheads(map_data)
|
39 |
+
total_score = 0
|
40 |
+
total_rating = 0
|
41 |
+
|
42 |
+
for trailhead in trailheads:
|
43 |
+
score, rating = explore_trails(map_data, trailhead)
|
44 |
+
total_score += score
|
45 |
+
total_rating += rating
|
46 |
+
|
47 |
+
return total_score, total_rating
|
48 |
+
|
49 |
+
def main():
|
50 |
+
file = "input.txt"
|
51 |
+
map_data = read_map(file)
|
52 |
+
total_score, total_rating = calculate_scores_and_ratings(map_data)
|
53 |
+
print(total_score)
|
54 |
+
print(total_rating)
|
55 |
+
|
56 |
+
main()
|
day10/solution_jerpint.py
ADDED
@@ -0,0 +1,106 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
def load_data(file):
|
2 |
+
with open(file) as f:
|
3 |
+
data = f.readlines()
|
4 |
+
return [list(line.strip("\n")) for line in data]
|
5 |
+
|
6 |
+
|
7 |
+
|
8 |
+
def find_zeros(grid):
|
9 |
+
M = len(grid)
|
10 |
+
N = len(grid[0])
|
11 |
+
|
12 |
+
positions = []
|
13 |
+
for i in range(M):
|
14 |
+
for j in range(N):
|
15 |
+
if grid[i][j] == "0":
|
16 |
+
positions.append((i,j))
|
17 |
+
return positions
|
18 |
+
|
19 |
+
|
20 |
+
def get_neighbors(grid, pos):
|
21 |
+
M = len(grid)
|
22 |
+
N = len(grid[0])
|
23 |
+
directions = [(0, 1), (1, 0), (-1, 0), (0, -1)]
|
24 |
+
ns = []
|
25 |
+
i, j = pos
|
26 |
+
for dx, dy in directions:
|
27 |
+
if (i+dx) in range(M) and (j+dy) in range(N):
|
28 |
+
ns.append((i+dx, j+dy))
|
29 |
+
return ns
|
30 |
+
|
31 |
+
|
32 |
+
def val_at_pos(grid, pos):
|
33 |
+
i, j = pos
|
34 |
+
return int(grid[i][j]) if grid[i][j] != "." else -100
|
35 |
+
|
36 |
+
def get_trailhead_score(grid, zero):
|
37 |
+
"""bfs algorithm."""
|
38 |
+
score = 0
|
39 |
+
|
40 |
+
i,j = zero
|
41 |
+
q = [(i,j)]
|
42 |
+
visited = set((i,j))
|
43 |
+
|
44 |
+
while len(q) > 0:
|
45 |
+
pos = q.pop(0)
|
46 |
+
cur_val = val_at_pos(grid, pos)
|
47 |
+
neighbors = get_neighbors(grid, pos)
|
48 |
+
for n_pos in neighbors:
|
49 |
+
n_val = val_at_pos(grid, n_pos)
|
50 |
+
|
51 |
+
|
52 |
+
if (n_val - cur_val) == 1:
|
53 |
+
q.append(n_pos)
|
54 |
+
|
55 |
+
if n_val == 9 and n_pos not in visited:
|
56 |
+
score += 1
|
57 |
+
visited.add(n_pos)
|
58 |
+
|
59 |
+
return score
|
60 |
+
|
61 |
+
|
62 |
+
grid = load_data("input.txt")
|
63 |
+
zeros = find_zeros(grid)
|
64 |
+
|
65 |
+
total = 0
|
66 |
+
for zero in zeros:
|
67 |
+
total += get_trailhead_score(grid, zero)
|
68 |
+
|
69 |
+
print(total)
|
70 |
+
|
71 |
+
|
72 |
+
## Part 2
|
73 |
+
|
74 |
+
def get_trailhead_rating(grid, zero):
|
75 |
+
"""bfs algorithm."""
|
76 |
+
score = 0
|
77 |
+
|
78 |
+
i,j = zero
|
79 |
+
q = [(i,j)]
|
80 |
+
visited = set((i,j))
|
81 |
+
|
82 |
+
while len(q) > 0:
|
83 |
+
pos = q.pop(0)
|
84 |
+
cur_val = val_at_pos(grid, pos)
|
85 |
+
neighbors = get_neighbors(grid, pos)
|
86 |
+
for n_pos in neighbors:
|
87 |
+
n_val = val_at_pos(grid, n_pos)
|
88 |
+
|
89 |
+
|
90 |
+
if (n_val - cur_val) == 1:
|
91 |
+
q.append(n_pos)
|
92 |
+
|
93 |
+
if n_val == 9:
|
94 |
+
score += 1
|
95 |
+
|
96 |
+
return score
|
97 |
+
|
98 |
+
|
99 |
+
grid = load_data("input.txt")
|
100 |
+
zeros = find_zeros(grid)
|
101 |
+
|
102 |
+
total = 0
|
103 |
+
for zero in zeros:
|
104 |
+
total += get_trailhead_rating(grid, zero)
|
105 |
+
|
106 |
+
print(total)
|
day11/solution_claude-3-5-sonnet-20241022.py
ADDED
@@ -0,0 +1,41 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
def read_input(filename):
|
2 |
+
with open(filename, 'r') as f:
|
3 |
+
return [int(x) for x in f.read().strip().split()]
|
4 |
+
|
5 |
+
def has_even_digits(n):
|
6 |
+
return len(str(n)) % 2 == 0
|
7 |
+
|
8 |
+
def split_number(n):
|
9 |
+
s = str(n)
|
10 |
+
mid = len(s) // 2
|
11 |
+
left = int(s[:mid])
|
12 |
+
right = int(s[mid:])
|
13 |
+
return [left, right]
|
14 |
+
|
15 |
+
def transform_stones(stones):
|
16 |
+
new_stones = []
|
17 |
+
for stone in stones:
|
18 |
+
if stone == 0:
|
19 |
+
new_stones.append(1)
|
20 |
+
elif has_even_digits(stone):
|
21 |
+
new_stones.extend(split_number(stone))
|
22 |
+
else:
|
23 |
+
new_stones.append(stone * 2024)
|
24 |
+
return new_stones
|
25 |
+
|
26 |
+
def simulate_blinks(stones, blinks):
|
27 |
+
current_stones = stones[:]
|
28 |
+
for _ in range(blinks):
|
29 |
+
current_stones = transform_stones(current_stones)
|
30 |
+
return len(current_stones)
|
31 |
+
|
32 |
+
# Read input
|
33 |
+
stones = read_input("./input.txt")
|
34 |
+
|
35 |
+
# Part 1: 25 blinks
|
36 |
+
result1 = simulate_blinks(stones, 25)
|
37 |
+
print(str(result1))
|
38 |
+
|
39 |
+
# Part 2: 75 blinks
|
40 |
+
result2 = simulate_blinks(stones, 75)
|
41 |
+
print(str(result2))
|
day11/solution_gemini-1.5-pro.py
ADDED
@@ -0,0 +1,33 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
def solve():
|
2 |
+
with open("./input.txt", "r") as f:
|
3 |
+
initial_stones = [int(x) for x in f.readline().split()]
|
4 |
+
|
5 |
+
def blink(stones):
|
6 |
+
new_stones = []
|
7 |
+
for stone in stones:
|
8 |
+
if stone == 0:
|
9 |
+
new_stones.append(1)
|
10 |
+
elif len(str(stone)) % 2 == 0:
|
11 |
+
s = str(stone)
|
12 |
+
mid = len(s) // 2
|
13 |
+
left = int(s[:mid])
|
14 |
+
right = int(s[mid:])
|
15 |
+
new_stones.append(left)
|
16 |
+
new_stones.append(right)
|
17 |
+
else:
|
18 |
+
new_stones.append(stone * 2024)
|
19 |
+
return new_stones
|
20 |
+
|
21 |
+
# Part 1: 25 blinks
|
22 |
+
stones = initial_stones[:] # Create a copy to avoid modifying the original
|
23 |
+
for _ in range(25):
|
24 |
+
stones = blink(stones)
|
25 |
+
print(len(stones))
|
26 |
+
|
27 |
+
# Part 2: 75 blinks
|
28 |
+
stones = initial_stones[:] # Reset to the initial state
|
29 |
+
for _ in range(75):
|
30 |
+
stones = blink(stones)
|
31 |
+
print(len(stones))
|
32 |
+
|
33 |
+
solve()
|
day11/solution_gpt-4o.py
ADDED
@@ -0,0 +1,35 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
def transform_stones(stones):
|
2 |
+
new_stones = []
|
3 |
+
for stone in stones:
|
4 |
+
if stone == 0:
|
5 |
+
new_stones.append(1)
|
6 |
+
elif len(str(stone)) % 2 == 0:
|
7 |
+
# Split the stone
|
8 |
+
str_stone = str(stone)
|
9 |
+
mid = len(str_stone) // 2
|
10 |
+
left = int(str_stone[:mid])
|
11 |
+
right = int(str_stone[mid:])
|
12 |
+
new_stones.extend([left, right])
|
13 |
+
else:
|
14 |
+
# Multiply by 2024
|
15 |
+
new_stones.append(stone * 2024)
|
16 |
+
return new_stones
|
17 |
+
|
18 |
+
def simulate_blinks(initial_stones, blinks):
|
19 |
+
stones = initial_stones
|
20 |
+
for _ in range(blinks):
|
21 |
+
stones = transform_stones(stones)
|
22 |
+
return stones
|
23 |
+
|
24 |
+
# Read input from file
|
25 |
+
file = "input.txt"
|
26 |
+
with open(file, "r") as f:
|
27 |
+
initial_stones = list(map(int, f.read().strip().split()))
|
28 |
+
|
29 |
+
# Part 1: After 25 blinks
|
30 |
+
stones_after_25_blinks = simulate_blinks(initial_stones, 25)
|
31 |
+
print(len(stones_after_25_blinks))
|
32 |
+
|
33 |
+
# Part 2: After 75 blinks
|
34 |
+
stones_after_75_blinks = simulate_blinks(initial_stones, 75)
|
35 |
+
print(len(stones_after_75_blinks))
|
day11/solution_jerpint.py
ADDED
@@ -0,0 +1,81 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
def load_data(file):
|
2 |
+
with open(file) as f:
|
3 |
+
data = f.read()
|
4 |
+
return list(data.split())
|
5 |
+
|
6 |
+
|
7 |
+
|
8 |
+
def update_stone(stone):
|
9 |
+
|
10 |
+
if stone == "0":
|
11 |
+
return ["1"]
|
12 |
+
|
13 |
+
elif len(stone) % 2 == 0:
|
14 |
+
L = len(stone)
|
15 |
+
first_half = str(int(stone[:L//2]))
|
16 |
+
second_half = str(int(stone[L//2:]))
|
17 |
+
|
18 |
+
|
19 |
+
|
20 |
+
return [first_half, second_half]
|
21 |
+
else:
|
22 |
+
return [str(int(stone)*2024)]
|
23 |
+
|
24 |
+
|
25 |
+
def blink(stones):
|
26 |
+
new_stones = []
|
27 |
+
for stone in stones:
|
28 |
+
next_stones = update_stone(stone)
|
29 |
+
new_stones.extend(next_stones)
|
30 |
+
return new_stones
|
31 |
+
|
32 |
+
|
33 |
+
stones = load_data("input.txt")
|
34 |
+
blinks = 25
|
35 |
+
for i in range(blinks):
|
36 |
+
stones = blink(stones)
|
37 |
+
|
38 |
+
print(len(stones))
|
39 |
+
|
40 |
+
|
41 |
+
|
42 |
+
|
43 |
+
## Part two
|
44 |
+
|
45 |
+
# Use a dict to keep track instead of a list
|
46 |
+
|
47 |
+
def get_counts(stones):
|
48 |
+
counts = {}
|
49 |
+
for stone in stones:
|
50 |
+
counts[stone] = counts.get(stone, 0) + 1
|
51 |
+
|
52 |
+
return counts
|
53 |
+
|
54 |
+
|
55 |
+
def blink(stone_counts):
|
56 |
+
new_counts = {}
|
57 |
+
for stone, count in stone_counts.items():
|
58 |
+
new_stones = update_stone(stone)
|
59 |
+
|
60 |
+
for s in new_stones:
|
61 |
+
new_counts[s] = new_counts.get(s, 0) + 1 * count
|
62 |
+
|
63 |
+
return new_counts
|
64 |
+
|
65 |
+
|
66 |
+
def get_total_stones(stone_counts):
|
67 |
+
total = 0
|
68 |
+
|
69 |
+
for stone, count in stone_counts.items():
|
70 |
+
total += count
|
71 |
+
return total
|
72 |
+
|
73 |
+
|
74 |
+
stones = load_data("input.txt")
|
75 |
+
stone_counts = get_counts(stones)
|
76 |
+
blinks = 75
|
77 |
+
|
78 |
+
for idx in range(blinks):
|
79 |
+
stone_counts = blink(stone_counts)
|
80 |
+
|
81 |
+
print(get_total_stones(stone_counts))
|
day12/solution_claude-3-5-sonnet-20241022.py
ADDED
@@ -0,0 +1,84 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
def find_regions(grid):
|
2 |
+
height = len(grid)
|
3 |
+
width = len(grid[0])
|
4 |
+
visited = set()
|
5 |
+
regions = []
|
6 |
+
|
7 |
+
def flood_fill(x, y, char):
|
8 |
+
if (x, y) in visited or x < 0 or y < 0 or x >= height or y >= width or grid[x][y] != char:
|
9 |
+
return set()
|
10 |
+
|
11 |
+
region = {(x, y)}
|
12 |
+
visited.add((x, y))
|
13 |
+
|
14 |
+
# Check all four directions
|
15 |
+
for dx, dy in [(0, 1), (1, 0), (0, -1), (-1, 0)]:
|
16 |
+
new_x, new_y = x + dx, y + dy
|
17 |
+
region.update(flood_fill(new_x, new_y, char))
|
18 |
+
|
19 |
+
return region
|
20 |
+
|
21 |
+
for i in range(height):
|
22 |
+
for j in range(width):
|
23 |
+
if (i, j) not in visited:
|
24 |
+
region = flood_fill(i, j, grid[i][j])
|
25 |
+
if region:
|
26 |
+
regions.append((grid[i][j], region))
|
27 |
+
|
28 |
+
return regions
|
29 |
+
|
30 |
+
def calculate_perimeter(region_cells, grid_height, grid_width):
|
31 |
+
perimeter = 0
|
32 |
+
for x, y in region_cells:
|
33 |
+
# Check all four sides
|
34 |
+
for dx, dy in [(0, 1), (1, 0), (0, -1), (-1, 0)]:
|
35 |
+
new_x, new_y = x + dx, y + dy
|
36 |
+
if (new_x, new_y) not in region_cells:
|
37 |
+
if new_x < 0 or new_y < 0 or new_x >= grid_height or new_y >= grid_width:
|
38 |
+
perimeter += 1
|
39 |
+
elif (new_x, new_y) not in region_cells:
|
40 |
+
perimeter += 1
|
41 |
+
return perimeter
|
42 |
+
|
43 |
+
def count_sides(region_cells, grid_height, grid_width):
|
44 |
+
edges = set()
|
45 |
+
for x, y in region_cells:
|
46 |
+
for dx, dy in [(0, 1), (1, 0), (0, -1), (-1, 0)]:
|
47 |
+
new_x, new_y = x + dx, y + dy
|
48 |
+
if (new_x, new_y) not in region_cells:
|
49 |
+
# Store the edge as a tuple of two points to make it unique
|
50 |
+
edge = tuple(sorted([(x, y), (x + dx/2, y + dy/2)]))
|
51 |
+
edges.add(edge)
|
52 |
+
return len(edges)
|
53 |
+
|
54 |
+
def solve_part1(grid):
|
55 |
+
regions = find_regions(grid)
|
56 |
+
total_price = 0
|
57 |
+
height, width = len(grid), len(grid[0])
|
58 |
+
|
59 |
+
for char, region in regions:
|
60 |
+
area = len(region)
|
61 |
+
perimeter = calculate_perimeter(region, height, width)
|
62 |
+
total_price += area * perimeter
|
63 |
+
|
64 |
+
return str(total_price)
|
65 |
+
|
66 |
+
def solve_part2(grid):
|
67 |
+
regions = find_regions(grid)
|
68 |
+
total_price = 0
|
69 |
+
height, width = len(grid), len(grid[0])
|
70 |
+
|
71 |
+
for char, region in regions:
|
72 |
+
area = len(region)
|
73 |
+
sides = count_sides(region, height, width)
|
74 |
+
total_price += area * sides
|
75 |
+
|
76 |
+
return str(total_price)
|
77 |
+
|
78 |
+
# Read input
|
79 |
+
with open('./input.txt', 'r') as file:
|
80 |
+
grid = [list(line.strip()) for line in file]
|
81 |
+
|
82 |
+
# Print results
|
83 |
+
print(solve_part1(grid))
|
84 |
+
print(solve_part2(grid))
|
day12/solution_gemini-1.5-pro.py
ADDED
@@ -0,0 +1,86 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from collections import defaultdict
|
2 |
+
|
3 |
+
def solve():
|
4 |
+
file = "input.txt"
|
5 |
+
grid = []
|
6 |
+
with open(file, 'r') as f:
|
7 |
+
for line in f:
|
8 |
+
grid.append(line.strip())
|
9 |
+
|
10 |
+
rows = len(grid)
|
11 |
+
cols = len(grid[0])
|
12 |
+
|
13 |
+
def get_neighbors(r, c):
|
14 |
+
neighbors = []
|
15 |
+
for dr, dc in [(0, 1), (0, -1), (1, 0), (-1, 0)]:
|
16 |
+
nr, nc = r + dr, c + dc
|
17 |
+
if 0 <= nr < rows and 0 <= nc < cols:
|
18 |
+
neighbors.append((nr, nc))
|
19 |
+
return neighbors
|
20 |
+
|
21 |
+
def bfs(r, c, char):
|
22 |
+
q = [(r, c)]
|
23 |
+
visited = set([(r, c)])
|
24 |
+
area = 0
|
25 |
+
perimeter = 0
|
26 |
+
while q:
|
27 |
+
cr, cc = q.pop(0)
|
28 |
+
area += 1
|
29 |
+
for nr, nc in get_neighbors(cr, cc):
|
30 |
+
if (nr, nc) not in visited:
|
31 |
+
if grid[nr][nc] == char:
|
32 |
+
visited.add((nr, nc))
|
33 |
+
q.append((nr, nc))
|
34 |
+
else:
|
35 |
+
perimeter += 1
|
36 |
+
return area, perimeter
|
37 |
+
|
38 |
+
def bfs2(r, c, char):
|
39 |
+
q = [(r, c)]
|
40 |
+
visited = set([(r, c)])
|
41 |
+
area = 0
|
42 |
+
sides = 0
|
43 |
+
while q:
|
44 |
+
cr, cc = q.pop(0)
|
45 |
+
area += 1
|
46 |
+
|
47 |
+
ns = 0
|
48 |
+
for nr, nc in get_neighbors(cr, cc):
|
49 |
+
if grid[nr][nc] != char:
|
50 |
+
ns +=1
|
51 |
+
sides += ns
|
52 |
+
|
53 |
+
for nr, nc in get_neighbors(cr, cc):
|
54 |
+
if (nr, nc) not in visited and grid[nr][nc] == char:
|
55 |
+
visited.add((nr, nc))
|
56 |
+
q.append((nr, nc))
|
57 |
+
return area, sides
|
58 |
+
|
59 |
+
|
60 |
+
total_price = 0
|
61 |
+
visited = set()
|
62 |
+
for r in range(rows):
|
63 |
+
for c in range(cols):
|
64 |
+
if (r, c) not in visited:
|
65 |
+
char = grid[r][c]
|
66 |
+
area, perimeter = bfs(r, c, char)
|
67 |
+
total_price += area * perimeter
|
68 |
+
for rr, cc in bfs(r,c, char)[2]:
|
69 |
+
visited.add((rr,cc))
|
70 |
+
|
71 |
+
print(total_price)
|
72 |
+
|
73 |
+
total_price2 = 0
|
74 |
+
visited = set()
|
75 |
+
for r in range(rows):
|
76 |
+
for c in range(cols):
|
77 |
+
if (r, c) not in visited:
|
78 |
+
char = grid[r][c]
|
79 |
+
area, sides = bfs2(r, c, char)
|
80 |
+
total_price2 += area * sides
|
81 |
+
for rr, cc in bfs(r,c, char)[2]:
|
82 |
+
visited.add((rr,cc))
|
83 |
+
print(total_price2)
|
84 |
+
|
85 |
+
|
86 |
+
solve()
|
day12/solution_gpt-4o.py
ADDED
@@ -0,0 +1,77 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
file = "input.txt"
|
2 |
+
|
3 |
+
def read_input(file):
|
4 |
+
with open(file, 'r') as f:
|
5 |
+
return [line.strip() for line in f]
|
6 |
+
|
7 |
+
def get_neighbors(x, y, max_x, max_y):
|
8 |
+
neighbors = []
|
9 |
+
if x > 0:
|
10 |
+
neighbors.append((x - 1, y))
|
11 |
+
if x < max_x - 1:
|
12 |
+
neighbors.append((x + 1, y))
|
13 |
+
if y > 0:
|
14 |
+
neighbors.append((x, y - 1))
|
15 |
+
if y < max_y - 1:
|
16 |
+
neighbors.append((x, y + 1))
|
17 |
+
return neighbors
|
18 |
+
|
19 |
+
def flood_fill(grid, x, y, visited):
|
20 |
+
plant_type = grid[x][y]
|
21 |
+
stack = [(x, y)]
|
22 |
+
region = []
|
23 |
+
while stack:
|
24 |
+
cx, cy = stack.pop()
|
25 |
+
if (cx, cy) in visited:
|
26 |
+
continue
|
27 |
+
visited.add((cx, cy))
|
28 |
+
region.append((cx, cy))
|
29 |
+
for nx, ny in get_neighbors(cx, cy, len(grid), len(grid[0])):
|
30 |
+
if grid[nx][ny] == plant_type and (nx, ny) not in visited:
|
31 |
+
stack.append((nx, ny))
|
32 |
+
return region
|
33 |
+
|
34 |
+
def calculate_perimeter(region, grid):
|
35 |
+
perimeter = 0
|
36 |
+
for x, y in region:
|
37 |
+
for nx, ny in get_neighbors(x, y, len(grid), len(grid[0])):
|
38 |
+
if grid[nx][ny] != grid[x][y]:
|
39 |
+
perimeter += 1
|
40 |
+
return perimeter
|
41 |
+
|
42 |
+
def calculate_sides(region, grid):
|
43 |
+
sides = set()
|
44 |
+
for x, y in region:
|
45 |
+
for nx, ny in get_neighbors(x, y, len(grid), len(grid[0])):
|
46 |
+
if grid[nx][ny] != grid[x][y]:
|
47 |
+
sides.add(((x, y), (nx, ny)))
|
48 |
+
return len(sides)
|
49 |
+
|
50 |
+
def calculate_total_price(grid, use_sides=False):
|
51 |
+
visited = set()
|
52 |
+
total_price = 0
|
53 |
+
for x in range(len(grid)):
|
54 |
+
for y in range(len(grid[0])):
|
55 |
+
if (x, y) not in visited:
|
56 |
+
region = flood_fill(grid, x, y, visited)
|
57 |
+
area = len(region)
|
58 |
+
if use_sides:
|
59 |
+
sides = calculate_sides(region, grid)
|
60 |
+
price = area * sides
|
61 |
+
else:
|
62 |
+
perimeter = calculate_perimeter(region, grid)
|
63 |
+
price = area * perimeter
|
64 |
+
total_price += price
|
65 |
+
return total_price
|
66 |
+
|
67 |
+
def main():
|
68 |
+
grid = read_input(file)
|
69 |
+
# Part 1
|
70 |
+
total_price_part1 = calculate_total_price(grid, use_sides=False)
|
71 |
+
print(total_price_part1)
|
72 |
+
|
73 |
+
# Part 2
|
74 |
+
total_price_part2 = calculate_total_price(grid, use_sides=True)
|
75 |
+
print(total_price_part2)
|
76 |
+
|
77 |
+
main()
|
day12/solution_jerpint.py
ADDED
@@ -0,0 +1,176 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
def load_data(file):
|
2 |
+
with open(file) as f:
|
3 |
+
data = f.readlines()
|
4 |
+
return [l.strip("\n") for l in data]
|
5 |
+
|
6 |
+
|
7 |
+
def get_neighbours(pos, grid):
|
8 |
+
M = len(grid)
|
9 |
+
N = len(grid[0])
|
10 |
+
|
11 |
+
i, j = pos
|
12 |
+
|
13 |
+
directions = [(0,1), (1,0), (-1,0), (0, -1)]
|
14 |
+
|
15 |
+
n_positions = []
|
16 |
+
n_values = []
|
17 |
+
for dx, dy in directions:
|
18 |
+
if (i+dx) in range(M) and (j+dy) in range(N):
|
19 |
+
n_positions.append((i+dx,j+dy))
|
20 |
+
n_values.append(grid[i+dx][j+dy])
|
21 |
+
|
22 |
+
return n_positions, n_values
|
23 |
+
|
24 |
+
|
25 |
+
def get_perimeter(num_equal_neighbors: int):
|
26 |
+
return 4 - num_equal_neighbors
|
27 |
+
|
28 |
+
|
29 |
+
def bfs(pos, grid):
|
30 |
+
visited = set()
|
31 |
+
queue = [pos]
|
32 |
+
|
33 |
+
current_val = grid[pos[0]][pos[1]]
|
34 |
+
total_area = 0
|
35 |
+
total_perimeter = 0
|
36 |
+
while len(queue) > 0:
|
37 |
+
|
38 |
+
pos = queue.pop(0)
|
39 |
+
|
40 |
+
if pos in visited:
|
41 |
+
continue
|
42 |
+
|
43 |
+
n_positions, n_values = get_neighbours(pos, grid)
|
44 |
+
|
45 |
+
# print(pos, grid[pos[0]][pos[1]])
|
46 |
+
# print(n_positions)
|
47 |
+
# print(n_values)
|
48 |
+
|
49 |
+
num_equal_neighbors = 0
|
50 |
+
for n_pos, n_val in zip(n_positions, n_values):
|
51 |
+
if n_val == current_val:
|
52 |
+
num_equal_neighbors += 1
|
53 |
+
|
54 |
+
if n_pos not in visited:
|
55 |
+
queue.append(n_pos)
|
56 |
+
|
57 |
+
visited.add(pos)
|
58 |
+
|
59 |
+
total_area += 1
|
60 |
+
total_perimeter += get_perimeter(num_equal_neighbors)
|
61 |
+
|
62 |
+
price = total_area * total_perimeter
|
63 |
+
# print(f"Visited a region of {current_val} plants with price = {total_area}*{total_perimeter}={price}")
|
64 |
+
return visited, price
|
65 |
+
|
66 |
+
|
67 |
+
|
68 |
+
# grid = load_data("test.txt")
|
69 |
+
grid = load_data("input.txt")
|
70 |
+
|
71 |
+
M = len(grid)
|
72 |
+
N = len(grid[0])
|
73 |
+
|
74 |
+
total_price = 0
|
75 |
+
visited = set()
|
76 |
+
for i in range(M):
|
77 |
+
for j in range(N):
|
78 |
+
pos = (i,j)
|
79 |
+
if pos not in visited:
|
80 |
+
|
81 |
+
next_visited, price = bfs(pos, grid)
|
82 |
+
visited = visited.union(next_visited)
|
83 |
+
total_price += price
|
84 |
+
|
85 |
+
|
86 |
+
print(total_price)
|
87 |
+
|
88 |
+
|
89 |
+
## Part two
|
90 |
+
|
91 |
+
|
92 |
+
|
93 |
+
def bfs(pos, grid):
|
94 |
+
visited = set()
|
95 |
+
queue = [pos]
|
96 |
+
|
97 |
+
current_val = grid[pos[0]][pos[1]]
|
98 |
+
total_area = 0
|
99 |
+
total_perimeter = 0
|
100 |
+
while len(queue) > 0:
|
101 |
+
|
102 |
+
pos = queue.pop(0)
|
103 |
+
|
104 |
+
if pos in visited:
|
105 |
+
continue
|
106 |
+
|
107 |
+
n_positions, n_values = get_neighbours(pos, grid)
|
108 |
+
|
109 |
+
# print(pos, grid[pos[0]][pos[1]])
|
110 |
+
# print(n_positions)
|
111 |
+
# print(n_values)
|
112 |
+
|
113 |
+
num_equal_neighbors = 0
|
114 |
+
for n_pos, n_val in zip(n_positions, n_values):
|
115 |
+
if n_val == current_val:
|
116 |
+
num_equal_neighbors += 1
|
117 |
+
|
118 |
+
if n_pos not in visited:
|
119 |
+
queue.append(n_pos)
|
120 |
+
|
121 |
+
visited.add(pos)
|
122 |
+
|
123 |
+
# total_area += 1
|
124 |
+
# total_perimeter += get_perimeter(num_equal_neighbors)
|
125 |
+
|
126 |
+
# price = total_area * total_perimeter
|
127 |
+
# print(f"Visited a region of {current_val} plants with price = {total_area}*{total_perimeter}={price}")
|
128 |
+
return visited
|
129 |
+
|
130 |
+
|
131 |
+
# grid = load_data("test.txt")
|
132 |
+
# grid = load_data("input.txt")
|
133 |
+
|
134 |
+
# M = len(grid)
|
135 |
+
# N = len(grid[0])
|
136 |
+
|
137 |
+
# total_price = 0
|
138 |
+
# visited = set()
|
139 |
+
# for i in range(M):
|
140 |
+
# for j in range(N):
|
141 |
+
# pos = (i,j)
|
142 |
+
# if pos not in visited:
|
143 |
+
|
144 |
+
# next_visited = bfs(pos, grid)
|
145 |
+
# # visited = visited.union(next_visited)
|
146 |
+
# # total_price += price
|
147 |
+
# break
|
148 |
+
|
149 |
+
|
150 |
+
# # print(total_price)
|
151 |
+
|
152 |
+
# def get_perimeter(visited):
|
153 |
+
# # Horizontal
|
154 |
+
# visited = list(next_visited)
|
155 |
+
# visited.sort(key=lambda x: x[0])
|
156 |
+
|
157 |
+
# # First and last coords
|
158 |
+
# i_min = visited[0][0]
|
159 |
+
# i_max = visited[-1][0]
|
160 |
+
|
161 |
+
# h_perimeter = 0
|
162 |
+
# for i in range(i_min, i_max+1):
|
163 |
+
# js = [v[1] for v in visited if v[0] == i]
|
164 |
+
# js.sort()
|
165 |
+
|
166 |
+
# h_perimeter += 1
|
167 |
+
# for idx in range(len(js)-1):
|
168 |
+
# if js[idx+1] - js[idx] != 1:
|
169 |
+
# h_perimeter += 1
|
170 |
+
|
171 |
+
# print(h_perimeter)
|
172 |
+
|
173 |
+
# get_perimeter(next_visited)
|
174 |
+
|
175 |
+
|
176 |
+
|
day13/solution_claude-3-5-sonnet-20241022.py
ADDED
@@ -0,0 +1,105 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from math import gcd
|
2 |
+
from typing import Optional, Tuple
|
3 |
+
|
4 |
+
def extended_gcd(a: int, b: int) -> Tuple[int, int, int]:
|
5 |
+
if a == 0:
|
6 |
+
return b, 0, 1
|
7 |
+
gcd, x1, y1 = extended_gcd(b % a, a)
|
8 |
+
x = y1 - (b // a) * x1
|
9 |
+
y = x1
|
10 |
+
return gcd, x, y
|
11 |
+
|
12 |
+
def find_solution(a: int, b: int, c: int) -> Optional[Tuple[int, int]]:
|
13 |
+
g = gcd(a, b)
|
14 |
+
if c % g != 0:
|
15 |
+
return None
|
16 |
+
|
17 |
+
# Scale everything down by gcd
|
18 |
+
a, b, c = a//g, b//g, c//g
|
19 |
+
|
20 |
+
# Find base solution using extended GCD
|
21 |
+
_, x0, y0 = extended_gcd(a, b)
|
22 |
+
x0 *= c
|
23 |
+
y0 *= c
|
24 |
+
|
25 |
+
# Find the general solution
|
26 |
+
# x = x0 + k*(b/g)
|
27 |
+
# y = y0 - k*(a/g)
|
28 |
+
# We need both x and y to be non-negative
|
29 |
+
k_min = -(x0 // b) if x0 < 0 else -((y0) // a)
|
30 |
+
k_max = (y0 // a) if y0 > 0 else (x0 // b)
|
31 |
+
|
32 |
+
# Find k that gives minimum positive solution
|
33 |
+
for k in range(k_min, k_max + 1):
|
34 |
+
x = x0 + k * b
|
35 |
+
y = y0 - k * a
|
36 |
+
if x >= 0 and y >= 0:
|
37 |
+
return (x, y)
|
38 |
+
return None
|
39 |
+
|
40 |
+
def solve_machine(ax: int, ay: int, bx: int, by: int, px: int, py: int, max_presses: Optional[int] = None) -> Optional[int]:
|
41 |
+
# Find solution for x-coordinate
|
42 |
+
sol_x = find_solution(ax, bx, px)
|
43 |
+
if not sol_x:
|
44 |
+
return None
|
45 |
+
|
46 |
+
# Find solution for y-coordinate
|
47 |
+
sol_y = find_solution(ay, by, py)
|
48 |
+
if not sol_y:
|
49 |
+
return None
|
50 |
+
|
51 |
+
# Check if solutions match
|
52 |
+
if sol_x[0] != sol_y[0] or sol_x[1] != sol_y[1]:
|
53 |
+
return None
|
54 |
+
|
55 |
+
# Check max presses constraint if specified
|
56 |
+
if max_presses and (sol_x[0] > max_presses or sol_x[1] > max_presses):
|
57 |
+
return None
|
58 |
+
|
59 |
+
# Calculate tokens needed (3 for A, 1 for B)
|
60 |
+
return 3 * sol_x[0] + sol_x[1]
|
61 |
+
|
62 |
+
def parse_input(filename: str):
|
63 |
+
machines = []
|
64 |
+
with open(filename, 'r') as f:
|
65 |
+
lines = f.read().strip().split('\n\n')
|
66 |
+
for machine in lines:
|
67 |
+
lines = machine.strip().split('\n')
|
68 |
+
ax = int(lines[0].split('X+')[1].split(',')[0])
|
69 |
+
ay = int(lines[0].split('Y+')[1])
|
70 |
+
bx = int(lines[1].split('X+')[1].split(',')[0])
|
71 |
+
by = int(lines[1].split('Y+')[1])
|
72 |
+
px = int(lines[2].split('X=')[1].split(',')[0])
|
73 |
+
py = int(lines[2].split('Y=')[1])
|
74 |
+
machines.append((ax, ay, bx, by, px, py))
|
75 |
+
return machines
|
76 |
+
|
77 |
+
def solve_part1(machines):
|
78 |
+
total_tokens = 0
|
79 |
+
for machine in machines:
|
80 |
+
tokens = solve_machine(*machine, max_presses=100)
|
81 |
+
if tokens is not None:
|
82 |
+
total_tokens += tokens
|
83 |
+
return str(total_tokens)
|
84 |
+
|
85 |
+
def solve_part2(machines):
|
86 |
+
offset = 10**13
|
87 |
+
total_tokens = 0
|
88 |
+
modified_machines = []
|
89 |
+
for ax, ay, bx, by, px, py in machines:
|
90 |
+
modified_machines.append((ax, ay, bx, by, px + offset, py + offset))
|
91 |
+
|
92 |
+
for machine in modified_machines:
|
93 |
+
tokens = solve_machine(*machine)
|
94 |
+
if tokens is not None:
|
95 |
+
total_tokens += tokens
|
96 |
+
return str(total_tokens)
|
97 |
+
|
98 |
+
# Read and parse input
|
99 |
+
machines = parse_input("input.txt")
|
100 |
+
|
101 |
+
# Solve part 1
|
102 |
+
print(solve_part1(machines))
|
103 |
+
|
104 |
+
# Solve part 2
|
105 |
+
print(solve_part2(machines))
|
day13/solution_gemini-1.5-pro.py
ADDED
@@ -0,0 +1,42 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import re
|
2 |
+
|
3 |
+
file = "input.txt"
|
4 |
+
|
5 |
+
def solve():
|
6 |
+
with open(file, 'r') as f:
|
7 |
+
machines = []
|
8 |
+
for line in f:
|
9 |
+
match = re.match(r"Button A: X\+(\d+), Y\+(\d+)\nButton B: X\+(\d+), Y\+(\d+)\nPrize: X=(\d+), Y=(\d+)", line.strip())
|
10 |
+
if match:
|
11 |
+
ax, ay, bx, by, px, py = map(int, match.groups())
|
12 |
+
machines.append(((ax, ay), (bx, by), (px, py)))
|
13 |
+
|
14 |
+
def get_min_cost(machines):
|
15 |
+
min_costs = []
|
16 |
+
for (ax, ay), (bx, by), (px, py) in machines:
|
17 |
+
min_cost = float('inf')
|
18 |
+
for a in range(101):
|
19 |
+
for b in range(101):
|
20 |
+
if a * ax + b * bx == px and a * ay + b * by == py:
|
21 |
+
min_cost = min(min_cost, 3 * a + b)
|
22 |
+
min_costs.append(min_cost)
|
23 |
+
return min_costs
|
24 |
+
|
25 |
+
min_costs = get_min_cost(machines)
|
26 |
+
total_cost = sum(cost for cost in min_costs if cost != float('inf'))
|
27 |
+
num_prizes = sum(1 for cost in min_costs if cost != float('inf'))
|
28 |
+
|
29 |
+
print(total_cost)
|
30 |
+
|
31 |
+
offset = 10000000000000
|
32 |
+
machines_part2 = []
|
33 |
+
for (ax, ay), (bx, by), (px, py) in machines:
|
34 |
+
machines_part2.append(((ax, ay), (bx, by), (px + offset, py + offset)))
|
35 |
+
|
36 |
+
min_costs_part2 = get_min_cost(machines_part2)
|
37 |
+
total_cost_part2 = sum(cost for cost in min_costs_part2 if cost != float('inf'))
|
38 |
+
|
39 |
+
|
40 |
+
print(total_cost_part2)
|
41 |
+
|
42 |
+
solve()
|