Compare commits

...

10 commits

Author SHA1 Message Date
dece a903132905 2015 day 4 2020-12-27 20:50:29 +01:00
dece 7f9920447a 2015 day 3 2020-12-27 20:25:15 +01:00
dece 6ad8e84551 2015 day 2 2020-12-27 20:05:18 +01:00
dece 351fc11bed 2015 day 1 (redoing lost challenges) 2020-12-27 19:32:47 +01:00
dece 56ec1d2ec7 Improve new day scripts 2020-12-27 19:13:14 +01:00
dece 1310c4314e Day 20; 2020 is done! 2020-12-27 18:22:13 +01:00
dece 2a07c6458c Day 25 2020-12-27 15:19:58 +01:00
dece 595db92c73 Day 24 2020-12-27 15:19:53 +01:00
dece b8cc585455 Clean day 15 2020-12-24 03:21:36 +01:00
dece 7c6bfc0ac1 Day 23 2020-12-24 03:12:58 +01:00
20 changed files with 532 additions and 46 deletions

1
2015/.gitignore vendored Normal file
View file

@ -0,0 +1 @@
target/

14
2015/Cargo.lock generated Normal file
View file

@ -0,0 +1,14 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
[[package]]
name = "aoc2015"
version = "0.1.0"
dependencies = [
"md5",
]
[[package]]
name = "md5"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "490cc448043f947bae3cbee9c203358d62dbee0db12107a74be5c30ccfd09771"

11
2015/Cargo.toml Normal file
View file

@ -0,0 +1,11 @@
[package]
name = "aoc2015"
version = "0.1.0"
authors = ["dece <shgck@pistache.land>"]
edition = "2018"
[lib]
name = "aoc"
[dependencies]
md5 = "0.7.0"

31
2015/src/bin/day1.rs Normal file
View file

@ -0,0 +1,31 @@
use aoc::input;
fn main() {
let lines = input::read_lines();
let line = lines[0].to_owned();
// Part 1
let mut floor = 0;
for c in line.chars() {
match c {
'(' => floor += 1,
')' => floor -= 1,
_ => {}
}
}
println!("Floor: {}", floor);
// Part 2
floor = 0;
for (i, c) in line.chars().enumerate() {
match c {
'(' => floor += 1,
')' => floor -= 1,
_ => {}
}
if floor == -1 {
println!("Entered -1 at {}.", i + 1);
return
}
}
}

28
2015/src/bin/day2.rs Normal file
View file

@ -0,0 +1,28 @@
use aoc::input;
fn main() {
let lines = input::read_lines();
let dimensions: Vec<Vec<u32>> = lines.iter().map(|line| {
line.split('x').map(|dim| dim.parse::<u32>().unwrap()).collect()
}).collect();
// Part 1
let total: u32 = dimensions.iter().map(|dim| {
let (l, w, h) = (dim[0], dim[1], dim[2]);
let areas = vec!(l * w, w * h, h * l);
let slack = areas.iter().min().unwrap();
areas.iter().map(|a| 2 * a).sum::<u32>() + slack
}).sum();
println!("Needed paper: {}.", total);
// Part 2
let total: u32 = dimensions.iter().map(|dim| {
let mut sdim = dim.clone();
sdim.sort();
let (d1, d2, d3) = (sdim[0], sdim[1], sdim[2]);
let wrap = 2 * d1 + 2 * d2;
let bow = d1 * d2 * d3;
wrap + bow
}).sum();
println!("Needed ribbon: {}.", total);
}

45
2015/src/bin/day3.rs Normal file
View file

@ -0,0 +1,45 @@
use std::collections::HashMap;
use aoc::input;
fn main() {
let chars = input::read_chars();
// Part 1
let mut houses = HashMap::<(i32, i32), u32>::new();
let (mut x, mut y) = (0, 0);
houses.insert((0, 0), 1);
for c in chars.chars() {
match c {
'>' => x += 1,
'<' => x -= 1,
'v' => y += 1,
'^' => y -= 1,
_ => {}
}
let presents = houses.entry((x, y)).or_insert(0);
*presents += 1;
}
let num_houses = houses.values().filter(|v| **v > 0).count();
println!("Houses delivered: {}.", num_houses);
// Part 2
let mut houses = HashMap::<(i32, i32), u32>::new();
let (mut x, mut y, mut rx, mut ry) = (0, 0, 0, 0);
houses.insert((0, 0), 1);
let mut is_robot_turn = false;
for c in chars.chars() {
match c {
'>' => if is_robot_turn { rx += 1 } else { x += 1 },
'<' => if is_robot_turn { rx -= 1 } else { x -= 1 },
'v' => if is_robot_turn { ry += 1 } else { y += 1 },
'^' => if is_robot_turn { ry -= 1 } else { y -= 1 },
_ => {}
}
let presents = houses.entry(if is_robot_turn { (rx, ry) } else { (x, y) }).or_insert(0);
*presents += 1;
is_robot_turn = !is_robot_turn;
}
let num_houses = houses.values().filter(|v| **v > 0).count();
println!("Houses delivered: {}.", num_houses);
}

30
2015/src/bin/day4.rs Normal file
View file

@ -0,0 +1,30 @@
use aoc::input;
fn main() {
let lines = input::read_lines();
let mut key = lines[0].to_string();
let key_len = key.len();
// Part 1
let mut n = 0;
loop {
key.replace_range(key_len.., &n.to_string());
let hash = md5::compute(key.as_bytes());
if &hash[..2] == [0, 0] && hash[2] < 0x10 {
break
}
n += 1
}
println!("Found coin with n {}.", n);
// Part 2
loop {
key.replace_range(key_len.., &n.to_string());
let hash = md5::compute(key.as_bytes());
if &hash[..3] == [0, 0, 0] {
break
}
n += 1
}
println!("Found coin with n {}.", n);
}

21
2015/src/input.rs Normal file
View file

@ -0,0 +1,21 @@
use std::io;
pub fn read_lines() -> Vec<String> {
let mut lines = vec!();
loop {
let mut text = String::new();
let read_count = io::stdin().read_line(&mut text).unwrap();
if read_count == 0 {
break
}
if let Some(stripped) = text.strip_suffix("\n") {
text = stripped.to_string()
}
lines.push(text);
}
lines
}
pub fn read_chars() -> String {
read_lines()[0].to_owned()
}

1
2015/src/lib.rs Normal file
View file

@ -0,0 +1 @@
pub mod input;

View file

@ -1,7 +0,0 @@
def main():
with open("", "rt") as f:
lines = [line.rstrip() for line in f.readlines()]
if __name__ == "__main__":
main()

View file

@ -3,7 +3,7 @@ from collections import defaultdict
def main():
nl = list(map(lambda s: int(s), sys.stdin.read().rstrip().split(",")))
nl = list(map(int, sys.stdin.read().rstrip().split(",")))
# Part 1
print("Part 1:", get_nth(2020, nl))

133
2020/day20.py Normal file
View file

@ -0,0 +1,133 @@
import math
import sys
DIM = 10
def main():
groups = sys.stdin.read().rstrip().split("\n\n")
tiles = {}
for group in groups:
lines = [line.rstrip() for line in group.splitlines()]
tile_id = int(lines[0][5:].rstrip(":"))
tiles[tile_id] = list(map(list, lines[1:]))
idim = int(math.sqrt(len(tiles)))
# Part 1
# instructions are not very clean, but looks like we only have to check 3
# rotations + optionally horizontal flip. vertical flip is not needed on
# example, and a vflip is actually rot180 + hflip.
perms = {}
for tile_id, tile in tiles.items():
perms[tile_id] = permutations(tile)
image = search({}, 0, 0, [], perms, idim)
tl = image[(0, 0)]
tr = image[(0, idim - 1)]
bl = image[(idim - 1, 0)]
br = image[(idim - 1, idim - 1)]
print("Multiplied corners:", tl[0] * tr[0] * bl[0] * br[0])
# Part 2
pattern = [
(0, 0), (1, 1),
(4, 1), (5, 0), (6, 0), (7, 1),
(10, 1), (11, 0), (12, 0), (13, 1),
(16, 1), (17, 0), (18, 0), (18, -1), (19, 0)
]
merged = merge_tiles(image, perms, idim)
global DIM
DIM = len(merged)
perms = permutations(merged)
max_monsters = max(find_monsters(perm, pattern) for perm in perms)
num_dashes = sum(c == "#" for row in merged for c in row)
monster_dashes = max_monsters * len(pattern)
print("Roughness:", num_dashes - monster_dashes)
def permutations(tile):
return [
tile,
hflip(tile),
rot90(tile),
rot90(hflip(tile)),
rot90(rot90(tile)),
rot90(rot90(hflip(tile))),
rot90(rot90(rot90(tile))),
rot90(rot90(rot90(hflip(tile)))),
]
def rot90(tile):
rtile = [[None for _ in range(DIM)] for _ in range(DIM)]
for x in range(DIM):
for y in range(DIM):
rtile[x][y] = tile[DIM - y - 1][x]
return rtile
def hflip(tile):
return [row[::] for row in tile[::-1]]
def search(image, x, y, placed, perms, idim):
fitting = find_fitting(image, x, y, placed, perms)
if (x, y) == (idim - 1, idim - 1) and len(fitting) == 1:
image[(idim - 1, idim - 1)] = fitting[0]
return image
for fit in fitting:
nimage = image.copy()
nimage[(x, y)] = fit
nx = (x + 1) % idim
ny = y if nx > 0 else y + 1
nplaced = placed + [fit[0]]
if (complete_image := search(nimage, nx, ny, nplaced, perms, idim)):
return complete_image
def find_fitting(image, x, y, placed, perms):
up_perm = image.get((x, y - 1))
up = perms[up_perm[0]][up_perm[1]] if up_perm else None
left_perm = image.get((x - 1, y))
left = perms[left_perm[0]][left_perm[1]] if left_perm else None
down_perm = image.get((x, y + 1))
down = perms[down_perm[0]][down_perm[1]] if down_perm else None
right_perm = image.get((x + 1, y))
right = perms[right_perm[0]][right_perm[1]] if right_perm else None
return [
(tile_id, tile_perm_id)
for tile_id, tile_perms in perms.items()
if tile_id not in placed
for tile_perm_id, tile_perm in enumerate(tile_perms)
if fits(tile_perm, up, left, down, right)
]
def fits(tile, up, left, down, right):
return all((
not up or tile[0] == up[-1],
not down or tile[-1] == down[0],
not left or [r[0] for r in tile] == [r[-1] for r in left],
not right or [r[-1] for r in tile] == [r[0] for r in right],
))
def merge_tiles(image, perms, idim):
tdim = DIM - 2
merged = [[] for _ in range(idim * tdim)]
for ix in range(idim):
for iy in range(idim):
i_tile_id, i_perm_id = image[(ix, iy)]
tile = perms[i_tile_id][i_perm_id]
for y, row in enumerate(tile[1:-1]):
merged[iy * tdim + y] += row[1:-1]
return merged
def find_monsters(image, pattern):
w, h = len(image[0]), len(image)
num_monsters = 0
for x in range(w):
for y in range(h):
for px, py in pattern:
ix, iy = x + px, y + py
if not 0 <= ix < w or not 0 <= iy < h:
break
if image[iy][ix] != "#":
break
else:
num_monsters += 1
return num_monsters
if __name__ == "__main__":
main()

51
2020/day23.py Normal file
View file

@ -0,0 +1,51 @@
import sys
def main():
lines = [line.rstrip() for line in sys.stdin]
# Part 1
cups = list(map(int, lines[0]))
for _ in range(100):
move(cups)
while cups[0] != 1:
cups.append(cups.pop(0))
print("Part 1:", "".join(map(str, cups))[1:])
# Part 2
# No spare 256TB of RAM nor repetitions it seems! Use another structure.
cups_list = list(map(int, lines[0]))
cups_list += list(range(10, 1000001))
cups = {
cups_list[i]: cups_list[(i + 1) % 1000000]
for i in range(len(cups_list))
}
cc = cups_list[0]
for _ in range(10000000):
zyoooom(cups, cc)
cc = cups[cc]
print("Part 2:", cups[1] * cups[cups[1]])
def move(cups):
cc = cups[0]
pick = cups[1:4]
d = cc - 1 or 9
while d in pick:
d = d - 1 if d > 1 else 9
del cups[1:4]
di = cups.index(d) + 1
cups[di:di] = pick
cups.append(cups.pop(0))
def zyoooom(cups, c):
pick = cups[c], cups[cups[c]], cups[cups[cups[c]]]
after_pick = cups[pick[2]]
dest = c - 1 or 1000000
while dest in pick:
dest = dest - 1 if dest > 1 else 1000000
after_dest = cups[dest]
cups[c] = after_pick
cups[dest] = pick[0]
cups[pick[2]] = after_dest
if __name__ == "__main__":
main()

65
2020/day24.py Normal file
View file

@ -0,0 +1,65 @@
import sys
from grid import Grid
def main():
lines = [line.rstrip() for line in sys.stdin]
g = Grid(value_factory=int)
# Part 1
for line in lines:
x, y = 0, 0
while line:
if line[0] in ("e", "w"):
x, y = move(x, y, line[0])
line = line[1:]
else:
x, y = move(x, y, line[:2])
line = line[2:]
g.setv(x, y, abs(g.getv(x, y) - 1))
print("Black tiles:", sum(v for _, _, v in g.values_gen()))
# Part 2 aka stop using this grid class it's horrible
for _ in range(100):
expand(g)
yagol(g)
print("Black tiles p2:", sum(v for _, _, v in g.values_gen()))
def move(x, y, d):
if d in ("e", "w"):
x += 1 if d == "e" else -1
else:
dy, dx = d[0], d[1]
if y % 2 == 0 and dx == "w":
x -= 1
elif y % 2 == 1 and dx == "e":
x += 1
y += 1 if dy == "s" else -1
return x, y
D = ["e", "se", "sw", "w", "nw", "ne"]
def expand(g):
for x, y, _ in list(g.values_gen()):
for d in D:
dx, dy = move(x, y, d)
if not g.hasv(dx, dy):
g.setv(dx, dy, 0)
def yagol(g):
c = {}
for x, y, v in g.values_gen():
bn = 0
for d in D:
nx, ny = move(x, y, d)
if g.hasv(nx, ny):
bn += g.getv(nx, ny)
if v == 1 and (bn == 0 or bn > 2):
c[(x, y)] = 0
elif v == 0 and bn == 2:
c[(x, y)] = 1
for (x, y), v in c.items():
g.setv(x, y, v)
if __name__ == "__main__":
main()

29
2020/day25.py Normal file
View file

@ -0,0 +1,29 @@
import sys
def main():
lines = [line.rstrip() for line in sys.stdin]
cpk, dpk = int(lines[0]), int(lines[1])
dls = crack(dpk, 7) # cracking door's key is much quicker
k = gen_key(cpk, dls)
print(k)
def step(v, sn):
return (v * sn) % 20201227
def crack(pk, sn):
i = 0
v = 1
while v != pk:
v = step(v, sn)
i += 1
return i
def gen_key(sn, ls):
v = 1
for _ in range(ls):
v = step(v, sn)
return v
if __name__ == "__main__":
main()

View file

@ -1,19 +0,0 @@
#!/usr/bin/env python3
import os
from datetime import date
TEMPLATE = """\
import sys
def main():
lines = [line.rstrip() for line in sys.stdin]
if __name__ == "__main__":
main()
"""
day = date.today().day
with open(f"day{day}.py", "wt") as f:
f.write(TEMPLATE)
os.system(f"python ../fetch.py {day}")

View file

@ -1,19 +0,0 @@
from datetime import datetime
import os
import requests
import sys
if len(sys.argv) < 2:
print("Usage: <fetch> day")
sys.exit()
day = sys.argv[1]
year = sys.argv[2] if len(sys.argv) > 2 else str(datetime.now().year)
URL = "https://adventofcode.com/{}/day/{}/input"
SESSION_ID = os.environ["AOC_SESSION"]
response = requests.get(URL.format(year, day), cookies={"session": SESSION_ID})
response.raise_for_status()
with open("day{}.txt".format(day), "wt") as output_file:
output_file.write(response.text)

59
prepare.py Normal file
View file

@ -0,0 +1,59 @@
import argparse
import os
import shutil
import webbrowser
from datetime import datetime
from pathlib import Path
import requests
URL = "https://adventofcode.com/{}/day/{}"
SESSION_ID = os.environ["AOC_SESSION"]
def main():
now = datetime.now()
parser = argparse.ArgumentParser()
parser.add_argument("day", type=int, default=now.day)
parser.add_argument("year", type=int, default=now.year)
parser.add_argument("--lang", default="py")
args = parser.parse_args()
day_url = get_url(args.day, args.year)
input_url = day_url + "/input"
input_text = fetch(input_url)
create_files(input_text, args.day, args.year, args.lang)
webbrowser.open_new_tab(day_url)
webbrowser.open_new_tab(input_url)
def fetch(input_url):
response = requests.get(input_url, cookies={"session": SESSION_ID})
response.raise_for_status()
return response.text
def get_url(day, year):
return URL.format(year, day)
def create_files(text, day, year, lang):
root_dir = Path(__file__).parent.resolve()
year_dir = root_dir / str(year)
year_dir.mkdir(exist_ok=True)
input_path = year_dir / f"day{day}.txt"
with open(input_path, "wt") as output_file:
output_file.write(text)
template_path = root_dir / f"template.{lang}"
if template_path.exists():
if lang == "rs":
bin_path = year_dir / "src" / "bin"
bin_path.mkdir(exist_ok=True)
script_path = bin_path / f"day{day}.{lang}"
else:
script_path = year_dir / f"day{day}.{lang}"
shutil.copyfile(template_path, script_path)
else:
print(f"No template for {lang}.")
if __name__ == "__main__":
main()

7
template.py Normal file
View file

@ -0,0 +1,7 @@
import sys
def main():
lines = [line.rstrip() for line in sys.stdin]
if __name__ == "__main__":
main()

5
template.rs Normal file
View file

@ -0,0 +1,5 @@
use aoc::input;
fn main() {
}