2015 Advent of Code (Python)

By telleropnul, March 31, 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: adventofcode2015inputs.zip

2015 Day 25

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import numpy as np
 
data = open('input').read().replace('.','').replace(',','').split(' ')
row = int(data[-3]) # 3010
col = int(data[-1]) # 3019
 
# moving to next colum adds 2, 3, 4, ... to the value
grid = np.sum(np.arange(2, col + 1)) + 1
# moving to next row adds the column number, +1, +2 ... to the value
grid = grid + np.sum(np.arange(col, col+row-1))
 
code = 20151125
for i in range(grid - 1):
    code = code * 252533 % 33554393
print(code)

2015 Day 24 Part 02

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import itertools
from math import prod
 
data = open('input').read().splitlines()
data = [int(d) for d in data]
goal = int(sum(data) / 4)
 
quantum = []
# search for sets of 5 presents which sum to goal (none exist)
for i in range (1,10):
    for group in itertools.combinations(data, i):
        if sum(group) == goal:
            quantum.append(prod(group))
            break
#print(quantum)
print(min(quantum))

2015 Day 24 Part 01

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import itertools
from math import prod
 
data = open('input').read().splitlines()
data = [int(d) for d in data]
goal = int(sum(data) / 3)
 
quantum = []
for i in range (1,10):
    for group in itertools.combinations(data, i):
        if sum(group) == goal:
            quantum.append(prod(group))
            break
#print(quantum)
print(min(quantum))

2015 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
data = open('input').read().splitlines()
cmds = [d.replace(',', '').split(' ') for d in data]
 
def runCmd(cmd, num, vals):
    if cmd[0] == "hlf":
        vals[cmd[1]] /= 2
        return num+1
    elif cmd[0] == "tpl":
        vals[cmd[1]] *= 3
        return num+1
    elif cmd[0] == "inc":
        vals[cmd[1]] += 1
        return num+1
    elif cmd[0] == "jmp":
        return num + int(cmd[1])
    elif cmd[0] == "jie":
        if vals[cmd[1]] % 2 == 0: return num + int(cmd[2])
        else: return num + 1
    elif cmd[0] == "jio":
        if vals[cmd[1]] == 1: return num + int(cmd[2])
        else: return num + 1
    print(cmd)
    return -1
 
vals = {"a":0, "b":0}
run = 1
while run > 0 and run <= len(cmds):
    run = runCmd(cmds[run-1], run, vals)
print(str(vals["b"]))
 
vals = {"a":1, "b":0}
run = 1
while run > 0 and run <= len(cmds):
    run = runCmd(cmds[run-1], run, vals)
print(str(vals["b"]))

2015 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
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
SPELL_COSTS = {
    'magic_missle': 53,
    'drain': 73,
    'shield': 113,
    'poison': 173,
    'recharge': 229,
}
 
 
def apply_effects(game):
    if game['shield_timer']:
        game['shield_timer'] -= 1
        if game['shield_timer'] == 0:
            game['player_armor'] = 0
    if game['poison_timer']:
        game['boss_hp'] -= 3
        game['poison_timer'] -= 1
    if game['recharge_timer']:
        game['player_mana'] +=  101
        game['recharge_timer'] -= 1
 
 
def player_turn(game, spell):
    if spell == 'magic_missle':
        game['boss_hp'] -= 4
    elif spell == 'drain':
        game['boss_hp'] -= 2
        game['player_hp'] += 2
    elif spell == 'shield':
        game['shield_timer'] = 6
        game['player_armor'] += 7
    elif spell == 'poison':
        game['poison_timer'] = 6
    elif spell == 'recharge':
        game['recharge_timer'] = 5
    game['player_mana'] -= SPELL_COSTS[spell]
 
 
def boss_turn(game):
    dmg = max(game['boss_dmg'] - game['player_armor'], 1)
    game['player_hp'] -= dmg
 
 
def check_for_endgame(game, min_mana_spent):
    if game['boss_hp'] <= 0:
        min_mana_spent = min(game['mana_spent'], min_mana_spent)
        return 1, min_mana_spent
    if game['player_hp'] <= 0:
        return 2, min_mana_spent
    return 0, min_mana_spent
 
 
def find_minimal_mana(game):
    min_mana_spent = 9999999
    games = [game]
    while len(games):
        games, min_mana_spent = try_all_games(games, min_mana_spent)
    return min_mana_spent
 
 
def try_all_games(games, min_mana_spent):
    new_games = []
    for game in games:
 
        # PART B
        # game['player_hp'] = game['player_hp'] - 1
        endgame, min_mana_spent = check_for_endgame(game, min_mana_spent)
        if endgame:
            continue
 
        # apply player's turn effects
        apply_effects(game)
        endgame, min_mana_spent = check_for_endgame(game, min_mana_spent)
        if endgame:
            continue
 
        min_mana_spent = try_all_spells(game, min_mana_spent, new_games)
 
    return new_games, min_mana_spent
 
 
def try_all_spells(game, min_mana_spent, new_games):
    castable_spells = [spell for spell, cost in SPELL_COSTS.items()
                       if cost <= game['player_mana']]
    if game['shield_timer'] and 'shield' in castable_spells:
        castable_spells.remove('shield')
    if game['poison_timer'] and 'poison' in castable_spells:
        castable_spells.remove('poison')
    if game['recharge_timer'] and 'recharge' in castable_spells:
        castable_spells.remove('recharge')
 
    for spell in castable_spells:
 
        sub_game = game.copy()
        sub_game['spells_cast'] = list(sub_game['spells_cast']) + [spell]
        sub_game['mana_spent'] = sub_game['mana_spent']+SPELL_COSTS[spell]
 
        # players turn
        player_turn(sub_game, spell)
        endgame, min_mana_spent = check_for_endgame(sub_game, min_mana_spent)
        if endgame:
            continue
 
        # end early is too much mana spent
        if sub_game['mana_spent'] > min_mana_spent:
            continue
 
        # boss's turn
        apply_effects(sub_game)
        endgame, min_mana_spent = check_for_endgame(sub_game, min_mana_spent)
        if endgame:
            continue
 
        boss_turn(sub_game)
        endgame, min_mana_spent = check_for_endgame(sub_game, min_mana_spent)
        if endgame:
            continue
 
        new_games.append(sub_game)
    return min_mana_spent
 
lines = open('input').read().splitlines()
lines = [ l.split(' ') for l in lines]
 
BOSS_HIT_POINTS = int(lines[0][2]) # 58
BOSS_DAMAGE = int(lines[1][1]) # 9
 
initial_game = {
    'player_hp': 50,
    'player_mana': 500,
    'player_armor': 0,
 
    'boss_hp': BOSS_HIT_POINTS,
    'boss_dmg': BOSS_DAMAGE,
 
    'shield_timer': 0,
    'poison_timer': 0,
    'recharge_timer': 0,
 
    'spells_cast': [],
    'mana_spent': 0,
}
print(find_minimal_mana(initial_game.copy()))

2015 Day 21 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
from collections import namedtuple
 
lines = open('input').read().splitlines()
lines = [ l.split(' ') for l in lines]
 
BOSS_HIT_POINTS = lines[0][2] # 104
BOSS_DAMAGE = lines[1][1] # 8
BOSS_ARMOR = lines [2][1] # 1
 
Item = namedtuple('Item', ['name', 'cost', 'dmg', 'armor'])
 
WEAPONS = [
    Item('Dagger',        8,     4,       0),
    Item('Shortsword',   10,     5,       0),
    Item('Warhammer',    25,     6,       0),
    Item('Longsword',    40,     7,       0),
    Item('Greataxe',     74,     8,       0),
]
ARMOR = [
    Item('Nothing',       0,     0,       0),
    Item('Leather',      13,     0,       1),
    Item('Chainmail',    31,     0,       2),
    Item('Splintmail',   53,     0,       3),
    Item('Bandedmail',   75,     0,       4),
    Item('Platemail',   102,     0,       5),
]
 
RINGS = [
    Item('Nothing 1',     0,     0,       0),
    Item('Nothing 2',     0,     0,       0),
    Item('Damage +1',    25,     1,       0),
    Item('Damage +2',    50,     2,       0),
    Item('Damage +3',   100,     3,       0),
    Item('Defense +1',   20,     0,       1),
    Item('Defense +2',   40,     0,       2),
    Item('Defense +3',   80,     0,       3),
]
 
 
def does_player_win(player_hit, player_dmg, player_armor,
                    boss_hit, boss_dmg, boss_armor):
 
    boss_loss_per_turn = player_dmg - boss_armor
    if boss_loss_per_turn < 1:
        boss_loss_per_turn = 1
    player_loss_per_turn = boss_dmg - player_armor
    if player_loss_per_turn < 1:
        player_loss_per_turn = 1
 
    # the player goes first and gets n+1 turns
    n, remain = divmod(boss_hit, boss_loss_per_turn)
    if remain == 0:
        n -= 1
    if player_loss_per_turn * (n) >= player_hit:
        return False
    return True
 
 
min_cost = 999
max_cost = 0
for weapon in WEAPONS:
    for armor in ARMOR:
        for ring1 in RINGS:
            for ring2 in RINGS:
 
                # cannot own two of the same ring
                if ring1.name == ring2.name:
                    continue
 
                player_hit = 100
                player_dmg = weapon.dmg + ring1.dmg + ring2.dmg
                player_armor = armor.armor + ring1.armor + ring2.armor
                cost = weapon.cost + armor.cost + ring1.cost + ring2.cost
 
                if does_player_win(player_hit, player_dmg, player_armor,
                                   BOSS_HIT_POINTS, BOSS_DAMAGE, BOSS_ARMOR):
                    # part a, lowest cost items to win
                    min_cost = min(cost, min_cost)
                else:
                    # part b, highest cost items and still lose
                    max_cost = max(cost, max_cost)
print(min_cost)
print(max_cost)

2015 Day 20 Part 02

1
2
3
4
5
6
7
import numpy as np
goal = int(open('input').read())
BIG_NUM = 1000000
houses = np.zeros(BIG_NUM)
for elf in range(1, BIG_NUM):
    houses[elf:(elf+1)*50:elf] += 11 * elf
print(np.nonzero(houses >= goal)[0][0])

2015 Day 20 Part 01

1
2
3
4
5
6
7
import numpy as np
goal = int(open('input').read())
BIG_NUM = 1000000
houses = np.zeros(BIG_NUM)
for elf in range(1, BIG_NUM):
    houses[elf::elf] += 10 * elf
print(np.nonzero(houses >= goal)[0][0])

2015 Day 19 Part 02

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import re
 
lines = open('input').read().splitlines()
medicine = lines.pop()
 
# LHS, RHS sorted with largest LHS first
replacements = [(l.split()[-1], l.split()[0]) for l in lines if '=>' in l]
replacements.sort(key=lambda x: len(x[0])) # sort by string length
replacements = replacements[::-1] # reverse
 
# largest replacements on medicine first, counting steps
total = 0
while medicine != 'e':
    for lhs, rhs in replacements:
        if lhs in medicine:
            medicine = medicine.replace(lhs, rhs, 1)
            total += 1
            break
print(total)

2015 Day 19 Part 01 v2

1
2
3
4
5
6
7
8
9
10
11
12
13
import re
 
lines= open('input').read().splitlines()
molecule = lines.pop()
lines.pop()
 
possibilities = set()
for l in lines:
    match, _, replace = l.split(' ')
    indices = [m.start() for m in re.finditer(match, molecule)]
    lm = len(match)
    possibilities.update( [molecule[:i] + replace + molecule[i+lm:] for i in indices] )
print(len(possibilities))

2015 Day 19 Part 01 v1

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
lines = open('input').read().splitlines()
molecule = lines.pop()
lines.pop()
 
changes = {}
for l in lines:
    match, _, replace = l.split(' ')
    if match not in changes:
        changes[match] = [replace]
    else:
        changes[match].append(replace)
print(changes)
 
possibilities = set()
for i in range(len(molecule)):
    if i < len(molecule)-1 and molecule[i+1].islower():
        if molecule[i:i+2] in changes:
            for x in changes[molecule[i:i+2]]:
                possibilities.add(molecule[:i] + x + molecule[i+2:])
    else:
        if molecule[i] in changes:
            for x in changes[molecule[i]]:
                possibilities.add(molecule[:i] + x + molecule[i+1:])
print(len(possibilities))

2015 Day 18 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
# Game of life
# https://playgameoflife.com
 
lines = open('input').read().splitlines()
 
# Turning all of the corners on
lines[0] = "#" + lines[0][1:-1] + "#"
lines[-1] = "#" + lines[-1][1:-1] + "#"
 
# Running one round
def round(grid):
    next = []
    for i in range(len(grid)):
        temp  = ""
        for j in range(len(grid[i])):
            count = 0
            up = i > 0
            down = i < len(grid)-1
            left = j > 0
            right = j < len(grid[i])-1
            if up:
                if left:
                    if grid[i-1][j-1] == "#": count += 1
                if grid[i-1][j] == "#": count += 1 
                if right:
                    if grid[i-1][j+1] == "#": count += 1
            if right:
                if grid[i][j+1] == "#": count += 1
            if left:
                if grid[i][j-1] == "#": count += 1
            if down:
                if left:
                    if grid[i+1][j-1] == "#": count += 1
                if grid[i+1][j] == "#": count += 1
                if right:
                    if grid[i+1][j+1] == "#": count += 1
            if grid[i][j] == "." and count == 3: temp += "#"
            elif grid[i][j] == "#" and (count == 2 or count == 3): temp += "#"
            else: temp += "."
        next.append(temp)
    # Turning the corners on
    next[0] = "#" + next[0][1:-1] + "#"
    next[-1] = "#" + next[-1][1:-1] + "#"
    return next
 
# Running for 100 rounds
for i in range(100):
    lines = round(lines)
 
# Getting the final count
num = 0
for i in lines:
    for j in i:
        if j == "#": num += 1
 
print(num)

2015 Day 18 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
# Game of life
# https://playgameoflife.com
 
lines = open('input').read().splitlines()
 
# Running one round
def round(grid):
    next = []
    for i in range(len(grid)):
        temp  = ""
        for j in range(len(grid[i])):
            count = 0
            up = i > 0
            down = i < len(grid)-1
            left = j > 0
            right = j < len(grid[i])-1
            if up:
                if left:
                    if grid[i-1][j-1] == "#": count += 1
                if grid[i-1][j] == "#": count += 1 
                if right:
                    if grid[i-1][j+1] == "#": count += 1
            if right:
                if grid[i][j+1] == "#": count += 1
            if left:
                if grid[i][j-1] == "#": count += 1
            if down:
                if left:
                    if grid[i+1][j-1] == "#": count += 1
                if grid[i+1][j] == "#": count += 1
                if right:
                    if grid[i+1][j+1] == "#": count += 1
            if grid[i][j] == "." and count == 3: temp += "#"
            elif grid[i][j] == "#" and (count == 2 or count == 3): temp += "#"
            else: temp += "."
        next.append(temp)
    return next
 
# Running for 100 rounds
for i in range(100):
    lines = round(lines)
 
# Getting the final count
num = 0
for i in lines:
    for j in i:
        if j == "#": num += 1
 
# Printing out the final result
print("After 100 rounds,", num, "lights are on.")

2015 Day 17 Part 02

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import itertools
 
data = open('input').read().splitlines()
 
sizes = []
for d in data:
    sizes.append (int(d))
#print(sizes)
 
num = 0
min = 999
for i in range(len(sizes)): # range(0,20)
    for subset in itertools.combinations(sizes, i):
        if sum(subset) == 150:
            if len(subset) == min:
                num += 1
                continue
            elif len(subset) < min:
                num = 1
                min = len(subset)
print(num)

2015 Day 17 Part 01

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import itertools
 
data = open('input').read().splitlines()
 
sizes = []
for d in data:
    sizes.append (int(d))
#print(sizes)
 
num = 0
for i in range(len(sizes)): # range(0,20)
    for subset in itertools.combinations(sizes, i):
        if sum(subset) == 150: num += 1
print(num)

2015 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
data = open('input').read().splitlines()
 
ticker = {
'children': 3,
'cats': 7,
'samoyeds': 2,
'pomeranians': 3,
'akitas': 0,
'vizslas': 0,
'goldfish': 5,
'trees': 3,
'cars': 2,
'perfumes': 1}
 
sues = []
for d in data:
    vals = d.replace(":", "").replace(",", "").split(" ")
    sues.append ( {"number":vals[1], 
                    vals[2]:int(vals[3]),
                    vals[4]:int(vals[5]),
                    vals[6]:int(vals[7])} )
#print(sues)
for sue in sues:
    match = True
    checks = list(sue.keys())
    checks.pop(0)
    for i in checks:
        if i in ["cats", "trees"]:
            if sue[i] <= ticker[i]: match = False ; break
        elif i in ["pomeranians", "goldfish"]:
            if sue[i] >= ticker[i]: match = False ; break
        elif sue[i] != ticker[i]: match = False ; break
    if match == True:
        print(sue)

2015 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
data = open('input').read().splitlines()
 
ticker = {
'children': 3,
'cats': 7,
'samoyeds': 2,
'pomeranians': 3,
'akitas': 0,
'vizslas': 0,
'goldfish': 5,
'trees': 3,
'cars': 2,
'perfumes': 1}
 
sues = []
for d in data:
    vals = d.replace(":", "").replace(",", "").split(" ")
    sues.append ( {"number":vals[1], 
                    vals[2]:int(vals[3]),
                    vals[4]:int(vals[5]),
                    vals[6]:int(vals[7])} )
#print(sues)
for sue in sues:
    match = True
    checks = list(sue.keys())
    checks.pop(0)
    for i in checks:
        if sue[i] != ticker[i]:
            match = False
            break   
    if match == True:
        print(sue)

2015 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
data = open('input').read().splitlines()
 
info = []
for d in data:
    _, _, cap, _, dur, _, flv, _, tex, _, cal = d.replace(",", "").split(" ")
    info.append ( [int(cap), int(dur),int(flv), int(tex), int(cal)] )
#print(info)
 
# The break statement is used to terminate the loop immediately
# The continue statement is used to skip the current iteration of the loop
scores = []
for i in range(100):
    for j in range(100):
        if j + i > 100:
            break
        for k in range(100):
            if j + k + i > 100: 
                break
            for l in range(100):
                if j + k + l + i > 100:
                    break
                elif j + k + l + i != 100:
                    continue
                # 500 calory requirement (part 2)
                #if info[0][4]*i + info[1][4]*j + info[2][4]*k + info[3][4]*l != 500: continue
                # Getting the scores
                cap = info[0][0]*i + info[1][0]*j + info[2][0]*k + info[3][0]*l
                dur = info[0][1]*i + info[1][1]*j + info[2][1]*k + info[3][1]*l
                flv = info[0][2]*i + info[1][2]*j + info[2][2]*k + info[3][2]*l
                tex = info[0][3]*i + info[1][3]*j + info[2][3]*k + info[3][3]*l
                if cap < 1 or dur < 1 or flv < 1 or tex < 1: break
                scores.append(cap*dur*flv*tex)
 
# Finding the highest score and returning it
scores.sort()
print(scores[-1])

2015 Day 14 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
data = open('input').read().splitlines()
 
barn = {}
for d in data:
    deer, _, _, speed, _, _, duration, _, _, _, _, _, _, rest, _ =  d.split()
    barn[deer]= (int(speed), int(duration), int(rest))
#print (barn)
 
def dist(barn,total_time):
    dist = {}
    for deer, nums in barn.items():
        speed, duration, rest = nums
        lap_time = duration + rest
        run_time, rem_time = divmod (total_time, lap_time)
        run_distance = speed * (run_time * duration)
        if rem_time > duration:
            run_distance += speed * duration
        else:
            run_distance += speed * rem_time
        dist[deer] = run_distance
    return dist
 
#total_time = 2503
#print(dist(barn,total_time))
#print(max(dist(barn, total_time).values()))
 
points = {}
for reindeer in barn:
    points[reindeer] = 0
for time in range(1, 2504):
    dists = dist(barn, time)
    max_dist = max(dists.values())
    for reindeer in barn:
        if dists[reindeer] == max_dist:
            points[reindeer] += 1
print(max(points[reindeer] for reindeer in barn))

2015 Day 14 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
data = open('input').read().splitlines()
 
barn = {}
for d in data:
    deer, _, _, speed, _, _, duration, _, _, _, _, _, _, rest, _ =  d.split()
    barn[deer] = (int(speed), int(duration), int(rest))
#print (barn)
 
total_time = 2503
dist = {}
 
for deer, nums in barn.items():
    speed, duration, rest = nums
    lap_time = duration + rest
 
    run_time = total_time // lap_time
    rem_time = total_time % lap_time
 
    run_distance = speed * (run_time * duration)
 
    if rem_time > duration:
        run_distance += speed * duration
    else:
        run_distance += speed * rem_time
 
    dist[deer] = run_distance
 
#print(dist)
print(max(dist.values()))

2015 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
import itertools
 
happiness = {}
people = set()
 
data = open('input').read().splitlines()
data = [d.split(' ') for d in data]
 
for d in data:
    #d = d.split()
    person1 = d[0]
    direction = d[2]
    amount = int(d[3])
    person2 = d[10][:-1]
    #print(person1, direction, amount, person2)
    people.add(person1)
    people.add(person2)
    if direction == 'lose':
        happiness[person1+person2] = -amount
    else:
        assert direction == 'gain'
        happiness[person1+person2] = amount
#print(people)
#print(happiness)
 
 
def find_maximum_happiness(people, happiness):
    maximum_happiness = 0
    for arrangement in itertools.permutations(people):
        happiness_gained = 0
        for person1, person2 in zip(arrangement[:-1], arrangement[1:]):
            happiness_gained += happiness[person1 + person2]
            happiness_gained += happiness[person2 + person1]
        # add happiness for first and last pair
        person1 = arrangement[0]
        person2 = arrangement[-1]
        happiness_gained += happiness[person1 + person2]
        happiness_gained += happiness[person2 + person1]
        maximum_happiness = max(maximum_happiness, happiness_gained)
        #print(arrangement, happiness_gained)
    return maximum_happiness
 
print(find_maximum_happiness(people, happiness))
 
# part b
for person in people:
    happiness['Self' + person] = 0
    happiness[person + 'Self'] = 0
people.add('Self')
print(find_maximum_happiness(people, happiness))

2015 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
import json
 
def sum_of_item(item, skip_red=False):
 
    if isinstance(item, list):
        return sum([sum_of_item(i, skip_red) for i in item])
 
    if isinstance(item, dict):
        if skip_red and 'red' in item.values():
            return 0
        return sum([sum_of_item(i, skip_red) for i in item.values()])
 
    if isinstance(item, str):
        return 0
 
    if isinstance(item, int):
        return item
 
with open('input') as f:
    abacus = json.load(f)
print(sum_of_item(abacus))
print(sum_of_item(abacus, skip_red=True))

2015 Day 12 Part 01

1
2
3
4
5
6
7
8
9
10
11
data = open("input").read()
nums = []
curr = ''
for d in data:
    if d in "-1234567890":
        curr += d
    else:
        if len(curr) > 0:
            nums.append(int(curr))
        curr = ''
print(sum(nums))

2015 Day 11 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
data = open("input").read()
data = "hxbxwxba"
alphas = "abcdefghjkmnpqrstuvwxyz"
 
def is_valid(pwd):
 
    # i, o, l shouldn't be in the password
    forbidden = 'iol'
    for letter in forbidden:
        if letter in pwd:
            return False
 
    # increasing consecutive three letters
    condition_1 = False
    for i in range(len(pwd) - 2):
        a, b, c = pwd[i], pwd[i + 1], pwd[i + 2]
        if (alphas.index(b) - alphas.index(a)) == 1 and (alphas.index(c) - alphas.index(b)) == 1:
            condition_1 = True
            break
 
    if not condition_1: return False
 
    # Non overlapping pairs
    total_pairs = 0
    last_pair_ind = 1
    for i in range(len(pwd) - 1):
        a, b = pwd[i], pwd[i + 1]
        if a == b:
            if i == last_pair_ind:
                continue
            else:
                total_pairs += 1
                last_pair_ind = i + 1
 
    if total_pairs < 2: return False
 
    return True
 
def next_password(pwd):
    if pwd == '':
        return ''
    elif pwd[-1] == 'z':
        return next_password(pwd[:-1]) + 'a'
    else:
        return pwd[:-1] + alphas[alphas.index(pwd[-1]) + 1]
 
 
while not is_valid(data):
    data = next_password(data)
 
print(f"Part 1: {data}")
 
data = next_password(data)
 
while not is_valid(data):
    data = next_password(data)
 
print(f"Part 2: {data}")

2015 Day 10 Part 01 + 02

1
2
3
4
5
6
7
8
9
from itertools import groupby
s = open("input").read()
for i in range(40):
    s = ''.join([str(len(list(g)))+k for k, g in groupby(s)])
print(len(s))
# Part Two, 10 additional application
for i in range(10):
    s = ''.join([str(len(list(g)))+k for k, g in groupby(s)])
print(len(s))

2015 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
39
import itertools
 
data = open("input").read().splitlines()
data = [d.split(' ') for d in data]
 
path = {}
locations = []
 
for d in data:
    city1 = d[0]
    city2 = d[2]
    distance = int(d[4])
 
    path[city1 + city2] = distance
    path[city2 + city1] = distance
 
    locations.append(city1)
    locations.append(city2)
 
locations = set(locations)  # find unique locations
 
#print(locations)
#print(path)
 
# find shortest and longest route
shortest = 999999
longest = 0
for route in itertools.permutations(locations):
    route_length = 0
    for city1, city2 in zip(route[:-1], route[1:]):
        route_length += path[city1 + city2]
    if route_length < shortest:
        shortest = route_length
    if route_length > longest:
        longest = route_length
    #print(route, route_length)
 
print("Shortest route length:", shortest)
print("Longest route length:", longest)

2015 Day 08 Part 02

1
2
3
4
5
6
7
8
data = open("input").read().splitlines()
code_total = 0
encode_total = 0
for d in data:
    code_total += len(d)
    encode_line = '"' + d.replace('\\', '\\\\').replace('\"', '\\\"').replace('"', '\"') + '"'
    encode_total += len(encode_line)
print(encode_total - code_total)

2015 Day 08 Part 01

1
2
3
4
5
6
7
data = open("input").read().splitlines()
code_total = 0
mem_total = 0
for d in data:
    code_total += len(d)
    mem_total += len(eval(d))
print(code_total - mem_total)

2015 Day 07 Part 02

1
2
3
4
5
6
7
...
tmp = solve('a')
for d in data:
    signal, wire = d.split(' -> ')
    wires[wire] = signal
wires['b']= tmp
print(solve('a'))

2015 Day 07 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
70
71
72
73
74
75
76
data = [
'123 -> x',
'456 -> y',
'x AND y -> d',
'x OR y -> e',
'x LSHIFT 2 -> f',
'y RSHIFT 2 -> g',
'NOT x -> h',
'NOT y -> i'
]
 
data = open("input").read().splitlines()
 
wires={}
 
AND = ' AND '
OR = ' OR '
LSHIFT = ' LSHIFT '
RSHIFT = ' RSHIFT '
NOT = 'NOT'
 
for d in data:
    signal, wire = d.split(' -> ')
    wires[wire] = signal
#print(wires)
 
def solve(wire):    
    if wire.isnumeric():
        return int(wire)
 
    signal = wires[wire]
 
    if type(signal) == int or signal.isnumeric():
        wires[wire] = int(signal)
 
    else:
        if AND in signal:
            a, b = signal.split(AND)
            wires[wire] = solve(a) & solve(b)
 
        elif OR in signal:
            a, b = signal.split(OR)
            wires[wire] = solve(a) | solve(b)
 
        elif LSHIFT in signal:
            a, b = signal.split(LSHIFT)
            wires[wire] = solve(a) << int(b)
 
        elif RSHIFT in signal:
            a, b = signal.split(RSHIFT)
            wires[wire] = solve(a) >> int(b)
 
        elif NOT in signal:
            _, a = signal.split()
            wires[wire] = ~(solve(a))
            '''
            Tilde (~n) operator is the bitwise negation operator:
            it takes the number n as binary number and “flips” all bits
            e.g. 0 to 1 and 1 to 0 to obtain the complement binary number.
            '''
 
        else:
            wires[wire] = solve(signal)
 
    return wires[wire]
 
'''
print(solve('d'))
print(solve('e'))
print(solve('f'))
print(solve('g'))
print(solve('h'))
print(solve('x'))
print(solve('y'))
'''
print(solve('a'))

2015 Day 06 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
import numpy as np
data = open("input").read().splitlines()
data = [d.split(' ') for d in data]
grid = np.zeros((1000, 1000), 'int32')
for d in data:
    # Turn on/off
    if d[0] == 'turn':
        x1, y1 = d[2].split(',')
        x2, y2 = d[4].split(',')
        x1, x2, y1, y2 = int(x1), int(x2), int(y1), int(y2)
        if d[1] == 'on':
            grid[x1:x2+1, y1:y2+1] += 1
        else:
            assert d[1] == 'off'
            grid[x1:x2+1, y1:y2+1] -= 1
            grid[grid < 0] = 0
    # Toggle
    else:
        assert d[0] == 'toggle'
        x1, y1 = d[1].split(',')
        x2, y2 = d[3].split(',')
        x1, x2, y1, y2 = int(x1), int(x2), int(y1), int(y2)
        grid[x1:x2+1, y1:y2+1] += 2
print(np.sum(grid))

2015 Day 06 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
import numpy as np
data = open("input").read().splitlines()
data = [d.split(' ') for d in data]
grid = np.zeros((1000, 1000), 'int32')
for d in data:
    # Turn on/off
    if d[0] == 'turn':
        x1, y1 = d[2].split(',')
        x2, y2 = d[4].split(',')
        x1, x2, y1, y2 = int(x1), int(x2), int(y1), int(y2)
        if d[1] == 'on':
            grid[x1:x2+1, y1:y2+1] = 1
        else:
            assert d[1] == 'off'
            grid[x1:x2+1, y1:y2+1] = 0
    # Toggle
    else:
        assert d[0] == 'toggle'
        x1, y1 = d[1].split(',')
        x2, y2 = d[3].split(',')
        x1, x2, y1, y2 = int(x1), int(x2), int(y1), int(y2)
        grid[x1:x2+1, y1:y2+1] = np.logical_not(grid[x1:x2+1, y1:y2+1])
print(np.sum(grid))

2015 Day 05 Part 02

1
2
3
from re import search
data = open("input").read().splitlines()
print(sum(1 for d in data if search(r"(..).*\1", d) and search(r"(.).\1", d)))

2015 Day 05 Part 01

1
2
3
from re import search
data = open("input").read().splitlines()
print(sum(1 for d in data if search("([aeiou].*){3}", d) and search(r"(.)\1", d) and not search("ab|cd|pq|xy", d)))

2015 Day 04 Part 02

1
2
3
4
5
6
7
8
9
10
import hashlib
data = open("input").read().strip()
num = 0
while True:
    str2hash = data + str(num)
    result = hashlib.md5(str2hash.encode())
    if result.hexdigest()[0:6] == "000000":
        break
    num+=1
print(num)

2015 Day 04 Part 01

1
2
3
4
5
6
7
8
9
10
import hashlib
data = open("input").read().strip()
num = 0
while True:
    str2hash = data + str(num)
    result = hashlib.md5(str2hash.encode())
    if result.hexdigest()[0:5] == "00000":
        break
    num+=1
print(num)

2015 Day 03 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
import numpy as np
data = open("input").read().strip()
step = {
    '^': (0, 1),
    'v': (0, -1),
    '>': (1, 0),
    '<': (-1, 0),
}
grid = np.zeros((1000, 1000), dtype='int32')
santa_x = 500
santa_y = 500
robot_x = 500
robot_y = 500
grid[santa_x, santa_y] += 1
grid[robot_x, robot_y] += 1
for i, direction in enumerate(data):
    delta_x, delta_y = step[direction]
    if (i % 2) == 0:
        santa_x += delta_x
        santa_y += delta_y
        grid[santa_x, santa_y] += 1
    else:
        robot_x += delta_x
        robot_y += delta_y
        grid[robot_x, robot_y] += 1
print("At least one present:", len(np.nonzero(grid)[0]))

2015 Day 03 Part 01

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import numpy as np
data = open("input").read().strip()
step = {
    '^': (0, 1),
    'v': (0, -1),
    '>': (1, 0),
    '<': (-1, 0),
}
grid = np.zeros((1000, 1000), dtype='int32')
x = 500
y = 500
grid[x, y] = 1
for direction in data:
    delta_x, delta_y = step[direction]
    x += delta_x
    y += delta_y
    grid[x, y] += 1
print("At least one present:", len(np.nonzero(grid)[0]))

2015 Day 02 Part 02

1
2
3
4
5
6
7
8
data = open("input").read().splitlines()
data = [d.split('x') for d in data]
data = [list(map(int, d)) for d in data]
ribbon = 0
for d in data:
    d = sorted(d)
    ribbon += 2*d[0] + 2*d[1] + d[0]*d[1]*d[2]
print (ribbon)

2015 Day 02 Part 01

1
2
3
4
5
6
7
8
data = open("input").read().splitlines()
data = [d.split('x') for d in data]
data = [list(map(int, d)) for d in data]
paper = 0
for d in data:
    sides = [ d[0]*d[1], d[1]*d[2], d[0]*d[2] ]
    paper += 2*sides[0] + 2*sides[1] + 2*sides[2] + min(sides)
print (paper)

2015 Day 01 Part 02

1
2
3
4
5
6
7
8
9
10
11
12
data = open("input").read()
floor = 0
pos = 1
for d in data:
    if d == "(":
        floor+=1
    elif d == ")":
        floor-=1
    if floor < 0:
        break
    pos += 1
print (pos)

2015 Day 01 Part 01

1
2
3
4
5
6
7
8
data = open("input").read()
floor = 0
for d in data:
    if d == "(":
        floor+=1
    elif d == ")":
        floor-=1
print (floor)