Giter Site home page Giter Site logo

aoc2021's Introduction

Day 1

Julia
const ints = parse.(Int, readlines("../input1"))

p1 = sum(>(0), diff(ints))

println("Day1: $p1")

p2 = sum(4:lastindex(ints)) do idx
    ints[idx-3] < ints[idx]
end

println("Day1 p2: $p2")
OCaml
(* memory-efficient solution
 * uses continuation-passing style
 *)

(* part 1 *)
let input = open_in "../input1"
let _ = print_endline "part 1:"
(* calculates num increments as well as first depth
 * calls f num_incr first_depth
 *)
let rec k_num_incr ic f =
  match input_line ic with
        exception End_of_file -> f 0 None
      | line ->
          let this_depth = int_of_string line in
          k_num_incr ic (fun num_incr next_depth_opt ->
            match next_depth_opt with
                  None -> f 0 (Some this_depth)
                | Some next_depth ->
                    f (num_incr + if this_depth < next_depth then 1 else 0) (Some this_depth)
          )

let _ = k_num_incr input (fun num_incr _ -> (print_int num_incr; print_endline ""))

(* part 2 *)
let _ = print_endline "part 2:"
let input = open_in "../input1"
(* same idea, now storing 3 future depths instead of just 1 *)
let rec k_num_incr3 ic f =
  match input_line ic with
        exception End_of_file -> f 0 None None None
      | line ->
          let d0 = int_of_string line in
          k_num_incr3 ic
          (fun num_incr d1opt d2opt d3opt ->
            match d3opt with
                  None -> f num_incr (Some d0) d1opt d2opt
                | Some d3 -> f (num_incr + if d0 < d3 then 1 else 0) (Some d0) d1opt d2opt
          )
let _ = k_num_incr3 input (fun num_incr _ _ _ -> (print_int num_incr; print_endline ""))
                  




(* golfy solution *)
(* part 1 only *)
let input = open_in "../input1"
let _ = print_endline "part 1:"
(* reads entire file into list *)
let rec get_depths ic =
  match input_line ic with
        exception End_of_file -> []
      | line -> (int_of_string line)::(get_depths ic)

let depths = get_depths input
(* fold by keeping & updating a tuple:
 * current num increments, previous depth
 *)
let (result, _) = List.fold_left (fun (num_incr, prev_depth) this_depth -> ((num_incr + if this_depth > prev_depth then 1 else 0), this_depth)) (0, List.hd depths) (List.tl depths)

let _ = print_int result; print_endline ""

Python
import numpy as np

def count_increment(sonar_meas : np.ndarray) -> float:
    depth_diff = np.diff(sonar_meas)
    return np.sum(depth_diff > 0)

input_depth = np.loadtxt("../input1")
print(f"Ans: {count_increment(input_depth)}")

filter_size = 3
filter = np.ones(filter_size)
rolling = np.convolve(input_depth, filter)[filter_size-1:-2]
print(f"Part2 ans: {count_increment(rolling)}")
Cpp
#include <iostream>
#include <fstream>
#include <xtensor/xio.hpp>
#include <xtensor/xarray.hpp>
#include <xtensor/xcsv.hpp>
#include <xtensor/xview.hpp>
#include <string>

int main(int argc, char** argv) {
    std::string fname = "../../input1";
    std::ifstream in_file;
    in_file.open(fname);

    xt::xarray<double> data = xt::load_csv<double>(in_file);
    xt::xarray<double> depth_diff = xt::diff(data, 1, 0);

    auto val = xt::sum(depth_diff > 0);
    std::cout << "Part 1:" << val << std::endl;

    int result = 0;
    int this_val = 0;
    for (size_t i = 0; i < data.size() - 3; i++) {
        this_val = data(i+3, 0) > data(i, 0) ? 1 : 0;
        result += this_val;
    }

    std::cout << "Part 2:" << result << std::endl;

    return 0;
}

Day 2

Julia
ins = readlines("../input2")

function part1_2(arr)
    x = z = aim = 0
    for l in arr
        i = parse(Int, last(l))
        if l[1] == 'f'
            x += i
            z += i*aim
        else
            aim += ifelse(l[1] == 'd', i, -i)
        end
    end
    x .* (aim, z)
end
println("Day1 p1, p2: $(part1_2(ins))")
OCaml
let input = open_in "../input2"
let rec k_get_lines ic f =
  match input_line ic with
        exception End_of_file -> f []
      | line -> k_get_lines ic (fun ls ->
          let split = String.split_on_char ' ' line in
          f ((List.hd split, int_of_string (List.nth split 1))::ls)
        )
let lines = k_get_lines input (fun x -> x)


let forwards = List.filter (function ("forward", _) -> true | _ -> false) lines
let ups = List.filter (function ("up", _) -> true | _ -> false) lines
let downs = List.filter (function ("down", _) -> true | _ -> false) lines
let x = List.fold_left (fun total (_, dx) -> total + dx) 0 forwards
let y0 = List.fold_left (fun total (_, dy) -> total + dy) 0 ups
let y1 = List.fold_left (fun total (_, dy) -> total + dy) 0 downs
let _ = print_string "part 1: "; print_int (x * (y1 - y0)); print_endline ""




let rec calc cmds x y a =
  match cmds with
        [] -> x * y
      | ("forward", amount)::cs -> calc cs (x + amount) (y + amount * a) a
      | ("up", amount)::cs -> calc cs x y (a - amount)
      | ("down", amount)::cs -> calc cs x y (a + amount)
      | _ -> raise (Failure "bad")
let _ = print_string "part 2: "; print_int (calc lines 0 0 0); print_endline ""
Python
import numpy as np

f_name = "../input2"
lines = None
with open(f_name, 'r') as f:
    lines = f.readlines()

# Action Mapping
action = {
    'forward' : [0, 1],
    'down' : [1, 1],
    'up' : [1, -1]
}

# Dead Reckoning
position = np.array([0, 0])
for l in lines:
    p = l.split(" ")
    action_key = p[0]
    movement = float(p[1])
    position[action[action_key][0]] += movement * action[action_key][1]

print(f"prod: {np.prod(position)}")

print("Part 2:")

# Change Action mapping since down and up affect aims now
action = {
    'forward' : [0, 1],
    'down' : [2, 1],
    'up' : [2, -1]
}

# Accordingly position aim
position_with_aim = np.array([0, 0, 0])
for l in lines:
    p = l.split(" ")
    action_key = p[0]
    movement = float(p[1])

    # Add to aim and position
    position_with_aim[action[action_key][0]] += movement * action[action_key][1]
    if action_key == "forward":
        position_with_aim[1] += movement * position_with_aim[2]

print(f"prod: {np.prod(position_with_aim[:-1])}")

Day 3

Julia
rows = map(eachline("../input3")) do line
    parse.(Bool, collect(line))
end
rowidxs = eachindex(rows[1])
mode(rows, j, op = >=) = op(sum(r->r[j], rows), length(rows)÷2)
f(x) = evalpoly(2, reverse(x))

### part 1
γ = [mode(rows, j) for j in rowidxs]
ϵ = .~(γ) # bit flips
println("p1: ",  prod(f, (γ, ϵ)))

### part 2
part2(rows, j, op) = filter(r -> r[j]==mode(rows, j, op), rows)
let oxygen = rows, CO2 = rows
    for j in rowidxs
        length(oxygen)==1 && length(CO2)==1 && break
        oxygen = part2(oxygen, j, >=)
        CO2 = part2(CO2, j, <)
    end
    println("p2: ",  prod(fonly, (oxygen, CO2)))
end
OCaml
let input = open_in "../input3"
let rec k_get_lines ic f =
  match input_line ic with
        exception End_of_file -> f []
      | line -> k_get_lines ic (fun ls -> f (line::ls))
let lines = k_get_lines input (fun x -> x)
let nbits = String.length (List.hd lines)
let bgamma = List.init nbits (fun i ->
  let zeros = List.filter (fun ln -> ln.[i] = '0') lines in
  if (2 * List.length zeros > List.length lines) then 0 else 1
)
let bepsilon = List.map (function 0 -> 1 | _ -> 0) bgamma

let rec k_rblist2dec rbls f =
  match rbls with
        [] -> f 0
      | b::rbs -> k_rblist2dec rbs (fun x -> f (2 * x + b))


let gamma = k_rblist2dec (List.rev bgamma) (fun x -> x)
let epsilon = k_rblist2dec (List.rev bepsilon) (fun x -> x)

let _ = print_string "part 1: "; print_int (gamma * epsilon); print_endline ""






let rec find_gas lines i oxygen =
  match lines with
        [] -> raise (Failure "bad")
      | [x] -> x
      | _ ->
          let zeros = List.filter (fun ln -> ln.[i] = '0') lines in
          let x =
            let more_zero = compare (2 * List.length zeros) (List.length lines) in
            if more_zero <= 0
              then (if oxygen then 1 else 0)
              else (if oxygen then 0 else 1) in
          let newlines = List.filter (fun ln -> String.sub ln i 1 = string_of_int x ) lines in
          find_gas newlines (i + 1) oxygen
let o2s = find_gas lines 0 true
let co2s = find_gas lines 0 false
let o2b = List.init (String.length o2s) (fun i -> int_of_string (String.sub o2s i 1)) 
let co2b = List.init (String.length co2s) (fun i -> int_of_string (String.sub co2s i 1)) 
let o2 = k_rblist2dec (List.rev o2b) (fun x -> x)
let co2 = k_rblist2dec (List.rev co2b) (fun x -> x)
let _ = print_string "part 2: "; print_int (o2 * co2); print_endline ""
Python
# Too much of a havoc on the prompt side, not going to document

import numpy as np

fname = "../input3"
lines = None
with open(fname, 'r') as f:
    lines = f.readlines()

col = len(lines[0].strip())
row = len(lines)

data_mat = np.zeros((row, col))
for i, l in enumerate(lines):
    l = l.strip()
    for j in range(col):
        data_mat[i, j] = int(l[j])

gamma = np.round(np.sum(data_mat, axis=0) / row).astype(int)
epsilon = 1 - gamma

def matrix_bit_to_num(bit_mat : np.ndarray) -> float:
    power = np.arange(len(bit_mat))[::-1]

    gamma_val = np.sum(np.power(bit_mat * 2, power))

    if bit_mat[-1] == 0:
        gamma_val -= 1
    
    return gamma_val

print(f'prod: {matrix_bit_to_num(gamma) * matrix_bit_to_num(epsilon)}')

oxygen_mask = np.ones(row, dtype=bool)
oxygen_string = np.zeros(col)
for i in range(col):
    this_mat = data_mat[oxygen_mask, :]

    this_rating = int((np.sum(this_mat[:, i]) * 2) >= len(this_mat))
    oxygen_string[i] = this_rating
    
    this_mask = this_mat[:, i] == this_rating
    oxygen_mask[oxygen_mask] = np.bitwise_and(oxygen_mask[oxygen_mask], this_mask)

scrubber_mask = np.ones(row, dtype=bool)
scrubber_string = np.zeros(col)
for i in range(col):
    this_mat = data_mat[scrubber_mask, :]
    
    if len(this_mat) == 1:
        scrubber_string[i:] = this_mat[0, i:]
        break
    this_rating = int((np.sum(this_mat[:, i]) * 2) < len(this_mat))
    scrubber_string[i] = this_rating
    
    this_mask = this_mat[:, i] == this_rating
    scrubber_mask[scrubber_mask] = np.bitwise_and(scrubber_mask[scrubber_mask], this_mask)

print(f"prod: {matrix_bit_to_num(oxygen_string) * matrix_bit_to_num(scrubber_string)}")

Day 4

Julia
inputs = split(strip(read("../input4", String), '\n'), "\n\n")
draws = parse.(Int, split(inputs[1], ","))
boards = map(inputs[2:end]) do board
    parse.(Int, mapreduce(split, hcat, split(board, "\n")))
end
p = fill(-1,5)
wincon(m) = any(==(p), eachrow(m)) || any(==(p), eachcol(m))

# part 1 & 2
done = Set{Int}()
res = Int[]
for num in draws, (i, b) in enumerate(boards)
    replace!(b, num => -1)
    if idone && wincon(b)
        push!(done, i)
        push!(res, num * sum(filter(>(0), b)))
    end
end
println(res[[begin, end]])
OCaml
let input = open_in "../input4"
let rec k_get_lines ic f =
  match input_line ic with
        exception End_of_file -> f []
      | line -> k_get_lines ic (fun ls -> f (line::ls))
let lines = k_get_lines input (fun x -> x)


let nums = List.map int_of_string (String.split_on_char ',' (List.hd lines))

let line_to_row line =
  let split = String.split_on_char ' ' line in
  List.map int_of_string (List.filter (fun x -> x <> "") split)

let rec k_get_boards lines f =
  match lines with
        [] -> f []
      | line::ls -> k_get_boards ls (fun boards ->
          if line = ""
            then f ([]::boards)
            else (if boards = []
              then f [[line_to_row line]]
              else (
                let top_board = List.hd boards in
                f (((line_to_row line)::top_board)::(List.tl boards))
              )
            )
        )

let boards = k_get_boards (List.tl (List.tl lines)) (fun x -> x)
let checks = List.map (List.map (List.map (function _ -> false))) boards

let rec update_row_status row checks num =
  match row with
        [] -> []
      | x::xs -> (if x = num then true else (List.hd checks))::(update_row_status xs (List.tl checks) num)

let rec update_status board checks num =
  match board with
        [] -> checks
      | row::rows -> (if List.mem num row
          then (update_row_status row (List.hd checks) num)
          else (List.hd checks))::(update_status rows (List.tl checks) num)

let rec one_row_done one_board_checks =
  match one_board_checks with
        [] -> false
      | row::rows -> one_row_done rows || row = [true; true; true; true; true]

let one_col_done bchecks =
  let transposed = List.init 5 (fun i -> List.init 5 (fun j -> List.nth (List.nth bchecks j) i)) in
  one_row_done transposed

let board_is_done one_board_checks =
  one_row_done one_board_checks || one_col_done one_board_checks


let rec find_done_board all_checks =
  match all_checks with
        [] -> None
      | bcheck::bchecks ->
          (match find_done_board bchecks with
                 Some i -> Some (i + 1)
               | None -> if board_is_done bcheck then Some 0 else None)

let (win_checks, done_board, win_num) =
List.fold_left (fun ((checks, done_board, win_num) as state) num ->
  match done_board with
        Some _ -> state
      | None ->
          let new_checks = List.mapi (fun i one_board_check -> update_status (List.nth boards i) one_board_check num) checks in
          match find_done_board new_checks with
                None -> (new_checks, None, -1)
              | Some i -> (new_checks, Some i, num)
) (checks, None, -1) nums


let win_board = match done_board with None -> raise (Failure "bad") | Some i -> i
let win_board_checks = List.nth win_checks win_board

let rec sum_win_board board checks =
  match board with
        [] -> 0
      | row::rows -> List.fold_left (+) 0 (List.mapi (fun i x -> if List.nth (List.hd checks) i then 0 else x) row) + sum_win_board rows (List.tl checks)

let calc_win_score board checks num =
  let sum_num = sum_win_board board checks in
  sum_num * num

let _ = print_string "part 1: "; print_int (calc_win_score (List.nth boards win_board) win_board_checks win_num); print_endline ""


let (_, last_score) =
List.fold_left (fun (boards_and_checks, last_score) num ->
  let new_bc = List.map (fun (board, check) ->
    (board, update_status board check num)
  ) boards_and_checks in
  let not_done = List.filter (fun (b, c) -> not (board_is_done c)) new_bc in
  let done_stuff = List.filter (fun (b, c) -> (board_is_done c)) new_bc in
  let (done_boards, done_boards_checks) = List.split done_stuff in
  if done_stuff <> []
    then (not_done, calc_win_score (List.nth done_boards (List.length done_boards - 1)) (List.nth done_boards_checks (List.length done_boards - 1)) num)
    else (not_done, last_score)
) (List.combine boards checks, -1) nums


let _ = print_string "part 2: "; print_int last_score; print_endline ""
Python
import numpy as np
import tempfile

# Read File input
file_name = "../input4"
lines = None
with open(file_name, 'r') as f:
    lines = f.readlines()
commands = lines[0].strip()
commands = commands.split(",")

# Read Matrix through tempFile so that loadtxt works
try:
    matricies = lines[1:]
    f = tempfile.NamedTemporaryFile(mode='w+t')
    f.writelines(matricies)
    f.seek(0)
    mat = np.loadtxt(f.name)
finally:
    f.close()

def calculate_score(mat : np.ndarray, mask : np.ndarray, command : float) -> float:
    '''
    Calculate Score with matrix and mask
    '''
    return command * np.sum(mat[np.logical_not(mask)])

# Reshape Matrix to N x 5 x 5 Matrices
(_, n) = mat.shape
n_dim_mat = mat.reshape((-1, n, n))
mask = np.zeros_like(n_dim_mat, dtype=int)

score = -1
term_mask = None

once = True
for num in commands[:-1]:
    # Update Mask
    this_num = int(num)
    mask[n_dim_mat == this_num] = 1

    # Check Termination Condition, since diagonal doesn't count
    hori_sum = np.sum(mask, axis=2)
    vert_sum = np.sum(mask, axis=1)

    if np.any(np.isclose(hori_sum, 5)):
        term_mask = hori_sum
    elif np.any(np.isclose(vert_sum, 5)):
        term_mask = vert_sum

    if term_mask is not None:
        indices = np.argwhere(np.isclose(term_mask, n))
        score = calculate_score(n_dim_mat[indices[0, 0]], mask[indices[0, 0]], command = this_num)
        if once:
            print(f"Part 1 Answer: {score}")
            once = False

        n_dim_mat[indices[:, 0]] = -1
        mask[indices[:, 0]] = -1
        term_mask = None

print(f"Part 2 Answer: {score}")

Day 5

Julia
using LinearAlgebra
CI = CartesianIndex
_sign(x1,x2) = x1>=x2 ? -1 : 1

lines = split.(readlines("../input5"), r",| -> ")
coords = map(lines) do line
    c = parse.(Int, line)
    step = CI(_sign(c[1], c[3]), _sign(c[2], c[4]))
    CI(c[1], c[2]):step:CI(c[3], c[4])
end
# part 1
M = zeros(Int, 1000, 1000)
for c in coords
    any(==(1), size(c)) && (M[c] .+= 1)
end
println("P1: ", sum(>=(2), M))
# part 2
for c in coords
    any(==(1), size(c)) || (M[diag(c)] .+= 1)
end
println("P2: ", sum(>=(2), M))
OCaml
let input = open_in "../input5"
let rec k_get_lines ic f =
  match input_line ic with
        exception End_of_file -> f []
      | line -> k_get_lines ic (fun ls ->
          let intlist = (List.map int_of_string (Str.split (Str.regexp ",\\| -> ") line)) in
          match intlist with
                [a; b; c; d] -> f ((a, b, c, d)::ls)
              | _ -> raise (Failure "bad")
        )
let lines = k_get_lines input (fun x -> x)

let grid = Array.make_matrix 1000 1000 0

let rec update_col grid x0 x1 y =
  if x0 > x1 then () else
  let row = Array.get grid x0 in
  let _ = Array.set row y (1 + Array.get row y) in
  update_col grid (x0 + 1) x1 y

let rec update_row row y0 y1 =
  if y0 > y1 then () else
  let _ = Array.set row y0 (1 + Array.get row y0) in
  update_row row (y0 + 1) y1

let rec update_diag_up grid (x0, y0, x1, y1) =
  if x0 > x1 then () else
  let row = Array.get grid x0 in
  let _ = Array.set row y0 (1 + Array.get row y0) in
  update_diag_up grid (x0 + 1, y0 - 1, x1, y1)

let rec update_diag_down grid (x0, y0, x1, y1) =
  if x0 > x1 then () else
  let row = Array.get grid x0 in
  let _ = Array.set row y0 (1 + Array.get row y0) in
  update_diag_down grid (x0 + 1, y0 + 1, x1, y1)

let rec update_diag grid (x0, y0, x1, y1) =
  if x0 > x1 then update_diag grid (x1, y1, x0, y0) else
  if y0 > y1
    then update_diag_up grid (x0, y0, x1, y1)
    else update_diag_down grid (x0, y0, x1, y1)

let update_grid grid (x0, y0, x1, y1) =
  if x0 = x1
    then if y0 > y1 then update_row (Array.get grid x0) y1 y0 else update_row (Array.get grid x0) y0 y1
    else if x0 < x1 then update_col grid x0 x1 y0 else update_col grid x1 x0 y0

let _ = List.iter (fun ((x0, y0, x1, y1) as line) ->
  if x0 <> x1 && y0 <> y1
    then update_diag grid line
    else update_grid grid line
) lines

let count = Array.fold_left (fun count row -> count + Array.fold_left (fun c x -> c + if x > 1 then 1 else 0) 0 row) 0 grid
Python
import numpy as np

def parse_input(fname : str) -> np.ndarray:
    '''
        Parse Inputs
    '''
    lines = None
    with open(fname, 'r') as f:
        lines = f.readlines()
    
    ret_map = np.zeros((len(lines), 4), dtype=int)
    for idx, l in enumerate(lines):
        l = l.strip()
        start, end = l.split(" -> ")
        start = start.split(",")
        end = end.split(",")
        ret_map[idx, [0, 1]] = int(start[0]), int(start[1])
        ret_map[idx, [2, 3]] = int(end[0]), int(end[1])

    return ret_map

def create_map_from_input(input : np.ndarray) -> np.ndarray:
    '''
    Create Map from input
    '''

    max_x = np.max(input[:, [0, 2]]).astype(int)
    max_y = np.max(input[:, [1, 3]]).astype(int)
    print(f"max_x: {max_x}")
    print(f"max_y: {max_y}")
    return np.zeros((max_y + 1, max_x + 1))

def map_world(map : np.ndarray, input: np.ndarray, part1 : bool = True) -> np.ndarray:
    '''
    Generate Map
    '''
    for obs in input:
        min_x = np.min(obs[[1,3]])
        max_x = np.max(obs[[1,3]])
        min_y = np.min(obs[[0,2]])
        max_y = np.max(obs[[0,2]])

        if (min_x != max_x) and (min_y != max_y):
            # Non axial
            if part1:
                continue
            x_idx = np.arange(min_x, max_x + 1)
            y_idx = np.arange(min_y, max_y + 1)
            if obs[0] != min_y:
                y_idx = y_idx[::-1]
            
            if obs[1] != min_x:
                x_idx = x_idx[::-1]

            map[x_idx, y_idx] += 1

        else:
            map[min_x : max_x + 1, min_y:max_y + 1] += 1
    return map

def count_overlap(map : np.ndarray, num_overlap : int) -> int:
    '''
    Number of cells with 2 or larger
    '''
    return np.sum(map >= num_overlap)

fname = "../input5"
observation = parse_input(fname)
empty_map = create_map_from_input(observation)
filled_map = map_world(empty_map.copy(), observation)
print(f"Part1: {count_overlap(filled_map, 2)}")

part2_filled_map = map_world(empty_map.copy(), observation, part1=False)
print(f"Part2: {count_overlap(part2_filled_map, 2)}")

Day 6

Julia
using LinearAlgebra
const ary = parse.(Int, split(readline("../input6"), ","))

function f(ary, days)
    counts = zeros(Int, 9)
    for a in ary
        counts[a+1] += 1
    end
    ker = Int[0 0 0 0 0 0 1 0 1
        I(8) zeros(8)]
    sum(counts' * ker^days)
end

println("P1: $(f(ary, 80)), P2: $(f(ary, 256))")
OCaml
let list_set l n x =
  List.init (List.length l) (fun i -> if i = n then x else List.nth l i)

let grow count =
  match count with [] -> raise (Failure "bad") |
        zero::rest -> (list_set rest 6 (List.nth rest 6 + zero)) @ [zero]

let rec grow_days count days =
  match days with
        0 -> count
      | _ -> grow_days (grow count) (days - 1)






let input_path = "../input6"
let days = 256
let fish = List.map int_of_string (String.split_on_char ',' (input_line (open_in input_path)))
let count = List.fold_left (fun c f -> list_set c f (1 + List.nth c f)) (List.init 9 (fun _ -> 0)) fish
let end_count = grow_days count days
let total = List.fold_left (+) 0 end_count
Python
import numpy as np

def parse_input(fname : str) -> np.ndarray:
    '''
    Parse input state
    '''
    initial_state = np.loadtxt(fname, delimiter=",")
    return initial_state

def step(fish_state : np.ndarray) -> np.ndarray:
    '''
    '''
    # 1. Check if fish reaches zero state
    mask = fish_state == 0

    # 2. Reset zeros to 7, add num of zeros to end with 9
    num_birth = np.sum(mask)
    fish_state[mask] = 7
    fish_state = np.hstack((fish_state, 9 * np.ones(num_birth)))

    # 3. Minus day
    fish_state -= 1
    return fish_state

def particle_methods(init_state : np.ndarray, num_days : int) -> int:
    '''
    Naive particle ways of getting answer
    '''
    state = init_state.copy().astype(int)
    for i in range(num_days):
        state = step(state)

    return len(state)

def binning_method(initial_state : np.ndarray, num_days : int) -> int:
    '''
    Count number of fish in each life cycle
    '''
    
    fish_bins = np.bincount(initial_state.astype(int), minlength=9)
    for i in range(num_days):
        fish_bins = np.roll(fish_bins, -1)
        fish_bins[6] += fish_bins[-1]
    return np.sum(fish_bins)

fname = "../input6"
state = parse_input(fname)

part1_days, part2_days = 80, 256
part1_ans = particle_methods(state.copy(), part1_days)
print(f"{part1_days} days of fish: {part1_ans}")

part2_ans = binning_method(state.copy(), part2_days)
print(f"{part2_days} days of fish: {part2_ans}")

Day 7

Julia
const ints = parse.(Int, split(readline("../input7"), ',')) |> sort
println("p1: ", sum(abs, ints .- ints[end÷2]) |> Int)

cost(x) = sum(1:abs(x))
println("p2: ", minimum(sum(cost, ints .- idx) for idx in 0:maximum(ints)))
OCaml
let get2fuel x y =
  let d = abs (x - y) in
  (1 + d) * d / 2



(* part 1 is getting median *)
let input_path = "./input7"
let crabs = List.sort compare (List.map int_of_string (String.split_on_char ',' (input_line (open_in input_path))))
let middle = (List.length crabs) / 2
let middle_fuel = List.fold_left (fun total crab -> total + abs (crab - List.nth crabs middle)) 0 crabs






(* part 2 is brute force search *)
let fuels2 = List.hd (List.sort compare (List.map (fun pos -> List.fold_left (fun fuel crab -> fuel + get2fuel crab pos) 0 crabs) (List.init (List.nth crabs (List.length crabs - 1)) (fun i -> i + List.hd crabs))))
Python
import numpy as np
from typing import Any, Callable
from numbers import Number

def cost_p1(crab_loc : np.ndarray, loc : float) -> float:
    return np.sum(np.abs(crab_loc - loc))

def cost_p2(crab_loc : np.ndarray, loc : Any) -> float:
    if isinstance(loc, Number):
        loc = np.array([loc])
    diff = np.abs(crab_loc[:, np.newaxis] - loc[np.newaxis, :])
    return np.sum((1 + diff) * diff / 2, axis=0)

def grad_p1(crab_loc : np.ndarray, soln : float) -> float:
    return np.mean(np.sign(crab_loc - soln))

def min_hori_dist(crab_loc : np.ndarray, grad_fn : Callable) -> float:
    '''
    Momentum to account for L1 non-differentiability :)
    '''

    cont_soln = np.mean(crab_loc) # L2 solution 
    grad = 0
    last_grad = 0
    gamma = 0.9

    max_iter = 1000
    for i in range(max_iter):
        curr_grad = grad_fn(crab_loc, cont_soln)
        grad = grad * gamma + curr_grad
        cont_soln = cont_soln + grad

        if i > 50:
            # Early Termination
            if np.sign(last_grad) != np.sign(grad):
                break
        last_grad = grad

    return np.round(cont_soln)

def part2(crab_loc : np.ndarray) -> float:
    min_d = np.mean(crab_loc) # Exact solution, since it's a L2 cost now
    d = np.array([np.ceil(min_d), np.floor(min_d)])
    return np.min(cost_p2(crab_loc, d))

fname = "../input7"
crab_loc = np.loadtxt(fname, delimiter=",")
min_loc = min_hori_dist(crab_loc, grad_p1)
print(f"Part1 index: {min_loc}, cost: {cost_p1(crab_loc, 362)}")
print(f"Part2 cost: {part2(crab_loc)}")

Day 8

Julia
const lines = split.(readlines("../input8"), r" \| | ")

# part 1
p1 = sum(lines) do line
    sum(x -> length(x)(2,4,3,7), line[end-3:end])
end
println("P1: ", p1)

# part 2
# one-liner
println(sum(parse.(Int,["4725360918"[[sum([L...].r)÷2%15%11+1 for r in split(R)]] 
for (L,R) in split.(readlines("../input8"),'|')])))

# you can read this off from the standard segments pattern
standard_patterns = ["abcefg", "cf", "acdeg", "acdfg", "bcdf", 
    "abdfg", "abdefg", "acf", "abcdefg", "abcdfg"]

# make a scoremap for how many times each segments show up in 0-9 pattern
scoremap = Dict(x => sum(==(x), standard_patterns |> join) for x in 'a':'g')

# each pattern can be uniquely determined by summing the lit segments' score
# here we make a standard score look up table with known segments mapping
const standards = map(standard_patterns) do s
    sum(scoremap[c] for c in s)
end

function disam(patterns, output)
    # make score for each new set of patterns
    _scores = Dict(x => sum(==(x), patterns |> join) for x in 'a':'g')
    
    # get the sum of segments for the four output digits
    _digits = map(output) do s
        sum(_scores[c] for c in s)
    end
    # look up corresponding digits in the standard pattern (by index)
    # offset by one because 0/1 indexing...
    [findfirst(==(x), standards) - 1 for x in _digits]
end

p2 = sum(lines) do line
    patterns = line[1:10]
    output = line[11:end]
    res = disam(patterns, output)
    evalpoly(10, reverse(res))
end
Python
from typing import Dict, List
import numpy as np

num_segment = {
    0 : 6,
    1 : 2,
    2 : 5,
    3 : 5,
    4 : 4,
    5 : 5,
    6 : 6,
    7 : 3,
    8 : 7,
    9 : 6
}

def sort_str(in_str  : str) -> str:
    return "".join(sorted(in_str))

def gen_decode_map(in_str : str) -> Dict:
    '''
    '''
    
    entries = in_str.split(" ")
    sorted_entry = sorted(entries, key=len)
    
    one = sort_str(sorted_entry[0])
    four = sort_str(sorted_entry[2])
    
    ret_entry = dict()
    ret_entry[one] = 1
    ret_entry[four] = 4
    ret_entry[sort_str(sorted_entry[1])] = 7
    ret_entry[sort_str(sorted_entry[-1])] = 8
    
    # Of length 6
    six_char_cand = [sort_str(sorted_entry[-2]),
                     sort_str(sorted_entry[-3]), sort_str(sorted_entry[-4])]
   
    mask = np.array([0, 0, 0])
    nine_str = None
    for idx, i in enumerate(six_char_cand):
        if (four[0] in i) and (four[1] in i) and (four[2] in i) and (four[3] in i):
            ret_entry[i] = 9
            nine_str = i
            mask[idx] = 1
            continue
            
        if (one[0] in i) and (one[1] in i) and i not in ret_entry:
            ret_entry[i] = 0
            mask[idx] = 1
            continue
    
    ret_entry[six_char_cand[np.argmin(mask)]] = 6

    five_char_cand = [sort_str(sorted_entry[3]),
                     sort_str(sorted_entry[4]), sort_str(sorted_entry[5])]
    
    for idx, c in enumerate(five_char_cand):
        if (one[0] in c) and (one[1] in c):
            ret_entry[c] = 3
            continue
        
        five_in_nine = True
        for s in c:
            five_in_nine = five_in_nine and (s in nine_str)
        if five_in_nine:
            ret_entry[c] = 5
            continue

        ret_entry[c] = 2

    return ret_entry

def decode_str(in_str : str, decode_map : Dict[str, int]) -> int:
    '''
        are you happy with one liners :)
    '''
    nums = [int(decode_map[sort_str(s)]) * (10 ** (3 - i)) for i, s in enumerate(in_str.split(" "))]
    return np.sum(nums)

def count_unique_nums(input: List[str]) -> int:
    '''
    '''
    unique_set = {2, 4, 3, 7}
    ret_num = 0
    for line in input:
        out_str = line.split(" ")
        k = [len(digit) in unique_set for digit in out_str]
        ret_num += np.sum(np.array(k))
    return ret_num
    
def main(fname : str = "../input8"):

    with open(fname, 'r') as f:
        line = f.readlines()

    part1_lines = [l.strip().split(" | ")[1] for l in line]

    print(f"P1: {count_unique_nums(part1_lines)}")

    part2_lines = [l.strip().split(" | ")[0] for l in line]
    ret_val = 0
    for idx, p2 in enumerate(part2_lines):
        decode_map = gen_decode_map(p2)
        num = decode_str(part1_lines[idx], decode_map)
        ret_val += num
    print(f"P2: {ret_val}")
        
if __name__ == "__main__":
    main()

Day 9

Julia
const CI = CartesianIndex
const neighbors(coor) = [coor + c for c in (CI(0,1), CI(0,-1), CI(1,0), CI(-1,0))]
const M = fill(9, 102, 102)
M[2:101, 2:101] = mapreduce(x->parse.(Int, collect(x))', vcat, readlines("../input9"))

function walk(M, coor)
    size = 0
    todo = Set((coor, ))
    done = Set{CI}()
    while !isempty(todo)
        size += 1
        p = pop!(todo)
        push!(done, p)
        candidates = neighbors(p)
        for s in candidates
            sdone && M[s]<9 && (push!(todo, s))
        end
    end
    return size
end

# part 1 + 2
let p1=0; p2=Int[]
    for coor in CartesianIndices((2:101, 2:101))
        ns = neighbors(coor)
        if all(>(M[coor]), M[ns]) 
            p1 += M[coor]+1
            push!(p2, walk(M, coor))
        end
    end
    println(p1)
    println(*(sort(p2)[end-2:end]...) |> sum)
end
Python
from typing import Tuple
import numpy as np
from sklearn.cluster import DBSCAN # :)

def parse_input(fname : str) -> np.ndarray:
    with open(fname, 'r') as f:
        lines = f.readlines()

    ret_mat = np.zeros((len(lines), len(lines[0]) - 1))
    for row, l in enumerate(lines):
        l = l.strip()
        for col, k in enumerate(l):
            ret_mat[row, col] = int(k)

    return ret_mat

def find_low_point(map_2d : np.ndarray) -> np.ndarray:
    hori_diff = np.diff(map_2d, axis=1)
    vert_diff = np.diff(map_2d, axis=0)

    saddle_map = np.ones_like(map_2d, dtype=bool)
    saddle_map[:, :-1] = hori_diff > 0
    saddle_map[:, 1:] = np.logical_and(saddle_map[:, 1:], hori_diff < 0)

    saddle_map[:-1, :] = np.logical_and(saddle_map[:-1, :], vert_diff > 0)
    saddle_map[1:, :] = np.logical_and(saddle_map[1:, :], vert_diff < 0)

    return saddle_map

def part1_sum(locations : Tuple[np.ndarray], map_2d : np.ndarray) -> float:
    return np.sum(map_2d[locations]) + len(locations[0])

def part2_cluster(map_2d : np.ndarray, largest_n : int = 3) -> float:
    map_2d[map_2d == 0] = 1
    map_2d[map_2d == 9] = 0
    coords = np.vstack(np.nonzero(map_2d)).T

    db = DBSCAN(eps=1.1, min_samples=3).fit(coords)
    alter = np.histogram(db.labels_, np.unique(db.labels_))[0]
    return np.sort(alter)[-largest_n:]

def main():
    fname = "../input9"
    map_2d = parse_input(fname)
    saddle_map = find_low_point(map_2d)
    print(f"part1: {part1_sum(np.nonzero(saddle_map),map_2d)}")
    val = part2_cluster(map_2d, 3)
    print(f"Part2: {np.prod(val)}")
    
if __name__ == "__main__":
    main()

Day 10

Julia
function checker(line, part2)
    res1 = res2 = 0
    match = Dict('(' => ')', '[' => ']', '{' => '}', '<' => '>')
    smap =  Dict('(' => 1,   '[' => 2,   '{' => 3,   '<' => 4,
                 ')' => 3,   ']' => 57,  '}' => 1197,'>' => 25137)
    stack = Char[]
    for t in line
        if t  "([{<"
            push!(stack, t)
        elseif t == match[last(stack)]
            pop!(stack)
        else
            res1 = smap[t]
            @goto corrupted
        end
    end
    foreach(reverse(stack)) do r
        res2 *= 5
        res2 += smap[r]
    end
    push!(part2, res2)
    @label corrupted
    res1
end

const p2 = Int[]
const ls = readlines("../input10")
L(l) = checker(l, p2)
empty!(p2)
println("P1: ", sum(L, ls))
println("P2: ", sort(p2)[end ÷ 2 + 1])
@time sum(L, ls)
@time sort(p2)[end ÷ 2 + 1]
Python
from typing import List
import numpy as np

def isValid(s : str) -> bool:
    correspondence = {
        "(" : ")",
        "[" : "]",
        "{" : "}",
        "<" : ">"
    }

    left = {
        "(",
        "[",
        "{",
        "<"
    }

    score = {
        ")" : 3,
        "]" : 57,
        "}" : 1197,
        ">" : 25137
    }

    book_keeping = list()

    for idx, char in enumerate(s):
        if char in left:
            book_keeping.append(char)
        else:
            if len(book_keeping) == 0:
                return score[char], book_keeping
            
            val = book_keeping.pop()
            if correspondence[val] != char:
                return score[char], book_keeping
    
    return 0, book_keeping

def reconstruct(l_c : List[str]) -> str:
    correspondence = {
        "(" : ")",
        "[" : "]",
        "{" : "}",
        "<" : ">"
    }

    score = {
        ")" : 1,
        "]" : 2,
        "}" : 3,
        ">" : 4
    }

    ret_str = 0
    for s in reversed(l_c):
        ret_str = ret_str * 5 + score[correspondence[s]]
    return ret_str

def part1(fname : str) -> int:
    with open(fname, 'r') as f:
        lines = f.readlines()
    syn_err = 0
    for l in lines:
        syn_err += isValid(l.strip())[0]
    return syn_err

def part2(fname : str) -> int:
    with open(fname, 'r') as f:
        lines = f.readlines()
    
    v = np.zeros(0)
    for l in lines:
        res = isValid(l.strip())
        remaining_str = res[1]
        if res[0] <= 0:
            score = reconstruct(remaining_str)
            v = np.concatenate((v, [score]))
    return v

def main():
    fname = "../myinput10"
    syn_err = part1(fname)
    print(f"Part1: {syn_err}")
    val = part2(fname)
    sorted_val = np.sort(val)
    print(np.median(sorted_val))

if __name__ == "__main__":
    main()

Day 11

Julia
CI = CartesianIndex
const M = mapreduce(l->parse.(Int, collect(l))', vcat, eachline("../input11"))
const M2 = copy(M)

const adj = setdiff(CI(-1,-1):CI(1,1), (CI(0,0), ))
flash!(M, CM, c) = [M[c+a]+=1 for a in adj if c+a  CM]

function step!(M)
    CM = CartesianIndices(M)
    M .+= 1
    @label again
    for c in CM
        (M[c] > 20 || M[c] < 10) && continue
        M[c] = 30
        flash!(M, CM, c)
        @goto again
    end
    M[M .> 9] .= 0
    sum(iszero, M)
end

println("p1: ", sum(x->step!(M), 1:100))
println("p2: ", findfirst(x->step!(M2)==100, 1:1000))

Day 12

Julia
const pairs = [=>(split(x, '-')...) for x in eachline("../input12")]

function islegal(name, path, twice)
    name == "start" && return false
    if name  path || all(isuppercase, name)
        return true
    elseif twice[]
        twice[] = false
        return true
    end
    return false
end

function explore(path=["start"]; res=[0], twice=[false])
    here = last(path)
    if here == "end" 
        res[] += 1
        return res[]
    end
    for p in pairs
        t1 = copy(twice); t2 = copy(twice)
        p[1] == here && islegal(p[2], path, t1) && explore([path; p[2]]; res, twice=t1)
        p[2] == here && islegal(p[1], path, t2) && explore([path; p[1]]; res, twice=t2)
    end
    res[]
end

println("P1 :", explore())
println("P2 :", explore(twice=[true]))

Day 13

Julia
using SparseArrays

raw_p, raw_i = strip.(split(read("../input13", String), "\n\n"))
points = [parse.(Int, (y,x)) .+ 1 for (x,y) in split.(split(raw_p, "\n"), ",")]
instructions = split(raw_i, "\n")

let M = sparse(first.(points), last.(points), true)
    for (i, line) in enumerate(instructions)
        @show size(M)
        M = if contains(line, "y=")
            M[1:end÷2, :] .|| M[end:-1:end÷2+2, :]
        else
            M[:, 1:end÷2] .|| M[:, end:-1:end÷2+2]
        end
        i == 1 && println("P1: ", sum(!iszero, M))
    end
    display(M)
end

Day 14

Julia
raw_t, _, raw_rules... = readlines("../input14")
const rules = Dict(=>(x...) for x in split.(raw_rules, " -> "))
add!(dict, key, val) = dict[key] = val + get(dict, key, 0)

let polymer = Dict("$x"=>count(==(x), raw_t) for x in raw_t)
    local atoms
    temp = Dict(k=>0 for k in keys(polymer))
    for i in 1:lastindex(raw_t)-1 # initial pairs
        polymer[raw_t[i:i+1]] = 1
    end
    # Part 1 + 2
    for i = 1:40
        map!(zero, values(temp)) # clear buffer
        for (pair, insertion) in rules
            left, right = pair[1]*insertion, insertion*pair[2]
            Npair = get(polymer, pair, 0)
            add!.(Ref(temp), (left, insertion, right), Npair)
            add!(temp, pair, -Npair) # breaking pairs
        end
        add!.(Ref(polymer), keys(temp), values(temp)) # use buffer
        atoms = sort([v for (k,v) in polymer if length(k) == 1])
        i == 10 && println("P1: ", last(atoms) - first(atoms))
    end
    println("P2: ", last(atoms) - first(atoms))
end

Day 15

Julia
const M = parse.(Int, mapreduce(collect, hcat, eachline("../input15")))
const M2 = hvncat((5,5), false, [@. mod1(M+i+j, 9) for i=0:4, j=0:4]...)
const CI = CartesianIndex
const adjs = Tuple(CI(x) for x in ((0,1), (0,-1), (1,0), (-1,0)))

function main(M)
    CM = CartesianIndices(M)
    Q = [0=>CI(1,1)]
    tot_risks = fill(typemax(Int), size(M))
    while true
        risk, here = pop!(Q)
        tot_risks[here] <= risk && continue # not a better route
        tot_risks[here] = risk
        here == last(CM) && return risk # reached goal

        for a in adjs
            (ne = here+a)  CM && continue
            s = risk + M[ne] => ne
            i = searchsortedfirst(Q, s; rev=true)
            insert!(Q, i, s)
        end
    end
end
println("P1: ", main(M))
println("P2: ", main(M2))
Python
from collections import defaultdict
import numpy as np
from numpy import ravel_multi_index as ep
import heapq

def parse_input(fname : str) -> np.ndarray:
    with open(fname, 'r') as f:
        lines = f.readlines()

    ret_mat = np.zeros((len(lines), len(lines[0]) - 1))
    for row, l in enumerate(lines):
        l = l.strip()
        for col, k in enumerate(l):
            ret_mat[row, col] = int(k)

    return ret_mat

def default_val():
    return np.Infinity

def manhattan_dist(x : int, map_2d : np.ndarray) -> np.ndarray:
    mp_s = map_2d.shape
    end = np.array(mp_s) - 1

    x = np.unravel_index(x, mp_s)
    return np.sum(np.abs(x - end))

def reconstruct_path(came_from : dict, current_node : int, map_2d : np.ndarray) -> float:
    start = np.zeros(2, dtype=int)
    start_ravel_idx = ep(start, map_2d.shape)

    path = [current_node]
    while path[-1] != start_ravel_idx:
        path.append(came_from[path[-1]])
    return np.array(path)

def get_neighbor(ravel_idx : int, map_2d : np.ndarray) -> np.ndarray:
    '''
    Returns (4,)
    '''
    map_coords = np.array(np.unravel_index(ravel_idx, map_2d.shape))

    neighbors = np.zeros(0)
    if map_coords[0] > 0:
        up_neighbor = map_coords.copy()
        up_neighbor[0] -= 1

        neighbors = np.concatenate([neighbors, [ep(up_neighbor, map_2d.shape)]])

    if map_coords[0] < map_2d.shape[0] - 1:
        up_neighbor = map_coords.copy()
        up_neighbor[0] += 1

        neighbors = np.concatenate([neighbors, [ep(up_neighbor, map_2d.shape)]])
        
    if map_coords[1] > 0:
        up_neighbor = map_coords.copy()
        up_neighbor[1] -= 1

        neighbors = np.concatenate([neighbors, [ep(up_neighbor, map_2d.shape)]])
        
    if map_coords[1] < map_2d.shape[1] - 1:
        up_neighbor = map_coords.copy()
        up_neighbor[1] += 1

        neighbors = np.concatenate([neighbors, [ep(up_neighbor, map_2d.shape)]])
        
    return neighbors.astype(int)

def a_star(map_2d : np.ndarray) -> np.ndarray:
    start = np.zeros(2, dtype=int)
    mp_s = map_2d.shape
    end = np.array(mp_s).astype(int) - 1
    start_ravel_idx = ep(start, mp_s)
    end_ravel_idx = ep(end, mp_s)

    open_set = [(map_2d[start], start_ravel_idx)]
    heapq.heapify(open_set)

    came_from = dict()
    gScore = defaultdict(default_val)
    gScore[start_ravel_idx] = 0

    fScore = defaultdict(default_val)
    fScore[start_ravel_idx] = manhattan_dist(start_ravel_idx, map_2d)

    while len(open_set) > 0:
        (_, node_ravel_idx) = heapq.heappop(open_set)
        if node_ravel_idx == end_ravel_idx:
            return reconstruct_path(came_from, node_ravel_idx, map_2d)

        for neighbor in get_neighbor(node_ravel_idx, map_2d):
            tentative_gScore = gScore[node_ravel_idx] + map_2d[np.unravel_index(neighbor, map_2d.shape)]

            if tentative_gScore < gScore[neighbor]:
                came_from[neighbor] = node_ravel_idx
                gScore[neighbor] = tentative_gScore
                fScore[neighbor] = tentative_gScore + manhattan_dist(neighbor, map_2d)

                # TODO: O(n) look up, should fix with a parallel map look up
                # print(open_set)
                # if neighbor not in open_set:
                if not any(neighbor == i[1] for i in open_set):
                    heapq.heappush(open_set, (fScore[neighbor], neighbor))

    raise Exception("No Path Found")
    
def generate_big_map(map_2d : np.ndarray) -> np.ndarray:
    (r, c) = map_2d.shape
    big_map = np.zeros((5 * r, 5 * c))

    increment_map2d = map_2d.copy()
    big_map[:r, :c] = map_2d
    for i in range(1, 5):
        increment_map2d += 1
        increment_map2d[increment_map2d == 10] = 1
        big_map[:r, i * c : (i + 1) * c] = increment_map2d

    increment_map2d = big_map[:r, :].copy()
    for i in range(1, 5):
        increment_map2d += 1
        increment_map2d[increment_map2d == 10] = 1
        big_map[i * r : (i + 1) * r, :] = increment_map2d

    return big_map

def test_map_gen(small_map : str, big_map : str) -> bool:
    small_map = parse_input(small_map)

    big_map = parse_input(big_map)

    gen_map = generate_big_map(small_map)

    return np.allclose(big_map, gen_map)

def main():
    fname = "../myinput15"
    map2d = parse_input(fname)

    # Part 1
    result = a_star(map2d)
    path = np.unravel_index(result, map2d.shape)
    # It seems it doesn't want the starting point's cost
    print(f"P1: {np.sum(map2d[path]) - map2d[0, 0]}")

    # Part 2:
    # Generate Map
    big_map = generate_big_map(map2d)

    result = a_star(big_map)
    path = np.unravel_index(result, big_map.shape)
    print(f"P2: {np.sum(big_map[path]) - big_map[0, 0]}")

if __name__ == "__main__":
    main()

Day 16

Day 17

Julia
const AX, AY = eval(Meta.parse("($(match(r"x.*", replace(readchomp("../input17"), ".."=>":")).match))"))

let y_max = 0, res=0
    for vx=-AX.stop:AX.stop, vy=AY.start:-AY.start
        x=y=_ymax=0
        while true
            x,y,vx,vy = x+vx, y+vy, vx - sign(vx), vy-1
            _ymax = max(y, _ymax)
            if x  AX && y  AY
                y_max = max(y_max, _ymax)
                res+=1
                break
            end
            y <= AY.start && vy < 0 && break # impossible to come back
        end
    end
    println("P1: ", y_max)
    println("P2: ", res)
end

Day 18

Julia
function explode(s)
    count = 0
    target = 0:-1
    for (i, c) in enumerate(s)
        count += c == '['
        count -= c == ']'
        if count == 5
            target = i:findnext(']', s, i)
            break
        end
    end
    if !isempty(target)
        _s = s[target]
        tar_l, tar_r = parse.(Int, x.match for x in eachmatch(r"\d+", _s))
        s = replace(s, _s=>"   0"; count=1)
        l = findnext(r"\d+", s, max(target.start-5, 1))
        if !isnothing(l) && l.stop+1 != target.stop
            l_res = parse(Int, s[l]) + tar_l
            s = s[begin:l.start-1] * string(l_res) * s[l.stop+1:end]
        end
        r = findnext(r"\d+", s, target.stop+1)
        if !isnothing(r)
            r_res = parse(Int, s[r]) + tar_r
            s = s[begin:r.start-1] * string(r_res) * s[r.stop+1:end]
        end
    end
    return replace(s, " "=>"")
end

function spli(s)
    idxs = findall(r"\d+", s)
    iid = findfirst(x-> parse(Int, s[x]) >= 10, idxs)
    isnothing(iid) && return s
    num_str = s[idxs[iid]]
    num = parse(Int, num_str)
    l,r = fld(num, 2), cld(num, 2)
    replace(s, num_str => "[$l,$r]"; count=1)
end

function (s1, s2)
    _s = s = "[$s1,$s2]"
    while true
        s = explode(s)
        if s != _s
            _s = s
            continue
        end
        s = spli(s)
        if s != _s
            _s = s
            continue
        end
        break
    end
    return s
end

Day 19

Day 20

Julia
using SparseArrays
const CI = CartesianIndex
const algo = parse.(Int, collect(replace(readlines("../input20")[1], '.'=>0, '#'=>1)));
const raw_mat = Matrix{Int64}(mapreduce(l->permutedims(replace(collect(l), '.'=>0, '#'=>1)), vcat, readlines("../input20")[3:end]))

function conv(M, ci)
    window = ci-CI(1,1):ci+CI(1,1)
    idx = evalpoly(2, vec(M[window]') |> reverse) + 1
    algo[idx]
end

function main(N=2)
    M1 = raw_mat
    _s = size(M1, 1) + 2*N + 4
    M2 = zeros(Int, _s, _s)
    M2[3+N:end-N-2, 3+N:end-N-2] .= M1
    for _ = 1:N
        M1 = copy(M2)
        for c in CartesianIndices((_s-2, _s-2)) .+ CI(1,1)
            M2[c] = conv(M1, c)
        end
    end
    count(>(0), M2[N+1:end-N, N+1:end-N])
end

println("P1: ", main())

Day 21

Julia
using Base.Iterators: Stateful, take, cycle
p1, p2 = parse.(Int, last.(eachline("../input21")))

function game(p1,p2)
    det_die = Stateful(cycle(1:100))
    s1 = s2 = 0
    while true
        p1 = mod1(p1 + sum(take(det_die, 3)), 10)
        s1 += p1
        s1 >= 1000 && return det_die.taken * s2

        p2 = mod1(p2 + sum(take(det_die, 3)), 10)
        s2 += p2
        s2 >= 1000 && return det_die.taken * s1
    end
end

println("P1: ", game(p1,p2))

Day 22

Julia
function sol(N=50)
    M = falses(2N+1, 2N+1, 2N+1)
    for l in eachline("../input22")
        mat = eachmatch(r"(-?\d+)\.\.(-?\d+)", l)
        coords = mapreduce(x->parse.(Int, x.captures), vcat, mat) .+ (N+1)
        any(c -> (c<0 || c>2N), coords) && continue
        x1,x2,y1,y2,z1,z2  = coords
        region = CartesianIndices((x1:x2, y1:y2, z1:z2))
        val = startswith(l, "on") ? 1 : 0
        M[region] .= val
    end
    sum(M)
end

println("P1: ", sol())

Day 23

Day 24

Day 25

Day 26

Day 27

Day 28

Day 29

Day 30

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.