jerpint commited on
Commit
a4da721
·
1 Parent(s): 5d88935

Add solution files

Browse files
This view is limited to 50 files because it contains too many changes.   See raw diff
Files changed (50) hide show
  1. day01/solution_claude-3-5-sonnet-20241022.py +46 -0
  2. day01/solution_gemini-1.5-pro.py +27 -0
  3. day01/solution_gpt-4o.py +35 -0
  4. day01/solution_jerpint.py +36 -0
  5. day02/solution_claude-3-5-sonnet-20241022.py +52 -0
  6. day02/solution_gemini-1.5-pro.py +45 -0
  7. day02/solution_gpt-4o.py +45 -0
  8. day02/solution_jerpint.py +51 -0
  9. day03/solution_claude-3-5-sonnet-20241022.py +53 -0
  10. day03/solution_gemini-1.5-pro.py +36 -0
  11. day03/solution_gpt-4o.py +56 -0
  12. day03/solution_jerpint.py +37 -0
  13. day04/solution_claude-3-5-sonnet-20241022.py +66 -0
  14. day04/solution_gemini-1.5-pro.py +52 -0
  15. day04/solution_gpt-4o.py +45 -0
  16. day04/solution_jerpint.py +75 -0
  17. day05/solution_claude-3-5-sonnet-20241022.py +102 -0
  18. day05/solution_gemini-1.5-pro.py +61 -0
  19. day05/solution_gpt-4o.py +77 -0
  20. day05/solution_jerpint.py +92 -0
  21. day06/solution_claude-3-5-sonnet-20241022.py +76 -0
  22. day06/solution_gemini-1.5-pro.py +67 -0
  23. day06/solution_gpt-4o.py +78 -0
  24. day06/solution_jerpint.py +205 -0
  25. day07/solution_claude-3-5-sonnet-20241022.py +57 -0
  26. day07/solution_gemini-1.5-pro.py +56 -0
  27. day07/solution_gpt-4o.py +34 -0
  28. day07/solution_jerpint.py +115 -0
  29. day08/solution_claude-3-5-sonnet-20241022.py +86 -0
  30. day08/solution_gemini-1.5-pro.py +48 -0
  31. day08/solution_gpt-4o.py +80 -0
  32. day08/solution_jerpint.py +142 -0
  33. day09/solution_claude-3-5-sonnet-20241022.py +99 -0
  34. day09/solution_gemini-1.5-pro.py +82 -0
  35. day09/solution_gpt-4o.py +80 -0
  36. day09/solution_jerpint.py +159 -0
  37. day10/solution_claude-3-5-sonnet-20241022.py +70 -0
  38. day10/solution_gemini-1.5-pro.py +72 -0
  39. day10/solution_gpt-4o.py +56 -0
  40. day10/solution_jerpint.py +106 -0
  41. day11/solution_claude-3-5-sonnet-20241022.py +41 -0
  42. day11/solution_gemini-1.5-pro.py +33 -0
  43. day11/solution_gpt-4o.py +35 -0
  44. day11/solution_jerpint.py +81 -0
  45. day12/solution_claude-3-5-sonnet-20241022.py +84 -0
  46. day12/solution_gemini-1.5-pro.py +86 -0
  47. day12/solution_gpt-4o.py +77 -0
  48. day12/solution_jerpint.py +176 -0
  49. day13/solution_claude-3-5-sonnet-20241022.py +105 -0
  50. 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()