This year I'll do my best to master the “Advent of Code”-Challenge. I'll update my Github every day, with my solution.

Star

-- Day 1: Report Repair --

Click here, to go to the challenge.

target = 2020

def parseNumList(filename):
    with open(filename, 'r') as file:
        return [int(line) for line in file]
        
def part_one(input, target):
    for n in input:
        if (target - n) in input:  # If 2020 minus $number is in the input, we found the missing part
            return n*(target-n)

def part_two(input, target):
    for n in input:
        b = part_one(input, target - n)
        if b:
            return n*b
            
input_data = parseNumList('input.txt')

print("Part 1: ", part_one(input_data, target))
print("Part 2: ", part_two(input_data, target))

-- Day 2: Password Philosophy --

Click here, to go to the challenge.

def line_to_in(in_line):
    out = {}
    in_line = in_line.split(" ")
    out["min"] = int(in_line[0].split("-")[0])
    out["max"] = int(in_line[0].split("-")[1])
    out["char"] = in_line[1].replace(':', '')
    out["pass"] = in_line[2]
    return out

def parse_pw_data(filename):
    with open(filename, 'r') as file:
        return [line_to_in(line) for line in file]

def part_one(in_d):
    c = 0
    for char in in_d["pass"]:
        if char == in_d["char"]:
            c += 1
            if c > in_d["max"]:
                return False
    return True if c >= in_d["min"] else False

def part_two(in_d):
    o = 1  # Toboggan Corporate Policies have no concept of "index zero"!
    return True if (in_d["pass"][in_d["min"]-o] == in_d["char"]) is not (in_d["pass"][in_d["max"]-o] == in_d["char"]) else False

input_data = parse_pw_data('input.txt')
p1_c = 0
p2_c = 0

for in_d in input_data:
    p1_c += 1 if part_one(in_d) else 0
    p2_c += 1 if part_two(in_d) else 0
    
print("Part1: ", p1_c, " - Part2: ", p2_c)

-- Day 3: Toboggan Trajectory --

Click here, to go to the challenge.

from functools import reduce
filepath = 'input.txt'
slopes = [(1, 1), (3, 1), (5, 1), (7, 1), (1, 2)]

def expand_map(in_map):
    return [x + x for x in in_map]

def get_count_for_slope(slope, w_map):
    c = 0
    pos = (0, 0)
    while pos[1] < len(w_map)-1:
        pos = tuple(map(sum, zip(pos, slope)))
        if len(w_map[0]) <= pos[0]:
            w_map = expand_map(w_map)
        if w_map[pos[1]][pos[0]] is "#":
            c += 1
    return c

def part_one(slope, w_map):
    return get_count_for_slope(slope, w_map)

def part_two(slopes, w_map):
    return reduce(lambda x, y: x*y, [get_count_for_slope(s, w_map) for s in slopes])

with open(filepath, 'r') as file:
    work_map = [list(line.replace("\n", "")) for line in file]

print('Part1: ', part_one((3, 1), work_map), ' - Part2: ', part_two(slopes, work_map))

-- Day 4: Passport Processing --

Click here, to go to the challenge.

import re

def is_digit_in_range(in_str, span):
    return True if in_str.isdigit() and span[0] <= int(in_str) <= span[1] else False

def is_digit_with_len(in_str, t_len):
    return True if len(in_str) is t_len and in_str.isdigit() else False

def check_pass_for_hgt(passport):
    if re.compile(r'^\d{3}cm$').match(passport['hgt']):
        return True if 150 <= int(passport['hgt'].replace("cm", "")) <= 193 else False
    elif re.compile(r'^\d{2,3}in$').match(passport['hgt']):
        return True if 59 <= int(passport['hgt'].replace("in", "")) <= 76 else False
    return False

def check_pass_for_hcl(passport):
    return True if re.compile(r'^#\w{6}$').match(passport['hcl']) else False

def check_pass_for_ecl(passport):
    check_list = ["amb", "blu", "brn", "gry", "grn", "hzl", "oth"]
    return True if passport['ecl'] in check_list else False

def check_pass(passport, to_check):
    return True if all(elem in passport.keys() for elem in to_check) \
        and is_digit_in_range(passport['byr'], (1920, 2002)) \
        and is_digit_in_range(passport['iyr'], (2010, 2020)) \
        and is_digit_in_range(passport['eyr'], (2020, 2030)) \
        and is_digit_with_len(passport['pid'], 9)  \
        and check_pass_for_ecl(passport) \
        and check_pass_for_hcl(passport) \
        and check_pass_for_hgt(passport) else False

to_check = ["byr", "iyr", "eyr", "hgt", "hcl", "ecl", "pid"]  # cid is optional

with open('input.txt') as file:
    passports = [{item.split(":")[0]:item.split(":")[1] for item in x.replace(
        "\n", " ").split(" ") if len(item.split(":")) > 1} for x in file.read().split("\n\n")]

prat_one = len(list(filter(lambda d: all(elem in d.keys() for elem in to_check), passports)))
prat_two = len(list(filter(lambda d: check_pass(d, to_check), passports)))
print("Part1: ", prat_one, " - Part2: ", prat_two)

-- Day 5: Binary Boarding --

Click here, to go to the challenge.

import decimal
decimal.getcontext().rounding = decimal.ROUND_HALF_UP

input_file = 'input.txt'
plane_col_max = 7
plane_row_max = 127

def to_int(in_num):
    return int(decimal.Decimal(in_num).to_integral_value())

def calc_pos(in_str, max_idx, up_c, down_c):
    span = (0, max_idx)
    for c in in_str:
        if c == up_c:
            span = (span[0]+to_int((span[1]-span[0])/2), span[1])
        elif c == down_c:
            span = (span[0], span[1]-to_int((span[1]-span[0])/2))
    return min(span)

def calc_id(row, col):
    return row*8+col

def part_one(file_path):
    with open(file_path) as file:
        return sorted([calc_id(calc_pos(f[:7], plane_row_max, 'B', 'F'), calc_pos(f[7:], plane_col_max, 'R', 'L')) for f in file])

def part_two(p1_out):
    return set(range(p1_out[0], p1_out[-1])) - set(p1_out)

p1_out = part_one(input_file)
p2_out = part_two(p1_out)
print('Part1: ', max(p1_out), ' - Part2: ', max(p2_out))

-- Day 6: Custom Customs --

Click here, to go to the challenge.

from functools import reduce

def part_one(set_list):
    return len(reduce(set.union, set_list))

def part_two(set_list):
    return len(reduce(set.intersection, set_list))

with open('input.txt') as file:
    p1_c = 0
    p2_c = 0
    parsed_input = [[set(x) for x in line.split('\n')] for line in file.read().split("\n\n")]
    for decl in parsed_input:
        p1_c += part_one(decl)
        p2_c += part_two(decl)

print('Part1: ', p1_c, ' - Part2: ', p2_c)

-- Day 7: Handy Haversacks --

Click here, to go to the challenge.

import re

def parse_input_line(in_str):
    def parse_contain(contain_str):
        regex = r"(\d)\s(\w*\s\w*)\D*"
        return {x[1]: int(x[0]) for x in re.findall(regex, contain_str)}

    regex = r"^(.*) bags contain (.*)."
    regex_res = re.search(regex, in_str)
    return regex_res.group(1), parse_contain(regex_res.group(2))

def part_one(in_dict, search_str):
    out_set = set()
    for bag, colors in rule_dict.items():
        if search_str in colors:
            out_set.add(bag)
            out_set.update(part_one(in_dict, bag))
    return out_set

def part_two(in_dict, search_str):
    sum = 0
    for k, v in in_dict[search_str].items():
        sum += v + part_two(in_dict, k)*v
    return sum

with open('input.txt') as file:
    rule_dict = {k: v for k, v in (parse_input_line(line) for line in file)}

print('Part1: ', len(part_one(rule_dict, 'shiny gold')), ' - Part2: ', part_two(rule_dict, 'shiny gold'))

-- Day 8: Handheld Halting --

Click here, to go to the challenge.

def accumulator(in_data):
    done = []
    acc = 0
    cursor = 0
    while cursor not in done:
        if cursor >= len(in_data):
            return {'acc': acc, 'terminated': True, 'positions': done}
        cur_task = in_data[cursor]
        done.append(cursor)
        if cur_task[0] == 'jmp':
            cursor += cur_task[1]
        else:
            if cur_task[0] == 'acc':
                acc += cur_task[1]
            cursor += 1
    return {'acc': acc, 'terminated': False, 'positions': done}

def part_one(in_data):
    return accumulator(in_data)

def part_two(in_data):
    swap = {"nop": "jmp", "jmp": "nop"}
    def modify_in_data(swap,in_data, idx):
        n_in_data = in_data.copy()
        n_in_data[idx] = (swap[n_in_data[idx][0]],n_in_data[idx][1])
        return n_in_data

    # Do a run to find potential swap-candidates:
    acc_jmp = [i for i in accumulator(in_data)['positions'] if in_data[i][0] in swap.keys()]
    for i in acc_jmp:
        out = accumulator(modify_in_data(swap, in_data, i))
        if out['terminated'] is True:
            return out

with open('input.txt') as file:
    in_data = [(op,int(arg)) for op, arg in (line.split() for line in file)]

print('Part1: ', part_one(in_data)['acc'], ' - Part2: ', part_two(in_data)['acc'])

-- Day 9: Encoding Error --

Click here, to go to the challenge.

def check_in_range(watch_list, val):
    for i in watch_list:
        if val-i in watch_list:
            return True

def find_wrong_number(in_data, back_look=25):
    for cursor in range(back_look, len(in_data)):
        if not check_in_range(in_data[cursor-back_look:cursor], in_data[cursor]):
            return in_data[cursor]

def find_encryption_weakness(in_data, invalid_val):
    for l in range(2, len(in_data)):
        for i in range(len(in_data)-l):
            if sum(in_data[i:i+l]) == invalid_val:
                return min(in_data[i:i+l]) + max(in_data[i:i+l])

with open('input.txt') as file:
    in_data = [int(i) for i in file]
    p1 = find_wrong_number(in_data)
    p2 = find_encryption_weakness(in_data, p1)
    print('Part1: ', p1, ' - Part2: ', p2)

-- Day 10: Encoding Error --

Click here, to go to the challenge.

from collections import Counter

def part_one(adapters):
    adapters = [0]+adapters+[adapters[-1]+3]
    differeces = [adapters[i+1]-x for i,
                  x in enumerate(adapters) if i+1 < len(adapters)]
    return differeces.count(1)*differeces.count(3)

def part_two(jolts):
    jolts.append(jolts[-1] + 3)
    dp = Counter()
    dp[0] = 1
    for jolt in jolts:
        dp[jolt] = dp[jolt - 1] + dp[jolt - 2] + dp[jolt - 3]
    return dp[jolts[-1]]

with open('input.txt') as file:
    adapters = [int(i) for i in file]
    adapters.sort()
    print('Part1: ', part_one(adapters), ' - Part2: ', part_two(adapters))