Skip to content

alexenge/advent-of-code-2022

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

18 Commits
 
 
 
 
 
 

Repository files navigation

Advent of Code 2022

Alexander Enge 2022-12-01

Hi! 👋

This repository contains my solutions for the 2022 edition of Advent of Code.

From the Advent of Code website:

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. People use them as interview prep, company training, university coursework, practice problems, a speed contest, or to challenge each other.

I’ll be using a mix of Python, Base R, and tidyverse-style R.

Day 1: Calorie Counting 🍕

Part one: Python

max_elf = 0
this_elf = 0

with open('data/day1.txt') as f:
    for line in f:

        if this_elf > max_elf:
            max_elf = this_elf

        if line == '\n':
            this_elf = 0
        else:
            this_elf += int(line.strip())

print(max_elf)
64929

Part one: Base R

lines <- readLines("data/day1.txt", warn = FALSE)

elf_indices <- cumsum(lines == "")

split(lines, elf_indices) |>
    lapply(as.numeric) |>
    lapply(sum, na.rm = TRUE) |>
    unlist() |>
    max()
[1] 64929

Part two: Python

max_elves = [0, 0, 0]
this_elf = 0

with open('data/day1.txt') as f:
    for line in f:

        max_elves.sort()
        if this_elf > max_elves[0]:
            max_elves[0] = this_elf

        if line == '\n':
            this_elf = 0
        else:
            this_elf += int(line.strip())

print(sum(max_elves))
193697

Part two: Base R

lines <- readLines("data/day1.txt", warn = FALSE)

elf_indices <- cumsum(lines == "")

split(lines, elf_indices) |>
    lapply(as.numeric) |>
    lapply(sum, na.rm = TRUE) |>
    unlist() |>
    sort() |>
    tail(3) |>
    sum()
[1] 193697

Day 2: Rock Paper Scissors ✂️

Part one: Tidyverse R

library(tidyverse)

read_table("data/day2.txt", col_names = c("other", "me")) %>%
    unite(col = "round", other, me, remove = FALSE) %>%
    mutate(
        game_score = case_when(
            round %in% c("A_Y", "B_Z", "C_X") ~ 6,
            round %in% c("A_X", "B_Y", "C_Z") ~ 3,
            round %in% c("A_Z", "B_X", "C_Y") ~ 0
        ),
        play_score = recode(me, "X" = 1, "Y" = 2, "Z" = 3),
        score = game_score + play_score
    ) %>%
    pull(score) %>%
    sum()
[1] 14297

Part two: Tidyverse R

read_table("data/day2.txt", col_names = c("other", "outcome")) %>%
    unite(col = "round", other, outcome, remove = FALSE) %>%
    mutate(
        me = case_when(
            round %in% c("A_X", "B_Z", "C_Y") ~ "Z",
            round %in% c("A_Y", "B_X", "C_Z") ~ "X",
            round %in% c("A_Z", "B_Y", "C_X") ~ "Y"
        ),
        game_score = recode(outcome, "X" = 0, "Y" = 3, "Z" = 6),
        play_score = recode(me, "X" = 1, "Y" = 2, "Z" = 3),
        score = game_score + play_score
    ) %>%
    pull(score) %>%
    sum()
[1] 10498

Day 3: Rucksack Reorganization 🎒

Part one: Python

from string import ascii_lowercase, ascii_uppercase

with open('data/day3.txt') as f:
    lines = [line.strip() for line in f.readlines()]

halves = [(line[:len(line) // 2], line[len(line) // 2:]) for line in lines]
items = [set(half_1).intersection(half_2).pop() for half_1, half_2 in halves]

letters = ascii_lowercase + ascii_uppercase
priorities = [letters.index(item) + 1 for item in items]
print(sum(priorities))
7795

Part two: Python

group_size = 3
groups = [lines[ix:ix + group_size] for ix in range(0, len(lines), group_size)]

badges = [set.intersection(*map(set, group)).pop() for group in groups]
priorities = [letters.index(badge) + 1 for badge in badges]
print(sum(priorities))
2703

Day 4: Camp Cleanup 🧹

Part one: Python

def is_contained(min_1, max_1, min_2, max_2):
    return (min_1 >= min_2 and max_1 <= max_2 or
            min_2 >= min_1 and max_2 <= max_1)


contained = 0
with open('data/day4.txt') as f:
    for line in f:
        elf_1, elf_2 = line.strip().split(',')
        min_1, max_1 = [int(section) for section in elf_1.split('-')]
        min_2, max_2 = [int(section) for section in elf_2.split('-')]
        contained += is_contained(min_1, max_1, min_2, max_2)

print(contained)
475

Part one: Tidyverse R

read_csv("data/day4.txt", col_names = c("elf_1", "elf_2")) %>%
    transmute(across(.fns = str_split, pattern = "-")) %>%
    unnest_wider(c(elf_1, elf_2), names_sep = "_", transform = as.integer) %>%
    mutate(
        contained_1 = elf_1_1 >= elf_2_1 & elf_1_2 <= elf_2_2,
        contained_2 = elf_2_1 >= elf_1_1 & elf_2_2 <= elf_1_2,
        contained = contained_1 | contained_2
    ) %>%
    pull(contained) %>%
    sum()
[1] 475

Part two: Python

def is_overlap(min_1, max_1, min_2, max_2):
    return (max_1 >= min_2 and max_2 >= min_1)

overlaps = 0
with open('data/day4.txt') as f:
    for line in f:
        elf_1, elf_2 = line.strip().split(',')
        min_1, max_1 = [int(section) for section in elf_1.split('-')]
        min_2, max_2 = [int(section) for section in elf_2.split('-')]
        overlaps += is_overlap(min_1, max_1, min_2, max_2)

print(overlaps)
825

Part two: Tidyverse R

read_csv("data/day4.txt", col_names = c("elf_1", "elf_2")) %>%
    transmute(across(.fns = str_split, pattern = "-")) %>%
    unnest_wider(c(elf_1, elf_2), names_sep = "_", transform = as.integer) %>%
    mutate(overlap = elf_1_2 >= elf_2_1 & elf_2_2 >= elf_1_1) %>%
    pull(overlap) %>%
    sum()
[1] 825

Day 5: Supply Stacks 🏗️

Part one: Base R

transpose_list <- function(l) as.list(as.data.frame(t(as.data.frame(l))))
drop_empty <- function(x) x[x != " "]

input <- read.csv("data/day5.txt", header = FALSE)

input_1 <- subset(input, grepl("\\[", V1))$V1
stack_ixs <- seq(2, max(nchar(input_1)), by = 4)
stacks <- lapply(input_1, substring, first = stack_ixs, last = stack_ixs) |>
    rev() |>
    transpose_list() |>
    lapply(drop_empty)

input_2 <- subset(input, grepl("move", V1))$V1
moves <- strsplit(input_2, split = " ")
for (move in moves) {
    from = as.integer(move[4])
    to = as.integer(move[6])
    n = as.integer(move[2])
    stacks[[to]] <- c(stacks[[to]], rev(tail(stacks[[from]], n)))
    stacks[[from]] <- head(stacks[[from]], -n)
}

lapply(stacks, tail, n = 1) |>
    paste(collapse = "")
[1] "QNNTGTPFN"

Part two: Base R

stacks <- lapply(input_1, substring, first = stack_ixs, last = stack_ixs) |>
    rev() |>
    transpose_list() |>
    lapply(drop_empty) # Same as before

for (move in moves) {
    from = as.integer(move[4])
    to = as.integer(move[6])
    n = as.integer(move[2])
    stacks[[to]] <- c(stacks[[to]], tail(stacks[[from]], n)) # Don't reverse
    stacks[[from]] <- head(stacks[[from]], -n)
}

lapply(stacks, tail, n = 1) |>
    paste(collapse = "")
[1] "GGNPJBTTR"

Day 6: Tuning Trouble 📻

Part one: Python

def find_marker(input, n_letters):
    for ix in range(len(input)):
        letters = input[ix:ix + n_letters]
        if len(letters) == len(set(letters)):
            return ix + n_letters


with open('data/day6.txt') as f:
    input = f.readline()

find_marker(input, n_letters=4)
1802

Part two: Python

find_marker(input, n_letters=14)
3551

Day 7: No Space Left On Device 📁

Part one: Base R

input <- readLines("data/day7.txt", warn = FALSE)
dirs = list(`/` = list(".dir_size" = 0))
for (cmd in input) {

    if (cmd == "$ cd /") path <- '/' 
    else if (cmd == "$ cd ..") path <- head(path, -1)
    else if (startsWith(cmd, "$ cd")) {
        dir_name <- tail(strsplit(cmd, " ")[[1]], 1)
        path <- c(path, dir_name)
    }

    else if (cmd == "$ ls") dirs[[path]] <- list(".dir_size" = 0)
    else if (!startsWith(cmd, "dir")) { # Only consider files, not directories
        file_size <- strsplit(cmd, " ")[[1]] |> head(1) |> as.numeric()
        for (ix in seq_along(path)) { # Add file size to all parent directories
            dir_size_path <- c(head(path, ix), ".dir_size")
            dirs[[dir_size_path]] <- dirs[[dir_size_path]] + file_size
        }
    }
    else next

}

str(dirs, list.len = 3) # Sanity check
List of 1
 $ /:List of 5
  ..$ .dir_size: num 44376732
  ..$ lhrfs    :List of 1
  .. ..$ .dir_size: num 240152
  ..$ nwh      :List of 2
  .. ..$ .dir_size: num 834387
  .. ..$ pmdj     :List of 1
  .. .. ..$ .dir_size: num 514336
  .. [list output truncated]
dir_sizes <- unlist(dirs)
sum(dir_sizes[dir_sizes < 1e5])
[1] 1243729

Part two: Base R

space_left <- 7e7 - dirs[[c("/", ".dir_size")]]
space_needed <- 3e7 - space_left
min(dir_sizes[dir_sizes > space_needed])
[1] 4443914

Day 8: Treetop Tree House 🌳

Part one: Python

import numpy as np


def is_visible(arr_1d):
    """Tests for each element in arr if it's visible from the front or back."""
    return [all(arr_1d[:ix] < elem) or all(arr_1d[ix + 1:] < elem)
            for ix, elem in enumerate(arr_1d)]


input = np.genfromtxt('data/day8.txt', delimiter=1)
vis = [np.apply_along_axis(is_visible, ax, input) for ax in range(input.ndim)]
np.any(vis, axis=0).sum()
1854

Part two: Python

def score_elem(arr, elem):
    """Computes the score (other visible trees) in one direction."""
    if arr.size == 0:
        score = 0 # Edge tree
    elif all(elem > arr):
        score = len(arr) # Largest tree, sees all other trees in the row
    else:
        score = np.where(arr >= elem)[0][0] + 1 # Sees until next equal tree
    return score


input = np.genfromtxt('data/day8.txt', dtype=int, delimiter=1)

scores = np.zeros((4,) + input.shape, dtype=int)
for row_ix, row in enumerate(input):
    for col_ix, elem in enumerate(row):

        left = np.flip(row[:col_ix])
        right = row[col_ix + 1:]
        up = np.flip(input[:row_ix, col_ix])
        down = input[row_ix + 1:, col_ix]

        for dir_ix, arr in enumerate([left, right, up, down]):
            scores[dir_ix, row_ix, col_ix] = score_elem(arr, elem)

scores = np.prod(scores, axis=0)
print(scores.max())
527340

That’s how far I’ve got for the 2022 edition of Advent of Code. :facepalm: Wish me better luck next time!