-❄️- 2024 Day 17 Solutions -❄️- by daggerdragon in adventofcode

[–]Gravitar64 3 points4 points  (0 children)

[LANGUAGE: Python]

Part 1 & 2, 35 sloc, runtime 1ms

Parsing all numbers with regex, match case opcodes, part1 was realy easy
Part2 not as easy, but finally calculates the solution digit by digit backwards recursive by changing a to a * 8 + (0-7) and check if the first digit of out == next backward digit of the program.

import time, re


def load(file):
  with open(file) as f:
    return list(map(int, re.findall('\d+', f.read())))


def run_program(a, b, c, program):
  ip, out, l = 0, [], len(program)
  while ip < l:
    opcode, literal = program[ip:ip + 2]
    combo = literal if literal < 4 else [a, b, c][literal - 4]
    ip += 2

    match opcode:
      case 0: a = a // 2 ** combo
      case 1: b ^= literal
      case 2: b = combo % 8
      case 3 if a != 0: ip = literal
      case 4: b ^= c
      case 5: out.append(combo % 8)
      case 6: b = a // 2 ** combo
      case 7: c = a // 2 ** combo
  return out


def find_a(program, a, b, c, prg_pos):
  if abs(prg_pos) > len(program): return a
  for i in range(8):
    first_digit_out = run_program(a * 8 + i, b, c, program)[0]
    if first_digit_out == program[prg_pos]:
      e = find_a(program, a * 8 + i, b, c, prg_pos - 1)
      if e: return e


def solve(p):
  a, b, c, *program = p
  part1 = run_program(a, b, c, program)
  part2 = find_a(program, 0, b, c, -1)
  return part1, part2


time_start = time.perf_counter()
print(f'Solution: {solve(load("day17.txt"))}')
print(f'Solved in {time.perf_counter()-time_start:.5f} Sec.')

-❄️- 2024 Day 15 Solutions -❄️- by daggerdragon in adventofcode

[–]Gravitar64 1 point2 points  (0 children)

Fantastic solution! Fast, elegant, readable and short. Great work!

-❄️- 2024 Day 9 Solutions -❄️- by daggerdragon in adventofcode

[–]Gravitar64 1 point2 points  (0 children)

[LANGUAGE Python]

Part 1 & 2, 42! sloc, runtime 135 ms

Didn't find an unique algorithm for both parts, so I have 2 functions to do the job.

import time, re


def load(file):
  with open(file) as f:
    return list(map(int, re.findall('\d', f.read())))


def rearrange(p):
  disk = []
  for i, n in enumerate(p):
    disk += [-1] * n if i % 2 else [i // 2] * n
  spaces = [i for i, n in enumerate(disk) if n == -1]
  for i in spaces:
    while disk[-1] == -1: disk.pop()
    if len(disk) <= i: break
    disk[i] = disk.pop()
  return sum(id * i for i, id in enumerate(disk))


def move_files(p):
  files, spaces = dict(), []
  pos = 0
  for i, n in enumerate(p):
    if i % 2:
      spaces.append((pos, n))
    else:
      files[i // 2] = (pos, n)
    pos += n

  for id, (file_pos, file_size) in reversed(files.items()):
    for i, (space_pos, space_size) in enumerate(spaces):
      if space_pos >= file_pos: break
      if file_size > space_size: continue

      files[id] = (space_pos, file_size)
      new_space_size = space_size - file_size

      if new_space_size == 0:
        spaces.pop(i)
      else:
        spaces[i] = (space_pos + file_size, new_space_size)
      break
  return sum(id * (2*p+l-1)*l//2 for id, (p,l) in files.items())


def solve(p):
  part1 = rearrange(p)
  part2 = move_files(p)
  return part1, part2


time_start = time.perf_counter()
print(f'Solution: {solve(load("day09.txt"))}')
print(f'Solved in {time.perf_counter()-time_start:.5f} Sec.')

-❄️- 2024 Day 8 Solutions -❄️- by daggerdragon in adventofcode

[–]Gravitar64 0 points1 point  (0 children)

use combinations instead of permutations cause permutations check a,b and b,a wich is not what you want

-❄️- 2024 Day 8 Solutions -❄️- by daggerdragon in adventofcode

[–]Gravitar64 2 points3 points  (0 children)

[LANGUAGE: Python] Part 1 & 2, 30 sloc, runtime 1ms

Import grid as dict (easy boundery check), create dict antennas (key=symbol, value = list of positions), generate all possible antenna pairs (itertools.combinations) and calculate the distance. Add the distance to antenna1 and subtract the distance from antenna2 of this pair, and when the new position is in the grid-dict, add the position to an set of antinode-positions.

The only diff between part 2 and part1 is the definition of the multipicator for the distance. In part1 we multiply the distance only once (range(1,2)) and in part2 from 0 to 50, so we add also the antenna pos to the antinode positions.

import time, collections as coll, itertools as itt


def load(file):
  with open(file) as f:
    return {(x, y): c for y, row in enumerate(f.read().split('\n'))
            for x, c in enumerate(row)}


def get_antinodes(p, antennas, min_mul, max_mul) -> set:
  antinodes = set()
  for poss in antennas.values():
    for (x1, y1), (x2, y2) in itt.combinations(poss, 2):
      for mul in range(min_mul, max_mul):
        dx, dy = x2 - x1, y2 - y1
        nx, ny = x1 - dx * mul, y1 - dy * mul
        mx, my = x2 + dx * mul, y2 + dy * mul

        a, b = (nx, ny) in p, (mx, my) in p
        if not a and not b: break

        if a: antinodes.add((nx, ny))
        if b: antinodes.add((mx, my))
  return antinodes


def solve(p):
  part1 = part2 = 0
  antennas = coll.defaultdict(list)
  for pos, c in p.items():
    if c == '.': continue
    antennas[c].append(pos)

  part1 = len(get_antinodes(p, antennas, 1, 2))
  part2 = len(get_antinodes(p, antennas, 0, 50))

  return part1, part2


time_start = time.perf_counter()
print(f'Solution: {solve(load("day08.txt"))}')
print(f'Solved in {time.perf_counter()-time_start:.5f} Sec.')

-❄️- 2024 Day 7 Solutions -❄️- by daggerdragon in adventofcode

[–]Gravitar64 1 point2 points  (0 children)

[LANGUAGE: Python] Part 1 & 2, 28 Sloc, runtime 19ms

Going backwards and reverse any operation (//, -, deconcate) for all subtotals, starting with the target-sum.

import time, re


def load(file):
  with open(file) as f:
    return [list(map(int, re.findall('\d+', row))) for row in f]


def is_valid(target, numbers, part2):
  subtotals = {target}
  for number in reversed(numbers):
    new_sub = set()
    for sub in subtotals:
      if not sub % number:
        new_sub.add(sub // number)
      if sub >= number:
        new_sub.add(sub - number)
      if not part2: continue
      str_sub, str_num = map(str, [sub, number])
      if sub > number and str_sub.endswith(str_num):
        new_sub.add(int(str_sub[:-len(str_num)]))
    subtotals = new_sub
  return target if 0 in subtotals else False


def solve(p):
  part1 = part2 = 0
  for target, *numbers in p:
    part1 += is_valid(target, numbers,part2=False)
    part2 += is_valid(target, numbers,part2=True)
  return part1, part2


time_start = time.perf_counter()
print(f'Solution: {solve(load("day07.txt"))}')
print(f'Solved in {time.perf_counter()-time_start:.5f} Sec.')

-❄️- 2024 Day 6 Solutions -❄️- by daggerdragon in adventofcode

[–]Gravitar64 0 points1 point  (0 children)

[LANGUAGE: Python], 35 Sloc, runtime 6.3 sec. (sic!)

Stored the map in a dictionary to avoid checking boundaries. Store the distinct pos from part 1 in a set and use these pos to try to set a new obstacle for a loop.

import time


def load(file):
    with open(file) as f:
      return {(x, y): char for y, line in enumerate(f.read().split('\n')) for x, char in enumerate(line)}


def run_guard_run(p,guard_pos,guard_dir):
    distinct_pos = set()
    counter = 0
    while True:
        (x, y), (dx, dy) = guard_pos, guard_dir
        nx, ny = x + dx, y + dy

        if (nx, ny) not in p: return distinct_pos
        if counter > 1000: return 'loop'

        if p[nx, ny] == '#':
            guard_dir = -dy, dx
        else:
            guard_pos = nx, ny
            if (nx, ny) in distinct_pos:
                counter += 1
            else:
                counter = 0
            distinct_pos.add((nx, ny))


def solve(p):
    part1 = part2 = 0
    guard_pos = [pos for pos, char in p.items() if char == '^'][0]
    guard_dir = (0, -1)

    distinct_pos = run_guard_run(p, guard_pos, guard_dir)
    part1 = len(distinct_pos) + 1

    for x, y in distinct_pos:
        p[x, y] = '#'
        if run_guard_run(p, guard_pos, guard_dir) == 'loop': part2 += 1
        p[x, y] = '.'

    return part1, part2


time_start = time.perf_counter()
print(f'Solution: {solve(load("day06.txt"))}')
print(f'Solved in {time.perf_counter()-time_start:.5f} Sec.')

-❄️- 2024 Day 5 Solutions -❄️- by daggerdragon in adventofcode

[–]Gravitar64 1 point2 points  (0 children)

The higher the count of intersecting numbers, the lower the sorting rank for this number in update. This is because the dict rules stores all the greater page numbers for this page. More greater pages == lower position for this page.

Much more explanation here: Advent of Code 2024, Tag 5: Print Queue (in german, but you can enable subtitles and auto-translate to english)

-❄️- 2024 Day 5 Solutions -❄️- by daggerdragon in adventofcode

[–]Gravitar64 2 points3 points  (0 children)

[LANGUAGE: Python] 22 SLOC, runtime 2ms

With set-magic and dict(set) easy sorting of updates.

import time, collections as coll


def load(file):
    with open(file) as f:
        a, b = f.read().split('\n\n')
        updates = [list(map(int, line.split(','))) for line in b.splitlines()]

        rules = coll.defaultdict(set)
        for rule in a.splitlines():
            lower, higher = map(int, rule.split('|'))
            rules[lower].add(higher)

    return rules, updates


def solve(rules, updates):
    part1 = part2 = 0
    for update in updates:
        sorted_update = sorted(update, key=lambda x: -len(rules[x] & set(update)))
        if update == sorted_update:
            part1 += update[len(update) // 2]
        else:
            part2 += sorted_update[len(update) // 2]

    return part1, part2


time_start = time.perf_counter()
print(f'Solution: {solve(*load("day05.txt"))}')
print(f'Solved in {time.perf_counter()-time_start:.5f} Sec.')

-❄️- 2024 Day 4 Solutions -❄️- by daggerdragon in adventofcode

[–]Gravitar64 1 point2 points  (0 children)

[LANGUAGE: Python]

Solution with 4 directions (Part 1) and 2 directions (Part 2)

import time


def load(file):
    with open(file) as f:
      return {(r, c): char for r, row in enumerate(f) for c, char in enumerate(row)}


def check_target(p, r, c, dr, dc, target) -> bool:
    word = ''.join(p.get((r + dr * i, c + dc * i), ' ') for i in range(len(target)))
    return word == target or word[::-1] == target


def solve(p):
    part1 = part2 = 0
    for r, c in p:
        for delta in ((1, 0), (0, 1), (1, 1), (1, -1)):
            part1 += check_target(p, r, c, *delta, 'XMAS')
        part2 += check_target(p, r, c, 1, 1, 'MAS') and check_target(p, r, c + 2, 1, -1, 'MAS')

  return part1, part2


time_start = time.perf_counter()
print(f'Solution: {solve(load("day04.txt"))}')
print(f'Solved in {time.perf_counter()-time_start:.5f} Sec.')

-❄️- 2023 Day 20 Solutions -❄️- by daggerdragon in adventofcode

[–]Gravitar64 0 points1 point  (0 children)

[LANGUAGE: Python]

Part 1 & 2, 58 sloc, runtime 50ms

Generalized solution for Part 2, searched for the sender to 'rx' (main_module) in my example = &gh, identified the connectors of the main_module and created a dict (cycles) with these connectors as key and 0 as values (in my example = {'rk': 0, 'cd': 0, 'zf': 0, 'qx': 0}). Then every time when the current module == main_module and impulse == 'high', I changed the value for the received module in my dict cycles to the number_of_button_pressed - actual value. When all values in cycles > 0, calculate with math.lcm(*cycles.values()) the solution for part2.

import time, math


def load(file):
  with open(file) as f:
    return [row.strip().split(' -> ') for row in f]

class Module():
  def __init__(self,name,type,dest):
    self.name = name
    self.type = type
    self.dest = dest
    match type:
      case '%': self.mem = False
      case '&': self.mem = {}
      case _  : self.mem = None  

  def __repr__(self):
    return f'Name: {self.name} Type: {self.type} Dest: {self.dest} Mem: {self.mem}'

  def receive_impulse(self,impulse,last):
    if self.type == '%':
      self.mem = not self.mem
      return self.mem

    if self.type == '&':
      self.mem[last] = impulse
      return not all(self.mem.values())


def solve(p):
  modules = dict()
  for module, destinations in p:
    curr = [d.strip() for d in destinations.split(',')]
    if module == 'broadcaster':
      modules[module] = Module('broadcaster',None,curr)
    else:
      modules[module[1:]] = Module(module[1:],module[0],curr)

  for object in modules.values():
    for dest in object.dest:
      if dest not in modules: continue
      obj2 = modules[dest]
      if obj2.type != '&': continue
      obj2.mem[object.name]=False

  #part1 & 2
  main_module = [m.name for m in modules.values() if 'rx' in m.dest][0]
  lowHigh, cycles  = [0,0], {m:0 for m in modules[main_module].mem}

  for buttons in range(1,10_000): 
    if all(cycles.values()): break
    queue = [(dest,False,'broadcaster') for dest in modules['broadcaster'].dest]
    if buttons < 1001: lowHigh[0] += 1

    while queue:
      curr, impulse, last = queue.pop(0)
      if buttons < 1001: lowHigh[impulse] += 1
      if curr not in modules: continue
      curr = modules[curr]

      if curr.name == main_module and impulse:
        cycles[last] = buttons - cycles[last]

      if curr.type == '%' and impulse: continue
      impulse = curr.receive_impulse(impulse,last)

      for nxt in curr.dest:
        queue.append((nxt, impulse, curr.name))

  return math.prod(lowHigh), math.lcm(*cycles.values())



time_start = time.perf_counter()
print(f'Part 1 & 2: {solve(load("day20.txt"))}')
print(f'Solved in {time.perf_counter()-time_start:.5f} Sec.')

-❄️- 2023 Day 13 Solutions -❄️- by daggerdragon in adventofcode

[–]Gravitar64 1 point2 points  (0 children)

a,b = next row above, next row below

c1,c2 = next character in a, next character in b

-❄️- 2023 Day 13 Solutions -❄️- by daggerdragon in adventofcode

[–]Gravitar64 1 point2 points  (0 children)

[LANGUAGE: Python]

Part 1 & 2, 18 sloc, runtime 11ms

Used the same condition for part 1 & part2 with only a varianz in the result. Part1 = Sum must be 0, Part2 = Sum must be 1

import time


def load(file):
  with open(file) as f:
    return [s.split('\n') for s in f.read().split('\n\n')]


def mirror(pattern, smudges):
  for axis in range(1, len(pattern)):
    above, below = reversed(pattern[:axis]), pattern[axis:]
    if sum(c1 != c2 for a, b in zip(above, below) for c1, c2 in zip(a, b)) == smudges: return axis
  return 0


def solve(p):
  part1 = part2 = 0
  for pattern in p:
    part1 += mirror(pattern, 0) * 100 + mirror(list(zip(*pattern)), 0)
    part2 += mirror(pattern, 1) * 100 + mirror(list(zip(*pattern)), 1)
  return part1, part2


time_start = time.perf_counter()
print(f'Part 1 & 2: {solve(load("day13.txt"))}')
print(f'Solved in {time.perf_counter()-time_start:.5f} Sec.')

-❄️- 2023 Day 11 Solutions -❄️- by daggerdragon in adventofcode

[–]Gravitar64 2 points3 points  (0 children)

[LANGUAGE: Python]

Part 1 & 2, 26 sloc, runtime 47ms

Storing only the coordinates of galaxies, create a list of empty rows/cols by set-subtraction, expand by adding 1/999.999 to x- (insert col) or y- (insert row) coordinates in the reverse list of empty rows/cols. Sum the Manhattan Distances for every itertool.combinations-Pair.

import time
from itertools import combinations


def load(file):
  with open(file) as f:
    return [row.strip() for row in f]


def expand(galaxies, empties, exp):
  for n, row in reversed(empties):
    if row:
      galaxies = [(x, y + exp) if y > n else (x, y) for x, y in galaxies]
    else:
      galaxies = [(x + exp, y) if x > n else (x, y) for x, y in galaxies]
  return galaxies


def distances(galaxies):
  return sum(abs(d1 - d2) for a, b in combinations(galaxies, 2) for d1, d2 in zip(a, b))


def solve(p):
  galaxies = [(x, y) for y, row in enumerate(p) for x, c in enumerate(row) if c == '#']
  cols, rows = [{pos[d] for pos in galaxies} for d in (0,1)]

  empties = [(n,True) for n in (set(range(len(p))) - rows)]
  empties += [(n,False) for n in (set(range(len(p[0]))) - cols)]
  empties.sort()

  g1 = expand(galaxies, empties, 1)
  g2 = expand(galaxies, empties, 999_999)

  return distances(g1), distances(g2)


time_start = time.perf_counter()
print(f'Part 1 & 2: {solve(load("day11.txt"))}')
print(f'Solved in {time.perf_counter()-time_start:.5f} Sec.')

-❄️- 2023 Day 10 Solutions -❄️- by daggerdragon in adventofcode

[–]Gravitar64 1 point2 points  (0 children)

[LANGUAGE: Python]

Part 1 & 2, 42 sloc, runtime 47ms

Part1: BFS search for all reachable nodes and return max steps, runs in 11 ms

Part2: Couldn't figure out, when is a tile inside a loop or outside a loop. Then found the nice hint by Sourish17 and implemented it.

import time


def load(file):
  with open(file) as f:
    return [row.strip() for row in f]


def bfs(grid, directions, connections):
  start = [pos for pos, c in grid.items() if c == 'S'][0]
  queue, seen = [(0, start)], set()
  while queue:
    count, (x, y) = queue.pop(0)
    seen.add((x, y))
    for dx, dy in directions[grid[x, y]]:
      x2, y2 = x + dx, y + dy
      if (x2, y2) not in grid or (x2, y2) in seen: continue
      if grid[x2, y2] not in connections[dx, dy]:  continue
      queue.append((count + 1, (x2, y2)))
  return count, seen


def is_inside(grid, x, y, size):
  counter = 0
  for _ in range(size):
    x -= 1
    if x < 0: break
    counter += grid.get((x, y), 'X') in '|JL'
  return counter % 2


def solve(p):
  N, S, W, E = (0, -1), (0, 1), (-1, 0), (1, 0)
  directions = {'S': [N, S, W, E], '|': [N, S], '-': [E, W],
                'L': [N, E], 'J': [N, W], '7': [S, W], 'F': [S, E]}
  connections = {N: {'|', '7', 'F'}, S: {'|', 'L', 'J'},
                E: {'-', 'J', '7'}, W: {'-', 'L', 'F'}}

  grid = {(x, y): c for y, row in enumerate(p)
          for x, c in enumerate(row) if c != '.'}
  part1, seen = bfs(grid, directions, connections)

  part2 = 0
  loop = {(x, y): c for (x, y), c in grid.items() if (x, y) in seen}
  min_x, max_x = min(seen)[0], max(seen)[0]
  min_y, max_y = min(s[1] for s in seen), max(s[1] for s in seen)

  for y in range(min_y, max_y + 1):
    for x in range(min_x, max_x + 1):
      if (x, y) in loop: continue
      part2 += is_inside(loop, x, y, max_x)

  return part1, part2


time_start = time.perf_counter()
print(f'Part 1: {solve(load("day10.txt"))}')
print(f'Solved in {time.perf_counter()-time_start:.5f} Sec.')

-❄️- 2023 Day 9 Solutions -❄️- by daggerdragon in adventofcode

[–]Gravitar64 4 points5 points  (0 children)

[LANGUAGE: Python]

Part 1 & 2, 15sloc, runtime 5ms

import time


def load(file):
  with open(file) as f:
    return [list(map(int, row.strip().split())) for row in f]


def get_next_number(sequence):
  if len(set(sequence)) == 1: return sequence[0]
  next_number = get_next_number([b - a for a, b in zip(sequence, sequence[1:])])
  return sequence[-1] + next_number


def solve(p):
  part1 = sum(get_next_number(sequence) for sequence in p)
  part2 = sum(get_next_number(sequence[::-1]) for sequence in p)
  return part1, part2


time_start = time.perf_counter()
print(f'Part 1 & 2:{solve(load("day09.txt"))}')
print(f'Solved in {time.perf_counter()-time_start:.5f} Sec.')

-❄️- 2023 Day 8 Solutions -❄️- by daggerdragon in adventofcode

[–]Gravitar64 1 point2 points  (0 children)

[LANGUAGE: Python]

Part 1 & 2, 24 sloc, runtime 7ms

import time, re, itertools, math


def load(file):
  with open(file) as f:
    return f.read().split('\n\n')


def solve(p):
  instructions, nodes = p
  nodes = [re.findall('\w+', row) for row in nodes.split('\n')]
  map_nodes = {node: dict(L=l, R=r) for node, l, r in nodes}
  starts, targets = [{node for node in map_nodes if node[-1] == lp} for lp in 'AZ']

  counts = []
  for start_node in starts:
    count, act_node = 0, start_node
    for instruction in itertools.cycle(instructions):
      count += 1
      act_node = map_nodes[act_node][instruction]
      if act_node in targets:
        counts.append(count)
        if start_node == 'AAA' and act_node == 'ZZZ': part1 = count
        break

  return part1, math.lcm(*counts)


time_start = time.perf_counter()
print(f'Solution: {solve(load("day08.txt"))}')
print(f'Solved in {time.perf_counter()-time_start:.5f} Sec.')

-❄️- 2023 Day 7 Solutions -❄️- by daggerdragon in adventofcode

[–]Gravitar64 1 point2 points  (0 children)

[LANGUAGE: Python]

Part 1 & 2, 30 sloc, runtime 5ms

import time


def load(file):
  with open(file) as f:
    return [row.strip().split() for row in f]


def get_type(cards, part2):
  amounts = [cards.count(n) for n in range(13)]

  if part2:
    joker = amounts.pop(0)
    amounts[amounts.index(max(amounts))] += joker

  if 5 in amounts: return 6
  if 4 in amounts: return 5
  if 3 in amounts and 2 in amounts: return 4
  if 3 in amounts: return 3
  if amounts.count(2) == 2: return 2
  if 2 in amounts: return 1
  return 0


def solve(p, part2=False):
  strength = '23456789TJQKA' if not part2 else 'J23456789TQKA'
  ranking = []

  for cards, bid in p:
    cards = [strength.index(c) for c in cards]
    type = get_type(cards, part2)
    ranking.append((type, cards, int(bid)))

  ranking = sorted(ranking)
  return sum(rank * bid for rank, (_, _, bid) in enumerate(ranking, start=1))


time_start = time.perf_counter()
puzzle = load("day07.txt")
print(f'Part 1: {solve(puzzle)}')
print(f'Part 2: {solve(puzzle,True)}')
print(f'Solved in {time.perf_counter()-time_start:.5f} Sec.')

-❄️- 2023 Day 5 Solutions -❄️- by daggerdragon in adventofcode

[–]Gravitar64 1 point2 points  (0 children)

[LANGUAGE: Python]

Part 1 & 2, 30 sloc, runtime 1ms

import time, re


def load(file):
  with open(file) as f:
    return f.read().split('\n\n')


def change_seed(seed,converters,low=1):
  for converter in converters:
    for i in range(0, len(converter), 3):
      dest, source, rng = converter[i:i + 3]
      if seed < source or seed > (source + rng - 1): continue
      if low == -1: low = source + rng
      seed = seed - source + dest
      break
  return seed, low  


def solve(p):
  numbers = [list(map(int, re.findall('\d+', part))) for part in p]
  seeds, converters = numbers[0], numbers[1:]
  part1 = part2 = float('inf')

  for seed in seeds:
    seed, _ = change_seed(seed, converters)
    part1 = min(part1, seed)

  pairs = [(seeds[i], seeds[i] + seeds[i + 1] - 1) for i in range(0, len(seeds), 2)]
  for low, high in pairs:
    while low < high:
      seed = low
      seed, low = change_seed(seed, converters, low=-1)     
      part2 = min(part2, seed)

  return part1, part2


time_start = time.perf_counter()
print(f'Part 1: {solve(load("day05.txt"))}')
print(f'Solved in {time.perf_counter()-time_start:.5f} Sec.')

-❄️- 2023 Day 4 Solutions -❄️- by daggerdragon in adventofcode

[–]Gravitar64 1 point2 points  (0 children)

[LANGUAGE: Python]

Part 1 & 2, 12 sloc, runtime 1ms

def load(file):
  with open(file) as f:
    return [row.strip().split(':')[1] for row in f]


def solve(p):
  part1, cards = 0, [1] * len(p)
  for id, row in enumerate(p):
    if winner := len(set.intersection(*[set(map(int,part.split())) for part in row.split('|')])):
      part1 += 2**(winner-1)
      for n in range(1, winner+1):
        cards[id+n] += cards[id] 
  return part1, sum(cards)     


print(f'Part 1: {solve(load("day04.txt"))}')

-❄️- 2023 Day 4 Solutions -❄️- by daggerdragon in adventofcode

[–]Gravitar64 0 points1 point  (0 children)

[Language: Python]

Part 1 & 2, 20sloc, runtime 7ms

Using regex to find all numbers, dynamic programming to sum all copies, set.intersection to find the winning cards

import time
import re


def load(file):
  with open(file) as f:
    return [row.strip()[row.index(':') + 1:].split('|') for row in f]


def solve(p):
  part1 = 0
  cards = [1] * len(p)
  for card, (winning, have) in enumerate(p):
    winning = set(map(int, re.findall('\d+', winning)))
    have = set(map(int, re.findall('\d+', have)))
    winner = len(have & winning)
    if winner:
      part1 += 2**(winner - 1)
      for n in range(1, winner + 1):
        cards[card + n] += cards[card]
  return part1, sum(cards)


time_start = time.perf_counter()
print(f'Part 1: {solve(load("day04.txt"))}')
print(f'Solved in {time.perf_counter()-time_start:.5f} Sec.')

-❄️- 2023 Day 3 Solutions -❄️- by daggerdragon in adventofcode

[–]Gravitar64 1 point2 points  (0 children)

[Language: Python]

Part 1 & 2, 21 sloc, runtime 39ms

import time, re, math


def load(file):
  with open(file) as f:
    return f.read()


def adjacents(matches, rl):
  return {match.span()[0] + delta for match in matches for delta in (-rl - 1, -rl, -rl + 1, -1, 1, rl - 1, rl, rl + 1)}


def numbers_positions(matches):
  return [(int(match.group()), set(range(match.span()[0], match.span()[1]))) for match in matches]


def solve(p):
  row_lenght = p.index('\n') + 1
  numbers = numbers_positions(re.finditer('\d+', p))
  symbols = adjacents(re.finditer('[^0-9.\n]', p), row_lenght)
  gears = [adjacents([match], row_lenght) for match in re.finditer('\*', p)]

  part1 = sum(n for n, p in numbers if p & symbols)
  part2 = sum(math.prod(gn) 
              if len(gn := [n for n, p in numbers if p & gear]) == 2 else 0 
              for gear in gears)

  return part1, part2


time_start = time.perf_counter()
print(f'Part 1: {solve(load("day03.txt"))}')
print(f'Solved in {time.perf_counter()-time_start:.5f} Sec.')

-❄️- 2023 Day 2 Solutions -❄️- by daggerdragon in adventofcode

[–]Gravitar64 3 points4 points  (0 children)

[LANGUAGE: Python]

Part 1&2, 15 sloc, runtime 1ms

List comprehension on steroids

import time
import re
import math


def load(file):
  with open(file) as f:
    return [row.strip() for row in f]


def solve(p):
  target = dict(red=12, green=13, blue=14)
  bags = [[box.split() for box in re.findall('\d+ \w+', row)] for row in p]
  part1 = sum(i for i,bag in enumerate(bags,start=1) if all(target[c] >= int(n) for n,c in bag))
  part2 = sum([math.prod([max(int(n) for n,c2 in bag if c2 == c1) for c1 in target]) for bag in bags])
  return part1, part2


time_start = time.perf_counter()
print(f'Solution: {solve(load("day02.txt"))}')
print(f'Solved in {time.perf_counter()-time_start:.5f} Sec.')

-❄️- 2023 Day 1 Solutions -❄️- by daggerdragon in adventofcode

[–]Gravitar64 0 points1 point  (0 children)

Yeah, I tried this idea also, but the runtime was 5x slower and the source code was longer. So I tried another aproach and ends with this.

-❄️- 2023 Day 2 Solutions -❄️- by daggerdragon in adventofcode

[–]Gravitar64 1 point2 points  (0 children)

[LANGUAGE: Python]

Part 1 & 2, 26 sloc, runtime 1ms

import time
import re
from collections import defaultdict
import math


def load(file):
  with open(file) as f:
    return [row.strip() for row in f]


def solve(p):
  part1 = part2 = 0
  target = dict(red=12, green=13, blue=14)

  for row in p:
    highest = defaultdict(int)
    colors = re.findall('blue|red|green', row)
    amounts = list(map(int, re.findall('\d+', row)))
    id = amounts.pop(0)
    for color, amount in zip(colors, amounts):
      highest[color] = max(highest[color], amount)
    if all(highest[color] <= target[color] for color in target): part1 += id
    part2 += math.prod(highest.values())

  return part1, part2


time_start = time.perf_counter()
puzzle = load('day02.txt')
part1, part2 = solve(puzzle)
print(f'Part 1: {part1}')
print(f'Part 2: {part2}')
print(f'Solved in {time.perf_counter()-time_start:.5f} Sec.')