├── .gitignore
├── 2019_01_p1.py
├── 2019_01_p2.py
├── 2019_02_p1.py
├── 2019_02_p2.py
├── 2019_03_p1.py
├── 2019_03_p2.py
├── 2019_04_p1.py
├── 2019_04_p2.py
├── 2019_05_p1.py
├── 2019_05_p2.py
├── 2019_06_p1.py
├── 2019_06_p2.py
├── 2019_07_p1.py
├── 2019_07_p2.py
├── 2019_08_animation.py
├── 2019_08_p1.py
├── 2019_08_p2.py
├── 2019_09_p1.py
├── 2019_09_p2.py
├── 2019_10_p1.py
├── 2019_10_p2.py
├── 2019_11_animation.py
├── 2019_11_p1.py
├── 2019_11_p2.py
├── 2019_12_p1.py
├── 2019_12_p2.py
├── 2019_13_p1.py
├── 2019_13_p2.py
├── 2019_14_p1.py
├── 2019_14_p2.py
├── 2019_15_animation.py
├── 2019_15_p1.py
├── 2019_15_p2.py
├── 2019_16_p1.py
├── 2019_16_p2.py
├── 2019_17_p1.py
├── 2019_17_p2.py
├── 2019_18_p1.py
├── 2019_18_p2.py
├── 2019_19_p1.py
├── 2019_19_p2.py
├── 2019_20_p1.py
├── 2019_20_p2.py
├── 2019_21_p1.py
├── 2019_21_p2.py
├── 2019_22_p1.py
├── 2019_22_p2.py
├── 2019_23_animation.py
├── 2019_23_p1.py
├── 2019_23_p2.py
├── 2019_24_p1.py
├── 2019_24_p2.py
├── 2019_25_p1.py
├── LICENSE
├── README.md
├── aoc.py
├── extras
├── README.md
├── day_11_hull.gif
├── day_15_area.gif
├── day_23_network.gif
└── day_8_bios.gif
└── files
├── input01
├── input02
├── input03
├── input04
├── input05
├── input06
├── input07
├── input08
├── input09
├── input10
├── input11
├── input12
├── input13
├── input14
├── input15
├── input16
├── input17
├── input18
├── input19
├── input20
├── input21
├── input22
├── input23
├── input24
└── input25
/.gitignore:
--------------------------------------------------------------------------------
1 | __pycache__/
--------------------------------------------------------------------------------
/2019_01_p1.py:
--------------------------------------------------------------------------------
1 | from aoc import read_file, timer
2 |
3 | def calc_cost(mass):
4 | return mass // 3 - 2
5 |
6 | @timer
7 | def solve():
8 | input = read_file("01")
9 | mass = [int(x) for x in input]
10 | return sum([calc_cost(m) for m in mass])
11 |
12 | result = solve()
13 | print(f"Solution: {result}")
--------------------------------------------------------------------------------
/2019_01_p2.py:
--------------------------------------------------------------------------------
1 | from aoc import read_file, timer
2 |
3 | def calc_cost(mass):
4 | return mass // 3 - 2
5 |
6 | @timer
7 | def solve():
8 | input = read_file("01")
9 | mass = [int(x) for x in input]
10 |
11 | result = 0
12 |
13 | for m in mass:
14 | while (m := calc_cost(m)) > 0:
15 | result += m
16 |
17 | return result
18 |
19 | result = solve()
20 | print(f"Solution: {result}")
--------------------------------------------------------------------------------
/2019_02_p1.py:
--------------------------------------------------------------------------------
1 | from aoc import read_file, timer
2 |
3 | @timer
4 | def solve():
5 | input = read_file("02")[0].split(",")
6 | input = [int(x) for x in input]
7 |
8 | pos = 0
9 | input[1] = 12
10 | input[2] = 2
11 |
12 | while input[pos] != 99:
13 | if input[pos] == 1:
14 | input[input[pos+3]] = input[input[pos+1]] + input[input[pos+2]]
15 |
16 | if input[pos] == 2:
17 | input[input[pos+3]] = input[input[pos+1]] * input[input[pos+2]]
18 |
19 | pos += 4
20 |
21 | return input[0]
22 |
23 | result = solve()
24 | print(f"Solution: {result}")
--------------------------------------------------------------------------------
/2019_02_p2.py:
--------------------------------------------------------------------------------
1 | from aoc import read_file, timer
2 |
3 | @timer
4 | def solve():
5 | input = read_file("02")[0].split(",")
6 | input_store = [int(x) for x in input]
7 |
8 | for noun in range(100):
9 | for verb in range(100):
10 | input = input_store[:]
11 | pos = 0
12 | input[1] = noun
13 | input[2] = verb
14 |
15 | while input[pos] != 99:
16 | if input[pos] == 1:
17 | input[input[pos+3]] = input[input[pos+1]] + input[input[pos+2]]
18 |
19 | if input[pos] == 2:
20 | input[input[pos+3]] = input[input[pos+1]] * input[input[pos+2]]
21 |
22 | pos += 4
23 |
24 | if input[0] == 19690720:
25 | return 100 * noun + verb
26 |
27 | result = solve()
28 | print(f"Solution: {result}")
--------------------------------------------------------------------------------
/2019_03_p1.py:
--------------------------------------------------------------------------------
1 | from aoc import read_file, timer
2 |
3 | def manhattan(pos):
4 | return abs(pos[0]) + abs(pos[1])
5 |
6 | def get_wire_positions(wire):
7 | x, y = 0, 0
8 | positions = set()
9 |
10 | for i in range(len(wire)):
11 | for _ in range(int(wire[i][1:])):
12 | direction = wire[i][0]
13 |
14 | if direction == "R":
15 | x +=1
16 | elif direction == "L":
17 | x -=1
18 | elif direction == "D":
19 | y +=1
20 | elif direction == "U":
21 | y -=1
22 |
23 | positions.add((x, y))
24 |
25 | return positions
26 |
27 | @timer
28 | def solve():
29 | input = read_file("03")
30 | wire_1 = input[0].split(",")
31 | wire_2 = input[1].split(",")
32 |
33 | positions_1 = get_wire_positions(wire_1)
34 | positions_2 = get_wire_positions(wire_2)
35 |
36 | crossings = positions_1.intersection(positions_2)
37 |
38 | return min(manhattan(pos) for pos in crossings)
39 |
40 | result = solve()
41 | print(f"Solution: {result}")
--------------------------------------------------------------------------------
/2019_03_p2.py:
--------------------------------------------------------------------------------
1 | from aoc import read_file, timer
2 | from collections import defaultdict
3 |
4 | def get_wire_positions(wire):
5 | x, y = 0, 0
6 | positions = set()
7 |
8 | for i in range(len(wire)):
9 | for _ in range(int(wire[i][1:])):
10 | direction = wire[i][0]
11 |
12 | if direction == "R":
13 | x +=1
14 | elif direction == "L":
15 | x -=1
16 | elif direction == "D":
17 | y +=1
18 | elif direction == "U":
19 | y -=1
20 |
21 | positions.add((x, y))
22 |
23 | return positions
24 |
25 | def get_distance_for_crossings(wire, crossings):
26 | crossing = defaultdict(int)
27 | distance = 0
28 | x, y = 0, 0
29 |
30 | for i in range(len(wire)):
31 | for _ in range(int(wire[i][1:])):
32 | direction = wire[i][0]
33 |
34 | if direction == "R":
35 | x +=1
36 | elif direction == "L":
37 | x -=1
38 | elif direction == "D":
39 | y +=1
40 | elif direction == "U":
41 | y -=1
42 |
43 | distance += 1
44 |
45 | if (x, y) in crossings:
46 | crossing[(x, y)] = distance
47 |
48 | return crossing
49 |
50 | @timer
51 | def solve():
52 | input = read_file("03")
53 | wire_1 = input[0].split(",")
54 | wire_2 = input[1].split(",")
55 |
56 | x1, y1, x2, y2 = 0, 0, 0, 0
57 |
58 | positions_1 = get_wire_positions(wire_1)
59 | positions_2 = get_wire_positions(wire_2)
60 |
61 | crossings = positions_1.intersection(positions_2)
62 |
63 | crossing_distance_1 = get_distance_for_crossings(wire_1, crossings)
64 | crossing_distance_2 = get_distance_for_crossings(wire_2, crossings)
65 |
66 | return min(crossing_distance_1[crossing] + crossing_distance_2[crossing] for crossing in crossings)
67 |
68 | result = solve()
69 | print(f"Solution: {result}")
--------------------------------------------------------------------------------
/2019_04_p1.py:
--------------------------------------------------------------------------------
1 | from aoc import read_file, timer
2 |
3 | def increases(num):
4 | num = list(str(num))
5 |
6 | if num == sorted(num):
7 | return True
8 | return False
9 |
10 | def all_digits_unique(num):
11 | num = str(num)
12 | return len(num) == len(set(num))
13 |
14 | @timer
15 | def solve():
16 | input = read_file("04")
17 | start, end = [int(num) for num in input[0].split("-")]
18 |
19 | return sum(increases(num) and not all_digits_unique(num) for num in range(start, end+1))
20 |
21 | result = solve()
22 | print(f"Solution: {result}")
--------------------------------------------------------------------------------
/2019_04_p2.py:
--------------------------------------------------------------------------------
1 | from aoc import read_file, timer
2 |
3 | def increases(num):
4 | num = list(str(num))
5 |
6 | if num == sorted(num):
7 | return True
8 | return False
9 |
10 | def has_double(num):
11 | num = list(str(num))
12 |
13 | for i in range(5):
14 | if num[i] == num[i+1] and \
15 | (i == 0 or num[i] != num[i-1]) and \
16 | (i == 4 or num[i] != num[i+2]):
17 | return True
18 | return False
19 |
20 | @timer
21 | def solve():
22 | input = read_file("04")
23 | start, end = [int(num) for num in input[0].split("-")]
24 |
25 | return sum(increases(num) and has_double(num) for num in range(start, end+1))
26 |
27 | result = solve()
28 | print(f"Solution: {result}")
29 |
--------------------------------------------------------------------------------
/2019_05_p1.py:
--------------------------------------------------------------------------------
1 | from aoc import read_file, timer
2 |
3 | def split_instruction(instruction):
4 | instruction = f"{instruction:05}"
5 | return instruction[3:], instruction[0:3]
6 |
7 | def get_values(input, pos, op, modes):
8 | mode_a, mode_b, mode_c = modes
9 | values = []
10 |
11 | if op in ["01", "02", "04"]:
12 | if mode_c == "0":
13 | values.append(input[input[pos+1]])
14 | else:
15 | values.append(input[pos+1])
16 |
17 | if op in ["01", "02"]:
18 | if mode_b == "0":
19 | values.append(input[input[pos+2]])
20 | else:
21 | values.append(input[pos+2])
22 |
23 | if op in []:
24 | if mode_a == "0":
25 | values.append(input[input[pos+3]])
26 | else:
27 | values.append(input[pos+3])
28 |
29 | return values
30 |
31 | @timer
32 | def solve():
33 | prog = read_file("05")[0].split(",")
34 | prog = [int(x) for x in prog]
35 |
36 | ip = 0
37 | input = 1
38 |
39 | while prog[ip] != 99:
40 | op, modes = split_instruction(prog[ip])
41 | values = get_values(prog, ip, op, modes)
42 |
43 | if op == "01": # Addition
44 | prog[prog[ip+3]] = values[0] + values[1]
45 | ip += 4
46 |
47 | if op == "02": # Multiplication
48 | prog[prog[ip+3]] = values[0] * values[1]
49 | ip += 4
50 |
51 | if op == "03": # Read and Store prog
52 | prog[prog[ip+1]] = input
53 | ip += 2
54 |
55 | if op == "04": # Print Output
56 | print(values[0])
57 | ip += 2
58 |
59 | result = solve()
60 |
--------------------------------------------------------------------------------
/2019_05_p2.py:
--------------------------------------------------------------------------------
1 | from aoc import read_file, timer
2 |
3 | def split_instruction(instruction):
4 | instruction = f"{instruction:05}"
5 | return instruction[3:], instruction[0:3]
6 |
7 | def get_values(input, pos, op, modes):
8 | mode_a, mode_b, mode_c = modes
9 | values = []
10 |
11 | if op in ["01", "02", "04", "05", "06", "07", "08"]:
12 | if mode_c == "0":
13 | values.append(input[input[pos+1]])
14 | else:
15 | values.append(input[pos+1])
16 |
17 | if op in ["01", "02", "05", "06", "07", "08"]:
18 | if mode_b == "0":
19 | values.append(input[input[pos+2]])
20 | else:
21 | values.append(input[pos+2])
22 |
23 | if op in []:
24 | if mode_a == "0":
25 | values.append(input[input[pos+3]])
26 | else:
27 | values.append(input[pos+3])
28 |
29 | return values
30 |
31 | @timer
32 | def solve():
33 | prog = read_file("05")[0].split(",")
34 | prog = [int(x) for x in prog]
35 |
36 | ip = 0
37 | input = 5
38 |
39 | while prog[ip] != 99:
40 | op, modes = split_instruction(prog[ip])
41 | values = get_values(prog, ip, op, modes)
42 |
43 | if op == "01": # Addition
44 | prog[prog[ip+3]] = values[0] + values[1]
45 | ip += 4
46 |
47 | if op == "02": # Multiplication
48 | prog[prog[ip+3]] = values[0] * values[1]
49 | ip += 4
50 |
51 | if op == "03": # Read and Store prog
52 | prog[prog[ip+1]] = input
53 | ip += 2
54 |
55 | if op == "04": # Print Output
56 | print(values[0])
57 | ip += 2
58 |
59 | if op == "05": # Jump-if-True
60 | if values[0]:
61 | ip = values[1]
62 | else:
63 | ip += 3
64 |
65 | if op == "06": # Jump-if-False
66 | if not values[0]:
67 | ip = values[1]
68 | else:
69 | ip += 3
70 |
71 | if op == "07": # Less than
72 | if values[0] < values[1]:
73 | prog[prog[ip+3]] = 1
74 | else:
75 | prog[prog[ip+3]] = 0
76 | ip += 4
77 |
78 | if op == "08": # Equals
79 | if values[0] == values[1]:
80 | prog[prog[ip+3]] = 1
81 | else:
82 | prog[prog[ip+3]] = 0
83 | ip += 4
84 |
85 | result = solve()
86 |
--------------------------------------------------------------------------------
/2019_06_p1.py:
--------------------------------------------------------------------------------
1 | from aoc import read_file, timer
2 |
3 | def get_pos(orbits, planet):
4 | pos = 0
5 | while planet in orbits.keys():
6 | planet = orbits[planet]
7 | pos += 1
8 | return pos
9 |
10 | def get_all_planets(input):
11 | return set(planet for line in input for planet in line.split(")"))
12 |
13 | def generate_orbit_dict(input):
14 | orbits = {}
15 |
16 | for line in input:
17 | planets = line.split(")")
18 | orbits[planets[1]] = planets[0]
19 |
20 | return orbits
21 |
22 | def solve():
23 | input = read_file("06")
24 |
25 | orbits = generate_orbit_dict(input)
26 | all_planets = get_all_planets(input)
27 |
28 | return sum(get_pos(orbits, k) for k in all_planets)
29 |
30 | result = solve()
31 | print(f"Solution: {result}")
32 |
--------------------------------------------------------------------------------
/2019_06_p2.py:
--------------------------------------------------------------------------------
1 | from aoc import read_file, timer
2 |
3 | def get_planet_position(orbits, planet):
4 | pos = 0
5 | while planet in orbits.keys():
6 | planet = orbits[planet]
7 | pos += 1
8 | return pos
9 |
10 | def get_planets(orbits, positions, planet):
11 | planets = []
12 |
13 | while planet in orbits.keys():
14 | planet = orbits[planet]
15 | planets.append(planet)
16 |
17 | return planets
18 |
19 | def get_all_planets(input):
20 | return set(planet for line in input for planet in line.split(")"))
21 |
22 | def get_all_positions(all_planets, orbits):
23 | positions = {}
24 |
25 | for planet in all_planets:
26 | positions[planet] = get_planet_position(orbits, planet)
27 |
28 | return positions
29 |
30 | def generate_orbit_dict(input):
31 | orbits = dict()
32 |
33 | for line in input:
34 | planets = line.split(")")
35 | orbits[planets[1]] = planets[0]
36 |
37 | return orbits
38 |
39 | def solve():
40 | input = read_file("06")
41 |
42 | orbits = generate_orbit_dict(input)
43 | all_planets = get_all_planets(input)
44 | positions = get_all_positions(all_planets, orbits)
45 |
46 | san = get_planets(orbits, positions, "SAN")
47 | you = get_planets(orbits, positions, "YOU")
48 |
49 | maximum = max(positions[planet] for planet in set(san).intersection(set(you)))
50 |
51 | return positions["SAN"] + positions["YOU"] - 2 * maximum - 2
52 |
53 | result = solve()
54 | print(f"Solution: {result}")
55 |
--------------------------------------------------------------------------------
/2019_07_p1.py:
--------------------------------------------------------------------------------
1 | from aoc import read_file, timer
2 | from itertools import permutations
3 |
4 | def split_instruction(instruction):
5 | instruction = f"{instruction:05}"
6 | return instruction[3:], instruction[0:3]
7 |
8 | def get_values(input, pos, op, modes):
9 | mode_a, mode_b, mode_c = modes
10 | values = []
11 |
12 | if op in ["01", "02", "04", "05", "06", "07", "08"]:
13 | if mode_c == "0":
14 | values.append(input[input[pos+1]])
15 | else:
16 | values.append(input[pos+1])
17 |
18 | if op in ["01", "02", "05", "06", "07", "08"]:
19 | if mode_b == "0":
20 | values.append(input[input[pos+2]])
21 | else:
22 | values.append(input[pos+2])
23 |
24 | if op in []:
25 | if mode_a == "0":
26 | values.append(input[input[pos+3]])
27 | else:
28 | values.append(input[pos+3])
29 |
30 | return values
31 |
32 | def run_amp(phase, input, prog):
33 | input_counter = 0
34 | ip = 0
35 |
36 | while prog[ip] != 99:
37 | op, modes = split_instruction(prog[ip])
38 | values = get_values(prog, ip, op, modes)
39 |
40 | if op == "01": # Addition
41 | prog[prog[ip+3]] = values[0] + values[1]
42 | ip += 4
43 |
44 | if op == "02": # Multiplication
45 | prog[prog[ip+3]] = values[0] * values[1]
46 | ip += 4
47 |
48 | if op == "03": # Read and Store prog
49 | if not input_counter:
50 | prog[prog[ip+1]] = phase
51 | else:
52 | prog[prog[ip+1]] = input
53 | input_counter += 1
54 | ip += 2
55 |
56 | if op == "04": # Print Output
57 | output = values[0]
58 | ip += 2
59 |
60 | if op == "05": # Jump-if-True
61 | if values[0]:
62 | ip = values[1]
63 | else:
64 | ip += 3
65 |
66 | if op == "06": # Jump-if-False
67 | if not values[0]:
68 | ip = values[1]
69 | else:
70 | ip += 3
71 |
72 | if op == "07": # Less than
73 | if values[0] < values[1]:
74 | prog[prog[ip+3]] = 1
75 | else:
76 | prog[prog[ip+3]] = 0
77 | ip += 4
78 |
79 | if op == "08": # Equals
80 | if values[0] == values[1]:
81 | prog[prog[ip+3]] = 1
82 | else:
83 | prog[prog[ip+3]] = 0
84 | ip += 4
85 | return output
86 |
87 |
88 | def solve():
89 | prog = read_file("07")[0].split(",")
90 | prog = [int(x) for x in prog]
91 |
92 | max_thrust = 0
93 |
94 | for phases in permutations(range(5), 5):
95 | output = 0
96 |
97 | for phase in phases:
98 | output = run_amp(phase, output, prog)
99 | max_thrust = max(max_thrust, output)
100 |
101 | return max_thrust
102 |
103 | result = solve()
104 | print(f"Solution: {result}")
--------------------------------------------------------------------------------
/2019_07_p2.py:
--------------------------------------------------------------------------------
1 | from aoc import read_file, timer
2 | from itertools import permutations
3 |
4 | class Amp:
5 | def __init__(self, prog):
6 | self.prog = prog[:]
7 | self.ip = 0
8 | self.output = 0
9 | self.input_counter = 0
10 | self.halt = False
11 |
12 | def split_instruction(instruction):
13 | instruction = f"{instruction:05}"
14 | return instruction[3:], instruction[0:3]
15 |
16 | def get_values(input, pos, op, modes):
17 | mode_a, mode_b, mode_c = modes
18 | values = []
19 |
20 | if op in ["01", "02", "04", "05", "06", "07", "08"]:
21 | if mode_c == "0":
22 | values.append(input[input[pos+1]])
23 | else:
24 | values.append(input[pos+1])
25 |
26 | if op in ["01", "02", "05", "06", "07", "08"]:
27 | if mode_b == "0":
28 | values.append(input[input[pos+2]])
29 | else:
30 | values.append(input[pos+2])
31 |
32 | if op in []:
33 | if mode_a == "0":
34 | values.append(input[input[pos+3]])
35 | else:
36 | values.append(input[pos+3])
37 |
38 | return values
39 |
40 | def run_amp(phase, input, amp):
41 | while amp.prog[amp.ip] != 99:
42 | op, modes = split_instruction(amp.prog[amp.ip])
43 | values = get_values(amp.prog, amp.ip, op, modes)
44 |
45 | if op == "01": # Addition
46 | amp.prog[amp.prog[amp.ip+3]] = values[0] + values[1]
47 | amp.ip += 4
48 |
49 | if op == "02": # Multiplication
50 | amp.prog[amp.prog[amp.ip+3]] = values[0] * values[1]
51 | amp.ip += 4
52 |
53 | if op == "03": # Read and Store input
54 | if not amp.input_counter:
55 | amp.prog[amp.prog[amp.ip+1]] = phase
56 | else:
57 | amp.prog[amp.prog[amp.ip+1]] = input
58 |
59 | amp.input_counter += 1
60 | amp.ip += 2
61 |
62 | if op == "04": # Print Output
63 | amp.output = values[0]
64 | amp.ip += 2
65 | return amp
66 |
67 | if op == "05": # Jump-if-True
68 | if values[0]:
69 | amp.ip = values[1]
70 | else:
71 | amp.ip += 3
72 |
73 | if op == "06": # Jump-if-False
74 | if not values[0]:
75 | amp.ip = values[1]
76 | else:
77 | amp.ip += 3
78 |
79 | if op == "07": # Less than
80 | if values[0] < values[1]:
81 | amp.prog[amp.prog[amp.ip+3]] = 1
82 | else:
83 | amp.prog[amp.prog[amp.ip+3]] = 0
84 | amp.ip += 4
85 |
86 | if op == "08": # Equals
87 | if values[0] == values[1]:
88 | amp.prog[amp.prog[amp.ip+3]] = 1
89 | else:
90 | amp.prog[amp.prog[amp.ip+3]] = 0
91 | amp.ip += 4
92 |
93 | amp.halt = True
94 | return amp
95 |
96 | @timer
97 | def solve():
98 | prog = read_file("07")[0].split(",")
99 | prog = [int(x) for x in prog]
100 | max_thrust = 0
101 |
102 | for phases in permutations(range(5, 10), 5):
103 | amps = [Amp(prog) for i in range(5)]
104 | active = 0
105 |
106 | while not amps[4].halt:
107 | amps[active] = run_amp(phases[active], amps[active-1].output, amps[active])
108 | active = (active + 1) % 5
109 |
110 | max_thrust = max(max_thrust, amps[4].output)
111 | return max_thrust
112 |
113 | result = solve()
114 | print(f"Solution: {result}")
115 |
116 |
--------------------------------------------------------------------------------
/2019_08_animation.py:
--------------------------------------------------------------------------------
1 | from aoc import read_file, timer
2 | from collections import defaultdict
3 | from os import path
4 | from PIL import Image, ImageDraw
5 |
6 | def extract_layers(raw, size):
7 | layers = defaultdict(list)
8 | layer = -1
9 |
10 | for i in range(len(raw)):
11 | if not i % size:
12 | layer += 1
13 | layers[layer].append(raw[i])
14 |
15 | return layers
16 |
17 | def print_image(layers):
18 | scale = 20
19 | for layer in range(100):
20 | img = Image.new('RGBA', (25*scale, 6*scale), (255, 0, 0, 0))
21 | draw = ImageDraw.Draw(img)
22 | for y in range(6):
23 | for x in range(25):
24 | pos = x + y * 25
25 | if layers[layer][pos] == 0:
26 | draw.ellipse((x*scale, y*scale, x*scale+scale-1, y*scale+scale-1), fill=(255, 255, 255))
27 | elif layers[layer][pos] == 1:
28 | draw.ellipse((x*scale, y*scale, x*scale+scale-1, y*scale+scale-1), fill=(0, 0, 0))
29 | file_path = path.join('images', f"BIOS{100-layer:03}.png")
30 | img.save(file_path, 'PNG')
31 |
32 | @timer
33 | def solve():
34 | input = read_file("08")
35 | input = [int(x) for x in input[0]]
36 |
37 | size = 25 * 6
38 | layers = extract_layers(input, size)
39 |
40 | print_image(layers)
41 |
42 | solve()
--------------------------------------------------------------------------------
/2019_08_p1.py:
--------------------------------------------------------------------------------
1 | from aoc import read_file, timer
2 | from collections import defaultdict
3 |
4 | def extract_layers(raw, size):
5 | layers = defaultdict(list)
6 | layer = -1
7 |
8 | for i in range(len(raw)):
9 | if not i % size:
10 | layer += 1
11 | layers[layer].append(raw[i])
12 |
13 | return layers
14 |
15 | @timer
16 | def solve():
17 | input = read_file("08")
18 | input = [int(x) for x in input[0]]
19 |
20 | size = 25 * 6
21 | layers = extract_layers(input, size)
22 |
23 | zero_counts = [pixels.count(0) for pixels in layers.values()]
24 | result_layer = zero_counts.index(min(zero_counts))
25 |
26 | return layers[result_layer].count(1) * layers[result_layer].count(2)
27 |
28 | result = solve()
29 | print(f"Solution: {result}")
--------------------------------------------------------------------------------
/2019_08_p2.py:
--------------------------------------------------------------------------------
1 | from aoc import read_file, timer
2 | from collections import defaultdict
3 |
4 | def extract_layers(raw, size):
5 | layers = defaultdict(list)
6 | layer = -1
7 |
8 | for i in range(len(raw)):
9 | if not i % size:
10 | layer += 1
11 | layers[layer].append(raw[i])
12 |
13 | return layers
14 |
15 | def print_image(layers):
16 | for y in range(6):
17 | for x in range(25):
18 | pos = x + y * 25
19 | for lay in range(100):
20 | if layers[lay][pos] == 2:
21 | continue
22 | if layers[lay][pos] == 1:
23 | print("#", end="")
24 | else:
25 | print(" ", end="")
26 | break
27 | print()
28 |
29 | @timer
30 | def solve():
31 | input = read_file("08")
32 | input = [int(x) for x in input[0]]
33 |
34 | size = 25 * 6
35 | layers = extract_layers(input, size)
36 |
37 | print_image(layers)
38 |
39 | solve()
--------------------------------------------------------------------------------
/2019_09_p1.py:
--------------------------------------------------------------------------------
1 | from aoc import read_file, timer
2 | from collections import defaultdict
3 |
4 | class Booster:
5 | def __init__(self, prog):
6 | self.prog = prog
7 | self.ip = 0
8 | self.output = 0
9 | self.rel_base = 0
10 |
11 | def split_instruction(instruction):
12 | instruction = f"{instruction:05}"
13 | return instruction[3:], instruction[0:3]
14 |
15 | def get_values(input, pos, op, modes, booster):
16 | mode_a, mode_b, mode_c = modes
17 | values = []
18 | offset = 0
19 |
20 | if op in ["01", "02", "04", "05", "06", "07", "08", "09"]:
21 | if mode_c == "0":
22 | values.append(input[input[pos+1]])
23 | elif mode_c == "1":
24 | values.append(input[pos+1])
25 | elif mode_c == "2":
26 | values.append(input[input[pos+1]+booster.rel_base])
27 |
28 | if op in ["01", "02", "05", "06", "07", "08"]:
29 | if mode_b == "0":
30 | values.append(input[input[pos+2]])
31 | elif mode_b == "1":
32 | values.append(input[pos+2])
33 | elif mode_b == "2":
34 | values.append(input[input[pos+2]+booster.rel_base])
35 |
36 | if op in []:
37 | if mode_a == "0":
38 | values.append(input[input[pos+3]])
39 | elif mode_a == "1":
40 | values.append(input[pos+3])
41 | elif mode_a == "2":
42 | values.append(input[input[pos+3]+booster.rel_base])
43 |
44 | if op in ["01", "02", "07", "08"]:
45 | if mode_a == "2":
46 | offset = booster.rel_base
47 |
48 | if op in ["03"]:
49 | if mode_c == "2":
50 | offset = booster.rel_base
51 |
52 | return values, offset
53 |
54 | def run_booster(input, booster):
55 | while booster.prog[booster.ip] != 99:
56 | op, modes = split_instruction(booster.prog[booster.ip])
57 | values, offset = get_values(booster.prog, booster.ip, op, modes, booster)
58 |
59 | if op == "01": # Addition
60 | booster.prog[booster.prog[booster.ip+3] + offset] = values[0] + values[1]
61 | booster.ip += 4
62 |
63 | if op == "02": # Multiplication
64 | booster.prog[booster.prog[booster.ip+3] + offset] = values[0] * values[1]
65 | booster.ip += 4
66 |
67 | if op == "03": # Read and Store input
68 | booster.prog[booster.prog[booster.ip+1] + offset] = input
69 | booster.ip += 2
70 |
71 | if op == "04": # Print Output
72 | booster.output = values[0]
73 | print(booster.output)
74 | booster.ip += 2
75 |
76 | if op == "05": # Jump-if-True
77 | if values[0]:
78 | booster.ip = values[1]
79 | else:
80 | booster.ip += 3
81 |
82 | if op == "06": # Jump-if-False
83 | if not values[0]:
84 | booster.ip = values[1]
85 | else:
86 | booster.ip += 3
87 |
88 | if op == "07": # Less than
89 | if values[0] < values[1]:
90 | booster.prog[booster.prog[booster.ip+3] + offset] = 1
91 | else:
92 | booster.prog[booster.prog[booster.ip+3] + offset] = 0
93 | booster.ip += 4
94 |
95 | if op == "08": # Equals
96 | if values[0] == values[1]:
97 | booster.prog[booster.prog[booster.ip+3] + offset] = 1
98 | else:
99 | booster.prog[booster.prog[booster.ip+3] + offset] = 0
100 | booster.ip += 4
101 |
102 | if op == "09": # Adjust Relative Base
103 | booster.rel_base += values[0]
104 | booster.ip += 2
105 |
106 | return booster
107 |
108 | def create_program(input):
109 | prog = defaultdict(int)
110 |
111 | for i in range(len(input)):
112 | prog[i] = int(input[i])
113 |
114 | return prog
115 |
116 | @timer
117 | def solve():
118 | input = read_file("09")[0].split(",")
119 | prog = create_program(input)
120 |
121 | booster = Booster(prog)
122 | booster = run_booster(1, booster)
123 |
124 | return(booster.output)
125 |
126 | result = solve()
127 | print(f"Solution: {result}")
128 |
129 |
--------------------------------------------------------------------------------
/2019_09_p2.py:
--------------------------------------------------------------------------------
1 | from aoc import read_file, timer
2 | from collections import defaultdict
3 |
4 | class Booster:
5 | def __init__(self, prog):
6 | self.prog = prog
7 | self.ip = 0
8 | self.output = 0
9 | self.rel_base = 0
10 |
11 | def split_instruction(instruction):
12 | instruction = f"{instruction:05}"
13 | return instruction[3:], instruction[0:3]
14 |
15 | def get_values(input, pos, op, modes, booster):
16 | mode_a, mode_b, mode_c = modes
17 | values = []
18 | offset = 0
19 |
20 | if op in ["01", "02", "04", "05", "06", "07", "08", "09"]:
21 | if mode_c == "0":
22 | values.append(input[input[pos+1]])
23 | elif mode_c == "1":
24 | values.append(input[pos+1])
25 | elif mode_c == "2":
26 | values.append(input[input[pos+1]+booster.rel_base])
27 |
28 | if op in ["01", "02", "05", "06", "07", "08"]:
29 | if mode_b == "0":
30 | values.append(input[input[pos+2]])
31 | elif mode_b == "1":
32 | values.append(input[pos+2])
33 | elif mode_b == "2":
34 | values.append(input[input[pos+2]+booster.rel_base])
35 |
36 | if op in []:
37 | if mode_a == "0":
38 | values.append(input[input[pos+3]])
39 | elif mode_a == "1":
40 | values.append(input[pos+3])
41 | elif mode_a == "2":
42 | values.append(input[input[pos+3]+booster.rel_base])
43 |
44 | if op in ["01", "02", "07", "08"]:
45 | if mode_a == "2":
46 | offset = booster.rel_base
47 |
48 | if op in ["03"]:
49 | if mode_c == "2":
50 | offset = booster.rel_base
51 |
52 | return values, offset
53 |
54 | def run_booster(input, booster):
55 | while booster.prog[booster.ip] != 99:
56 | op, modes = split_instruction(booster.prog[booster.ip])
57 | values, offset = get_values(booster.prog, booster.ip, op, modes, booster)
58 |
59 | if op == "01": # Addition
60 | booster.prog[booster.prog[booster.ip+3] + offset] = values[0] + values[1]
61 | booster.ip += 4
62 |
63 | if op == "02": # Multiplication
64 | booster.prog[booster.prog[booster.ip+3] + offset] = values[0] * values[1]
65 | booster.ip += 4
66 |
67 | if op == "03": # Read and Store input
68 | booster.prog[booster.prog[booster.ip+1] + offset] = input
69 | booster.ip += 2
70 |
71 | if op == "04": # Print Output
72 | booster.output = values[0]
73 | print(booster.output)
74 | booster.ip += 2
75 |
76 | if op == "05": # Jump-if-True
77 | if values[0]:
78 | booster.ip = values[1]
79 | else:
80 | booster.ip += 3
81 |
82 | if op == "06": # Jump-if-False
83 | if not values[0]:
84 | booster.ip = values[1]
85 | else:
86 | booster.ip += 3
87 |
88 | if op == "07": # Less than
89 | if values[0] < values[1]:
90 | booster.prog[booster.prog[booster.ip+3] + offset] = 1
91 | else:
92 | booster.prog[booster.prog[booster.ip+3] + offset] = 0
93 | booster.ip += 4
94 |
95 | if op == "08": # Equals
96 | if values[0] == values[1]:
97 | booster.prog[booster.prog[booster.ip+3] + offset] = 1
98 | else:
99 | booster.prog[booster.prog[booster.ip+3] + offset] = 0
100 | booster.ip += 4
101 |
102 | if op == "09": # Adjust Relative Base
103 | booster.rel_base += values[0]
104 | booster.ip += 2
105 |
106 | return booster
107 |
108 | def create_program(input):
109 | prog = defaultdict(int)
110 |
111 | for i in range(len(input)):
112 | prog[i] = int(input[i])
113 |
114 | return prog
115 |
116 | @timer
117 | def solve():
118 | input = read_file("09")[0].split(",")
119 | prog = create_program(input)
120 |
121 | booster = Booster(prog)
122 | booster = run_booster(2, booster)
123 |
124 | return(booster.output)
125 |
126 | result = solve()
127 | print(f"Solution: {result}")
128 |
129 |
--------------------------------------------------------------------------------
/2019_10_p1.py:
--------------------------------------------------------------------------------
1 | from aoc import read_file, timer
2 |
3 | def greatest_common_divisor(x, y):
4 | if not x:
5 | return abs(y)
6 | if not y:
7 | return abs(x)
8 |
9 | while y:
10 | x, y = y, x % y
11 | return abs(x)
12 |
13 | # Get all directions where an asteroid can be seen from station
14 | def get_directions(station, asteroids):
15 | directions = set()
16 | for asteroid in asteroids:
17 | if asteroid == station:
18 | continue
19 |
20 | vec = (asteroid[0] - station[0], asteroid[1] - station[1])
21 | gcd = greatest_common_divisor(vec[0], vec[1])
22 | vec = (vec[0] // gcd, vec[1] // gcd)
23 |
24 | directions.add(vec)
25 |
26 | return directions
27 |
28 | @timer
29 | def solve():
30 | input = read_file("10")
31 | size_x, size_y = len(input[0]), len(input)
32 |
33 | asteroids = [(x, y) for y in range(size_y) for x in range(size_x) if input[y][x] == "#"]
34 |
35 | return max([len(get_directions(station, asteroids)) for station in asteroids])
36 |
37 | result = solve()
38 | print(f"Solution: {result}")
39 |
40 |
41 |
--------------------------------------------------------------------------------
/2019_10_p2.py:
--------------------------------------------------------------------------------
1 | from aoc import read_file, timer
2 | from math import atan, degrees
3 |
4 | from collections import defaultdict
5 |
6 | def greatest_common_divisor(x, y):
7 | if not x:
8 | return abs(y)
9 | if not y:
10 | return abs(x)
11 |
12 | while y:
13 | x, y = y, x % y
14 | return abs(x)
15 |
16 | # Get all directions where an asteroid can be seen from station
17 | def get_directions(station, asteroids):
18 | directions = set()
19 | for asteroid in asteroids:
20 | if asteroid == station:
21 | continue
22 |
23 | vec = (asteroid[0] - station[0], asteroid[1] - station[1])
24 | gcd = greatest_common_divisor(vec[0], vec[1])
25 | vec = (vec[0] // gcd, vec[1] // gcd)
26 |
27 | directions.add(vec)
28 |
29 | return directions
30 |
31 | def get_best_station(asteroids):
32 | asteroids_in_los = [len(get_directions(station, asteroids)) for station in asteroids]
33 | station_id = asteroids_in_los.index(max(asteroids_in_los))
34 | return asteroids[station_id]
35 |
36 | def calc_angle(vector):
37 | if vector[1] == 0:
38 | return 90
39 | return degrees(atan(vector[0]/vector[1]))
40 |
41 | def split_quadrants(directions):
42 | quadrants = []
43 | quadrants.append([x for x in directions if x[0] >= 0 and x[1] < 0])
44 | quadrants.append([x for x in directions if x[0] > 0 and x[1] >= 0])
45 | quadrants.append([x for x in directions if x[0] <= 0 and x[1] > 0])
46 | quadrants.append([x for x in directions if x[0] < 0 and x[1] <= 0])
47 | return quadrants
48 |
49 | def shoot(quadrants, station, asteroids, size_x, size_y):
50 | target_amount = len(asteroids) - 200
51 | while True:
52 | for quadrant in quadrants:
53 | for direction in quadrant:
54 | multi = 1
55 | while True:
56 | coord = (station[0] + direction[0] * multi, station[1] + direction[1] * multi)
57 |
58 | if coord[0] < 0 or coord[0] > size_x or \
59 | coord[1] < 0 or coord[1] > size_y:
60 | break
61 |
62 | if coord in asteroids:
63 | asteroids.remove(coord)
64 |
65 | if len(asteroids) == target_amount:
66 | return coord[0]*100 + coord[1]
67 | break
68 | multi += 1
69 |
70 | @timer
71 | def solve():
72 | input = read_file("10")
73 | size_x, size_y = len(input[0]), len(input)
74 |
75 | asteroids = [(x, y) for y in range(size_y) for x in range(size_x) if input[y][x] == "#"]
76 | station = get_best_station(asteroids)
77 |
78 | directions = list(get_directions(station, asteroids))
79 | directions.sort(key = lambda direction:calc_angle(direction), reverse = True)
80 |
81 | quadrants = split_quadrants(directions)
82 |
83 | return shoot(quadrants, station, asteroids, size_x, size_y)
84 |
85 | result = solve()
86 | print(f"Solution: {result}")
87 |
--------------------------------------------------------------------------------
/2019_11_animation.py:
--------------------------------------------------------------------------------
1 | from aoc import read_file, timer
2 | from collections import defaultdict
3 | from PIL import Image, ImageDraw
4 |
5 | class Painter:
6 | def __init__(self, prog):
7 | self.prog = prog
8 | self.ip = 0
9 | self.output = 0
10 | self.rel_base = 0
11 | self.halt = False
12 |
13 | def split_instruction(instruction):
14 | instruction = f"{instruction:05}"
15 | return instruction[3:], instruction[0:3]
16 |
17 | def get_values(input, pos, op, modes, painter):
18 | mode_a, mode_b, mode_c = modes
19 | values = []
20 | offset = 0
21 |
22 | if op in ["01", "02", "04", "05", "06", "07", "08", "09"]:
23 | if mode_c == "0":
24 | values.append(input[input[pos+1]])
25 | elif mode_c == "1":
26 | values.append(input[pos+1])
27 | elif mode_c == "2":
28 | values.append(input[input[pos+1]+painter.rel_base])
29 |
30 | if op in ["01", "02", "05", "06", "07", "08"]:
31 | if mode_b == "0":
32 | values.append(input[input[pos+2]])
33 | elif mode_b == "1":
34 | values.append(input[pos+2])
35 | elif mode_b == "2":
36 | values.append(input[input[pos+2]+painter.rel_base])
37 |
38 | if op in []:
39 | if mode_a == "0":
40 | values.append(input[input[pos+3]])
41 | elif mode_a == "1":
42 | values.append(input[pos+3])
43 | elif mode_a == "2":
44 | values.append(input[input[pos+3]+painter.rel_base])
45 |
46 | if op in ["01", "02", "07", "08"]:
47 | if mode_a == "2":
48 | offset = painter.rel_base
49 |
50 | if op in ["03"]:
51 | if mode_c == "2":
52 | offset = painter.rel_base
53 |
54 | return values, offset
55 |
56 | def run_booster(input, painter):
57 | while painter.prog[painter.ip] != 99:
58 | op, modes = split_instruction(painter.prog[painter.ip])
59 | values, offset = get_values(painter.prog, painter.ip, op, modes, painter)
60 |
61 | if op == "01": # Addition
62 | painter.prog[painter.prog[painter.ip+3] + offset] = values[0] + values[1]
63 | painter.ip += 4
64 |
65 | if op == "02": # Multiplication
66 | painter.prog[painter.prog[painter.ip+3] + offset] = values[0] * values[1]
67 | painter.ip += 4
68 |
69 | if op == "03": # Read and Store input
70 | painter.prog[painter.prog[painter.ip+1] + offset] = input
71 | painter.ip += 2
72 |
73 | if op == "04": # Print Output
74 | painter.output = values[0]
75 | #print(painter.output)
76 | painter.ip += 2
77 | return painter
78 |
79 | if op == "05": # Jump-if-True
80 | if values[0]:
81 | painter.ip = values[1]
82 | else:
83 | painter.ip += 3
84 |
85 | if op == "06": # Jump-if-False
86 | if not values[0]:
87 | painter.ip = values[1]
88 | else:
89 | painter.ip += 3
90 |
91 | if op == "07": # Less than
92 | if values[0] < values[1]:
93 | painter.prog[painter.prog[painter.ip+3] + offset] = 1
94 | else:
95 | painter.prog[painter.prog[painter.ip+3] + offset] = 0
96 | painter.ip += 4
97 |
98 | if op == "08": # Equals
99 | if values[0] == values[1]:
100 | painter.prog[painter.prog[painter.ip+3] + offset] = 1
101 | else:
102 | painter.prog[painter.prog[painter.ip+3] + offset] = 0
103 | painter.ip += 4
104 |
105 | if op == "09": # Adjust Relative Base
106 | painter.rel_base += values[0]
107 | painter.ip += 2
108 |
109 | painter.halt = True
110 | return painter
111 |
112 | def create_program(input):
113 | prog = defaultdict(int)
114 |
115 | for i in range(len(input)):
116 | prog[i] = int(input[i])
117 |
118 | return prog
119 |
120 | def turn_and_move(pos, dir, turn):
121 | if turn == 0:
122 | dir = (dir - 1) % 4
123 | else:
124 | dir = (dir + 1) % 4
125 |
126 | if dir == 0: # up
127 | pos = (pos[0], pos[1] - 1)
128 | elif dir == 1: # right
129 | pos = (pos[0]+1, pos[1])
130 | elif dir == 2: # down
131 | pos = (pos[0], pos[1] + 1)
132 | elif dir == 3: # left
133 | pos = (pos[0] - 1, pos[1])
134 |
135 | return pos, dir
136 |
137 | def draw_panel(panel, robot, step):
138 | scale = 12
139 |
140 | img = Image.new('RGBA', (40*scale, 6*scale), (255, 0, 0, 0))
141 | draw = ImageDraw.Draw(img)
142 | for y in range(6):
143 | for x in range(40):
144 | if panel[(x, y)] == 0:
145 | draw.ellipse((x*scale, y*scale, x*scale+scale-1, y*scale+scale-1), fill=(255, 255, 255))
146 | elif panel[(x, y)] == 1:
147 | draw.ellipse((x*scale, y*scale, x*scale+scale-1, y*scale+scale-1), fill=(0, 0, 0))
148 |
149 | draw.ellipse((robot[0]*scale, robot[1]*scale, robot[0]*scale+scale-1, robot[1]*scale+scale-1), fill=(255, 0, 0))
150 |
151 | img.save(f"images\Panel{250-step}.png", 'PNG')
152 |
153 | @timer
154 | def solve():
155 | input = read_file("11")[0].split(",")
156 | prog = create_program(input)
157 |
158 | panel = defaultdict(int)
159 | painter = Painter(prog)
160 |
161 | dir = 0
162 | pos = (-1, 0)
163 | panel[(-1, 0)] = 1
164 | step = 0
165 |
166 | while not painter.halt:
167 | painter = run_booster(panel[pos], painter)
168 | color = painter.output
169 | painter = run_booster(panel[pos], painter)
170 | turn = painter.output
171 |
172 | panel[pos] = color
173 |
174 | pos, dir = turn_and_move(pos, dir, turn)
175 |
176 | draw_panel(panel, pos, step)
177 | step += 1
178 |
179 | solve()
--------------------------------------------------------------------------------
/2019_11_p1.py:
--------------------------------------------------------------------------------
1 | from aoc import read_file, timer
2 | from collections import defaultdict
3 |
4 | class Painter:
5 | def __init__(self, prog):
6 | self.prog = prog
7 | self.ip = 0
8 | self.output = 0
9 | self.rel_base = 0
10 | self.halt = False
11 |
12 | def split_instruction(instruction):
13 | instruction = f"{instruction:05}"
14 | return instruction[3:], instruction[0:3]
15 |
16 | def get_values(input, pos, op, modes, painter):
17 | mode_a, mode_b, mode_c = modes
18 | values = []
19 | offset = 0
20 |
21 | if op in ["01", "02", "04", "05", "06", "07", "08", "09"]:
22 | if mode_c == "0":
23 | values.append(input[input[pos+1]])
24 | elif mode_c == "1":
25 | values.append(input[pos+1])
26 | elif mode_c == "2":
27 | values.append(input[input[pos+1]+painter.rel_base])
28 |
29 | if op in ["01", "02", "05", "06", "07", "08"]:
30 | if mode_b == "0":
31 | values.append(input[input[pos+2]])
32 | elif mode_b == "1":
33 | values.append(input[pos+2])
34 | elif mode_b == "2":
35 | values.append(input[input[pos+2]+painter.rel_base])
36 |
37 | if op in []:
38 | if mode_a == "0":
39 | values.append(input[input[pos+3]])
40 | elif mode_a == "1":
41 | values.append(input[pos+3])
42 | elif mode_a == "2":
43 | values.append(input[input[pos+3]+painter.rel_base])
44 |
45 | if op in ["01", "02", "07", "08"]:
46 | if mode_a == "2":
47 | offset = painter.rel_base
48 |
49 | if op in ["03"]:
50 | if mode_c == "2":
51 | offset = painter.rel_base
52 |
53 | return values, offset
54 |
55 | def run_booster(input, painter):
56 | while painter.prog[painter.ip] != 99:
57 | op, modes = split_instruction(painter.prog[painter.ip])
58 | values, offset = get_values(painter.prog, painter.ip, op, modes, painter)
59 |
60 | if op == "01": # Addition
61 | painter.prog[painter.prog[painter.ip+3] + offset] = values[0] + values[1]
62 | painter.ip += 4
63 |
64 | if op == "02": # Multiplication
65 | painter.prog[painter.prog[painter.ip+3] + offset] = values[0] * values[1]
66 | painter.ip += 4
67 |
68 | if op == "03": # Read and Store input
69 | painter.prog[painter.prog[painter.ip+1] + offset] = input
70 | painter.ip += 2
71 |
72 | if op == "04": # Print Output
73 | painter.output = values[0]
74 | #print(painter.output)
75 | painter.ip += 2
76 | return painter
77 |
78 | if op == "05": # Jump-if-True
79 | if values[0]:
80 | painter.ip = values[1]
81 | else:
82 | painter.ip += 3
83 |
84 | if op == "06": # Jump-if-False
85 | if not values[0]:
86 | painter.ip = values[1]
87 | else:
88 | painter.ip += 3
89 |
90 | if op == "07": # Less than
91 | if values[0] < values[1]:
92 | painter.prog[painter.prog[painter.ip+3] + offset] = 1
93 | else:
94 | painter.prog[painter.prog[painter.ip+3] + offset] = 0
95 | painter.ip += 4
96 |
97 | if op == "08": # Equals
98 | if values[0] == values[1]:
99 | painter.prog[painter.prog[painter.ip+3] + offset] = 1
100 | else:
101 | painter.prog[painter.prog[painter.ip+3] + offset] = 0
102 | painter.ip += 4
103 |
104 | if op == "09": # Adjust Relative Base
105 | painter.rel_base += values[0]
106 | painter.ip += 2
107 |
108 | painter.halt = True
109 | return painter
110 |
111 | def create_program(input):
112 | prog = defaultdict(int)
113 |
114 | for i in range(len(input)):
115 | prog[i] = int(input[i])
116 |
117 | return prog
118 |
119 | def turn_and_move(pos, dir, turn):
120 | if turn == 0:
121 | dir = (dir - 1) % 4
122 | else:
123 | dir = (dir + 1) % 4
124 |
125 | if dir == 0: # up
126 | pos = (pos[0], pos[1] + 1)
127 | elif dir == 1: # right
128 | pos = (pos[0]+1, pos[1])
129 | elif dir == 2: # down
130 | pos = (pos[0], pos[1] - 1)
131 | elif dir == 3: # left
132 | pos = (pos[0] - 1, pos[1])
133 |
134 | return pos, dir
135 |
136 | @timer
137 | def solve():
138 | input = read_file("11")[0].split(",")
139 | prog = create_program(input)
140 |
141 | panel = defaultdict(int)
142 | painted = defaultdict(int)
143 | painter = Painter(prog)
144 |
145 | dir = 0
146 | pos = (0, 0)
147 |
148 | while not painter.halt:
149 | painter = run_booster(panel[pos], painter)
150 | color = painter.output
151 | painter = run_booster(panel[pos], painter)
152 | turn = painter.output
153 |
154 | painted[pos] = 1
155 | panel[pos] = color
156 |
157 | pos, dir = turn_and_move(pos, dir, turn)
158 |
159 | return len(painted)
160 |
161 | result = solve()
162 | print(f"Solution: {result}")
--------------------------------------------------------------------------------
/2019_11_p2.py:
--------------------------------------------------------------------------------
1 | from aoc import read_file, timer
2 | from collections import defaultdict
3 |
4 | class Painter:
5 | def __init__(self, prog):
6 | self.prog = prog
7 | self.ip = 0
8 | self.output = 0
9 | self.rel_base = 0
10 | self.halt = False
11 |
12 | def split_instruction(instruction):
13 | instruction = f"{instruction:05}"
14 | return instruction[3:], instruction[0:3]
15 |
16 | def get_values(input, pos, op, modes, painter):
17 | mode_a, mode_b, mode_c = modes
18 | values = []
19 | offset = 0
20 |
21 | if op in ["01", "02", "04", "05", "06", "07", "08", "09"]:
22 | if mode_c == "0":
23 | values.append(input[input[pos+1]])
24 | elif mode_c == "1":
25 | values.append(input[pos+1])
26 | elif mode_c == "2":
27 | values.append(input[input[pos+1]+painter.rel_base])
28 |
29 | if op in ["01", "02", "05", "06", "07", "08"]:
30 | if mode_b == "0":
31 | values.append(input[input[pos+2]])
32 | elif mode_b == "1":
33 | values.append(input[pos+2])
34 | elif mode_b == "2":
35 | values.append(input[input[pos+2]+painter.rel_base])
36 |
37 | if op in []:
38 | if mode_a == "0":
39 | values.append(input[input[pos+3]])
40 | elif mode_a == "1":
41 | values.append(input[pos+3])
42 | elif mode_a == "2":
43 | values.append(input[input[pos+3]+painter.rel_base])
44 |
45 | if op in ["01", "02", "07", "08"]:
46 | if mode_a == "2":
47 | offset = painter.rel_base
48 |
49 | if op in ["03"]:
50 | if mode_c == "2":
51 | offset = painter.rel_base
52 |
53 | return values, offset
54 |
55 | def run_booster(input, painter):
56 | while painter.prog[painter.ip] != 99:
57 | op, modes = split_instruction(painter.prog[painter.ip])
58 | values, offset = get_values(painter.prog, painter.ip, op, modes, painter)
59 |
60 | if op == "01": # Addition
61 | painter.prog[painter.prog[painter.ip+3] + offset] = values[0] + values[1]
62 | painter.ip += 4
63 |
64 | if op == "02": # Multiplication
65 | painter.prog[painter.prog[painter.ip+3] + offset] = values[0] * values[1]
66 | painter.ip += 4
67 |
68 | if op == "03": # Read and Store input
69 | painter.prog[painter.prog[painter.ip+1] + offset] = input
70 | painter.ip += 2
71 |
72 | if op == "04": # Print Output
73 | painter.output = values[0]
74 | #print(painter.output)
75 | painter.ip += 2
76 | return painter
77 |
78 | if op == "05": # Jump-if-True
79 | if values[0]:
80 | painter.ip = values[1]
81 | else:
82 | painter.ip += 3
83 |
84 | if op == "06": # Jump-if-False
85 | if not values[0]:
86 | painter.ip = values[1]
87 | else:
88 | painter.ip += 3
89 |
90 | if op == "07": # Less than
91 | if values[0] < values[1]:
92 | painter.prog[painter.prog[painter.ip+3] + offset] = 1
93 | else:
94 | painter.prog[painter.prog[painter.ip+3] + offset] = 0
95 | painter.ip += 4
96 |
97 | if op == "08": # Equals
98 | if values[0] == values[1]:
99 | painter.prog[painter.prog[painter.ip+3] + offset] = 1
100 | else:
101 | painter.prog[painter.prog[painter.ip+3] + offset] = 0
102 | painter.ip += 4
103 |
104 | if op == "09": # Adjust Relative Base
105 | painter.rel_base += values[0]
106 | painter.ip += 2
107 |
108 | painter.halt = True
109 | return painter
110 |
111 | def create_program(input):
112 | prog = defaultdict(int)
113 |
114 | for i in range(len(input)):
115 | prog[i] = int(input[i])
116 |
117 | return prog
118 |
119 | def turn_and_move(pos, dir, turn):
120 | if turn == 0:
121 | dir = (dir - 1) % 4
122 | else:
123 | dir = (dir + 1) % 4
124 |
125 | if dir == 0: # up
126 | pos = (pos[0], pos[1] + 1)
127 | elif dir == 1: # right
128 | pos = (pos[0]+1, pos[1])
129 | elif dir == 2: # down
130 | pos = (pos[0], pos[1] - 1)
131 | elif dir == 3: # left
132 | pos = (pos[0] - 1, pos[1])
133 |
134 | return pos, dir
135 |
136 | def draw_panel(panel):
137 | top, right, bottom, left = -1e8, -1e8, 1e8, 1e8
138 | for position, value in panel.items():
139 | if value:
140 | top = max(top, position[1])
141 | right = max(right, position[0])
142 | bottom = min(bottom, position[1])
143 | left = min(left, position[0])
144 |
145 | for y in range(top, bottom-1, -1):
146 | for x in range(left, right+1):
147 | if panel[(x, y)] == 1:
148 | print("#", end="")
149 | if panel[(x, y)] == 0:
150 | print(" ", end="")
151 | print()
152 |
153 | @timer
154 | def solve():
155 | input = read_file("11")[0].split(",")
156 | prog = create_program(input)
157 |
158 | panel = defaultdict(int)
159 | painter = Painter(prog)
160 |
161 | dir = 0
162 | pos = (0, 0)
163 | panel[(0, 0)] = 1
164 |
165 | while not painter.halt:
166 | painter = run_booster(panel[pos], painter)
167 | color = painter.output
168 | painter = run_booster(panel[pos], painter)
169 | turn = painter.output
170 |
171 | panel[pos] = color
172 |
173 | pos, dir = turn_and_move(pos, dir, turn)
174 |
175 | draw_panel(panel)
176 |
177 | solve()
--------------------------------------------------------------------------------
/2019_12_p1.py:
--------------------------------------------------------------------------------
1 | from aoc import read_file, timer
2 | from re import findall
3 |
4 | def extract_ints(line):
5 | return [int(x) for x in findall(r'-?\d+', line)]
6 |
7 | def apply_gravity(moons, velocities):
8 | for m_1 in range(3):
9 | for m_2 in range(m_1+1, 4):
10 | for dim in range(3):
11 | if moons[m_1][dim] > moons[m_2][dim]:
12 | velocities[m_1][dim] -= 1
13 | velocities[m_2][dim] += 1
14 | elif moons[m_1][dim] < moons[m_2][dim]:
15 | velocities[m_1][dim] += 1
16 | velocities[m_2][dim] -= 1
17 |
18 | def apply_velocity(moons, velocities):
19 | for m in range(4):
20 | for dim in range(3):
21 | moons[m][dim] += velocities[m][dim]
22 |
23 | def calculate_energy(moons, velocities):
24 | e = 0
25 | for i in range(4):
26 | pot = sum([abs(moon) for moon in moons[i]])
27 | kin = sum([abs(velocity) for velocity in velocities[i]])
28 | e += pot * kin
29 | return e
30 |
31 | @timer
32 | def solve():
33 | input = read_file("12")
34 |
35 | moons = [extract_ints(line) for line in input]
36 | velocities = [[0 for x in range(3)] for y in range(4)]
37 |
38 | for _ in range(1000):
39 | apply_gravity(moons, velocities)
40 | apply_velocity(moons, velocities)
41 |
42 | return calculate_energy(moons, velocities)
43 |
44 | result = solve()
45 | print(f"Solution: {result}")
--------------------------------------------------------------------------------
/2019_12_p2.py:
--------------------------------------------------------------------------------
1 | from aoc import read_file, timer
2 | from re import findall
3 |
4 | def extract_ints(line):
5 | return [int(x) for x in findall(r'-?\d+', line)]
6 |
7 | def get_prime_factors(n):
8 | i = 2
9 | factors = []
10 | while i * i <= n:
11 | if n % i:
12 | i += 1
13 | else:
14 | n //= i
15 | factors.append(i)
16 | if n > 1:
17 | factors.append(n)
18 | return factors
19 |
20 | def calculate_lcm(steps):
21 | primes_per_dimension = [get_prime_factors(step) for step in steps]
22 | all_primes = set([prime for primes in primes_per_dimension for prime in primes])
23 |
24 | lcm = 1
25 | for prime in all_primes:
26 | amount = max(primes_per_dimension[dim].count(prime) for dim in range(3))
27 | lcm *= prime**amount
28 | return lcm
29 |
30 | def apply_gravity(moons_dim, velocities_dim):
31 | for m_1 in range(3):
32 | for m_2 in range(m_1+1, 4):
33 | if moons_dim[m_1] > moons_dim[m_2]:
34 | velocities_dim[m_1] -= 1
35 | velocities_dim[m_2] += 1
36 | elif moons_dim[m_1] < moons_dim[m_2]:
37 | velocities_dim[m_1] += 1
38 | velocities_dim[m_2] -= 1
39 |
40 | def apply_velocity(moons_dim, velocities_dim):
41 | for m in range(4):
42 | moons_dim[m] += velocities_dim[m]
43 |
44 | def matches_start_state(moons, velocities, moons_dim, velocities_dim, dim):
45 | for i in range(4):
46 | if moons[i][dim] != moons_dim[i] or \
47 | velocities[i][dim] != velocities_dim[i]:
48 | return False
49 | return True
50 |
51 | @timer
52 | def solve():
53 | input = read_file("12")
54 |
55 | moons = [extract_ints(line) for line in input]
56 | velocities = [[0 for x in range(3)] for y in range(4)]
57 |
58 | steps = [0, 0, 0]
59 |
60 | for dim in range(3):
61 | moons_dim = [moon[dim] for moon in moons]
62 | velocities_dim = [velocity[dim] for velocity in velocities]
63 |
64 | while True:
65 | apply_gravity(moons_dim, velocities_dim)
66 | apply_velocity(moons_dim, velocities_dim)
67 |
68 | steps[dim] += 1
69 |
70 | if matches_start_state(moons, velocities, moons_dim, velocities_dim, dim):
71 | break
72 |
73 | return calculate_lcm(steps)
74 |
75 | result = solve()
76 | print(f"Solution: {result}")
--------------------------------------------------------------------------------
/2019_13_p1.py:
--------------------------------------------------------------------------------
1 | from aoc import read_file, timer
2 | from collections import defaultdict
3 |
4 | class Game:
5 | def __init__(self, prog):
6 | self.prog = prog
7 | self.ip = 0
8 | self.output = 0
9 | self.rel_base = 0
10 | self.halt = False
11 |
12 | def split_instruction(instruction):
13 | instruction = f"{instruction:05}"
14 | return instruction[3:], instruction[0:3]
15 |
16 | def get_values(input, pos, op, modes, game):
17 | mode_a, mode_b, mode_c = modes
18 | values = []
19 | offset = 0
20 |
21 | if op in ["01", "02", "04", "05", "06", "07", "08", "09"]:
22 | if mode_c == "0":
23 | values.append(input[input[pos+1]])
24 | elif mode_c == "1":
25 | values.append(input[pos+1])
26 | elif mode_c == "2":
27 | values.append(input[input[pos+1]+game.rel_base])
28 |
29 | if op in ["01", "02", "05", "06", "07", "08"]:
30 | if mode_b == "0":
31 | values.append(input[input[pos+2]])
32 | elif mode_b == "1":
33 | values.append(input[pos+2])
34 | elif mode_b == "2":
35 | values.append(input[input[pos+2]+game.rel_base])
36 |
37 | if op in []:
38 | if mode_a == "0":
39 | values.append(input[input[pos+3]])
40 | elif mode_a == "1":
41 | values.append(input[pos+3])
42 | elif mode_a == "2":
43 | values.append(input[input[pos+3]+game.rel_base])
44 |
45 | if op in ["01", "02", "07", "08"]:
46 | if mode_a == "2":
47 | offset = game.rel_base
48 |
49 | if op in ["03"]:
50 | if mode_c == "2":
51 | offset = game.rel_base
52 |
53 | return values, offset
54 |
55 | def run_game(game, input = 0):
56 | while game.prog[game.ip] != 99:
57 | op, modes = split_instruction(game.prog[game.ip])
58 | values, offset = get_values(game.prog, game.ip, op, modes, game)
59 |
60 | if op == "01": # Addition
61 | game.prog[game.prog[game.ip+3] + offset] = values[0] + values[1]
62 | game.ip += 4
63 |
64 | if op == "02": # Multiplication
65 | game.prog[game.prog[game.ip+3] + offset] = values[0] * values[1]
66 | game.ip += 4
67 |
68 | if op == "03": # Read and Store input
69 | game.prog[game.prog[game.ip+1] + offset] = input
70 | game.ip += 2
71 |
72 | if op == "04": # Print Output
73 | game.output = values[0]
74 | game.ip += 2
75 | return game
76 |
77 | if op == "05": # Jump-if-True
78 | if values[0]:
79 | game.ip = values[1]
80 | else:
81 | game.ip += 3
82 |
83 | if op == "06": # Jump-if-False
84 | if not values[0]:
85 | game.ip = values[1]
86 | else:
87 | game.ip += 3
88 |
89 | if op == "07": # Less than
90 | if values[0] < values[1]:
91 | game.prog[game.prog[game.ip+3] + offset] = 1
92 | else:
93 | game.prog[game.prog[game.ip+3] + offset] = 0
94 | game.ip += 4
95 |
96 | if op == "08": # Equals
97 | if values[0] == values[1]:
98 | game.prog[game.prog[game.ip+3] + offset] = 1
99 | else:
100 | game.prog[game.prog[game.ip+3] + offset] = 0
101 | game.ip += 4
102 |
103 | if op == "09": # Adjust Relative Base
104 | game.rel_base += values[0]
105 | game.ip += 2
106 |
107 | game.halt = True
108 | return game
109 |
110 | def create_program(input):
111 | prog = defaultdict(int)
112 |
113 | for i in range(len(input)):
114 | prog[i] = int(input[i])
115 |
116 | return prog
117 |
118 | @timer
119 | def solve():
120 | input = read_file("13")[0].split(",")
121 | prog = create_program(input)
122 | game = Game(prog)
123 | result = 0
124 |
125 | while not game.halt:
126 | for i in range(3):
127 | run_game(game)
128 | if game.output == 2:
129 | result += 1
130 |
131 | return result
132 |
133 | result = solve()
134 | print(f"Solution: {result}")
--------------------------------------------------------------------------------
/2019_13_p2.py:
--------------------------------------------------------------------------------
1 | from aoc import read_file, timer
2 | from collections import defaultdict
3 |
4 | class Game:
5 | def __init__(self, prog):
6 | self.prog = prog
7 | self.ip = 0
8 | self.output = 0
9 | self.rel_base = 0
10 | self.halt = False
11 |
12 | def split_instruction(instruction):
13 | instruction = f"{instruction:05}"
14 | return instruction[3:], instruction[0:3]
15 |
16 | def get_values(input, pos, op, modes, game):
17 | mode_a, mode_b, mode_c = modes
18 | values = []
19 | offset = 0
20 |
21 | if op in ["01", "02", "04", "05", "06", "07", "08", "09"]:
22 | if mode_c == "0":
23 | values.append(input[input[pos+1]])
24 | elif mode_c == "1":
25 | values.append(input[pos+1])
26 | elif mode_c == "2":
27 | values.append(input[input[pos+1]+game.rel_base])
28 |
29 | if op in ["01", "02", "05", "06", "07", "08"]:
30 | if mode_b == "0":
31 | values.append(input[input[pos+2]])
32 | elif mode_b == "1":
33 | values.append(input[pos+2])
34 | elif mode_b == "2":
35 | values.append(input[input[pos+2]+game.rel_base])
36 |
37 | if op in []:
38 | if mode_a == "0":
39 | values.append(input[input[pos+3]])
40 | elif mode_a == "1":
41 | values.append(input[pos+3])
42 | elif mode_a == "2":
43 | values.append(input[input[pos+3]+game.rel_base])
44 |
45 | if op in ["01", "02", "07", "08"]:
46 | if mode_a == "2":
47 | offset = game.rel_base
48 |
49 | if op in ["03"]:
50 | if mode_c == "2":
51 | offset = game.rel_base
52 |
53 | return values, offset
54 |
55 | def run_game(game, input = 0):
56 | while game.prog[game.ip] != 99:
57 | op, modes = split_instruction(game.prog[game.ip])
58 | values, offset = get_values(game.prog, game.ip, op, modes, game)
59 |
60 | if op == "01": # Addition
61 | game.prog[game.prog[game.ip+3] + offset] = values[0] + values[1]
62 | game.ip += 4
63 |
64 | if op == "02": # Multiplication
65 | game.prog[game.prog[game.ip+3] + offset] = values[0] * values[1]
66 | game.ip += 4
67 |
68 | if op == "03": # Read and Store input
69 | game.prog[game.prog[game.ip+1] + offset] = input
70 | game.ip += 2
71 |
72 | if op == "04": # Print Output
73 | game.output = values[0]
74 | game.ip += 2
75 | return game
76 |
77 | if op == "05": # Jump-if-True
78 | if values[0]:
79 | game.ip = values[1]
80 | else:
81 | game.ip += 3
82 |
83 | if op == "06": # Jump-if-False
84 | if not values[0]:
85 | game.ip = values[1]
86 | else:
87 | game.ip += 3
88 |
89 | if op == "07": # Less than
90 | if values[0] < values[1]:
91 | game.prog[game.prog[game.ip+3] + offset] = 1
92 | else:
93 | game.prog[game.prog[game.ip+3] + offset] = 0
94 | game.ip += 4
95 |
96 | if op == "08": # Equals
97 | if values[0] == values[1]:
98 | game.prog[game.prog[game.ip+3] + offset] = 1
99 | else:
100 | game.prog[game.prog[game.ip+3] + offset] = 0
101 | game.ip += 4
102 |
103 | if op == "09": # Adjust Relative Base
104 | game.rel_base += values[0]
105 | game.ip += 2
106 |
107 | game.halt = True
108 | return game
109 |
110 | def create_program(input):
111 | prog = defaultdict(int)
112 |
113 | for i in range(len(input)):
114 | prog[i] = int(input[i])
115 |
116 | return prog
117 |
118 | def get_joy(ball, paddle):
119 | if ball > paddle:
120 | return 1
121 | elif ball == paddle:
122 | return 0
123 | else:
124 | return -1
125 |
126 | @timer
127 | def solve():
128 | input = read_file("13")[0].split(",")
129 | prog = create_program(input)
130 | prog[0] = 2
131 |
132 | game = Game(prog)
133 | ball, paddle = 0, 0
134 | coord = defaultdict(int)
135 |
136 | while not game.halt:
137 | for output in ["x", "y", "tile"]:
138 | run_game(game, get_joy(ball, paddle))
139 | coord[output] = game.output
140 | if coord["tile"] == 3:
141 | paddle = coord["x"]
142 | elif coord["tile"] == 4:
143 | ball = coord["x"]
144 | if coord["x"] == -1:
145 | score = coord["tile"]
146 |
147 | return score
148 |
149 | result = solve()
150 | print(f"Solution: {result}")
--------------------------------------------------------------------------------
/2019_14_p1.py:
--------------------------------------------------------------------------------
1 | from aoc import read_file, timer
2 | from collections import defaultdict
3 |
4 | def produce(comp, amount, requirements, rest = None):
5 | if rest == None:
6 | rest = defaultdict(int)
7 |
8 | for prod, sources in requirements.items():
9 | if prod[1] == comp:
10 | break
11 |
12 | made = rest[prod[1]]
13 |
14 | if not (amount - made) % prod[0]:
15 | rest[prod[1]] = 0
16 | a = (amount - made) // prod[0]
17 | else:
18 | rest[prod[1]] = prod[0] - ((amount - made) % prod[0])
19 | a = (amount - made) // prod[0] + 1
20 |
21 | ore = 0
22 |
23 | for source in sources:
24 | if source[1] == "ORE":
25 | ore += source[0]*a
26 | else:
27 | cost = produce(source[1], source[0]*a, requirements, rest)
28 | ore += cost
29 |
30 | return ore
31 |
32 | @timer
33 | def solve():
34 | input = read_file("14")
35 | requirements = defaultdict(list)
36 |
37 | for line in input:
38 | parts = line.replace(",", "").split(" ")
39 | for i in range((len(parts) - 3) // 2):
40 | requirements[(int(parts[-2]), parts[-1])].append((int(parts[i*2]), parts[i*2+1]))
41 |
42 | return produce("FUEL", 1, requirements)
43 |
44 | result = solve()
45 | print(f"Solution: {result}")
--------------------------------------------------------------------------------
/2019_14_p2.py:
--------------------------------------------------------------------------------
1 | from aoc import read_file, timer
2 | from collections import defaultdict
3 |
4 | def produce(comp, amount, requirements, rest = None):
5 | if rest == None:
6 | rest = defaultdict(int)
7 |
8 | for prod, sources in requirements.items():
9 | if prod[1] == comp:
10 | break
11 |
12 | made = rest[prod[1]]
13 |
14 | if not (amount - made) % prod[0]:
15 | rest[prod[1]] = 0
16 | a = (amount - made) // prod[0]
17 | else:
18 | rest[prod[1]] = prod[0] - ((amount - made) % prod[0])
19 | a = (amount - made) // prod[0] + 1
20 |
21 | ore = 0
22 |
23 | for source in sources:
24 | if source[1] == "ORE":
25 | ore += source[0]*a
26 | else:
27 | cost = produce(source[1], source[0]*a, requirements, rest)
28 | ore += cost
29 |
30 | return ore
31 |
32 | @timer
33 | def solve():
34 | input = read_file("14")
35 |
36 | req = defaultdict(list)
37 |
38 | for line in input:
39 | line = list(line)
40 | while "," in line:
41 | line.remove(",")
42 | line = "".join(line)
43 |
44 | x = line.split(" ")
45 | comps = (len(x) - 3) // 2
46 | for i in range(comps):
47 | req[(int(x[-2]), x[-1])].append((int(x[i*2]), x[i*2+1]))
48 |
49 | available = 1000000000000
50 |
51 | low = available // produce("FUEL", 1, req)
52 | high = 2 * low
53 |
54 | while high - low > 1:
55 | middle = (high + low) // 2
56 | cost = produce("FUEL", middle, req)
57 | if cost <= available:
58 | low = middle
59 | else:
60 | high = middle
61 |
62 | return low
63 |
64 |
65 | result = solve()
66 | print(f"Solution: {result}")
--------------------------------------------------------------------------------
/2019_16_p1.py:
--------------------------------------------------------------------------------
1 | from aoc import read_file, timer
2 | from collections import deque
3 |
4 | @timer
5 | def solve():
6 | input = read_file("16")[0]
7 | elements = [int(num) for num in input]
8 |
9 | for phase in range(100):
10 | for pos in range(len(elements)):
11 | pattern = deque([p for p in [0, 1, 0, -1] for _ in range(pos+1)])
12 | pattern.rotate(-1)
13 |
14 | res = 0
15 | for i in range(len(elements)):
16 | res += elements[i] * pattern[0]
17 | pattern.rotate(-1)
18 | elements[pos] = abs(res) % 10
19 |
20 | return "".join(str(x) for x in elements[:8])
21 |
22 | result = solve()
23 | print(f"Solution: {result}")
24 |
25 |
--------------------------------------------------------------------------------
/2019_16_p2.py:
--------------------------------------------------------------------------------
1 | from aoc import read_file, timer
2 |
3 | @timer
4 | def solve():
5 | input = read_file("16")[0]
6 | offset = int(input[:7])
7 | elements = [int(num) for _ in range(10000) for num in input][offset:]
8 |
9 | for _ in range(100):
10 | for i in range(-2, -len(elements)-1, -1):
11 | elements[i] = (elements[i] + elements[i+1]) % 10
12 |
13 | return "".join([str(x) for x in elements[:8]])
14 |
15 | result = solve()
16 | print(f"Solution: {result}")
--------------------------------------------------------------------------------
/2019_17_p1.py:
--------------------------------------------------------------------------------
1 | from aoc import read_file, timer
2 | from collections import defaultdict
3 |
4 | class Game:
5 | def __init__(self, prog):
6 | self.prog = prog
7 | self.ip = 0
8 | self.output = 0
9 | self.rel_base = 0
10 | self.halt = False
11 |
12 | def split_instruction(instruction):
13 | instruction = f"{instruction:05}"
14 | return instruction[3:], instruction[0:3]
15 |
16 | def get_values(input, pos, op, modes, game):
17 | mode_a, mode_b, mode_c = modes
18 | values = []
19 | offset = 0
20 |
21 | if op in ["01", "02", "04", "05", "06", "07", "08", "09"]:
22 | if mode_c == "0":
23 | values.append(input[input[pos+1]])
24 | elif mode_c == "1":
25 | values.append(input[pos+1])
26 | elif mode_c == "2":
27 | values.append(input[input[pos+1]+game.rel_base])
28 |
29 | if op in ["01", "02", "05", "06", "07", "08"]:
30 | if mode_b == "0":
31 | values.append(input[input[pos+2]])
32 | elif mode_b == "1":
33 | values.append(input[pos+2])
34 | elif mode_b == "2":
35 | values.append(input[input[pos+2]+game.rel_base])
36 |
37 | if op in []:
38 | if mode_a == "0":
39 | values.append(input[input[pos+3]])
40 | elif mode_a == "1":
41 | values.append(input[pos+3])
42 | elif mode_a == "2":
43 | values.append(input[input[pos+3]+game.rel_base])
44 |
45 | if op in ["01", "02", "07", "08"]:
46 | if mode_a == "2":
47 | offset = game.rel_base
48 |
49 | if op in ["03"]:
50 | if mode_c == "2":
51 | offset = game.rel_base
52 |
53 | return values, offset
54 |
55 | def run_game(game, input = 0):
56 | while game.prog[game.ip] != 99:
57 | op, modes = split_instruction(game.prog[game.ip])
58 | values, offset = get_values(game.prog, game.ip, op, modes, game)
59 |
60 | if op == "01": # Addition
61 | game.prog[game.prog[game.ip+3] + offset] = values[0] + values[1]
62 | game.ip += 4
63 |
64 | if op == "02": # Multiplication
65 | game.prog[game.prog[game.ip+3] + offset] = values[0] * values[1]
66 | game.ip += 4
67 |
68 | if op == "03": # Read and Store input
69 | game.prog[game.prog[game.ip+1] + offset] = input
70 | game.ip += 2
71 |
72 | if op == "04": # Print Output
73 | game.output = values[0]
74 | game.ip += 2
75 | return game
76 |
77 | if op == "05": # Jump-if-True
78 | if values[0]:
79 | game.ip = values[1]
80 | else:
81 | game.ip += 3
82 |
83 | if op == "06": # Jump-if-False
84 | if not values[0]:
85 | game.ip = values[1]
86 | else:
87 | game.ip += 3
88 |
89 | if op == "07": # Less than
90 | if values[0] < values[1]:
91 | game.prog[game.prog[game.ip+3] + offset] = 1
92 | else:
93 | game.prog[game.prog[game.ip+3] + offset] = 0
94 | game.ip += 4
95 |
96 | if op == "08": # Equals
97 | if values[0] == values[1]:
98 | game.prog[game.prog[game.ip+3] + offset] = 1
99 | else:
100 | game.prog[game.prog[game.ip+3] + offset] = 0
101 | game.ip += 4
102 |
103 | if op == "09": # Adjust Relative Base
104 | game.rel_base += values[0]
105 | game.ip += 2
106 |
107 | game.halt = True
108 | return game
109 |
110 | def create_program(input):
111 | prog = defaultdict(int)
112 |
113 | for i in range(len(input)):
114 | prog[i] = int(input[i])
115 |
116 | return prog
117 |
118 | def get_size(field):
119 | len_y, len_x = 0, 0
120 | for position in field.keys():
121 | len_x = max(len_x, position[0])
122 | len_y = max(len_y, position[1])
123 |
124 | return len_x, len_y
125 |
126 | def solve():
127 | input = read_file("17")[0].split(",")
128 | prog = create_program(input)
129 | game = Game(prog)
130 |
131 | field = defaultdict(str)
132 | x, y = 0, 0
133 |
134 | while not game.halt:
135 | run_game(game)
136 | if game.output == 10:
137 | y += 1
138 | x = 0
139 | else:
140 | field[(x, y)] = chr(game.output)
141 | x += 1
142 | print(chr(game.output), end = "")
143 |
144 | len_x, len_y = get_size(field)
145 | result = 0
146 |
147 | for y in range(1, len_y):
148 | for x in range(1, len_x):
149 | if field[(x-1, y)] == "#" and \
150 | field[(x, y-1)] == "#" and \
151 | field[(x+1, y)] == "#" and \
152 | field[(x, y+1)] == "#":
153 | result += x * y
154 |
155 | return result
156 |
157 | result = solve()
158 | print(f"Solution: {result}")
--------------------------------------------------------------------------------
/2019_17_p2.py:
--------------------------------------------------------------------------------
1 | from aoc import read_file, timer
2 | from collections import defaultdict, deque
3 |
4 | class Game:
5 | def __init__(self, prog):
6 | self.prog = prog
7 | self.ip = 0
8 | self.input = deque()
9 | self.output = deque()
10 | self.rel_base = 0
11 | self.halt = False
12 |
13 | def split_instruction(instruction):
14 | instruction = f"{instruction:05}"
15 | return instruction[3:], instruction[0:3]
16 |
17 | def get_values(input, pos, op, modes, game):
18 | mode_a, mode_b, mode_c = modes
19 | values = []
20 | offset = 0
21 |
22 | if op in ["01", "02", "04", "05", "06", "07", "08", "09"]:
23 | if mode_c == "0":
24 | values.append(input[input[pos+1]])
25 | elif mode_c == "1":
26 | values.append(input[pos+1])
27 | elif mode_c == "2":
28 | values.append(input[input[pos+1]+game.rel_base])
29 |
30 | if op in ["01", "02", "05", "06", "07", "08"]:
31 | if mode_b == "0":
32 | values.append(input[input[pos+2]])
33 | elif mode_b == "1":
34 | values.append(input[pos+2])
35 | elif mode_b == "2":
36 | values.append(input[input[pos+2]+game.rel_base])
37 |
38 | if op in []:
39 | if mode_a == "0":
40 | values.append(input[input[pos+3]])
41 | elif mode_a == "1":
42 | values.append(input[pos+3])
43 | elif mode_a == "2":
44 | values.append(input[input[pos+3]+game.rel_base])
45 |
46 | if op in ["01", "02", "07", "08"]:
47 | if mode_a == "2":
48 | offset = game.rel_base
49 |
50 | if op in ["03"]:
51 | if mode_c == "2":
52 | offset = game.rel_base
53 |
54 | return values, offset
55 |
56 | def read_output(game):
57 | if len(game.output):
58 | return game.output.popleft()
59 | return None
60 |
61 | def run_game(game, input = []):
62 | if len(input):
63 | game.input.extend(input)
64 |
65 | while game.prog[game.ip] != 99:
66 | op, modes = split_instruction(game.prog[game.ip])
67 | values, offset = get_values(game.prog, game.ip, op, modes, game)
68 |
69 | if op == "01": # Addition
70 | game.prog[game.prog[game.ip+3] + offset] = values[0] + values[1]
71 | game.ip += 4
72 |
73 | if op == "02": # Multiplication
74 | game.prog[game.prog[game.ip+3] + offset] = values[0] * values[1]
75 | game.ip += 4
76 |
77 | if op == "03": # Read and Store input
78 | game.prog[game.prog[game.ip+1] + offset] = game.input.popleft()
79 | game.ip += 2
80 |
81 | if op == "04": # Print Output
82 | game.output.append(values[0])
83 | game.ip += 2
84 | return game
85 |
86 | if op == "05": # Jump-if-True
87 | if values[0]:
88 | game.ip = values[1]
89 | else:
90 | game.ip += 3
91 |
92 | if op == "06": # Jump-if-False
93 | if not values[0]:
94 | game.ip = values[1]
95 | else:
96 | game.ip += 3
97 |
98 | if op == "07": # Less than
99 | if values[0] < values[1]:
100 | game.prog[game.prog[game.ip+3] + offset] = 1
101 | else:
102 | game.prog[game.prog[game.ip+3] + offset] = 0
103 | game.ip += 4
104 |
105 | if op == "08": # Equals
106 | if values[0] == values[1]:
107 | game.prog[game.prog[game.ip+3] + offset] = 1
108 | else:
109 | game.prog[game.prog[game.ip+3] + offset] = 0
110 | game.ip += 4
111 |
112 | if op == "09": # Adjust Relative Base
113 | game.rel_base += values[0]
114 | game.ip += 2
115 |
116 | game.halt = True
117 |
118 | def create_program(input):
119 | prog = defaultdict(int)
120 |
121 | for i in range(len(input)):
122 | prog[i] = int(input[i])
123 |
124 | return prog
125 |
126 | def print_message(game):
127 | while not len(game.output) or game.output[-1] != 10:
128 | run_game(game)
129 | while (output := read_output(game)):
130 | print(chr(output), end = "")
131 |
132 | def print_camera(game):
133 | while len(game.output) < 2 or game.output[-2] != 10 or game.output[-1] != 10:
134 | run_game(game)
135 |
136 | while (output := read_output(game)):
137 | print(chr(output), end = "")
138 |
139 | @timer
140 | def solve():
141 | input = read_file("17")[0].split(",")
142 | input[0] = 2
143 | prog = create_program(input)
144 | game = Game(prog)
145 |
146 | print_camera(game)
147 |
148 | # Solution derived on paper.
149 | # Automated solution will follow, when I find the time.
150 |
151 | instructions = []
152 | instructions.append([ord(c) for c in "A,B,A,C,A,B,C,B,C,A\n"])
153 | instructions.append([ord(c) for c in "L,12,R,4,R,4,L,6\n"])
154 | instructions.append([ord(c) for c in "L,12,R,4,R,4,R,12\n"])
155 | instructions.append([ord(c) for c in "L,10,L,6,R,4\n"])
156 | instructions.append([ord(c) for c in "n\n"])
157 |
158 | print_message(game)
159 |
160 | for inst in instructions:
161 | run_game(game, inst)
162 | print_message(game)
163 |
164 | print_camera(game)
165 | run_game(game)
166 |
167 | return read_output(game)
168 |
169 | result = solve()
170 | print(f"Solution: {result}")
171 |
172 |
--------------------------------------------------------------------------------
/2019_18_p1.py:
--------------------------------------------------------------------------------
1 | from aoc import read_file, timer
2 | from collections import defaultdict
3 | from copy import copy
4 |
5 | class Key:
6 | def __init__(self, dungeon, position, type):
7 | self.position = position
8 | self.type = type
9 | self.distance_to_keys = self.get_key_distances(dungeon)
10 | self.blocked_by_doors = self.get_blocking_doors(dungeon)
11 |
12 | def get_key_distances(self, dungeon):
13 | next_search = defaultdict(int)
14 | next_search[self.position] = 1
15 | keys = {}
16 | changes = True
17 | steps = 0
18 |
19 | while changes or not steps:
20 | changes = False
21 | steps += 1
22 | search = copy(next_search)
23 |
24 | for position, state in search.items():
25 | if state == -1:
26 | del next_search[position]
27 | if state == 1:
28 | next_search[position] = -1
29 | for side in [(-1,0), (0,-1), (1,0), (0,1)]:
30 | side_pos = (position[0] + side[0], position[1] + side[1])
31 | if dungeon.area[side_pos] != "#" and \
32 | not search[side_pos]:
33 | del search[side_pos]
34 | changes = True
35 | next_search[side_pos] = 1
36 | if dungeon.area[side_pos].islower():
37 | keys[dungeon.area[side_pos]] = steps
38 | return keys
39 |
40 | def get_blocking_doors(self, dungeon):
41 | if self.type == "@":
42 | return []
43 |
44 | next_search = defaultdict(list)
45 | next_search[dungeon.position] = [1]
46 |
47 | while True:
48 | search = copy(next_search)
49 |
50 | for position, state in search.items():
51 | if state[0] == -1:
52 | del next_search[position]
53 | if state[0] == 1:
54 | next_search[position] = [-1]
55 | for side in [(-1,0), (0,-1), (1,0), (0,1)]:
56 | side_pos = (position[0] + side[0], position[1] + side[1])
57 | if dungeon.area[side_pos] != "#" and \
58 | not search[side_pos]:
59 | del search[side_pos]
60 | next_search[side_pos] = copy(state)
61 | if dungeon.area[side_pos] == self.type:
62 | return state[1:]
63 | elif dungeon.area[side_pos].isupper():
64 | next_search[side_pos].append(dungeon.area[side_pos])
65 |
66 | class Dungeon:
67 | def __init__(self, layout):
68 | self.area = defaultdict(str)
69 | self.size_x = len(layout[0])
70 | self.size_y = len(layout)
71 | self.position = (0, 0)
72 | self.key_pos = {}
73 |
74 | for y in range(self.size_y):
75 | for x in range(self.size_x):
76 | self.area[(x, y)] = layout[y][x]
77 | if self.area[(x, y)].islower():
78 | self.key_pos[self.area[(x, y)]] = (x, y)
79 | elif self.area[(x, y)] == "@":
80 | self.position = (x, y)
81 |
82 | self.reduce_dungeon()
83 |
84 | def is_dead_end(self, pos, keys_allowed = False):
85 | tile = self.area[pos]
86 | if tile == "#" or tile == "@" or (not keys_allowed and tile.islower()):
87 | return False
88 |
89 | count = sum(self.area[(pos[0] + side[0], pos[1] + side[1])] != "#" for side in [(-1,0), (0,-1), (1,0), (0,1)])
90 |
91 | if count <= 1:
92 | return True
93 | return False
94 |
95 | def reduce_dungeon(self):
96 | reduced = True
97 | while reduced:
98 | reduced = False
99 | for y in range(1, self.size_y):
100 | for x in range(1, self.size_x):
101 | if self.is_dead_end((x, y)):
102 | self.area[(x, y)] = "#"
103 | reduced = True
104 |
105 | def create_keys(dungeon):
106 | keys = {}
107 | for type, position in dungeon.key_pos.items():
108 | keys[type] = Key(dungeon, position, type)
109 |
110 | keys["@"] = Key(dungeon, dungeon.position, "@")
111 |
112 | return keys
113 |
114 | def get_reachable_keys(keys, keys_taken, doors_opened):
115 | reachable = set()
116 | for key in keys.values():
117 | if len(set(key.blocked_by_doors) & doors_opened) == len(key.blocked_by_doors) and \
118 | key.type not in keys_taken:
119 | reachable.add(key)
120 | return reachable
121 |
122 | def fetch_keys(keys, keys_taken, doors_opened, current_key = "@", known_best = 1e8, path = 0, known_situations = {}):
123 | if len(keys_taken) == len(keys):
124 | return path
125 |
126 | best_path = known_best
127 | reachable_keys = get_reachable_keys(keys, keys_taken, doors_opened)
128 | reachable_keys = sorted(reachable_keys, key = lambda x: keys[current_key].distance_to_keys[x.type])
129 |
130 | for next_key in reachable_keys:
131 | next_path = path + keys[current_key].distance_to_keys[next_key.type]
132 |
133 | current_situation = tuple(sorted(list(keys_taken)) + [next_key.type])
134 |
135 | if current_situation in known_situations:
136 | if next_path >= known_situations[current_situation]:
137 | continue
138 |
139 | known_situations[current_situation] = next_path
140 |
141 | if next_path >= best_path:
142 | continue
143 |
144 | next_keys_taken = keys_taken | set(next_key.type)
145 | next_doors_opened = doors_opened | set(next_key.type.upper())
146 |
147 | this_path = fetch_keys(keys, next_keys_taken, next_doors_opened, next_key.type, best_path, next_path, known_situations)
148 | if this_path < best_path:
149 | best_path = this_path
150 |
151 | return best_path
152 |
153 | @timer
154 | def solve():
155 | input = read_file("18")
156 | dungeon = Dungeon(input)
157 | keys = create_keys(dungeon)
158 | keys_taken = set(("@"))
159 | doors_opened = set()
160 |
161 | return fetch_keys(keys, keys_taken, doors_opened)
162 |
163 | result = solve()
164 | print(f"Solution: {result}")
--------------------------------------------------------------------------------
/2019_18_p2.py:
--------------------------------------------------------------------------------
1 | from aoc import read_file, timer
2 | from collections import defaultdict
3 | from copy import copy, deepcopy
4 |
5 | class Key:
6 | def __init__(self, dungeon, position, type):
7 | self.position = position
8 | self.type = type
9 | self.distance_to_keys = self.get_key_distances(dungeon)
10 | self.bot = self.get_bot()
11 | self.blocked_by_doors = self.get_blocking_doors(dungeon)
12 |
13 | def get_key_distances(self, dungeon):
14 | next_search = defaultdict(int)
15 | next_search[self.position] = 1
16 | keys = {}
17 | changes = True
18 | steps = 0
19 |
20 | while changes or not steps:
21 | changes = False
22 | steps += 1
23 | search = copy(next_search)
24 |
25 | for position, state in search.items():
26 | if state == -1:
27 | del next_search[position]
28 | if state == 1:
29 | next_search[position] = -1
30 | for side in [(-1,0), (0,-1), (1,0), (0,1)]:
31 | side_pos = (position[0] + side[0], position[1] + side[1])
32 | if dungeon.area[side_pos] != "#" and \
33 | not search[side_pos]:
34 | del search[side_pos]
35 | changes = True
36 | next_search[side_pos] = 1
37 | if dungeon.area[side_pos].islower() or dungeon.area[side_pos].isnumeric():
38 | keys[dungeon.area[side_pos]] = steps
39 | return keys
40 |
41 | def get_blocking_doors(self, dungeon):
42 | if self.type.isnumeric():
43 | return []
44 |
45 | next_search = defaultdict(list)
46 | next_search[dungeon.bot_pos[self.bot]] = [1]
47 |
48 | while True:
49 | search = copy(next_search)
50 |
51 | for position, state in search.items():
52 | if state[0] == -1:
53 | del next_search[position]
54 | if state[0] == 1:
55 | next_search[position] = [-1]
56 | for side in [(-1,0), (0,-1), (1,0), (0,1)]:
57 | side_pos = (position[0] + side[0], position[1] + side[1])
58 | if dungeon.area[side_pos] != "#" and \
59 | not search[side_pos]:
60 | del search[side_pos]
61 | next_search[side_pos] = copy(state)
62 | if dungeon.area[side_pos] == self.type:
63 | return state[1:]
64 | elif dungeon.area[side_pos].isupper():
65 | next_search[side_pos].append(dungeon.area[side_pos])
66 |
67 | def get_bot(self):
68 | for key in self.distance_to_keys.keys():
69 | if key.isnumeric():
70 | return int(key)
71 |
72 |
73 | class Dungeon:
74 | def __init__(self, layout):
75 | self.area = defaultdict(str)
76 | self.size_x = len(layout[0])
77 | self.size_y = len(layout)
78 | self.position = (0, 0)
79 | self.key_pos = {}
80 | self.bot_pos = []
81 |
82 | for y in range(self.size_y):
83 | for x in range(self.size_x):
84 | self.area[(x, y)] = layout[y][x]
85 | if self.area[(x, y)].islower():
86 | self.key_pos[self.area[(x, y)]] = (x, y)
87 | elif self.area[(x, y)] == "@":
88 | self.position = (x, y)
89 |
90 | self.reduce_dungeon()
91 | self.patch_dungeon()
92 |
93 | def patch_dungeon(self):
94 | self.area[self.position] = "#"
95 | for side in [(-1,0), (0,-1), (1,0), (0,1)]:
96 | side_pos = (self.position[0] + side[0], self.position[1] + side[1])
97 | self.area[side_pos] = "#"
98 | for side_id in range(4):
99 | side = [(-1,1), (1,-1), (1,1), (-1,-1)][side_id]
100 | side_pos = (self.position[0] + side[0], self.position[1] + side[1])
101 | self.area[side_pos] = str(side_id)
102 | self.bot_pos.append(side_pos)
103 | self.key_pos[str(side_id)] = side_pos
104 |
105 | def is_dead_end(self, pos, keys_allowed = False):
106 | tile = self.area[pos]
107 | if tile == "#" or tile == "@" or (not keys_allowed and tile.islower()):
108 | return False
109 |
110 | count = sum(self.area[(pos[0] + side[0], pos[1] + side[1])] != "#" for side in [(-1,0), (0,-1), (1,0), (0,1)])
111 |
112 | if count <= 1:
113 | return True
114 | return False
115 |
116 | def reduce_dungeon(self):
117 | reduced = True
118 | while reduced:
119 | reduced = False
120 | for y in range(1, self.size_y):
121 | for x in range(1, self.size_x):
122 | if self.is_dead_end((x, y)):
123 | self.area[(x, y)] = "#"
124 | reduced = True
125 |
126 | def create_keys(dungeon):
127 | keys = {}
128 | for type, position in dungeon.key_pos.items():
129 | keys[type] = Key(dungeon, position, type)
130 |
131 | for bot_id in range(4):
132 | keys[str(bot_id)] = Key(dungeon, dungeon.bot_pos[bot_id], str(bot_id))
133 |
134 | return keys
135 |
136 | def get_reachable_keys(keys, keys_taken, doors_opened):
137 | reachable = set()
138 | for key in keys.values():
139 | if len(set(key.blocked_by_doors) & doors_opened) == len(key.blocked_by_doors) and \
140 | key.type not in keys_taken:
141 | reachable.add(key)
142 | return reachable
143 |
144 | def fetch_keys(key_pos, bot_pos, keys, keys_taken, doors_opened, known_best = 1e8, path = 0, known_situations = {}):
145 | if len(keys_taken) == len(keys):
146 | return path
147 |
148 | best_path = known_best
149 | reachable_keys = get_reachable_keys(keys, keys_taken, doors_opened)
150 |
151 | for next_key in reachable_keys:
152 | for current_key, position in key_pos.items():
153 | if bot_pos[next_key.bot] == position:
154 | break
155 |
156 | next_path = path + keys[current_key].distance_to_keys[next_key.type]
157 |
158 | current_situation = tuple(sorted(list(keys_taken)) + [bot_pos[bot_id] for bot_id in range(4)])
159 |
160 | if current_situation in known_situations:
161 | if next_path >= known_situations[current_situation]:
162 | continue
163 |
164 | known_situations[current_situation] = next_path
165 |
166 | if next_path >= best_path:
167 | continue
168 |
169 | next_keys_taken = keys_taken | set(next_key.type)
170 | next_doors_opened = doors_opened | set(next_key.type.upper())
171 | next_bot_pos = deepcopy(bot_pos)
172 | next_bot_pos[next_key.bot] = copy(key_pos[next_key.type])
173 |
174 | this_path = fetch_keys(key_pos, next_bot_pos, keys, next_keys_taken, next_doors_opened, best_path, next_path, known_situations)
175 | if this_path < best_path:
176 | best_path = this_path
177 |
178 | return best_path
179 |
180 | @timer
181 | def solve():
182 | input = read_file("18")
183 | dungeon = Dungeon(input)
184 | keys = create_keys(dungeon)
185 | keys_taken = set(str(bot_id) for bot_id in range(4))
186 | doors_opened = set()
187 |
188 | return fetch_keys(dungeon.key_pos, dungeon.bot_pos, keys, keys_taken, doors_opened)
189 |
190 | result = solve()
191 | print(f"Solution: {result}")
--------------------------------------------------------------------------------
/2019_19_p1.py:
--------------------------------------------------------------------------------
1 | from aoc import read_file, timer
2 | from collections import defaultdict, deque
3 | from copy import copy
4 |
5 | class Game:
6 | def __init__(self, prog):
7 | self.prog = prog
8 | self.ip = 0
9 | self.input = deque()
10 | self.output = deque()
11 | self.rel_base = 0
12 | self.halt = False
13 |
14 | def split_instruction(instruction):
15 | instruction = f"{instruction:05}"
16 | return instruction[3:], instruction[0:3]
17 |
18 | def get_values(input, pos, op, modes, game):
19 | mode_a, mode_b, mode_c = modes
20 | values = []
21 | offset = 0
22 |
23 | if op in ["01", "02", "04", "05", "06", "07", "08", "09"]:
24 | if mode_c == "0":
25 | values.append(input[input[pos+1]])
26 | elif mode_c == "1":
27 | values.append(input[pos+1])
28 | elif mode_c == "2":
29 | values.append(input[input[pos+1]+game.rel_base])
30 |
31 | if op in ["01", "02", "05", "06", "07", "08"]:
32 | if mode_b == "0":
33 | values.append(input[input[pos+2]])
34 | elif mode_b == "1":
35 | values.append(input[pos+2])
36 | elif mode_b == "2":
37 | values.append(input[input[pos+2]+game.rel_base])
38 |
39 | if op in []:
40 | if mode_a == "0":
41 | values.append(input[input[pos+3]])
42 | elif mode_a == "1":
43 | values.append(input[pos+3])
44 | elif mode_a == "2":
45 | values.append(input[input[pos+3]+game.rel_base])
46 |
47 | if op in ["01", "02", "07", "08"]:
48 | if mode_a == "2":
49 | offset = game.rel_base
50 |
51 | if op in ["03"]:
52 | if mode_c == "2":
53 | offset = game.rel_base
54 |
55 | return values, offset
56 |
57 | def read_output(game):
58 | if len(game.output):
59 | return game.output.popleft()
60 | return None
61 |
62 | def run_game(game, input = []):
63 | if len(input):
64 | game.input.extend(input)
65 |
66 | while game.prog[game.ip] != 99:
67 | op, modes = split_instruction(game.prog[game.ip])
68 | values, offset = get_values(game.prog, game.ip, op, modes, game)
69 |
70 | if op == "01": # Addition
71 | game.prog[game.prog[game.ip+3] + offset] = values[0] + values[1]
72 | game.ip += 4
73 |
74 | if op == "02": # Multiplication
75 | game.prog[game.prog[game.ip+3] + offset] = values[0] * values[1]
76 | game.ip += 4
77 |
78 | if op == "03": # Read and Store input
79 | game.prog[game.prog[game.ip+1] + offset] = game.input.popleft()
80 | game.ip += 2
81 |
82 | if op == "04": # Print Output
83 | game.output.append(values[0])
84 | game.ip += 2
85 | return
86 |
87 | if op == "05": # Jump-if-True
88 | if values[0]:
89 | game.ip = values[1]
90 | else:
91 | game.ip += 3
92 |
93 | if op == "06": # Jump-if-False
94 | if not values[0]:
95 | game.ip = values[1]
96 | else:
97 | game.ip += 3
98 |
99 | if op == "07": # Less than
100 | if values[0] < values[1]:
101 | game.prog[game.prog[game.ip+3] + offset] = 1
102 | else:
103 | game.prog[game.prog[game.ip+3] + offset] = 0
104 | game.ip += 4
105 |
106 | if op == "08": # Equals
107 | if values[0] == values[1]:
108 | game.prog[game.prog[game.ip+3] + offset] = 1
109 | else:
110 | game.prog[game.prog[game.ip+3] + offset] = 0
111 | game.ip += 4
112 |
113 | if op == "09": # Adjust Relative Base
114 | game.rel_base += values[0]
115 | game.ip += 2
116 |
117 | game.halt = True
118 |
119 | def create_program(input):
120 | prog = defaultdict(int)
121 |
122 | for i in range(len(input)):
123 | prog[i] = int(input[i])
124 |
125 | return prog
126 |
127 | @timer
128 | def solve():
129 | input = read_file("19")[0].split(",")
130 | prog = create_program(input)
131 | game = Game(prog)
132 |
133 | result = 0
134 |
135 | for y in range(50):
136 | for x in range(50):
137 | game = Game(copy(prog))
138 | run_game(game, [x, y])
139 | if read_output(game):
140 | result += 1
141 |
142 | return result
143 |
144 | result = solve()
145 | print(f"Solution: {result}")
--------------------------------------------------------------------------------
/2019_19_p2.py:
--------------------------------------------------------------------------------
1 | from aoc import read_file, timer
2 | from collections import defaultdict, deque
3 | from copy import copy
4 |
5 | class Game:
6 | def __init__(self, prog):
7 | self.prog = prog
8 | self.ip = 0
9 | self.input = deque()
10 | self.output = deque()
11 | self.rel_base = 0
12 | self.halt = False
13 |
14 | def split_instruction(instruction):
15 | instruction = f"{instruction:05}"
16 | return instruction[3:], instruction[0:3]
17 |
18 | def get_values(input, pos, op, modes, game):
19 | mode_a, mode_b, mode_c = modes
20 | values = []
21 | offset = 0
22 |
23 | if op in ["01", "02", "04", "05", "06", "07", "08", "09"]:
24 | if mode_c == "0":
25 | values.append(input[input[pos+1]])
26 | elif mode_c == "1":
27 | values.append(input[pos+1])
28 | elif mode_c == "2":
29 | values.append(input[input[pos+1]+game.rel_base])
30 |
31 | if op in ["01", "02", "05", "06", "07", "08"]:
32 | if mode_b == "0":
33 | values.append(input[input[pos+2]])
34 | elif mode_b == "1":
35 | values.append(input[pos+2])
36 | elif mode_b == "2":
37 | values.append(input[input[pos+2]+game.rel_base])
38 |
39 | if op in []:
40 | if mode_a == "0":
41 | values.append(input[input[pos+3]])
42 | elif mode_a == "1":
43 | values.append(input[pos+3])
44 | elif mode_a == "2":
45 | values.append(input[input[pos+3]+game.rel_base])
46 |
47 | if op in ["01", "02", "07", "08"]:
48 | if mode_a == "2":
49 | offset = game.rel_base
50 |
51 | if op in ["03"]:
52 | if mode_c == "2":
53 | offset = game.rel_base
54 |
55 | return values, offset
56 |
57 | def read_output(game):
58 | if len(game.output):
59 | return game.output.popleft()
60 | return None
61 |
62 | def run_game(game, input = []):
63 | if len(input):
64 | game.input.extend(input)
65 |
66 | while game.prog[game.ip] != 99:
67 | op, modes = split_instruction(game.prog[game.ip])
68 | values, offset = get_values(game.prog, game.ip, op, modes, game)
69 |
70 | if op == "01": # Addition
71 | game.prog[game.prog[game.ip+3] + offset] = values[0] + values[1]
72 | game.ip += 4
73 |
74 | if op == "02": # Multiplication
75 | game.prog[game.prog[game.ip+3] + offset] = values[0] * values[1]
76 | game.ip += 4
77 |
78 | if op == "03": # Read and Store input
79 | game.prog[game.prog[game.ip+1] + offset] = game.input.popleft()
80 | game.ip += 2
81 |
82 | if op == "04": # Print Output
83 | game.output.append(values[0])
84 | game.ip += 2
85 | return
86 |
87 | if op == "05": # Jump-if-True
88 | if values[0]:
89 | game.ip = values[1]
90 | else:
91 | game.ip += 3
92 |
93 | if op == "06": # Jump-if-False
94 | if not values[0]:
95 | game.ip = values[1]
96 | else:
97 | game.ip += 3
98 |
99 | if op == "07": # Less than
100 | if values[0] < values[1]:
101 | game.prog[game.prog[game.ip+3] + offset] = 1
102 | else:
103 | game.prog[game.prog[game.ip+3] + offset] = 0
104 | game.ip += 4
105 |
106 | if op == "08": # Equals
107 | if values[0] == values[1]:
108 | game.prog[game.prog[game.ip+3] + offset] = 1
109 | else:
110 | game.prog[game.prog[game.ip+3] + offset] = 0
111 | game.ip += 4
112 |
113 | if op == "09": # Adjust Relative Base
114 | game.rel_base += values[0]
115 | game.ip += 2
116 |
117 | game.halt = True
118 |
119 | def create_program(input):
120 | prog = defaultdict(int)
121 |
122 | for i in range(len(input)):
123 | prog[i] = int(input[i])
124 |
125 | return prog
126 |
127 | def try_y(prog, y, box_size = 100):
128 | output = 0
129 | x = y
130 |
131 | while output == 0:
132 | x += 1
133 | game = Game(copy(prog))
134 | run_game(game, [x, y])
135 | output = read_output(game)
136 |
137 | x1 = x
138 |
139 | while output == 1:
140 | x += 1
141 | game = Game(copy(prog))
142 | run_game(game, [x, y])
143 | output = read_output(game)
144 |
145 | x2 = x - 1
146 |
147 | if x2 - x1 + 1 < box_size:
148 | return -1, None
149 |
150 | game = Game(copy(prog))
151 | run_game(game, [x2-(box_size-1), y+box_size-1])
152 |
153 | if read_output(game) == 1:
154 | game = Game(copy(prog))
155 | run_game(game, [x2-(box_size-1), y+box_size])
156 | if read_output(game) == 0:
157 | return 0, (x2-(box_size-1), y)
158 | return 1, None
159 | return -1, None
160 |
161 | @timer
162 | def solve():
163 | input = read_file("19")[0].split(",")
164 | prog = create_program(input)
165 | game = Game(prog)
166 |
167 | mini, maxi = 50, 1000
168 | check = 1
169 |
170 | while check:
171 | middle = (maxi + mini) // 2
172 | check, corner = try_y(prog, middle)
173 | if check == -1:
174 | mini = middle
175 | elif check == 1:
176 | maxi = middle
177 |
178 | while not check:
179 | middle -= 1
180 | check, corner = try_y(prog, middle)
181 | if not check:
182 | real_corner = corner
183 |
184 | return real_corner[0] * 10000 + real_corner[1]
185 |
186 | result = solve()
187 | print(f"Solution: {result}")
--------------------------------------------------------------------------------
/2019_20_p1.py:
--------------------------------------------------------------------------------
1 | from aoc import read_file, timer
2 | from collections import defaultdict
3 | from copy import copy
4 |
5 | class Dungeon:
6 | def __init__(self, layout):
7 | self.area = defaultdict(str)
8 | self.size_x = len(layout[2])+4
9 | self.size_y = len(layout)
10 | self.portals = defaultdict(list)
11 | self.portal_names = defaultdict(str)
12 | self.paths = defaultdict(list)
13 |
14 | for y in range(self.size_y):
15 | for x in range(self.size_x):
16 | if x < len(layout[y]):
17 | self.area[(x, y)] = layout[y][x]
18 |
19 | self.reduce_dungeon()
20 | self.find_portals()
21 | self.find_paths()
22 |
23 | def is_dead_end(self, pos, keys_allowed = False):
24 | tile = self.area[pos]
25 | if tile == "#" or tile == "@" or (not keys_allowed and tile.islower()):
26 | return False
27 |
28 | count = sum(self.area[(pos[0] + side[0], pos[1] + side[1])] != "#" for side in [(-1,0), (0,-1), (1,0), (0,1)])
29 |
30 | if count <= 1:
31 | return True
32 | return False
33 |
34 | def reduce_dungeon(self):
35 | reduced = True
36 | while reduced:
37 | reduced = False
38 | for y in range(1, self.size_y):
39 | for x in range(1, self.size_x):
40 | if self.is_dead_end((x, y)):
41 | self.area[(x, y)] = "#"
42 | reduced = True
43 |
44 | def find_portals(self):
45 | for y in range(self.size_y):
46 | for x in range(self.size_x):
47 | if self.area[(x, y)].isupper():
48 | if self.area[(x+1, y)].isupper():
49 | name = self.area[(x, y)] + self.area[(x+1, y)]
50 | if not name in [p[0] for p in self.portals]:
51 | id = 0
52 | else:
53 | id = 1
54 | if self.area[(x+2, y)] == ".":
55 | self.portals[(name, id)].append((x+2, y))
56 | self.portal_names[(x+1, y)] = (name, id)
57 | elif self.area[(x-1, y)] == ".":
58 | self.portals[(name, id)].append((x-1, y))
59 | self.portal_names[(x, y)] = (name, id)
60 |
61 | elif self.area[(x, y+1)].isupper():
62 | name = self.area[(x, y)] + self.area[(x, y+1)]
63 | if not name in [p[0] for p in self.portals]:
64 | id = 0
65 | else:
66 | id = 1
67 | if self.area[(x, y+2)] == ".":
68 | self.portals[(name, id)].append((x, y+2))
69 | self.portal_names[(x, y+1)] = (name, id)
70 | elif self.area[(x, y-1)] == ".":
71 | self.portals[(name, id)].append((x, y-1))
72 | self.portal_names[(x, y)] = (name, id)
73 |
74 | def find_paths(self):
75 | for name, positions in self.portals.items():
76 | for pos in positions:
77 | next_search = defaultdict(int)
78 | next_search[pos] = 1
79 |
80 | changes = True
81 | steps = 0
82 |
83 | while changes or not steps:
84 | changes = False
85 | steps += 1
86 | search = copy(next_search)
87 |
88 | for position, state in search.items():
89 | if state == -1:
90 | del next_search[position]
91 | if state == 1:
92 | next_search[position] = -1
93 | for side in [(-1,0), (0,-1), (1,0), (0,1)]:
94 | side_pos = (position[0] + side[0], position[1] + side[1])
95 | if self.area[side_pos] != "#" and \
96 | not search[side_pos]:
97 | del search[side_pos]
98 | changes = True
99 | next_search[side_pos] = 1
100 | if self.area[side_pos].isupper():
101 | next_search[side_pos] = -1
102 | if steps > 1:
103 | target_name = self.portal_names[(side_pos)]
104 | self.paths[name].append((target_name, steps-1))
105 |
106 | def find_shortest_way(dungeon, path = [("AA", 0)], best_steps = 1e8, steps = 0):
107 | if path[-1] == ("ZZ", 1):
108 | return steps
109 |
110 | for next_portal in dungeon.paths[path[-1]]:
111 | if next_portal[0] in path:
112 | continue
113 |
114 | next_steps = steps + next_portal[1] + 1
115 | if next_steps >= best_steps:
116 | continue
117 |
118 | if next_portal[0][1] == 0:
119 | id = 1
120 | else:
121 | id = 0
122 |
123 | next_path = path + [next_portal] + [(next_portal[0][0], id)]
124 | this_steps = find_shortest_way(dungeon, next_path, best_steps, next_steps)
125 |
126 | if this_steps < best_steps:
127 | best_steps = this_steps
128 |
129 | return best_steps
130 |
131 | @timer
132 | def solve():
133 | input = read_file("20", strip = False)
134 | dungeon = Dungeon(input)
135 | return find_shortest_way(dungeon) - 1
136 |
137 | result = solve()
138 | print(f"Solution: {result}")
--------------------------------------------------------------------------------
/2019_20_p2.py:
--------------------------------------------------------------------------------
1 | from aoc import read_file, timer
2 | from collections import defaultdict
3 | from copy import copy
4 |
5 | class Dungeon:
6 | def __init__(self, layout):
7 | self.area = defaultdict(str)
8 | self.size_x = len(layout[2])+4
9 | self.size_y = len(layout)
10 | self.portals = defaultdict(list)
11 | self.portal_names = defaultdict(str)
12 | self.paths = defaultdict(list)
13 |
14 | for y in range(self.size_y):
15 | for x in range(self.size_x):
16 | if x < len(layout[y]):
17 | self.area[(x, y)] = layout[y][x]
18 |
19 | self.reduce_dungeon()
20 | self.find_portals()
21 | self.find_paths()
22 |
23 | def is_dead_end(self, pos, keys_allowed = False):
24 | tile = self.area[pos]
25 | if tile == "#" or tile == "@" or (not keys_allowed and tile.islower()):
26 | return False
27 |
28 | count = sum(self.area[(pos[0] + side[0], pos[1] + side[1])] != "#" for side in [(-1,0), (0,-1), (1,0), (0,1)])
29 |
30 | if count <= 1:
31 | return True
32 | return False
33 |
34 | def reduce_dungeon(self):
35 | reduced = True
36 | while reduced:
37 | reduced = False
38 | for y in range(1, self.size_y):
39 | for x in range(1, self.size_x):
40 | if self.is_dead_end((x, y)):
41 | self.area[(x, y)] = "#"
42 | reduced = True
43 |
44 | def find_portals(self):
45 | for y in range(self.size_y):
46 | for x in range(self.size_x):
47 | if self.area[(x, y)].isupper():
48 | if x > 10 and y > 10 and \
49 | x < self.size_x - 10 and y < self.size_y - 10:
50 | id = 1
51 | else:
52 | id = -1
53 |
54 | if self.area[(x+1, y)].isupper():
55 | name = self.area[(x, y)] + self.area[(x+1, y)]
56 | if self.area[(x+2, y)] == ".":
57 | self.portals[(name, id)].append((x+2, y))
58 | self.portal_names[(x+1, y)] = (name, id)
59 | elif self.area[(x-1, y)] == ".":
60 | self.portals[(name, id)].append((x-1, y))
61 | self.portal_names[(x, y)] = (name, id)
62 |
63 | elif self.area[(x, y+1)].isupper():
64 | name = self.area[(x, y)] + self.area[(x, y+1)]
65 | if self.area[(x, y+2)] == ".":
66 | self.portals[(name, id)].append((x, y+2))
67 | self.portal_names[(x, y+1)] = (name, id)
68 | elif self.area[(x, y-1)] == ".":
69 | self.portals[(name, id)].append((x, y-1))
70 | self.portal_names[(x, y)] = (name, id)
71 |
72 | def find_paths(self):
73 | for name, positions in self.portals.items():
74 | for pos in positions:
75 | next_search = defaultdict(int)
76 | next_search[pos] = 1
77 |
78 | changes = True
79 | steps = 0
80 |
81 | while changes or not steps:
82 | changes = False
83 | steps += 1
84 | search = copy(next_search)
85 |
86 | for position, state in search.items():
87 | if state == -1:
88 | del next_search[position]
89 | if state == 1:
90 | next_search[position] = -1
91 | for side in [(-1,0), (0,-1), (1,0), (0,1)]:
92 | side_pos = (position[0] + side[0], position[1] + side[1])
93 | if self.area[side_pos] != "#" and \
94 | not search[side_pos]:
95 | del search[side_pos]
96 | changes = True
97 | next_search[side_pos] = 1
98 | if self.area[side_pos].isupper():
99 | next_search[side_pos] = -1
100 | if steps > 1:
101 | target_name = self.portal_names[(side_pos)]
102 | self.paths[name].append((target_name, steps-1))
103 |
104 | def find_shortest_way(dungeon, path = [("AA", -1, 0)], best_steps = 1e8, steps = 0):
105 | depth = path[-1][2]
106 | if depth > 25:
107 | return 1e8
108 | if depth == -1:
109 | return steps
110 |
111 | for next_portal in sorted(dungeon.paths[(path[-1][0], path[-1][1])], key = lambda x: x[1], reverse = True):
112 | situation = (next_portal[0][0], next_portal[0][1], depth)
113 |
114 | if situation in path:
115 | continue
116 | if (situation[0] == "AA" or situation[0] == "ZZ") and situation[2] > 0:
117 | continue
118 | if (situation[0] != "AA" and situation[0] != "ZZ") and situation[1] == -1 and situation[2] == 0:
119 | continue
120 |
121 | next_steps = steps + next_portal[1] + 1
122 | if next_steps >= best_steps:
123 | continue
124 |
125 | if next_portal[0][1] == -1:
126 | id = 1
127 | else:
128 | id = -1
129 |
130 | next_path = path + [situation] + [(next_portal[0][0], id, depth + next_portal[0][1])]
131 | this_steps = find_shortest_way(dungeon, next_path, best_steps, next_steps)
132 |
133 | if this_steps < best_steps:
134 | best_steps = this_steps
135 |
136 | return best_steps
137 |
138 | @timer
139 | def solve():
140 | input = read_file("20", strip = False)
141 | dungeon = Dungeon(input)
142 | return find_shortest_way(dungeon) - 1
143 |
144 | result = solve()
145 | print(f"Solution: {result}")
--------------------------------------------------------------------------------
/2019_21_p1.py:
--------------------------------------------------------------------------------
1 | from aoc import read_file, timer
2 | from collections import defaultdict, deque
3 |
4 | class Game:
5 | def __init__(self, prog):
6 | self.prog = prog
7 | self.ip = 0
8 | self.input = deque()
9 | self.output = deque()
10 | self.rel_base = 0
11 | self.halt = False
12 |
13 | def split_instruction(instruction):
14 | instruction = f"{instruction:05}"
15 | return instruction[3:], instruction[0:3]
16 |
17 | def get_values(input, pos, op, modes, game):
18 | mode_a, mode_b, mode_c = modes
19 | values = []
20 | offset = 0
21 |
22 | if op in ["01", "02", "04", "05", "06", "07", "08", "09"]:
23 | if mode_c == "0":
24 | values.append(input[input[pos+1]])
25 | elif mode_c == "1":
26 | values.append(input[pos+1])
27 | elif mode_c == "2":
28 | values.append(input[input[pos+1]+game.rel_base])
29 |
30 | if op in ["01", "02", "05", "06", "07", "08"]:
31 | if mode_b == "0":
32 | values.append(input[input[pos+2]])
33 | elif mode_b == "1":
34 | values.append(input[pos+2])
35 | elif mode_b == "2":
36 | values.append(input[input[pos+2]+game.rel_base])
37 |
38 | if op in []:
39 | if mode_a == "0":
40 | values.append(input[input[pos+3]])
41 | elif mode_a == "1":
42 | values.append(input[pos+3])
43 | elif mode_a == "2":
44 | values.append(input[input[pos+3]+game.rel_base])
45 |
46 | if op in ["01", "02", "07", "08"]:
47 | if mode_a == "2":
48 | offset = game.rel_base
49 |
50 | if op in ["03"]:
51 | if mode_c == "2":
52 | offset = game.rel_base
53 |
54 | return values, offset
55 |
56 | def read_output(game):
57 | if len(game.output):
58 | return game.output.popleft()
59 | return None
60 |
61 | def run_game(game, input = []):
62 | if len(input):
63 | game.input.extend(input)
64 |
65 | while game.prog[game.ip] != 99:
66 | op, modes = split_instruction(game.prog[game.ip])
67 | values, offset = get_values(game.prog, game.ip, op, modes, game)
68 |
69 | if op == "01": # Addition
70 | game.prog[game.prog[game.ip+3] + offset] = values[0] + values[1]
71 | game.ip += 4
72 |
73 | if op == "02": # Multiplication
74 | game.prog[game.prog[game.ip+3] + offset] = values[0] * values[1]
75 | game.ip += 4
76 |
77 | if op == "03": # Read and Store input
78 | game.prog[game.prog[game.ip+1] + offset] = game.input.popleft()
79 | game.ip += 2
80 |
81 | if op == "04": # Print Output
82 | game.output.append(values[0])
83 | game.ip += 2
84 | return
85 |
86 | if op == "05": # Jump-if-True
87 | if values[0]:
88 | game.ip = values[1]
89 | else:
90 | game.ip += 3
91 |
92 | if op == "06": # Jump-if-False
93 | if not values[0]:
94 | game.ip = values[1]
95 | else:
96 | game.ip += 3
97 |
98 | if op == "07": # Less than
99 | if values[0] < values[1]:
100 | game.prog[game.prog[game.ip+3] + offset] = 1
101 | else:
102 | game.prog[game.prog[game.ip+3] + offset] = 0
103 | game.ip += 4
104 |
105 | if op == "08": # Equals
106 | if values[0] == values[1]:
107 | game.prog[game.prog[game.ip+3] + offset] = 1
108 | else:
109 | game.prog[game.prog[game.ip+3] + offset] = 0
110 | game.ip += 4
111 |
112 | if op == "09": # Adjust Relative Base
113 | game.rel_base += values[0]
114 | game.ip += 2
115 |
116 | game.halt = True
117 |
118 | def create_program(input):
119 | prog = defaultdict(int)
120 |
121 | for i in range(len(input)):
122 | prog[i] = int(input[i])
123 |
124 | return prog
125 |
126 | @timer
127 | def solve():
128 | input = read_file("21")[0].split(",")
129 | prog = create_program(input)
130 | game = Game(prog)
131 |
132 | output = 0
133 |
134 | while output != 10:
135 | run_game(game)
136 | if (output := read_output(game)):
137 | print(chr(output), end ="")
138 |
139 | instruction = "NOT C J\n" + \
140 | "NOT A T\n" + \
141 | "OR T J\n" + \
142 | "AND D J\n" + \
143 | "WALK\n"
144 |
145 | print(instruction)
146 | run_game(game, [ord(char) for char in instruction])
147 |
148 | while not game.halt:
149 | if (output := read_output(game)):
150 | if output > 255:
151 | return output
152 | else:
153 | print(chr(output), end ="")
154 | run_game(game)
155 |
156 | result = solve()
157 | print(f"Solution: {result}")
--------------------------------------------------------------------------------
/2019_21_p2.py:
--------------------------------------------------------------------------------
1 | from aoc import read_file, timer
2 | from collections import defaultdict, deque
3 |
4 | class Game:
5 | def __init__(self, prog):
6 | self.prog = prog
7 | self.ip = 0
8 | self.input = deque()
9 | self.output = deque()
10 | self.rel_base = 0
11 | self.halt = False
12 |
13 | def split_instruction(instruction):
14 | instruction = f"{instruction:05}"
15 | return instruction[3:], instruction[0:3]
16 |
17 | def get_values(input, pos, op, modes, game):
18 | mode_a, mode_b, mode_c = modes
19 | values = []
20 | offset = 0
21 |
22 | if op in ["01", "02", "04", "05", "06", "07", "08", "09"]:
23 | if mode_c == "0":
24 | values.append(input[input[pos+1]])
25 | elif mode_c == "1":
26 | values.append(input[pos+1])
27 | elif mode_c == "2":
28 | values.append(input[input[pos+1]+game.rel_base])
29 |
30 | if op in ["01", "02", "05", "06", "07", "08"]:
31 | if mode_b == "0":
32 | values.append(input[input[pos+2]])
33 | elif mode_b == "1":
34 | values.append(input[pos+2])
35 | elif mode_b == "2":
36 | values.append(input[input[pos+2]+game.rel_base])
37 |
38 | if op in []:
39 | if mode_a == "0":
40 | values.append(input[input[pos+3]])
41 | elif mode_a == "1":
42 | values.append(input[pos+3])
43 | elif mode_a == "2":
44 | values.append(input[input[pos+3]+game.rel_base])
45 |
46 | if op in ["01", "02", "07", "08"]:
47 | if mode_a == "2":
48 | offset = game.rel_base
49 |
50 | if op in ["03"]:
51 | if mode_c == "2":
52 | offset = game.rel_base
53 |
54 | return values, offset
55 |
56 | def read_output(game):
57 | if len(game.output):
58 | return game.output.popleft()
59 | return None
60 |
61 | def run_game(game, input = []):
62 | if len(input):
63 | game.input.extend(input)
64 |
65 | while game.prog[game.ip] != 99:
66 | op, modes = split_instruction(game.prog[game.ip])
67 | values, offset = get_values(game.prog, game.ip, op, modes, game)
68 |
69 | if op == "01": # Addition
70 | game.prog[game.prog[game.ip+3] + offset] = values[0] + values[1]
71 | game.ip += 4
72 |
73 | if op == "02": # Multiplication
74 | game.prog[game.prog[game.ip+3] + offset] = values[0] * values[1]
75 | game.ip += 4
76 |
77 | if op == "03": # Read and Store input
78 | game.prog[game.prog[game.ip+1] + offset] = game.input.popleft()
79 | game.ip += 2
80 |
81 | if op == "04": # Print Output
82 | game.output.append(values[0])
83 | game.ip += 2
84 | return
85 |
86 | if op == "05": # Jump-if-True
87 | if values[0]:
88 | game.ip = values[1]
89 | else:
90 | game.ip += 3
91 |
92 | if op == "06": # Jump-if-False
93 | if not values[0]:
94 | game.ip = values[1]
95 | else:
96 | game.ip += 3
97 |
98 | if op == "07": # Less than
99 | if values[0] < values[1]:
100 | game.prog[game.prog[game.ip+3] + offset] = 1
101 | else:
102 | game.prog[game.prog[game.ip+3] + offset] = 0
103 | game.ip += 4
104 |
105 | if op == "08": # Equals
106 | if values[0] == values[1]:
107 | game.prog[game.prog[game.ip+3] + offset] = 1
108 | else:
109 | game.prog[game.prog[game.ip+3] + offset] = 0
110 | game.ip += 4
111 |
112 | if op == "09": # Adjust Relative Base
113 | game.rel_base += values[0]
114 | game.ip += 2
115 |
116 | game.halt = True
117 |
118 | def create_program(input):
119 | prog = defaultdict(int)
120 |
121 | for i in range(len(input)):
122 | prog[i] = int(input[i])
123 |
124 | return prog
125 |
126 | @timer
127 | def solve():
128 | input = read_file("21")[0].split(",")
129 | prog = create_program(input)
130 | game = Game(prog)
131 |
132 | output = 0
133 |
134 | while output != 10:
135 | run_game(game)
136 | if (output := read_output(game)):
137 | print(chr(output), end ="")
138 |
139 | instruction = "NOT C J\n" + \
140 | "NOT B T\n" + \
141 | "OR T J\n" + \
142 | "NOT A T\n" + \
143 | "OR T J\n" + \
144 | "AND D J\n" + \
145 | "NOT E T\n" + \
146 | "NOT T T\n" + \
147 | "OR H T\n" + \
148 | "AND T J\n" + \
149 | "RUN\n"
150 |
151 | print(instruction)
152 | run_game(game, [ord(char) for char in instruction])
153 |
154 | while not game.halt:
155 | if (output := read_output(game)):
156 | if output > 255:
157 | return output
158 | else:
159 | print(chr(output), end ="")
160 | run_game(game)
161 |
162 | result = solve()
163 | print(f"Solution: {result}")
--------------------------------------------------------------------------------
/2019_22_p1.py:
--------------------------------------------------------------------------------
1 | from aoc import read_file, timer
2 |
3 | def cut(deck, amount):
4 | if amount > 0:
5 | return deck[amount:] + deck[:amount]
6 | elif amount < 0:
7 | return deck[len(deck)+amount:] + deck[:len(deck)+amount]
8 | return deck
9 |
10 | def deal_into_new_stack(deck):
11 | return deck[::-1]
12 |
13 | def deal_with_increment(deck, amount):
14 | new_deck = [0 for _ in range(len(deck))]
15 | for i in range(len(deck)):
16 | new_deck[(i*amount)%len(deck)] = deck[i]
17 | return new_deck
18 |
19 | @timer
20 | def solve():
21 | input = read_file("22")
22 | instructions = [line.split(" ") for line in input]
23 |
24 | deck = [num for num in range(10007)]
25 |
26 | for instruction in instructions:
27 | if instruction[-2] == "cut":
28 | deck = cut(deck, int(instruction[-1]))
29 |
30 | elif instruction[-1] == "stack":
31 | deck = deal_into_new_stack(deck)
32 |
33 | else:
34 | deck = deal_with_increment(deck, int(instruction[-1]))
35 |
36 | return deck.index(2019)
37 |
38 | result = solve()
39 | print(f"Solution: {result}")
--------------------------------------------------------------------------------
/2019_22_p2.py:
--------------------------------------------------------------------------------
1 | from aoc import read_file, timer
2 | from collections import deque
3 |
4 | def apply_operation(operations, addi, multi, size):
5 | operation = operations.popleft()
6 |
7 | if len(operations):
8 | addi, multi = apply_operation(operations, addi, multi, size)
9 |
10 | if operation[-2] == "cut":
11 | addi += int(operation[-1]) * multi
12 | elif operation[-1] == "stack":
13 | multi *= -1
14 | addi += multi
15 | else:
16 | multi *= pow(int(operation[-1]), -1, size)
17 |
18 | return addi, multi
19 |
20 | @timer
21 | def solve():
22 | input = read_file("22")
23 | operations = deque(reversed([line.split(" ") for line in input]))
24 |
25 | position = 2020
26 | size = 119315717514047
27 | iterations = 101741582076661
28 |
29 | addi, multi = apply_operation(operations, 0, 1, size)
30 |
31 | all_multi = pow(multi, iterations, size)
32 | all_addi = addi * (1 - pow(multi, iterations, size)) * pow(1 - multi, -1, size)
33 |
34 | return (position * all_multi + all_addi) % size
35 |
36 | result = solve()
37 | print(f"Solution: {result}")
--------------------------------------------------------------------------------
/2019_23_p1.py:
--------------------------------------------------------------------------------
1 | from aoc import read_file, timer
2 | from collections import defaultdict, deque
3 |
4 | class Game:
5 | def __init__(self, prog):
6 | self.prog = prog
7 | self.ip = 0
8 | self.input = deque()
9 | self.output = deque()
10 | self.rel_base = 0
11 | self.halt = False
12 |
13 | def split_instruction(instruction):
14 | instruction = f"{instruction:05}"
15 | return instruction[3:], instruction[0:3]
16 |
17 | def get_values(input, pos, op, modes, game):
18 | mode_a, mode_b, mode_c = modes
19 | values = []
20 | offset = 0
21 |
22 | if op in ["01", "02", "04", "05", "06", "07", "08", "09"]:
23 | if mode_c == "0":
24 | values.append(input[input[pos+1]])
25 | elif mode_c == "1":
26 | values.append(input[pos+1])
27 | elif mode_c == "2":
28 | values.append(input[input[pos+1]+game.rel_base])
29 |
30 | if op in ["01", "02", "05", "06", "07", "08"]:
31 | if mode_b == "0":
32 | values.append(input[input[pos+2]])
33 | elif mode_b == "1":
34 | values.append(input[pos+2])
35 | elif mode_b == "2":
36 | values.append(input[input[pos+2]+game.rel_base])
37 |
38 | if op in []:
39 | if mode_a == "0":
40 | values.append(input[input[pos+3]])
41 | elif mode_a == "1":
42 | values.append(input[pos+3])
43 | elif mode_a == "2":
44 | values.append(input[input[pos+3]+game.rel_base])
45 |
46 | if op in ["01", "02", "07", "08"]:
47 | if mode_a == "2":
48 | offset = game.rel_base
49 |
50 | if op in ["03"]:
51 | if mode_c == "2":
52 | offset = game.rel_base
53 |
54 | return values, offset
55 |
56 | def send_input(game, input):
57 | game.input.extend(input)
58 |
59 | def read_output(game):
60 | if len(game.output):
61 | return game.output.popleft()
62 | return None
63 |
64 | def run_game(game):
65 | if game.prog[game.ip] != 99:
66 | op, modes = split_instruction(game.prog[game.ip])
67 | values, offset = get_values(game.prog, game.ip, op, modes, game)
68 |
69 | if op == "01": # Addition
70 | game.prog[game.prog[game.ip+3] + offset] = values[0] + values[1]
71 | game.ip += 4
72 |
73 | if op == "02": # Multiplication
74 | game.prog[game.prog[game.ip+3] + offset] = values[0] * values[1]
75 | game.ip += 4
76 |
77 | if op == "03": # Read and Store input
78 | if len(game.input):
79 | game.prog[game.prog[game.ip+1] + offset] = game.input.popleft()
80 | else:
81 | game.prog[game.prog[game.ip+1] + offset] = -1
82 | game.ip += 2
83 |
84 | if op == "04": # Print Output
85 | game.output.append(values[0])
86 | game.ip += 2
87 | return
88 |
89 | if op == "05": # Jump-if-True
90 | if values[0]:
91 | game.ip = values[1]
92 | else:
93 | game.ip += 3
94 |
95 | if op == "06": # Jump-if-False
96 | if not values[0]:
97 | game.ip = values[1]
98 | else:
99 | game.ip += 3
100 |
101 | if op == "07": # Less than
102 | if values[0] < values[1]:
103 | game.prog[game.prog[game.ip+3] + offset] = 1
104 | else:
105 | game.prog[game.prog[game.ip+3] + offset] = 0
106 | game.ip += 4
107 |
108 | if op == "08": # Equals
109 | if values[0] == values[1]:
110 | game.prog[game.prog[game.ip+3] + offset] = 1
111 | else:
112 | game.prog[game.prog[game.ip+3] + offset] = 0
113 | game.ip += 4
114 |
115 | if op == "09": # Adjust Relative Base
116 | game.rel_base += values[0]
117 | game.ip += 2
118 | else:
119 | game.halt = True
120 |
121 | def create_program(input):
122 | prog = defaultdict(int)
123 |
124 | for i in range(len(input)):
125 | prog[i] = int(input[i])
126 |
127 | return prog
128 |
129 | def create_network(input):
130 | network, network_queue = [], []
131 |
132 | for network_address in range(50):
133 | prog = create_program(input)
134 | game = Game(prog)
135 | send_input(game, [network_address])
136 | network.append(game)
137 | network_queue.append([])
138 |
139 | return network, network_queue
140 |
141 | def find_first_nat_package(network, network_queue):
142 | while True:
143 | for network_address in range(50):
144 | run_game(network[network_address])
145 | if (output := read_output(network[network_address])):
146 | network_queue[network_address].append(output)
147 | if len(network_queue[network_address]) == 3:
148 | if network_queue[network_address][0] == 255:
149 | return network_queue[network_address]
150 | send_input(network[network_queue[network_address][0]], network_queue[network_address][1:])
151 | network_queue[network_address] = []
152 |
153 | @timer
154 | def solve():
155 | input = read_file("23")[0].split(",")
156 | network, network_queue = create_network(input)
157 |
158 | return find_first_nat_package(network, network_queue)[2]
159 |
160 | result = solve()
161 | print(f"Solution: {result}")
--------------------------------------------------------------------------------
/2019_23_p2.py:
--------------------------------------------------------------------------------
1 | from aoc import read_file, timer
2 | from collections import defaultdict, deque
3 |
4 | class Game:
5 | def __init__(self, prog):
6 | self.prog = prog
7 | self.ip = 0
8 | self.input = deque()
9 | self.output = deque()
10 | self.rel_base = 0
11 | self.halt = False
12 | self.is_waiting = False
13 |
14 | def split_instruction(instruction):
15 | instruction = f"{instruction:05}"
16 | return instruction[3:], instruction[0:3]
17 |
18 | def get_values(input, pos, op, modes, game):
19 | mode_a, mode_b, mode_c = modes
20 | values = []
21 | offset = 0
22 |
23 | if op in ["01", "02", "04", "05", "06", "07", "08", "09"]:
24 | if mode_c == "0":
25 | values.append(input[input[pos+1]])
26 | elif mode_c == "1":
27 | values.append(input[pos+1])
28 | elif mode_c == "2":
29 | values.append(input[input[pos+1]+game.rel_base])
30 |
31 | if op in ["01", "02", "05", "06", "07", "08"]:
32 | if mode_b == "0":
33 | values.append(input[input[pos+2]])
34 | elif mode_b == "1":
35 | values.append(input[pos+2])
36 | elif mode_b == "2":
37 | values.append(input[input[pos+2]+game.rel_base])
38 |
39 | if op in []:
40 | if mode_a == "0":
41 | values.append(input[input[pos+3]])
42 | elif mode_a == "1":
43 | values.append(input[pos+3])
44 | elif mode_a == "2":
45 | values.append(input[input[pos+3]+game.rel_base])
46 |
47 | if op in ["01", "02", "07", "08"]:
48 | if mode_a == "2":
49 | offset = game.rel_base
50 |
51 | if op in ["03"]:
52 | if mode_c == "2":
53 | offset = game.rel_base
54 |
55 | return values, offset
56 |
57 | def send_input(game, input):
58 | game.input.extend(input)
59 |
60 | def read_output(game):
61 | if len(game.output):
62 | return game.output.popleft()
63 | return None
64 |
65 | def run_game(game):
66 | if game.prog[game.ip] != 99:
67 | op, modes = split_instruction(game.prog[game.ip])
68 | values, offset = get_values(game.prog, game.ip, op, modes, game)
69 |
70 | if op == "01": # Addition
71 | game.prog[game.prog[game.ip+3] + offset] = values[0] + values[1]
72 | game.ip += 4
73 |
74 | if op == "02": # Multiplication
75 | game.prog[game.prog[game.ip+3] + offset] = values[0] * values[1]
76 | game.ip += 4
77 |
78 | if op == "03": # Read and Store input
79 | if len(game.input):
80 | game.prog[game.prog[game.ip+1] + offset] = game.input.popleft()
81 | game.is_waiting = False
82 | else:
83 | game.prog[game.prog[game.ip+1] + offset] = -1
84 | game.is_waiting = True
85 | game.ip += 2
86 |
87 | if op == "04": # Print Output
88 | game.output.append(values[0])
89 | game.ip += 2
90 | return
91 |
92 | if op == "05": # Jump-if-True
93 | if values[0]:
94 | game.ip = values[1]
95 | else:
96 | game.ip += 3
97 |
98 | if op == "06": # Jump-if-False
99 | if not values[0]:
100 | game.ip = values[1]
101 | else:
102 | game.ip += 3
103 |
104 | if op == "07": # Less than
105 | if values[0] < values[1]:
106 | game.prog[game.prog[game.ip+3] + offset] = 1
107 | else:
108 | game.prog[game.prog[game.ip+3] + offset] = 0
109 | game.ip += 4
110 |
111 | if op == "08": # Equals
112 | if values[0] == values[1]:
113 | game.prog[game.prog[game.ip+3] + offset] = 1
114 | else:
115 | game.prog[game.prog[game.ip+3] + offset] = 0
116 | game.ip += 4
117 |
118 | if op == "09": # Adjust Relative Base
119 | game.rel_base += values[0]
120 | game.ip += 2
121 | else:
122 | game.halt = True
123 |
124 | def create_program(input):
125 | prog = defaultdict(int)
126 |
127 | for i in range(len(input)):
128 | prog[i] = int(input[i])
129 |
130 | return prog
131 |
132 | def create_network(input):
133 | network, network_queue = [], []
134 |
135 | for network_address in range(50):
136 | prog = create_program(input)
137 | game = Game(prog)
138 | send_input(game, [network_address])
139 | network.append(game)
140 | network_queue.append([])
141 |
142 | return network, network_queue
143 |
144 | def is_idle(network, network_queue):
145 | for network_address in range(50):
146 | if len(network_queue[network_address]) or \
147 | not network[network_address].is_waiting:
148 | return False
149 | return True
150 |
151 | def find_double_nat_y(network, network_queue):
152 | nat, last_nat = [], None
153 |
154 | while True:
155 | for network_address in range(50):
156 | run_game(network[network_address])
157 | if (output := read_output(network[network_address])):
158 | network_queue[network_address].append(output)
159 | if len(network_queue[network_address]) == 3:
160 | if network_queue[network_address][0] == 255:
161 | nat = network_queue[network_address][1:]
162 | else:
163 | send_input(network[network_queue[network_address][0]], network_queue[network_address][1:])
164 | network_queue[network_address] = []
165 |
166 | if is_idle(network, network_queue) and len(nat):
167 | if nat[1] == last_nat:
168 | return last_nat
169 | send_input(network[0], nat)
170 | last_nat, nat = nat[1], []
171 |
172 | @timer
173 | def solve():
174 | input = read_file("23")[0].split(",")
175 | network, network_queue = create_network(input)
176 | return find_double_nat_y(network, network_queue)
177 |
178 | result = solve()
179 | print(f"Solution: {result}")
--------------------------------------------------------------------------------
/2019_24_p1.py:
--------------------------------------------------------------------------------
1 | from aoc import read_file, timer
2 |
3 | def num_neighbors(field, pos):
4 | count = 0
5 | for side in [(0, -1), (-1, 0), (0, 1), (1, 0)]:
6 | x, y = pos[1] + side[1], pos[0] + side[0]
7 | if x >= 0 and x <= 4 and \
8 | y >= 0 and y <= 4 and \
9 | field[y][x] == "#":
10 | count += 1
11 | return count
12 |
13 | def evolve(field):
14 | new_field = [["." for x in range(5)] for y in range(5)]
15 |
16 | for y in range(5):
17 | for x in range(5):
18 | if field[y][x] == "#":
19 | if num_neighbors(field, (y, x)) == 1:
20 | new_field[y][x] = "#"
21 | elif num_neighbors(field, (y, x)) == 1 or \
22 | num_neighbors(field, (y, x)) == 2:
23 | new_field[y][x] = "#"
24 |
25 | return new_field
26 |
27 | def bio_diversity(field):
28 | result, multi = 0, 1
29 |
30 | for y in range(5):
31 | for x in range(5):
32 | if field[y][x] == "#":
33 | result += multi
34 | multi *= 2
35 | return result
36 |
37 | @timer
38 | def solve():
39 | field = read_file("24")
40 | known = set()
41 |
42 | while (bio := bio_diversity(field)) not in known:
43 | known.add(bio)
44 | field = evolve(field)
45 |
46 | return bio
47 |
48 | result = solve()
49 | print(f"Solution: {result}")
--------------------------------------------------------------------------------
/2019_24_p2.py:
--------------------------------------------------------------------------------
1 | from aoc import read_file, timer
2 |
3 | def num_below(field, level, pos):
4 | if pos[0] == 1:
5 | return sum(field[level-1][0][x] == "#" for x in range(5))
6 |
7 | if pos[0] == 3:
8 | return sum(field[level-1][4][x] == "#" for x in range(5))
9 |
10 | if pos[1] == 1:
11 | return sum(field[level-1][y][0] == "#" for y in range(5))
12 |
13 | if pos[1] == 3:
14 | return sum(field[level-1][y][4] == "#" for y in range(5))
15 |
16 |
17 |
18 | def num_neighbors(field, level, pos):
19 | count = 0
20 |
21 | for side in [(0, -1), (-1, 0), (0, 1), (1, 0)]:
22 | new_pos = (pos[0] + side[0], pos[1] + side[1])
23 |
24 | if new_pos[0] == 2 and new_pos[1] == 2:
25 | count += num_below(field, level, pos)
26 |
27 | elif new_pos[0] == -1:
28 | if field[level+1][1][2] == "#":
29 | count += 1
30 |
31 | elif new_pos[0] == 5:
32 | if field[level+1][3][2] == "#":
33 | count += 1
34 |
35 | elif new_pos[1] == -1:
36 | if field[level+1][2][1] == "#":
37 | count += 1
38 |
39 | elif new_pos[1] == 5:
40 | if field[level+1][2][3] == "#":
41 | count += 1
42 |
43 | elif field[level][new_pos[0]][new_pos[1]] == "#":
44 | count += 1
45 |
46 | return count
47 |
48 | def evolve(field, iterations):
49 | new_field = [[["." for x in range(5)] for y in range(5)] for z in range(iterations*2+3)]
50 |
51 | for level in range(iterations*2+1):
52 | for y in range(5):
53 | for x in range(5):
54 | if x == 2 and y == 2:
55 | continue
56 |
57 | if field[level][y][x] == "#":
58 | if num_neighbors(field, level, (y, x)) == 1:
59 | new_field[level][y][x] = "#"
60 |
61 | else:
62 | if num_neighbors(field, level, (y, x)) == 1 or num_neighbors(field, level, (y, x)) == 2:
63 | new_field[level][y][x] = "#"
64 |
65 | return new_field
66 |
67 | @timer
68 | def solve():
69 | input = read_file("24")
70 | iterations = 200
71 | field = [[["." for x in range(5)] for y in range(5)] for z in range(iterations*2+3)]
72 |
73 | for y in range(5):
74 | for x in range(5):
75 | field[iterations+1][y][x] = input[y][x]
76 |
77 | for step in range(iterations):
78 | field = evolve(field, iterations)
79 |
80 | return sum(field[level][y][x] == "#" for level in range(iterations*2+3) for y in range(5) for x in range(5))
81 |
82 | result = solve()
83 | print(f"Solution: {result}")
--------------------------------------------------------------------------------
/2019_25_p1.py:
--------------------------------------------------------------------------------
1 | from aoc import read_file, timer
2 | from collections import defaultdict, deque
3 |
4 | class Game:
5 | def __init__(self, prog):
6 | self.prog = prog
7 | self.ip = 0
8 | self.input = deque()
9 | self.output = deque()
10 | self.rel_base = 0
11 | self.halt = False
12 |
13 | def split_instruction(instruction):
14 | instruction = f"{instruction:05}"
15 | return instruction[3:], instruction[0:3]
16 |
17 | def get_values(input, pos, op, modes, game):
18 | mode_a, mode_b, mode_c = modes
19 | values = []
20 | offset = 0
21 |
22 | if op in ["01", "02", "04", "05", "06", "07", "08", "09"]:
23 | if mode_c == "0":
24 | values.append(input[input[pos+1]])
25 | elif mode_c == "1":
26 | values.append(input[pos+1])
27 | elif mode_c == "2":
28 | values.append(input[input[pos+1]+game.rel_base])
29 |
30 | if op in ["01", "02", "05", "06", "07", "08"]:
31 | if mode_b == "0":
32 | values.append(input[input[pos+2]])
33 | elif mode_b == "1":
34 | values.append(input[pos+2])
35 | elif mode_b == "2":
36 | values.append(input[input[pos+2]+game.rel_base])
37 |
38 | if op in []:
39 | if mode_a == "0":
40 | values.append(input[input[pos+3]])
41 | elif mode_a == "1":
42 | values.append(input[pos+3])
43 | elif mode_a == "2":
44 | values.append(input[input[pos+3]+game.rel_base])
45 |
46 | if op in ["01", "02", "07", "08"]:
47 | if mode_a == "2":
48 | offset = game.rel_base
49 |
50 | if op in ["03"]:
51 | if mode_c == "2":
52 | offset = game.rel_base
53 |
54 | return values, offset
55 |
56 | def send_input(game, input):
57 | game.input.extend(input)
58 |
59 | def read_output(game):
60 | if len(game.output):
61 | return game.output.popleft()
62 | return None
63 |
64 | def run_game(game):
65 | if game.prog[game.ip] != 99:
66 | op, modes = split_instruction(game.prog[game.ip])
67 | values, offset = get_values(game.prog, game.ip, op, modes, game)
68 |
69 | if op == "01": # Addition
70 | game.prog[game.prog[game.ip+3] + offset] = values[0] + values[1]
71 | game.ip += 4
72 |
73 | if op == "02": # Multiplication
74 | game.prog[game.prog[game.ip+3] + offset] = values[0] * values[1]
75 | game.ip += 4
76 |
77 | if op == "03": # Read and Store input
78 | if len(game.input):
79 | game.prog[game.prog[game.ip+1] + offset] = game.input.popleft()
80 | else:
81 | game.prog[game.prog[game.ip+1] + offset] = -1
82 | game.ip += 2
83 |
84 | if op == "04": # Print Output
85 | game.output.append(values[0])
86 | game.ip += 2
87 | return
88 |
89 | if op == "05": # Jump-if-True
90 | if values[0]:
91 | game.ip = values[1]
92 | else:
93 | game.ip += 3
94 |
95 | if op == "06": # Jump-if-False
96 | if not values[0]:
97 | game.ip = values[1]
98 | else:
99 | game.ip += 3
100 |
101 | if op == "07": # Less than
102 | if values[0] < values[1]:
103 | game.prog[game.prog[game.ip+3] + offset] = 1
104 | else:
105 | game.prog[game.prog[game.ip+3] + offset] = 0
106 | game.ip += 4
107 |
108 | if op == "08": # Equals
109 | if values[0] == values[1]:
110 | game.prog[game.prog[game.ip+3] + offset] = 1
111 | else:
112 | game.prog[game.prog[game.ip+3] + offset] = 0
113 | game.ip += 4
114 |
115 | if op == "09": # Adjust Relative Base
116 | game.rel_base += values[0]
117 | game.ip += 2
118 | else:
119 | game.halt = True
120 |
121 | def create_program(input):
122 | prog = defaultdict(int)
123 |
124 | for i in range(len(input)):
125 | prog[i] = int(input[i])
126 |
127 | return prog
128 |
129 | def read_prompt(game, fin = False):
130 | output = None
131 |
132 | while output != ord("?"):
133 | while not (output := read_output(game)):
134 | if game.halt:
135 | return
136 | run_game(game)
137 |
138 | print(chr(output), end = "")
139 |
140 |
141 | @timer
142 | def solve():
143 | input = read_file("25")[0].split(",")
144 | prog = create_program(input)
145 | game = Game(prog)
146 |
147 | instructions = []
148 | instructions.append("south\n")
149 | instructions.append("take mouse\n")
150 | instructions.append("north\n")
151 | instructions.append("west\n")
152 | instructions.append("north\n")
153 | instructions.append("north\n")
154 | instructions.append("")
155 | instructions.append("west\n")
156 | instructions.append("take semiconductor\n")
157 | instructions.append("east\n")
158 | instructions.append("")
159 | instructions.append("south\n")
160 | instructions.append("west\n")
161 | instructions.append("south\n")
162 | instructions.append("take hypercube\n")
163 | instructions.append("north\n")
164 | instructions.append("east\n")
165 | instructions.append("south\n")
166 | instructions.append("west\n")
167 | instructions.append("take antenna\n")
168 | instructions.append("west\n")
169 | instructions.append("south\n")
170 | instructions.append("south\n")
171 | instructions.append("south\n")
172 |
173 | for instruction in instructions:
174 | read_prompt(game)
175 | send_input(game, [ord(c) for c in instruction])
176 | print(f"\n\n{instruction}")
177 |
178 | read_prompt(game, fin = True)
179 |
180 | solve()
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2019 Thomas Kerbl
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Advent of Code 2019
2 | My [Advent of Code](https://adventofcode.com/2019) (Season 2019) solutions written in Python 3.8. I will highlight some of the interesting Python mechanisms I am using on Twitter, so feel free to [follow me](https://twitter.com/Dementophobia).
3 |
4 | | Puzzle | Solution | Tips & Tricks |
5 | | ---------------------------------------------- | ------------------------------------------------------------ | ------------------------------------------------------------ |
6 | | [Day 1](https://adventofcode.com/2019/day/1) | [Part 1](./2019_01_p1.py) and [Part 2](./2019_01_p2.py) | [Assignment Expressions](https://twitter.com/Dementophobia/status/1201027632349040640) |
7 | | [Day 2](https://adventofcode.com/2019/day/2) | [Part 1](./2019_02_p1.py) and [Part 2](./2019_02_p2.py) | [Slicing Notation](https://twitter.com/Dementophobia/status/1201382006774468608) |
8 | | [Day 3](https://adventofcode.com/2019/day/3) | [Part 1](./2019_03_p1.py) and [Part 2](./2019_03_p2.py) | [List Comprehension](https://twitter.com/Dementophobia/status/1201749348083781632) |
9 | | [Day 4](https://adventofcode.com/2019/day/4) | [Part 1](./2019_04_p1.py) and [Part 2](./2019_04_p2.py) | [Short Circuiting](https://twitter.com/Dementophobia/status/1202108904429309952) |
10 | | [Day 5](https://adventofcode.com/2019/day/5) | [Part 1](./2019_05_p1.py) and [Part 2](./2019_05_p2.py) | [Zero Padding with F-Strings](https://twitter.com/Dementophobia/status/1202487898194546689) |
11 | | [Day 6](https://adventofcode.com/2019/day/6) | [Part 1](./2019_06_p1.py) and [Part 2](./2019_06_p2.py) | [Intersection of Sets](https://twitter.com/Dementophobia/status/1202848979328876546) |
12 | | [Day 7](https://adventofcode.com/2019/day/7) | [Part 1](./2019_07_p1.py) and [Part 2](./2019_07_p2.py) | [Permutations from Itertools](https://twitter.com/Dementophobia/status/1203214917697970178) |
13 | | [Day 8](https://adventofcode.com/2019/day/8) | [Part 1](./2019_08_p1.py) and [Part 2](./2019_08_p2.py)
Bonus: [Animated Solution](./extras/README.md#day-8---animation-using-python-and-gimp) | [Avoiding Line Breaks with Print](https://twitter.com/Dementophobia/status/1203560697940119553) |
14 | | [Day 9](https://adventofcode.com/2019/day/9) | [Part 1](./2019_09_p1.py) and [Part 2](./2019_09_p2.py) | [Defaultdict from Collections](https://twitter.com/Dementophobia/status/1203932274280022017) |
15 | | [Day 10](https://adventofcode.com/2019/day/10) | [Part 1](./2019_10_p1.py) and [Part 2](./2019_10_p2.py) | [Sorting with Function as Key](https://twitter.com/Dementophobia/status/1204468347917783042) |
16 | | [Day 11](https://adventofcode.com/2019/day/11) | [Part 1](./2019_11_p1.py) and [Part 2](./2019_11_p2.py)
Bonus: [Animated Solution](./extras/README.md#day-11---another-animation-using-python-and-gimp) | [Using Modulo for Turning](https://twitter.com/Dementophobia/status/1204657448361086976) |
17 | | [Day 12](https://adventofcode.com/2019/day/12) | [Part 1](./2019_12_p1.py) and [Part 2](./2019_12_p2.py) | [Regex based Integer Extractor](https://twitter.com/Dementophobia/status/1205239170219814934) |
18 | | [Day 13](https://adventofcode.com/2019/day/13) | [Part 1](./2019_13_p1.py) and [Part 2](./2019_13_p2.py) | - |
19 | | [Day 14](https://adventofcode.com/2019/day/14) | [Part 1](./2019_14_p1.py) and [Part 2](./2019_14_p2.py) | - |
20 | | [Day 15](https://adventofcode.com/2019/day/15) | [Part 1](./2019_15_p1.py) and [Part 2](./2019_15_p2.py)
Bonus: [Animated Solution](./extras/README.md#day-15---flooding-the-area-with-oxygen) | [Calling Function from List](https://twitter.com/Dementophobia/status/1206182646289780742) |
21 | | [Day 16](https://adventofcode.com/2019/day/16) | [Part 1](./2019_16_p1.py) and [Part 2](./2019_16_p2.py) | [Using Deques for Shifting](https://twitter.com/Dementophobia/status/1206619272946036736) |
22 | | [Day 17](https://adventofcode.com/2019/day/17) | [Part 1](./2019_17_p1.py) and [Part 2](./2019_17_p2.py) | - |
23 | | [Day 18](https://adventofcode.com/2019/day/18) | [Part 1](./2019_18_p1.py) and [Part 2](./2019_18_p2.py) | - |
24 | | [Day 19](https://adventofcode.com/2019/day/19) | [Part 1](./2019_19_p1.py) and [Part 2](./2019_19_p2.py) | - |
25 | | [Day 20](https://adventofcode.com/2019/day/20) | [Part 1](./2019_20_p1.py) and [Part 2](./2019_20_p2.py) | - |
26 | | [Day 21](https://adventofcode.com/2019/day/21) | [Part 1](./2019_21_p1.py) and [Part 2](./2019_21_p2.py) | - |
27 | | [Day 22](https://adventofcode.com/2019/day/22) | [Part 1](./2019_22_p1.py) and [Part 2](./2019_22_p2.py) | [Calculating the Modular Multiplicative Inverse](https://twitter.com/Dementophobia/status/1208778678710222848) |
28 | | [Day 23](https://adventofcode.com/2019/day/23) | [Part 1](./2019_23_p1.py) and [Part 2](./2019_23_p2.py)
Bonus: [Animated Solution](./extras/README.md#day-23---network-analysis) | - |
29 | | [Day 24](https://adventofcode.com/2019/day/24) | [Part 1](./2019_24_p1.py) and [Part 2](./2019_24_p2.py) | [Counting with List Comprehensions](https://twitter.com/Dementophobia/status/1209368903253708800) |
30 | | [Day 25](https://adventofcode.com/2019/day/25) | [Part 1](./2019_25_p1.py) | - |
31 |
32 |
--------------------------------------------------------------------------------
/aoc.py:
--------------------------------------------------------------------------------
1 | import time
2 |
3 | def read_file(name, strip = True):
4 | with open("files/input" + name) as f:
5 | content = f.readlines()
6 | if strip:
7 | return [x.strip() for x in content]
8 | return content
9 |
10 | def timer(func):
11 | def wrapper(*args, **kwargs):
12 | start_time = time.time()
13 | result = func(*args, **kwargs)
14 | print(f"\nTime required: {(time.time() - start_time)*1000:.2f} ms\n")
15 | return result
16 | return wrapper
--------------------------------------------------------------------------------
/extras/README.md:
--------------------------------------------------------------------------------
1 | # Advent of Code 2019 Extras
2 | This space highlight some of the more creative solutions I designed on top of the straight forward solutions in the [main section](../README.md), including additional information and showcases.
3 |
4 | ## Day 8 - Animation using Python and Gimp
5 |
6 | Part 2 of day 8 required mashing together a bunch of layers to get to the final solution. While this can be done in code alone, I decided to solve the puzzle with an animated GIF. My [python script](../2019_08_animation.py) creates a transparent image for each layer and saves it to the disk (in ./images/, which has to be created manually in advance, if you want to try it yourself).
7 |
8 | Afterwards, all you have to do is import the result in Gimp, tinker with the background and the timing as you please, and save it as animated GIF.
9 |
10 | Here is the result for my input:
11 |
12 |