Day01 - Javascript
const fs = require("fs");
const input = fs.readFileSync(0, "utf8");
const elves = input.split("\n\n").map((elf) =>
elf.split("\n").map(Number).reduce((a, b) => a + b)
).sort((a, b) => b - a);
console.log("Part 1:", elves[0]);
console.log("Part 2:", elves.slice(0, 3).reduce((a, b) => a + b, 0));
Output:
v19.2.0
Part 1: 71502
Part 2: 208191
Back to top
Day02 - Python
import sys
text = sys.stdin.read().splitlines()
p_map = {"X": 1, "Y": 2, "Z": 3}
d_map = {"A": "X", "B": "Y", "C": "Z"}
w_map = {"A": "Y", "B": "Z", "C": "X"}
l_map = {"A": "Z", "B": "X", "C": "Y"}
def part1():
points = 0
for line in text:
o, m = line.split()
if w_map[o] == m:
points += 6
if d_map[o] == m:
points += 3
points += p_map[m]
print("Part 1:", points)
def part2():
points = 0
for line in text:
o, m = line.split()
if m == "X":
points += p_map[l_map[o]]
if m == "Y":
points += 3 + p_map[d_map[o]]
if m == "Z":
points += 6 + p_map[w_map[o]]
print("Part 2:", points)
if __name__ == "__main__":
part1()
part2()
Output:
Python 3.11.0
Part 1: 11767
Part 2: 13886
Back to top
Day03 - Ruby
require 'set'
def calculate_priority(letter)
if letter >= 'a' and letter <= 'z'
return letter.ord - 'a'.ord + 1
end
return letter.ord - 'A'.ord + 27
end
def part1(input)
s = 0
input.each do |line|
h1, h2 = line.chars.each_slice(line.length / 2).map(&:join)
a = Set.new(h1.chars)
b = Set.new(h2.chars)
common = a & b
s += calculate_priority common.to_a[0]
end
puts "Part 1: #{s}"
end
def part2(input)
s = 0
input.each_slice(3) do |group|
a, b, c = group.map { |g| Set.new(g.chomp.chars) }
common = a & b & c
s += calculate_priority common.to_a[0]
end
puts "Part 2: #{s}"
end
input = ARGF.readlines
part1 input
part2 input
Output:
ruby 3.1.3p185 (2022-11-24 revision 1a6b16756e) [x86_64-linux]
Part 1: 8039
Part 2: 2510
Back to top
Day04 - Lua
pattern = "(%d+)-(%d+),(%d+)-(%d+)"
function is_subrange(a, b, c, d)
return (a <= c and b >= d) or (c <= a and d >= b)
end
function overlaps(a, b, c, d)
return (a >= c and a <= d) or (b >= c and b <= d) or (c >= a and c <= b) or (d >= a and d <= b)
end
p1_sum = 0
p2_sum = 0
for line in io.lines() do
local _, _, a, b, c, d = string.find(line, pattern)
if is_subrange(tonumber(a), tonumber(b), tonumber(c), tonumber(d)) then
p1_sum = p1_sum + 1
end
if overlaps(tonumber(a), tonumber(b), tonumber(c), tonumber(d)) then
p2_sum = p2_sum + 1
end
end
print("Part 1:", p1_sum)
print("Part 2:", p2_sum)
Output:
Lua 5.4.4 Copyright (C) 1994-2022 Lua.org, PUC-Rio
Part 1: 453
Part 2: 919
Back to top
Day05 - Crystal
stacks = [
['P', 'F', 'M', 'Q', 'W', 'G', 'R', 'T'],
['H', 'F', 'R'],
['P', 'Z', 'R', 'V', 'G', 'H', 'S', 'D'],
['Q', 'H', 'P', 'B', 'F', 'W', 'G'],
['P', 'S', 'M', 'J', 'H'],
['M', 'Z', 'T', 'H', 'S', 'R', 'P', 'L'],
['P', 'T', 'H', 'N', 'M', 'L'],
['F', 'D', 'Q', 'R'],
['D', 'S', 'C', 'N', 'L', 'P', 'H'],
]
input = ARGF.gets_to_end
_, moves = input.split "\n\n"
moves.each_line do |line|
/move (\d+) from (\d+) to (\d+)/.match line
amount, from, to = $1.to_i, $2.to_i, $3.to_i
stacks[to - 1] = stacks[to - 1] + (stacks[from - 1].pop amount).reverse
end
puts "Part 1: #{stacks.map { |stack| stack.last }.join}"
stacks = [
['P', 'F', 'M', 'Q', 'W', 'G', 'R', 'T'],
['H', 'F', 'R'],
['P', 'Z', 'R', 'V', 'G', 'H', 'S', 'D'],
['Q', 'H', 'P', 'B', 'F', 'W', 'G'],
['P', 'S', 'M', 'J', 'H'],
['M', 'Z', 'T', 'H', 'S', 'R', 'P', 'L'],
['P', 'T', 'H', 'N', 'M', 'L'],
['F', 'D', 'Q', 'R'],
['D', 'S', 'C', 'N', 'L', 'P', 'H'],
]
moves.each_line do |line|
/move (\d+) from (\d+) to (\d+)/.match line
amount, from, to = $1.to_i, $2.to_i, $3.to_i
stacks[to - 1] = stacks[to - 1] + stacks[from - 1].pop amount
end
puts "Part 2: #{stacks.map { |stack| stack.last }.join}"
Output:
Crystal 1.6.2 [879691b2e] (2022-11-03)
LLVM: 13.0.1
Default target: x86_64-unknown-linux-gnu
Part 1: TPGVQPFDH
Part 2: DMRDFRHHH
Back to top
Day06 - C
#include <stdio.h>
#include <stdbool.h>
bool are_all_different(char *recent, int n) {
for (int i = 0; i < n; ++i) {
for (int j = i+1; j < n; ++j) {
if (recent[i] == recent[j]) return false;
}
}
return true;
}
int main(void) {
char recent4[4], recent14[14];
bool p1_done = false;
for (int i = 0; ;i++) {
char c = getchar();
recent4[i % 4] = c;
recent14[i % 14] = c;
if (i > 3 && !p1_done && are_all_different(recent4, 4)) {
printf("Part 1: %d\n", i + 1);
p1_done = true;
}
if (i > 13 && are_all_different(recent14, 14)) {
printf("Part 2: %d\n", i + 1);
break;
}
}
return 0;
}
Output:
cc (GCC) 12.2.0
Copyright (C) 2022 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
Part 1: 1965
Part 2: 2773
Back to top
Day07 - Go
package main
import (
"bufio"
"fmt"
"os"
"strconv"
"strings"
)
func main() {
dirs := make(map[string]int64)
var path []string
scanner := bufio.NewScanner(os.Stdin)
for scanner.Scan() {
line := scanner.Text()
if strings.HasPrefix(line, "$ cd") {
dir := strings.Split(line, " ")[2]
if dir == "/" {
path = append(path, "/")
} else if dir == ".." {
path = path[:len(path)-1]
} else {
path = append(path, path[len(path)-1]+dir+"/")
}
}
if line[0] >= '0' && line[0] <= '9' {
for _, p := range path {
i, _ := strconv.ParseInt(strings.Split(line, " ")[0], 10, 64)
dirs[p] += i
}
}
}
var s1 int64
var s2 []int64
for _, v := range dirs {
if v <= 100_000 {
s1 += v
}
if v >= 30_000_000-(70_000_000-dirs["/"]) {
s2 = append(s2, v)
}
}
min := s2[0]
for _, v := range s2 {
if v < min {
min = v
}
}
fmt.Println("Part 1:", s1)
fmt.Println("Part 2:", min)
}
Output:
go version go1.19.4 linux/amd64
Part 1: 1232307
Part 2: 7268994
Back to top
Day08 - Cpp
#include <iostream>
#include <string>
#include <vector>
using grid = std::vector<std::string>;
bool see_right(const grid& g, int row, int col) {
auto c = g[row][col];
for (auto i = 0; i < col; ++i)
if (g[row][i] >= c) return false;
return true;
}
bool see_left(const grid& g, int row, int col) {
auto c = g[row][col];
for (auto i = col+1; i < g[row].size(); ++i)
if (g[row][i] >= c) return false;
return true;
}
bool see_up(const grid& g, int row, int col) {
auto c = g[row][col];
for (auto i = 0; i < row; ++i)
if (g[i][col] >= c) return false;
return true;
}
bool see_down(const grid& g, int row, int col) {
auto c = g[row][col];
for (auto i = row+1; i < g.size(); ++i)
if (g[i][col] >= c) return false;
return true;
}
int v_left(const grid& g, int row, int col) {
auto c = g[row][col];
auto d = 0;
for (auto i = col-1; i >= 0; --i) {
d++;
if (g[row][i] >= c) break;
}
return d;
}
int v_right(const grid& g, int row, int col) {
auto c = g[row][col];
auto d = 0;
for (auto i = col+1; i < g[row].size(); ++i) {
d++;
if (g[row][i] >= c) break;
}
return d;
}
int v_up(const grid& g, int row, int col) {
auto c = g[row][col];
auto d = 0;
for (auto i = row-1; i >= 0; --i) {
d++;
if (g[i][col] >= c) break;
}
return d;
}
int v_down(const grid& g, int row, int col) {
auto c = g[row][col];
auto d = 0;
for (auto i = row+1; i < g.size(); ++i) {
d++;
if (g[i][col] >= c) break;
}
return d;
}
int calc_v(const grid& g, int row, int col) {
return v_up(g, row, col)
* v_left(g, row, col)
* v_down(g, row, col)
* v_right(g, row, col);
}
int main() {
std::string s;
grid g;
while (std::getline(std::cin, s))
g.push_back(s);
auto p1 = 0, p2 = 0;
for (auto row = 0; row < g.size(); ++row) {
for (auto col = 0; col < g[row].size(); ++col) {
if (see_up(g, row, col) || see_left(g, row, col) || see_down(g, row, col) || see_right(g, row, col))
p1++;
}
}
for (auto row = 1; row < g.size(); ++row) {
for (auto col = 1; col < g[row].size(); ++col) {
auto v = calc_v(g, row, col);
p2 = v > p2 ? v : p2;
}
}
std::cout << "Part 1: " << p1 << '\n';
std::cout << "Part 2: " << p2 << '\n';
return 0;
}
Output:
g++ (GCC) 12.2.0
Copyright (C) 2022 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
Part 1: 1711
Part 2: 301392
Back to top
Day09 - Clojure
(ns solution
(:require [clojure.string :as str]
[clojure.java.io :as io]))
(def directions
{"U" [0 -1] "D" [0 1] "L" [-1 0] "R" [1 0]})
(defn parse-move [move]
(let [[dir amount] (str/split move #" ")]
(repeat (Integer/parseInt amount) dir)))
(def moves
(->> (line-seq (io/reader *in*))
(mapcat parse-move)))
(defn vec-add [[u1 u2] [v1 v2]]
[(+ u1 v1) (+ u2 v2)])
(defn vec-sub [[u1 u2] [v1 v2]]
[(- u1 v1) (- u2 v2)])
(defn vec-abs [v]
(vec (map abs v)))
(defn dist [a b]
(reduce max (vec-abs (vec-sub a b))))
(defn clamp [n min max]
(if (< n min) min (if (> n max) max n)))>
(defn normalize [v]
(vec (map #(clamp % -1 1) v)))
(defn move-tail [head tail]
(if (> (dist head tail) 1)
(vec-add tail (normalize (vec-sub head tail)))
tail))
(defn move-knot [rope knot]
(conj rope (move-tail (last rope) knot)))
(defn move [history dir]
(let [[head & rest] (first history)
next-head (vec-add head (directions dir))
rope (seq (reduce move-knot [next-head] rest))]
(conj history rope)))
(defn solve [length]
(->> (reduce move (seq [(repeat length [0 0])]) moves)
(map last)
(set)
(count)))
(println "Part 1:" (solve 2))
(println "Part 2:" (solve 10))
Output:
babashka v1.0.168
Part 1: 6642
Part 2: 2765
Back to top
Day10 - Nim
import std/strutils
import std/sugar
type
Clock = tuple
cycle: int
signalStrength: int
crt: seq[char]
proc inc(clock: var Clock; x: int) =
if clock.cycle mod 40 in @[x - 1, x, x + 1]:
clock.crt[clock.cycle] = '#'
clock.cycle += 1
if clock.cycle in @[20, 60, 100, 140, 180, 220]:
clock.signalStrength += x * clock.cycle
proc display(clock: Clock) =
for row in 0 ..< 6:
let s = row * 40
let e = s + 40
echo join(clock.crt[s ..< e])
let lines = readAll(io.stdin).strip().splitLines()
var x = 1
let initialCrt = collect(newSeq):
for _ in 0 ..< 240: ' '
var clock: Clock = (cycle: 0, signalStrength: 0, crt: initialCrt)
for line in lines:
if line == "noop":
clock.inc(x)
else:
let n = line.split(' ')[1].parseInt()
clock.inc(x)
clock.inc(x)
x += n
echo "Part 1: ", clock.signalStrength
echo "Part 2:"
clock.display()
Output:
Nim Compiler Version 1.6.10 [Linux: amd64]
Compiled at 2022-12-06
Copyright (c) 2006-2021 by Andreas Rumpf
active boot switches: -d:release
Part 1: 15680
Part 2:
#### #### ### #### # # ## # # ###
# # # # # # # # # # # # #
# ### ### ### #### # # # # #
# # # # # # # # ## # # ###
# # # # # # # # # # # #
#### # ### # # # ### ## #
Back to top
Day11 - Janet
# Hand parsed :^)
(defn get-monkeys
[]
@[{:items @[77 69 76 77 50 58]
:op |(* $ 11)
:test 5 :true 1 :false 5}
{:items @[75 70 82 83 96 64 62]
:op |(+ $ 8)
:test 17 :true 5 :false 6}
{:items @[53]
:op |(* $ 3)
:test 2 :true 0 :false 7}
{:items @[85 64 93 64 99]
:op |(+ $ 4)
:test 7 :true 7 :false 2}
{:items @[61 92 71]
:op |(* $ $)
:test 3 :true 2 :false 3}
{:items @[79 73 50 90]
:op |(+ $ 2)
:test 11 :true 4 :false 6}
{:items @[50 89]
:op |(+ $ 3)
:test 13 :true 4 :false 3}
{:items @[83 56 64 58 93 91 56 65]
:op |(+ $ 5)
:test 19 :true 1 :false 0}])
(defn dividers-lcm
[monkeys]
(let [dividers (map |($ :test) monkeys)]
(reduce math/lcm 1 dividers)))
(defn execute-round
[monkeys post inspected items]
(loop [[monkey-index monkey] :pairs monkeys]
(+= (inspected monkey-index) (length (monkey-index items)))
(each item (monkey-index items)
(let [u (post ((monkey :op) item))
dst (if (= 0 (% u (monkey :test))) :true :false)]
(array/push ((monkey dst) items) u)))
(set (items monkey-index) @[])))
(defn solve
[monkeys rounds post]
(let [inspected (array/new-filled (length monkeys) 0)
items (map |($ :items) monkeys)]
(loop [i :range [0 rounds]] (execute-round monkeys post inspected items))
(sort inspected >)
(* (0 inspected) (1 inspected))))
(print "Part 1: " (solve (get-monkeys) 20 |(math/floor (/ $ 3))))
(def lcm (dividers-lcm (get-monkeys)))
(print "Part 2: " (solve (get-monkeys) 10000 |(% $ lcm)))
Output:
1.24.1-c81c57f
Part 1: 57838
Part 2: 15050382231
Back to top
Day12 - Java
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.util.ArrayDeque;
import java.util.HashSet;
record Tuple(int i, int j, int dist, char c) {}
record Visited(int a, int b) {}
public class solution {
static String[] grid;
static ArrayDeque<Tuple> queue;
static HashSet<Visited> visited;
static void push(int i, int j, int d, char a) {
if (i < 0 || i >= grid.length || j < 0 || j >= grid[i].length()) return;
if (visited.contains(new Visited(i, j))) return;
var b = grid[i].charAt(j) == 'E' ? 'z' : grid[i].charAt(j);
if (b > a + 1) return;
visited.add(new Visited(i, j));
queue.add(new Tuple(i, j, d+1, b));
}
static int solve(String[] grid, char start, char start2) {
queue = new ArrayDeque<Tuple>();
visited = new HashSet<Visited>();
for (var i = 0; i < grid.length; i++) {
for (var j = 0; j < grid[i].length(); j++) {
if (grid[i].charAt(j) == start || grid[i].charAt(j) == start2) {
queue.add(new Tuple(i, j, 0, 'a'));
visited.add(new Visited(i, j));
}
}
}
while (!queue.isEmpty()) {
var cur = queue.poll();
if (grid[cur.i()].charAt(cur.j()) == 'E') return cur.dist();
push(cur.i() + 1, cur.j(), cur.dist(), cur.c());
push(cur.i() - 1, cur.j(), cur.dist(), cur.c());
push(cur.i(), cur.j() + 1, cur.dist(), cur.c());
push(cur.i(), cur.j() - 1, cur.dist(), cur.c());
}
return -1;
}
static int solve(String[] grid, char start) {
return solve(grid, start, start);
}
static String[] getInput() {
var reader = new BufferedReader(new InputStreamReader(System.in));
return reader.lines().toArray(String[]::new);
}
public static void main(String[] args) {
grid = getInput();
System.out.println("Part 1: " + solve(grid, 'S'));
System.out.println("Part 2: " + solve(grid, 'S', 'a'));
}
}
Output:
javac 19.0.1
openjdk 19.0.1 2022-10-18
OpenJDK Runtime Environment (build 19.0.1+10)
OpenJDK 64-Bit Server VM (build 19.0.1+10, mixed mode)
Part 1: 520
Part 2: 508
Back to top
Day13 - Julia
using JSON
less(a::Int, b::Int) = a < b
less(a::Vector, b::Int) = less(a, [b])
less(a::Int, b::Vector) = less([a], b)
less(a::Vector, b::Vector) =
!isempty(b) && (isempty(a)
|| less(a[1], b[1])
|| (!less(b[1], a[1]) && less(a[2:end], b[2:end])))
function read_input()::Vector{Tuple{Vector, Vector}}
pairs = []
while true
p1 = readline()
p2 = readline()
if p1 == "" || p2 == "" break end
push!(pairs, (JSON.parse(p1), JSON.parse(p2)))
readline()
end
pairs
end
input = read_input()
function part1()
sum([i for (i, (p1, p2)) in enumerate(input) if less(p1, p2)])
end
function part2()
v = collect(Iterators.flatten([[a, b] for (a, b) in input]))
push!(v, [[2]], [[6]])
sort!(v, lt=less)
findfirst(x -> x == [[2]], v) * findfirst(x -> x == [[6]], v)
end
println("Part 1: ", part1())
println("Part 2: ", part2())
Output:
julia version 1.8.3
Part 1: 5196
Part 2: 22134
Back to top
Day14 - Dart
import 'dart:io';
import 'dart:math';
Set<Point<int>> parse(String input) {
final grid = <Point<int>>{};
for (var line in input.split("\n")) {
final coords = line.split(" -> ");
for (var i = 1; i < coords.length; i++) {
final o = coords[i - 1].split(",").map(int.parse);
final d = coords[i].split(",").map(int.parse);
final p1 = Point(o.first, o.last);
final p2 = Point(d.first, d.last);
final v = Point((p2.x - p1.x).sign, (p2.y - p1.y).sign);
for (var p = p1; p != p2; p += v) {
grid.add(p);
}
grid.add(p2);
}
}
return grid;
}
void main() async {
final input = (await stdin.first).map(String.fromCharCode).join().trim();
final grid = parse(input);
final maxY = grid
.reduce((value, element) => (value.y > element.y) ? value : element)
.y;
for (int i = -2000; i < 2000; i++) {
grid.add(Point(i, maxY + 2));
}
final source = Point(500, 0);
final path = [source];
final order = [Point(0, 1), Point(-1, 1), Point(1, 1)];
var count = 0;
int part1 = 0;
while (path.isNotEmpty) {
final current = path.last;
if (grid.contains(source)) {
break;
}
if (current.y > maxY && part1 == 0) {
part1 = count;
}
var couldPlace = false;
for (var p in order) {
if (!grid.contains(current + p)) {
path.add(current + p);
couldPlace = true;
break;
}
}
if (!couldPlace) {
grid.add(current);
path.removeLast();
count++;
}
}
print("Part 1: $part1");
print("Part 2: $count");
}
Output:
Dart SDK version: 2.18.5 (stable) (Tue Nov 22 15:47:29 2022 +0000) on "linux_x64"
Part 1: 862
Part 2: 28744
Back to top
Day15 - Rust
use std::io;
#[derive(Debug, Clone, Copy)]
struct Sensor {
pos: (isize, isize),
close: (isize, isize),
dist: isize,
}
impl Sensor {
fn inside_p(&self, p: (isize, isize)) -> bool {
self.close != p && self.dist as usize >= self.pos.0.abs_diff(p.0) + self.pos.1.abs_diff(p.1)
}
fn parse(line: &str) -> Self {
let nums = line
.split_whitespace()
.into_iter()
.filter(|x| x.contains(|a: char| a.is_digit(10)))
.map(|x| x.chars().filter(|a| a == &'-' || a.is_digit(10)).collect())
.filter_map(|x: String| x.parse::<isize>().ok())
.collect::<Vec<_>>();
Self {
pos: (nums[0], nums[1]),
close: (nums[2], nums[3]),
dist: (nums[0].abs_diff(nums[2]) + nums[1].abs_diff(nums[3])) as isize,
}
}
}
fn main() {
let input = io::stdin()
.lines()
.map(|line| Sensor::parse(&line.unwrap()))
.collect::<Vec<_>>();
let left_b = input.iter().map(|s| s.pos.0 - s.dist).min().unwrap();
let right_b = input.iter().map(|s| s.pos.0 + s.dist).max().unwrap();
let part1 = (left_b..=right_b)
.filter(|&i| input.iter().any(|s| s.inside_p((i, 2_000_000))))
.collect::<Vec<_>>()
.len();
println!("Part 1: {}", part1);
let part2 = input
.iter()
.find_map(|s| {
((s.pos.0 - s.dist - 1).max(0)..=s.pos.0.min(4_000_000))
.zip(s.pos.1..=4_000_000)
.find_map(|p| {
input
.iter()
.all(|s| !s.inside_p(p))
.then(|| p.0 * 4_000_000 + p.1)
})
})
.unwrap();
println!("Part 2: {}", part2);
}
Output:
rustc 1.65.0 (897e37553 2022-11-02)
Part 1: 5100463
Part 2: 11557863040754
Back to top