2022 Advent of Code (Python)

By telleropnul, April 26, 2023

Advent of Code is an Advent calendar of small programming puzzles for a variety of skill sets and skill levels that can be solved in any programming language you like. Go check it out: Advent of Code

Input files: adventofcode2022inputs.zip

2022 Day 25

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
DIGITS = {"2": 2, "1": 1, "0": 0, "-": -1, "=": -2}
DIGITS_R = {v: k for k, v in DIGITS.items()}
 
total = 0
 
data = open("input", "r", encoding="utf-8").read().splitlines()
 
for num in data:
    for i, digit in enumerate(num.strip()[::-1]):
        total += DIGITS[digit] * 5**i
 
ans = ""
 
while total > 0:
    total, digit = divmod(total, 5)
    if digit > 2:
        digit -= 5
        total += 1
    ans += DIGITS_R[digit]
 
print (ans[::-1])

2022 Day 24 Part 01 + 02

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
from collections import deque
 
DIRECTIONS = [(1, 0, 0), (1, -1, 0), (1, 1, 0), (1, 0, -1), (1, 0, 1)]
 
 
def add_tuple(a, b):
    return tuple(sum(p) for p in zip(a, b))
 
 
def has_blizzard(grid, N, M, t, i, j):
    if (i, j) in ((-1, 0), (N, M - 1)):
        return False
    try:
        return (
            grid[(i - t) % N, j] == "v"
            or grid[(i + t) % N, j] == "^"
            or grid[i, (j - t) % M] == ">"
            or grid[i, (j + t) % M] == "<"
        )
    except KeyError:
        return True
 
 
def shortest(grid, N, M, start, end, start_t=0):
    visited = set()
    bfs = deque([(start_t, *start)])
 
    while len(bfs) > 0:
        t, i, j = p = bfs.popleft()
        if p in visited:
            continue
        visited.add(p)
 
        if (i, j) == end:
            return t
 
        for d in DIRECTIONS:
            x = add_tuple(p, d)
            if not has_blizzard(grid, N, M, *x):
                bfs.append(x)
 
 
'''PART 01'''
data = open("input", "r", encoding="utf-8").read()
lines = [row[1:-1] for row in data.splitlines()[1:-1]]
grid = {(i, j): cell for i, row in enumerate(lines) for j, cell in enumerate(row)}
N, M = len(lines), len(lines[0])
grid[-1, 0] = grid[N, M - 1] = "."
print (shortest(grid, N, M, (-1, 0), (N, M - 1)))
 
 
'''PART 02'''
data = open("input", "r", encoding="utf-8").read()
lines = [row[1:-1] for row in data.splitlines()[1:-1]]
#lines = [row[1:-1] for row in f.read().splitlines()[1:-1]]
grid = {(i, j): cell for i, row in enumerate(lines) for j, cell in enumerate(row)}
N, M = len(lines), len(lines[0])
grid[-1, 0] = grid[N, M - 1] = "."
 
t1 = shortest(grid, N, M, (-1, 0), (N, M - 1))
t2 = shortest(grid, N, M, (N, M - 1), (-1, 0), t1)
print (shortest(grid, N, M, (-1, 0), (N, M - 1), t2))

2022 Day 23 Part 01 + 02

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
from collections import defaultdict
 
data = open("input", "r", encoding="utf-8").read().splitlines()
 
E = set()
for r,row in enumerate(data):
    for c,ch in enumerate(row):
        if ch=='#':
            E.add((r,c))
 
def show(E):
    r1 = min(r for (r,c) in E)
    r2 = max(r for (r,c) in E)
    c1 = min(c for (r,c) in E)
    c2 = max(c for (r,c) in E)
    for r in range(r1,r2+1):
        row = ''
        for c in range(c1,c2+1):
            row += ('#' if (r,c) in E else '.')
        print(row)
    print('='*80)
 
dir_list = ['N', 'S', 'W', 'E']
for t in range(10000):
    any_moved = False
    # P[(r,c)] is the list of elves who want to move to (r,c)
    P = defaultdict(list)
    for (r,c) in E:
        # if you don't have any neighbor, stay put
        has_nbr = False
        for dr in [-1,0,1]:
            for dc in [-1,0,1]:
                if (dr!=0 or dc!=0) and (r+dr, c+dc) in E:
                    has_nbr = True
        if not has_nbr:
            continue
 
        moved = False
        for dir_ in dir_list:
            if dir_=='N' and (not moved) and (r-1,c) not in E and (r-1,c-1) not in E and (r-1,c+1) not in E:
                P[(r-1,c)].append((r,c))
                moved = True
            elif dir_=='S' and (not moved) and (r+1,c) not in E and (r+1, c-1) not in E and (r+1, c+1) not in E:
                P[(r+1,c)].append((r,c))
                moved = True
            elif dir_=='W' and (not moved) and (r, c-1) not in E and (r-1,c-1) not in E and (r+1,c-1) not in E:
                P[(r,c-1)].append((r,c))
                moved = True
            elif dir_=='E' and (not moved) and (r, c+1) not in E and (r-1,c+1) not in E and (r+1,c+1) not in E:
                P[(r,c+1)].append((r,c))
                moved = True
 
    dir_list = dir_list[1:]+[dir_list[0]]
    for k,vs in P.items():
        if len(vs) == 1:
            any_moved = True
            E.discard(vs[0])
            E.add(k)
 
    if not any_moved:
        print(t+1)
        break
    if t==9:
        r1 = min(r for (r,c) in E)
        r2 = max(r for (r,c) in E)
        c1 = min(c for (r,c) in E)
        c2 = max(c for (r,c) in E)
        ans = 0
        for r in range(r1,r2+1):
            for c in range(c1,c2+1):
                if (r,c) not in E:
                    ans += 1
        print(ans)

2022 Day 22 Part 01 + 02

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
import re
 
DIRECTIONS = [(0, 1), (1, 0), (0, -1), (-1, 0)]
 
 
def add_tuple(a, b):
    return tuple(x + y for x, y in zip(a, b))
 
 
def move1(grid, d, i, j):
    ni, nj = i, j
 
    while True:
        ni, nj = add_tuple((ni, nj), DIRECTIONS[d])
        ni %= 200
        nj %= 150
        if (ni, nj) in grid:
            break
 
    if grid[ni, nj] == "#":
        return i, j
 
    return ni, nj
 
 
def move2(grid, d, i, j):
    nd, ni, nj = d, *add_tuple((i, j), DIRECTIONS[d])
 
    match nd, ni, nj:
        case 0, _, 150 if ni in range(50):
            nd, ni, nj = 2, 149 - ni, 99
        case 0, _, 100 if ni in range(50, 100):
            nd, ni, nj = 3, 49, 50 + ni
        case 0, _, 100 if ni in range(100, 150):
            nd, ni, nj = 2, 149 - ni, 149
        case 0, _, 50 if ni in range(150, 200):
            nd, ni, nj = 3, 149, ni - 100
 
        case 1, 200, _ if nj in range(50):
            nd, ni, nj = 1, 0, nj + 100
        case 1, 150, _ if nj in range(50, 100):
            nd, ni, nj = 2, nj + 100, 49
        case 1, 50, _ if nj in range(100, 150):
            nd, ni, nj = 2, nj - 50, 99
 
        case 2, _, 49 if ni in range(0, 50):
            nd, ni, nj = 0, 149 - ni, 0
        case 2, _, 49 if ni in range(50, 100):
            nd, ni, nj = 1, 100, ni - 50
        case 2, _, -1 if ni in range(100, 150):
            nd, ni, nj = 0, 149 - ni, 50
        case 2, _, -1 if ni in range(150, 200):
            nd, ni, nj = 1, 0, ni - 100
 
        case 3, 99, _ if nj in range(50):
            nd, ni, nj = 0, 50 + nj, 50
        case 3, -1, _ if nj in range(50, 100):
            nd, ni, nj = 0, nj + 100, 0
        case 3, -1, _ if nj in range(100, 150):
            nd, ni, nj = 3, 199, nj - 100
 
    if grid[ni, nj] == ".":
        return nd, ni, nj
    elif grid[ni, nj] == "#":
        return d, i, j
 
 
'''PART 01'''
grid, instructions = open("input", "r", encoding="utf-8").read().split("\n\n")
grid = grid.splitlines()
grid = {(i, j): cell for i, row in enumerate(grid) for j, cell in enumerate(row) if cell != " "}
 
instructions = re.split(r"(?<=\d)(?=[LR])|(?<=[LR])(?=\d)", instructions)
d, i, j = 0, 0, next(j for j in range(150) if (0, j) in grid)
 
for c in instructions:
    match c:
        case "L":
            d -= 1
            d %= 4
        case "R":
            d += 1
            d %= 4
        case _:
            for _ in range(int(c)):
                i, j = move1(grid, d, i, j)
 
print ((i + 1) * 1000 + (j + 1) * 4 + d)
 
'''PART 02'''
grid, instructions = open("input", "r", encoding="utf-8").read().split("\n\n")
grid = grid.splitlines()
grid = {(i, j): cell for i, row in enumerate(grid) for j, cell in enumerate(row) if cell != " "}
 
instructions = re.split(r"(?<=\d)(?=[LR])|(?<=[LR])(?=\d)", instructions)
d, i, j = 0, 0, next(j for j in range(150) if (0, j) in grid)
 
for c in instructions:
    match c:
        case "L":
            d -= 1
            d %= 4
        case "R":
            d += 1
            d %= 4
        case _:
            for _ in range(int(c)):
                d, i, j = move2(grid, d, i, j)
 
print ((i + 1) * 1000 + (j + 1) * 4 + d)

2022 Day 21 Part 02

cmd
cd %LOCALAPPDATA%\Programs\Python\Python311\
(optionally add this to %PATH% environment variable.)	 
python --version	 
python -m pip --version
python -m pip install -U sympy

Learn sympy

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
import sympy
 
monkeys = { "humn": sympy.Symbol("x") }
 
data = open("input", "r", encoding="utf-8").read().splitlines()
x = [line.strip() for line in data]
 
ops = {
    "+": lambda x, y: x + y,
    "-": lambda x, y: x - y,
    "*": lambda x, y: x * y,
    "/": lambda x, y: x / y,
}
 
for a in x:
    name, expr = a.split(": ")
    if name in monkeys: continue
    if expr.isdigit():
        monkeys[name] = sympy.Integer(expr)
    else:
        left, op, right = expr.split()
        if left in monkeys and right in monkeys:
            if name == "root":
                print(sympy.solve(monkeys[left] - monkeys[right])[0])
                break
            monkeys[name] = ops[op](monkeys[left], monkeys[right])
        else:
            x.append(a)

2022 Day 21 Part 01

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
monkeys = {}
 
data = open("input", "r", encoding="utf-8").read().splitlines()
x = [line.strip() for line in data]
 
for a in x:
    name, expr = a.split(": ")
    if expr.isdigit():
        monkeys[name] = int(expr)
    else:
        left, op, right = expr.split()
        if left in monkeys and right in monkeys:
            monkeys[name] = eval(f"{monkeys[left]} {op} {monkeys[right]}")
        else:
            x.append(a)
 
print(monkeys["root"])

2022 Day 20 Part 01 + 02

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
from collections import deque
 
lines = open("input").read().splitlines()
 
def solve(part):
    X = [int(x) for x in lines]
    if part == 2:
        X = [x*811589153 for x in X]
    X = deque(list(enumerate(X)))
    for t in range(10 if part==2 else 1):
        for i in range(len(X)):
            for j in range(len(X)):
                if X[j][0]==i:
                    break
            while X[0][0]!=i:
                X.append(X.popleft())
            val = X.popleft()
            to_pop = val[1]
            to_pop %= len(X)
            assert 0<=to_pop < len(X)
 
            for _ in range(to_pop):
                X.append(X.popleft())
            X.append(val)
    for j in range(len(X)):
        if X[j][1] == 0:
            break
    return (X[(j+1000)%len(X)][1] + X[(j+2000)%len(X)][1] + X[(j+3000)%len(X)][1])
 
print(solve(1))
print(solve(2))

2022 Day 19 Part 02

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
import re
 
def dfs(bp, maxspend, cache, time, bots, amt):
    if time == 0:
        return amt[3]
 
    key = tuple([time, *bots, *amt])
    if key in cache:
        return cache[key]
 
    maxval = amt[3] + bots[3] * time
 
    for btype, recipe in enumerate(bp):
        if btype != 3 and bots[btype] >= maxspend[btype]:
            continue
 
        wait = 0
        for ramt, rtype in recipe:
            if bots[rtype] == 0:
                break
            wait = max(wait, -(-(ramt - amt[rtype]) // bots[rtype]))
        else:
            remtime = time - wait - 1
            if remtime <= 0:
                continue
            bots_ = bots[:]
            amt_ = [x + y * (wait + 1) for x, y in zip(amt, bots)]
            for ramt, rtype in recipe:
                amt_[rtype] -= ramt
            bots_[btype] += 1
            for i in range(3):
                amt_[i] = min(amt_[i], maxspend[i] * remtime)
            maxval = max(maxval, dfs(bp, maxspend, cache, remtime, bots_, amt_))
 
    cache[key] = maxval
    return maxval
 
total = 1
 
data = open("input", "r", encoding="utf-8").read().splitlines()
for line in list(data)[:3]:
    bp = []
    maxspend = [0, 0, 0]
    for section in line.split(": ")[1].split(". "):
        recipe = []
        for x, y in re.findall(r"(\d+) (\w+)", section):
            x = int(x)
            y = ["ore", "clay", "obsidian"].index(y)
            recipe.append((x, y))
            maxspend[y] = max(maxspend[y], x)
        bp.append(recipe)
    v = dfs(bp, maxspend, {}, 32, [1, 0, 0, 0], [0, 0, 0, 0])
    total *= v
 
print(total)

2022 Day 19 Part 01

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
import re
 
def dfs(bp, maxspend, cache, time, bots, amt):
    if time == 0:
        return amt[3]
 
    key = tuple([time, *bots, *amt])
    if key in cache:
        return cache[key]
 
    maxval = amt[3] + bots[3] * time
 
    for btype, recipe in enumerate(bp):
        if btype != 3 and bots[btype] >= maxspend[btype]:
            continue
 
        wait = 0
        for ramt, rtype in recipe:
            if bots[rtype] == 0:
                break
            wait = max(wait, -(-(ramt - amt[rtype]) // bots[rtype]))
        else:
            remtime = time - wait - 1
            if remtime <= 0:
                continue
            bots_ = bots[:]
            amt_ = [x + y * (wait + 1) for x, y in zip(amt, bots)]
            for ramt, rtype in recipe:
                amt_[rtype] -= ramt
            bots_[btype] += 1
            for i in range(3):
                amt_[i] = min(amt_[i], maxspend[i] * remtime)
            maxval = max(maxval, dfs(bp, maxspend, cache, remtime, bots_, amt_))
 
    cache[key] = maxval
    return maxval
 
total = 0
 
data = open("input", "r", encoding="utf-8").read().splitlines()
for i, line in enumerate(data):
    bp = []
    maxspend = [0, 0, 0]
    for section in line.split(": ")[1].split(". "):
        recipe = []
        for x, y in re.findall(r"(\d+) (\w+)", section):
            x = int(x)
            y = ["ore", "clay", "obsidian"].index(y)
            recipe.append((x, y))
            maxspend[y] = max(maxspend[y], x)
        bp.append(recipe)
    v = dfs(bp, maxspend, {}, 24, [1, 0, 0, 0], [0, 0, 0, 0])
    total += (i + 1) * v
 
print(total)

2022 Day 18 Part 01 + 02

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
from collections import deque
 
DIRECTIONS = [
    (0, 0, 1),
    (0, 1, 0),
    (1, 0, 0),
    (0, 0, -1),
    (0, -1, 0),
    (-1, 0, 0),
]
 
 
def add_tuples(a, b):
    return tuple(x + y for x, y in zip(a, b))
 
 
'''PART 01'''
data = open("input", "r", encoding="utf-8").read().splitlines()
pieces = set(tuple(map(int, line.split(","))) for line in data)
ans = len(pieces) * 6
for p in pieces:
    for d in DIRECTIONS:
        if add_tuples(p, d) in pieces:
            ans -= 1
print(ans)
 
'''PART 02'''
data = open("input", "r", encoding="utf-8").read().splitlines()
pieces = {tuple(map(int, line.split(","))): 0 for line in data}
 
min_coords = tuple(min(x) - 1 for x in zip(*pieces))
max_coords = tuple(max(x) + 1 for x in zip(*pieces))
 
start = deque([min_coords])
visited = set()
 
while len(start) > 0:
    u = start.pop()
    if u in visited:
        continue
    visited.add(u)
 
    for d in DIRECTIONS:
        v = add_tuples(u, d)
        if all(a <= b <= c for a, b, c in zip(min_coords, v, max_coords)):
            if v in pieces:
                pieces[v] += 1
            else:
                start.append(v)
 
print (sum(pieces.values()))

2022 Day 17 Part 01 + 02

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
from itertools import cycle
 
ROCKS = [
    {0 + 0j, 1 + 0j, 2 + 0j, 3 + 0j},
    {1 + 2j, 0 + 1j, 1 + 1j, 2 + 1j, 1 + 0j},
    {2 + 2j, 2 + 1j, 0 + 0j, 1 + 0j, 2 + 0j},
    {0 + 3j, 0 + 2j, 0 + 1j, 0 + 0j},
    {0 + 1j, 1 + 1j, 0 + 0j, 1 + 0j},
]
 
ROCKS_1 = cycle(ROCKS)
ROCKS_2 = cycle(enumerate(ROCKS))
DIRECTIONS = {">": 1, "<": -1}
 
 
def get_max_y(grid):
    return max(int(p.imag) for p in grid)
 
'''PART 01'''
data = open("input", "r", encoding="utf-8").read().strip()
wind = cycle(DIRECTIONS[x] for x in data)
rest = set(x - 1j for x in range(7))
 
for i in range(2022):
    start = 2 + (4 + get_max_y(rest)) * 1j
    rock = {start + p for p in next(ROCKS_1)}
 
    while True:
        w = next(wind)
        new_rock = {p + w for p in rock}
        if not new_rock & rest and all(0 <= p.real <= 6 for p in new_rock):
            rock = new_rock
 
        new_rock = {p - 1j for p in rock}
        if rest & new_rock:
            rest.update(rock)
            break
 
        rock = new_rock
 
print (get_max_y(rest) + 1)
 
'''PART 02'''
data = open("input", "r", encoding="utf-8").read().strip()
wind = cycle(enumerate(DIRECTIONS[x] for x in data))
rest = set(x - 1j for x in range(7))
last = {}
t = 1000000000000
 
while t > 0:
    max_y = get_max_y(rest)
    start = 2 + (4 + max_y) * 1j
    r_idx, rock = next(ROCKS_2)
    rock = {start + p for p in rock}
 
    while True:
        w_idx, w = next(wind)
        new_rock = {p + w for p in rock}
        if not new_rock & rest and all(0 <= p.real <= 6 for p in new_rock):
            rock = new_rock
 
        new_rock = {p - 1j for p in rock}
        if rest & new_rock:
            rest.update(rock)
            break
 
        rock = new_rock
 
    max_y = get_max_y(rest)
    heights = tuple(max_y - get_max_y(p for p in rest if p.real == i) for i in range(7))
    t -= 1
 
    try:
        old_t, old_max_y = last[r_idx, w_idx, heights]
        rest = {p + t // (old_t - t) * (max_y - old_max_y) * 1j for p in rest}
        t %= old_t - t
    except KeyError:
        last[r_idx, w_idx, heights] = t, max_y
 
print (get_max_y(rest) + 1)

2022 Day 16 Part 02

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
from collections import deque
 
valves = {}
tunnels = {}
 
lines = open("input", "r", encoding="utf-8").read().splitlines()
 
for line in lines:
    line = line.strip()
    valve = line.split()[1]
    flow = int(line.split(";")[0].split("=")[1])
    targets = line.split("to ")[1].split(" ", 1)[1].split(", ")
    valves[valve] = flow
    tunnels[valve] = targets
 
dists = {}
nonempty = []
 
for valve in valves:
    if valve != "AA" and not valves[valve]:
        continue
 
    if valve != "AA":
        nonempty.append(valve)
 
    dists[valve] = {valve: 0, "AA": 0}
    visited = {valve}
 
    queue = deque([(0, valve)])
 
    while queue:
        distance, position = queue.popleft()
        for neighbor in tunnels[position]:
            if neighbor in visited:
                continue
            visited.add(neighbor)
            if valves[neighbor]:
                dists[valve][neighbor] = distance + 1
            queue.append((distance + 1, neighbor))
 
    del dists[valve][valve]
    if valve != "AA":
        del dists[valve]["AA"]
 
indices = {}
 
for index, element in enumerate(nonempty):
    indices[element] = index
 
cache = {}
 
def dfs(time, valve, bitmask):
    if (time, valve, bitmask) in cache:
        return cache[(time, valve, bitmask)]
 
    maxval = 0
    for neighbor in dists[valve]:
        bit = 1 << indices[neighbor]
        if bitmask & bit:
            continue
        remtime = time - dists[valve][neighbor] - 1
        if remtime <= 0:
            continue
        maxval = max(maxval, dfs(remtime, neighbor, bitmask | bit) + valves[neighbor] * remtime)
 
    cache[(time, valve, bitmask)] = maxval
    return maxval
 
b = (1 << len(nonempty)) - 1
 
m = 0
 
for i in range((b + 1) // 2):
    m = max(m, dfs(26, "AA", i) + dfs(26, "AA", b ^ i))
 
print(m)

2022 Day 16 Part 01

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
from collections import deque
 
valves = {}
tunnels = {}
 
lines = open("input", "r", encoding="utf-8").read().splitlines()
 
for line in lines:
    line = line.strip()
    valve = line.split()[1]
    flow = int(line.split(";")[0].split("=")[1])
    targets = line.split("to ")[1].split(" ", 1)[1].split(", ")
    valves[valve] = flow
    tunnels[valve] = targets
 
dists = {}
nonempty = []
 
for valve in valves:
    if valve != "AA" and not valves[valve]:
        continue
 
    if valve != "AA":
        nonempty.append(valve)
 
    dists[valve] = {valve: 0, "AA": 0}
    visited = {valve}
 
    queue = deque([(0, valve)])
 
    while queue:
        distance, position = queue.popleft()
        for neighbor in tunnels[position]:
            if neighbor in visited:
                continue
            visited.add(neighbor)
            if valves[neighbor]:
                dists[valve][neighbor] = distance + 1
            queue.append((distance + 1, neighbor))
 
    del dists[valve][valve]
    if valve != "AA":
        del dists[valve]["AA"]
 
indices = {}
 
for index, element in enumerate(nonempty):
    indices[element] = index
 
cache = {}
 
def dfs(time, valve, bitmask):
    if (time, valve, bitmask) in cache:
        return cache[(time, valve, bitmask)]
 
    maxval = 0
    for neighbor in dists[valve]:
        bit = 1 << indices[neighbor]
        if bitmask & bit:
            continue
        remtime = time - dists[valve][neighbor] - 1
        if remtime <= 0:
            continue
        maxval = max(maxval, dfs(remtime, neighbor, bitmask | bit) + valves[neighbor] * remtime)
 
    cache[(time, valve, bitmask)] = maxval
    return maxval
 
print(dfs(30, "AA", 0))

2022 Day 15 Part 01 + 02

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
lines = open("input", "r", encoding="utf-8").read().splitlines()
 
S = set()
B = set()
sum_d = 0
for line in lines:
    words = line.split()
    sx,sy = words[2],words[3]
    bx,by = words[8],words[9]
    sx = int(sx[2:-1])
    sy = int(sy[2:-1])
    bx = int(bx[2:-1])
    by = int(by[2:])
    d = abs(sx-bx) + abs(sy-by)
    sum_d += d
    S.add((sx,sy,d))
    B.add((bx,by))
 
def valid(x,y,S):
    for (sx,sy,d) in S:
        dxy = abs(x-sx)+abs(y-sy)
        if dxy<=d:
            return False
    return True
 
p1 = 0
for x in range(-int(1e7),int(1e7)):
    y = int(2e6)
    if not valid(x,y,S) and (x,y) not in B:
        p1 += 1
print(p1)
 
n_checked = 0
# If there is only one possible position for another beacon, it *must* be distance d+1 from some beacon
# If not, we could find an adjacent position that is possible.
found_p2 = False
for (sx,sy,d) in S:
    # check all points that are d+1 away from (sx,sy)
    for dx in range(d+2):
        dy = (d+1)-dx
        for signx,signy in [(-1,-1),(-1,1),(1,-1),(1,1)]:
            n_checked += 1
            x = sx+(dx*signx)
            y = sy+(dy*signy)
            if not(0<=x<=4000000 and 0<=y<=4000000):
                continue
            assert abs(x-sx)+abs(y-sy)==d+1
            if valid(x,y,S) and (not found_p2):
                print(x*4000000 + y)
                found_p2 = True
#print(n_checked, 4*sum_d) # these are approximately equal

2022 Day 14 Part 01 + 02

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
data = open("input").read().strip()
lines = [x for x in data.split('\n')]
 
R = set()
for line in lines:
    prev = None
    for point in line.split('->'):
        x,y = point.split(',')
        x,y = int(x),int(y)
        if prev is not None:
            dx = x-prev[0]
            dy = y-prev[1]
            len_ = max(abs(dx),abs(dy))
            for i in range(len_+1):
                xx = prev[0]+i*(1 if dx>0 else (-1 if dx<0 else 0))
                yy = prev[1]+i*(1 if dy>0 else (-1 if dy<0 else 0))
                R.add((xx,yy))
        prev = (x,y)
 
floor = 2+max(r[1] for r in R)
#print(floor)
lo_x = min(r[0] for r in R)-2000
hi_x = max(r[0] for r in R)+2000
for x in range(lo_x, hi_x):
    R.add((x,floor))
 
did_p1 = False
for t in range(1000000):
    rock = (500,0)
    while True:
        if rock[1]+1>=floor and (not did_p1):
            did_p1 = True
            print(t)
        if (rock[0],rock[1]+1) not in R:
            rock = (rock[0],rock[1]+1)
        elif (rock[0]-1,rock[1]+1) not in R:
            rock = (rock[0]-1, rock[1]+1)
        elif (rock[0]+1, rock[1]+1) not in R:
            rock = (rock[0]+1, rock[1]+1)
        else:
            break
    if rock == (500,0):
        print(t+1)
        break
    R.add(rock)

2022 Day 13 Part 01 + 02

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
data = open("input", "r", encoding="utf-8").read().strip()
lines = [x for x in data.split('\n')]
 
def compare(p1,p2):
    if isinstance(p1, int) and isinstance(p2,int):
        if p1 < p2:
            return -1
        elif p1 == p2:
            return 0
        else:
            return 1
    elif isinstance(p1, list) and isinstance(p2, list):
        i = 0
        while i<len(p1) and i<len(p2):
            c = compare(p1[i], p2[i])
            if c==-1:
                return -1
            if c==1:
                return 1
            i += 1
        if i==len(p1) and i<len(p2):
            return -1
        elif i==len(p2) and i<len(p1):
            return 1
        else:
            return 0
    elif isinstance(p1, int) and isinstance(p2, list):
        return compare([p1], p2)
    else:
        return compare(p1, [p2])
 
packets = []
part1 = 0
for i,group in enumerate(data.split('\n\n')):
    p1,p2 = group.split('\n')
    p1 = eval(p1)
    p2 = eval(p2)
    packets.append(p1)
    packets.append(p2)
    if compare(p1, p2)==-1:
        part1 += 1+i
print(part1)
 
packets.append([[2]])
packets.append([[6]])
from functools import cmp_to_key
packets = sorted(packets, key=cmp_to_key(lambda p1,p2: compare(p1,p2)))
part2 = 1
for i,p in enumerate(packets):
    if p==[[2]] or p==[[6]]:
        part2 *= i+1
print(part2)

2022 Day 12 Part 01 + 02

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
from collections import deque
 
lines = open("input", "r", encoding="utf-8").read().splitlines()
 
G = []
for line in lines:
    G.append(line)
R = len(G)
C = len(G[0])
 
E = [[0 for _ in range(C)] for _ in range(R)]
for r in range(R):
    for c in range(C):
        if G[r][c]=='S':
            E[r][c] = 1
        elif G[r][c] == 'E':
            E[r][c] = 26
        else:
            E[r][c] = ord(G[r][c])-ord('a')+1
 
def bfs(part):
    Q = deque()
    for r in range(R):
        for c in range(C):
            if (part==1 and G[r][c]=='S') or (part==2 and E[r][c] == 1):
                Q.append(((r,c), 0))
 
    S = set()
    while Q:
        (r,c),d = Q.popleft()
        if (r,c) in S:
            continue
        S.add((r,c))
        if G[r][c]=='E':
            return d
        for dr,dc in [(-1,0),(0,1),(1,0),(0,-1)]:
            rr = r+dr
            cc = c+dc
            if 0<=rr<R and 0<=cc<C and E[rr][cc]<=1+E[r][c]:
                Q.append(((rr,cc),d+1))
print(bfs(1))
print(bfs(2))

2022 Day 11 Part 02

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
from collections import defaultdict
from math import prod
 
data = open("input", "r", encoding="utf-8").read()
 
def parse_monkey(lines):
    return {
        "items":   [int(x) for x in lines[1][18:].split(",")],
        "op":      lambda old: eval(lines[2][19:]),
        "test":    lambda x: x % int(lines[3][21:]) == 0,
        "testnum": int(lines[3][21:]),
        "throw": { 
                   True:  int(lines[4][29:]),
                   False: int(lines[5][30:]),
        },
    }
 
monkeys = [parse_monkey(d.splitlines()) for d in data.split("\n\n")]
active = defaultdict(int)
mod = prod(m["testnum"] for m in monkeys)
 
for r in range(10000):
    for i, m in enumerate(monkeys):
        for item in m["items"]:
            active[i] += 1
            new =   m["op"](item) % mod
            test =  m["test"](new)
            throw = m["throw"][test]
            monkeys[throw]["items"].append(new)
        m["items"] = []
 
a = sorted(active.values(), reverse=True)
print (a[0] * a[1])

2022 Day 11 Part 01

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
from collections import defaultdict
 
data = open("input", "r", encoding="utf-8").read()
 
def parse_monkey(lines):
    return {
        "items":   [int(x) for x in lines[1][18:].split(",")],
        "op":      lambda old: eval(lines[2][19:]),
        "test":    lambda x: x % int(lines[3][21:]) == 0,
        "testnum": int(lines[3][21:]),
        "throw": { 
                   True:  int(lines[4][29:]),
                   False: int(lines[5][30:]),
        },
    }
 
monkeys = [parse_monkey(d.splitlines()) for d in data.split("\n\n")]
active = defaultdict(int)
 
for r in range(20):
    for i, m in enumerate(monkeys):
        for item in m["items"]:
            active[i] += 1
            new =   m["op"](item) // 3
            test =  m["test"](new)
            throw = m["throw"][test]
            monkeys[throw]["items"].append(new)
        m["items"] = []
 
a = sorted(active.values(), reverse=True)
print (a[0] * a[1])

2022 Day 10 Part 02

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
data = open("input", "r", encoding="utf-8").read().splitlines()
 
x = 1
t = 0
signal = 0
 
def tick():
    global t
    if t % 40 in (x - 1, x, x + 1):
        print("#", end="")
    else:
        print(".", end="")
    t += 1
    if t % 40 == 0:
        print()
 
for d in data:
    match d.split():
        case ["addx", num]:
            tick()
            tick()
            x += int(num)
        case ["noop"]:
            tick()

2022 Day 10 Part 01

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
data = open("input", "r", encoding="utf-8").read().splitlines()
 
x = 1
t = 0
signal = 0
 
def tick():
    global signal, t
    t += 1
    if t in (20, 60, 100, 140, 180, 220):
        signal += t * x
 
for d in data:
    match d.split():
        case ["addx", num]:
            tick()
            tick()
            x += int(num)
        case ["noop"]:
            tick()
 
print(signal)

2022 Day 09 Part 01 + 02

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
lines = open("input", "r", encoding="utf-8").read().splitlines()
 
def adjust(H,T):
    dr = (H[0]-T[0]) # dr: difference rows.    gap between H and T in the 'rows' axis (horizontal)
    dc = (H[1]-T[1]) # dr: difference columns. gap between H and T in the 'columns' axis (vertical)
    # action is based on both horizontal and vertical gap between H-T
    if abs(dr)<=1 and abs(dc)<=1:
        # do nothing
        pass
    elif abs(dr)>=2 and abs(dc)>=2:
        # close the gap in both rows and columns direction.  mind the sign.
        T = (H[0]-1 if T[0]<H[0] else H[0]+1, H[1]-1 if T[1]<H[1] else H[1]+1)
    elif abs(dr)>=2:
        # close the gap by only moving left/right in rows direction.  mind the sign.
        T = (H[0]-1 if T[0]<H[0] else H[0]+1, H[1])
    elif abs(dc)>=2:
        # close the gap by only moving up/down in columns direction.  mind the sign.
        T = (H[0], H[1]-1 if T[1]<H[1] else H[1]+1)
    return T
 
H = (0,0)                              # 1 head
T = [(0,0) for _ in range(9)]          # 9 tails
DR = {'L': 0, 'U': -1, 'R': 0, 'D': 1} # DR = direction rows.          These two dicts break down a command L, U, R, or D
DC = {'L': -1, 'U': 0, 'R': 1, 'D': 0} # DC = direction columns.       into separate single step values for x and y axis
P1 = set([T[0]])                       # 1 Head, 1 Tail.   count tail only.  unique visits only.  one set containing one list.
P2 = set([T[8]])                       # 1 Head, 9 Tails.  count tail only.  unique visits only.  one set containing nine lists.
for line in lines:
    d,amt = line.split()
    amt = int(amt)
    for _ in range(amt):                # move step-by-step
        H = (H[0] + DR[d], H[1]+DC[d])  # move head.  previous pos + delta
        T[0] = adjust(H, T[0])          # move tail
        for i in range(1, 9):           # repeat for remaining tail elements.  previous tail is the new head
            T[i] = adjust(T[i-1], T[i])
        P1.add(T[0])
        P2.add(T[8])
print(len(P1))
print(len(P2))

2022 Day 08 Part 02

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
import numpy as np
 
def left(row,col):
  cnt=0
  tree = df[row][col]
  for i in range(col-1,-1,-1):
    cnt+=1
    neighbour = df[row][i]
    if tree <= neighbour:
      return cnt
  return cnt
 
def right(row,col):
  cnt=0
  tree = df[row][col]
  for i in range(col+1,cols):
    cnt+=1
    neighbour = df[row][i]
    if tree <= neighbour:
      return cnt
  return cnt
 
def top (row,col):
  cnt=0
  tree = df[row][col]
  for i in range(row-1,-1,-1):
    cnt+=1
    neighbour = df[i][col]
    if tree <= neighbour:
      return cnt
  return cnt
 
def bottom (row,col):
  cnt=0
  tree = df[row][col]
  for i in range(row+1,rows):
    cnt+=1
    neighbour = df[i][col]
    if tree <= neighbour:
      return cnt
  return cnt
 
data = open("input", "r", encoding="utf-8").read().splitlines()
df = np.array([[int(x) for x in line] for line in data])
rows, cols = df.shape
 
trees_interior = []
for row in range(1,rows-1):
  for col in range(1,cols-1):
    tree = df[row][col]
    trees_interior.append(left(row,col) * right(row,col) * top(row,col) * bottom(row,col))
print(max(trees_interior))

2022 Day 08 Part 01

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
import numpy as np
 
def left(row,col):
  tree = df[row][col]
  for i in range(0,col):
    neighbour = df[row][i]
    if tree <= neighbour:
      return False
  return True
 
def right(row,col):
  tree = df[row][col]
  for i in range(col+1,cols):
    neighbour = df[row][i]
    if tree <= neighbour:
      return False
  return True
 
def top (row,col):
  tree = df[row][col]
  for i in range(0,row):
    neighbour = df[i][col]
    if tree <= neighbour:
      return False
  return True
 
def bottom (row,col):
  tree = df[row][col]
  for i in range(row+1,rows):
    neighbour = df[i][col]
    if tree <= neighbour:
      return False
  return True
 
data = open("input", "r", encoding="utf-8").read().splitlines()
df = np.array([[int(x) for x in line] for line in data])
#rows_max = df.max(axis=1)
#cols_max = df.max(axis=0)
rows, cols = df.shape
 
trees_perimiter = 2 * rows + 2 * cols - 4
 
trees_interior = []
for row in range(1,rows-1):
  for col in range(1,cols-1):
    tree = df[row][col]
    if left(row,col) or right(row,col) or top(row,col) or bottom(row,col):
        trees_interior.append(tree)
print(len(trees_interior) + trees_perimiter)

2022 Day 07 Part 01 + 02 v2

Credits to Mark –https://styleincode.fun

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
data = open("input", "r", encoding="utf-8").read().splitlines()
 
dirs = {}
path = []
for line in data:
    cmd_mode = line.startswith('$')
    if cmd_mode:
        if line.startswith('$ cd '):
            _, _, directory = line.split()
            if directory == '..':
                path.pop()
            else:
                path.append(directory)
    else:
        cwd = '-'.join(path)
        files = dirs.get(cwd, [])
        atom = line.split()
        if atom[0] == 'dir':
            atom[1] = cwd + '-' + atom[1]
        files.append(atom)
        dirs[cwd] = files
 
def size(folder, structure):
    """ calculate size of folder and files in structure """
    total = 0
    for i, j in structure[folder]:
        if i == 'dir':
            total += size(j, structure)
        else:
            total += int(i)
    return total
 
# PART 01
candidates = []
for d in dirs:
    files = size(d, dirs)
    if files <= 100000:
        candidates.append(files)
print(sum(candidates))
 
# PART 02
disk = 70000000
needed = 30000000
free = disk - size('/', dirs)
 
candidates = []
for d in dirs:
    files = size(d, dirs)
    if files + free >= needed:
        candidates.append(files)
print(min(candidates))

2022 Day 07 Part 01 + 02

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
data = open("input", "r", encoding="utf-8").read().splitlines()
 
fs = [] #['/a/b/c/file|dir, size'],[...],[...]
filepath = []
 
for d in data:
    a = d.strip().split()
    if a[0] == '$':
        if a[1] =='cd':
            if a[2] == '..':
                filepath.pop()
            else:
                if a[2] == '/': a[2]=''
                filepath.append(a[2] + '/')
    elif a[0] == 'dir':
        fs.append([''.join(map(str,filepath)) + a[1] + '/' , 0        ])
    else:
        fs.append([''.join(map(str,filepath)) + a[1]       , int(a[0])])
 
fs.sort()
for f in fs:
    print(*f)
 
'''PART 01'''
total=0
grandtotal=0
for a,b in fs:
    if a[-1:]=='/': # directory
        for c,d in fs:
            if c.find(a) != -1 : # this file lives inside the directory
                total+=d
        if total<=100000:
            grandtotal+=total
        total=0
print('part 01: ',grandtotal)
 
'''PART 02'''
total=0
grandtotals=[]
for a,b in fs:
    if a[-1:]=='/': # directory
        for c,d in fs:
            if c.find(a) != -1 : # this file lives inside the directory
                total+=d
        grandtotals.append(total)
        total=0
 
grandtotals.sort()
 
used = sum(b for a,b in fs)
free = 70000000 - used
todelete = 30000000 - free
 
for g in grandtotals:
    if g >= todelete:
        print('part 02: ',g)
        break

2022 Day 06 Part 01 + 02 v2

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
data = open("input", "r", encoding="utf-8").read()
 
def findmarker(numchars):
    pos = 0
    buffer = []
    for d in data:
        pos+=1
        buffer.append(d) # keep appending a char
        buffer = buffer[-numchars:] # keep only buffer size chars from end (negative numbers = count right to left)
        # converting a list to a set will drop any duplicate elements
        if len(set(buffer)) == numchars:
            print(pos,*buffer)
            break
 
findmarker(4)
findmarker(14)

2022 Day 06 Part 01 + 02

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
from collections import Counter
 
def dups(input):
    string = Counter(input)
    for char, count in string.items():
        if (count > 1): return(True)
    return False
 
data = open("input", "r", encoding="utf-8").read()
 
def findmarker(numchars):
    for i in range(3,len(data)):
        buffer=data[i-numchars:i]
        if i >= numchars and not dups(buffer):
            print(i,buffer)
            break
 
findmarker(4)
findmarker(14)

2022 Day 05 Part 01 + 02 v2

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
data = open("input", "r", encoding="utf-8").read().splitlines()
 
def rearrange(cratemover):
    crates, moves = [],[]
    for d in data:
        if d[:4]=="move":
            _, qty, _, a, _, b = d.strip().split()
            moves.append([qty,a,b])
        elif d != '':
            row = [j for i, j in enumerate(d) if i % 4 == 1]
            # this is that:
            row=[]
            for i,j in enumerate(d):         # i,j:  0,'['  1,'P'  2,']'  3,' '
                if i % 4 == 1:               # 1, 4, 8, 12 
                    row.append(j)
            crates.append(row)               # ['P', ' ', 'C', ' ', ' ', 'M', ' ', ' ', ' ']
    stacks = {i: [] for i in crates.pop()}   # {'1': [], '2': [], '3': [], '4': [], '5': [], '6': [], '7': [], '8': [], '9': []}
    crates.reverse()
    for row in crates:                       # ['P', ' ', 'C', ' ', ' ', 'M', ' ', ' ', ' ']  [...]  [...]
        for i, j in enumerate(row):          # i,j:  0,'P'  1,' '  2,'C'  3,' '  4,' '  5,'M'  6,' '  7,' '  8,' '
            if not j.isspace():
                stacks[str(i + 1)].append(j) # transpose, omitting ' ' values
    for m in moves:
        crates = []
        for i in range(int(m[0])):
            crates.append(stacks[m[1]].pop())
        if cratemover == '9001':
            crates.reverse()
        stacks[m[2]] += crates
    return(''.join([stacks[i].pop() for i in stacks]))
 
print (rearrange('9000'))
print (rearrange('9001'))

2022 Day 05 Part 01 + 02

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
def rearrange(cratemover):
  data = open("input", "r", encoding="utf-8").read().splitlines()
  data = [ x.replace('    ',' [@]') for x in data ]
  data = [ x.replace('[','') for x in data ]
  data = [ x.replace(']','') for x in data ]
 
  for i in range(0,len(data)):
    if data[i] == ' 1   2   3   4   5   6   7   8   9 ':
      break
    i+=1
 
  stack = []
  stack = [ x.split(' ') for x in data[:i] ]
  stack.reverse()
  stack = [ list(x) for x in zip(*stack) ] #transpose
 
  for a in range(0,len(stack)):
    while '@' in stack[a]:
      stack[a].remove('@')
 
  moves = []
  moves = [ x for x in data[i+2:] ]
  moves = [ (x.strip().split()) for x in moves ]
  moves = [ (x,y,z) for _,x,_,y,_,z in moves ] # qty from to
  moves = [ list(map(int, x)) for x in moves ]
 
  for m in moves:
    x = []
    for i in range (0,m[0]):
      x.append(stack[m[1]-1].pop())
    if cratemover == '9001':
      x.reverse()
    stack[m[2]-1] += x
 
  top = ''
  for i in range(0,9):
    top += ''.join(stack[i][-1])
 
  return(top) 
 
print (rearrange('9000'))
print (rearrange('9001'))

2022 Day 04 Part 01 + 02 v2

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
def contains(x1,x2,y1,y2):
  """Whether range x1,x2 is contained in range y1,y2 or vice versa."""
  return (y1 <= x1 <= y2 and y1 <= x2 <= y2) or \
         (x1 <= y1 <= x2 and x1 <= y2 <= x2)
 
def overlaps(x1,x2,y1,y2):
  """Whether range x1,x2 and range y1,y2 overlap."""
  return x1 <= y2 and y1 <= x2
 
data = open("input", "r", encoding="utf-8").read().splitlines()
data = [x.replace(',','-') for x in data]
data = [x.split('-') for x in data]
data = [list(map(int, x)) for x in data]
 
print(sum(contains(a,b,c,d) for a,b,c,d in data))
print(sum(overlaps(a,b,c,d) for a,b,c,d in data))

2022 Day 04 Part 01 + 02

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
def range_contains(range1, range2):
  """Whether range1 is contained in range2 or vice versa."""
  x1, x2 = range1.start, range1.stop
  y1, y2 = range2.start, range2.stop
  return (y1 <= x1 <= y2 and y1 <= x2 <= y2) or (x1 <= y1 <= x2 and x1 <= y2 <= x2)
 
def range_overlaps(range1, range2):
  """Whether range1 and range 2 overlap."""
  x1, x2 = range1.start, range1.stop
  y1, y2 = range2.start, range2.stop
  return x1 <= y2 and y1 <= x2
 
data = open("input", "r", encoding="utf-8").read().splitlines()
data = [x.split(',') for x in data]
data = [ [ x.split('-'), y.split('-') ] for x,y in data ]
 
r = []
for x,y in data:
  r1 = range(int(x[0]),int(x[1]))
  r2 = range(int(y[0]),int(y[1]))
  r.append([r1,r2])
 
cnt=0
for r1,r2 in r:
  if range_contains(r1,r2): cnt += 1
print(cnt)
 
cnt=0
for r1,r2 in r:
  if range_overlaps(r1,r2): cnt += 1
print(cnt)

2022 Day 03 Part 02

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
lines = open("input", "r", encoding="utf-8").read().splitlines()
tri,comchars=[],[]
for i in range(1,len(lines)+1):
    tri.append(lines[-i])
    if i % 3 == 0: # 3 6 9 12 15 etc.
        #print(f"{tri}\n")
        # intersect of 3 strings in two operations
        comchar = ''.join(set(tri[0]).intersection(tri[1]))
        comchar = ''.join(set(tri[2]).intersection(comchar))
        # intersect of 3 strings in one operation
        comchar = ''.join(set(tri[0]) & set(tri[1]) & set(tri[2]))
        # intersect of N strings in one operation
        comchar = ''.join(set.intersection(*map(set,tri)))
        comchars.append(comchar)
        tri=[]       
score = 0
for comchar in comchars:
    if comchar.islower(): score += ord(comchar) - 96
    if comchar.isupper(): score += ord(comchar) - 38
print(score)

2022 Day 03 Part 01

1
2
3
4
5
6
7
8
9
data = open("input", "r", encoding="utf-8").read().splitlines()
data = list([ x[:len(x)//2] , x[len(x)//2:] ] for x in data)
comchars = []
comchars = ( ''.join(set(str1).intersection(str2)) for str1, str2 in data )
score = 0
for comchar in comchars:
    if comchar.islower(): score += ord(comchar) - 96
    if comchar.isupper(): score += ord(comchar) - 38
print(score)

2022 Day 02 Part 01 + 02 v2

Note: Keeps track of both players’ score.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
def play_game (input):
    cipher = {'X': 'A', 'Y': 'B', 'Z': 'C'}
    wins =   {'A': 'C', 'B': 'A', 'C': 'B'}
    val =    {'A': 1, 'B': 2, 'C': 3}
 
    p1, _, p2 = list(input)
    p2 = cipher[p2]
    s1 = val[p1] ; s2 = val[p2]
    if  p1 == p2: s1 += 3 ; s2 += 3
    elif wins[p1] == p2:
        s1 += 6 # p1 wins
    else:
        s2 += 6 # p2 wins
    return (s1,s2)
 
def revise (input):
    # X: You need to lose
    # Y: You need a draw
    # Z: You need to win
    cipher = {'A X': 'A Z', 'A Y': 'A X', 'A Z': 'A Y', \
              'B X': 'B X', 'B Y': 'B Y', 'B Z': 'B Z', \
              'C X': 'C Y', 'C Y': 'C Z', 'C Z': 'C X'}
    return (cipher[input])
 
data = open("input", "r", encoding="utf-8").read().splitlines()
 
score = []
score += [play_game(x) for x in data]
print(f"part 01: your final score: {sum(row[1] for row in score)}")
 
# recalculate your moves based on updated strategy guide interpretation
data = [revise(x) for x in data]
 
score = []
score += [play_game(x) for x in data]
print(f"part 02: your final score: {sum(row[1] for row in score)}")

2022 Day 02 Part 01 + 02

Note: Keeps track of both players’ score.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
# Rock defeats Scissors
# Scissors defeats Paper
# Paper defeats Rock
 
# Scoring:
# shape you selected: 1 for Rock, 2 for Paper, and 3 for Scissors
# plus outcome of the round: 0 if you lost, 3 if a draw, 6 if you won
 
def play_game (input):
    #          elf you
    # Rock:     A   X
    # Paper:    B   Y
    # Scissors: C   Z
    if input == 'A X': return (4,4)
    if input == 'A Y': return (1,8)
    if input == 'A Z': return (7,3)
    if input == 'B X': return (8,1)
    if input == 'B Y': return (5,5)
    if input == 'B Z': return (2,9)
    if input == 'C X': return (3,7)
    if input == 'C Y': return (9,2)
    if input == 'C Z': return (6,6)
 
def revise (input):
    # You need to lose: X
    # You need a draw:  Y
    # You need to win:  Z
    if input == 'A X': return ('A Z')
    if input == 'A Y': return ('A X')
    if input == 'A Z': return ('A Y')
    if input == 'B X': return ('B X')
    if input == 'B Y': return ('B Y')
    if input == 'B Z': return ('B Z')
    if input == 'C X': return ('C Y')
    if input == 'C Y': return ('C Z')
    if input == 'C Z': return ('C X')
 
data = open("input", "r", encoding="utf-8").read().splitlines()
 
score = []
score += [play_game(x) for x in data]
print(f"part 01: your final score: {sum(row[1] for row in score)}")
 
# recalculate your moves based on updated strategy guide interpretation
data = [revise(x) for x in data]
 
score = []
score += [play_game(x) for x in data]
print(f"part 02: your final score: {sum(row[1] for row in score)}")

2022 Day 01 Part 02

1
2
3
4
5
6
7
8
9
10
11
12
13
lines = open("input", "r").read().splitlines()
cal = 0
cals = []
for line in lines:
    if line.strip():
        print('The line is NOT empty ->', line)
        cal += int(line)
    else:
        print('The line is empty')
        cals.append (cal)
        cal = 0
cals.append (cal) # EOF
print(sum(sorted(cals, reverse=True)[:3]))

2022 Day 01 Part 01 v2

1
2
3
4
5
6
7
8
9
10
11
12
lines = open("input", "r").read().splitlines()
cal = 0
cals = []
for line in lines:
    if line.strip():
        print('The line is NOT empty ->', line)
        cal += int(line)      
    else:
        print('The line is empty')
        cals.append(cal)
        cal = 0
print(max(cals))

2022 Day 01 Part 01

1
2
3
4
5
6
7
8
9
10
11
12
lines = open("input", "r").read().splitlines()
cal, maxx = 0, 0
for line in lines:
    if line.strip():
        print('The line is NOT empty ->', line)
        cal += int(line)
        if cal > maxx:
            maxx = cal
    else:
        print('The line is empty')
        cal = 0
print(maxx)

How to Advent of Code (Python)


Notes:
When I say “list of strings” to “array of integers” I should have said “list of integers”.
When installing Python, tick the option “add to path.
When installing Python, change the folder to C:\python or D:\python for ease of use.