Compare commits

..
36 Commits
Author SHA1 Message Date
YK 1670ffc8d7 add macro for .next().unwrap() 2024-12-01 20:35:18 +03:00
YK 9f353660f1 solution: day 21 2024-11-27 17:03:06 +03:00
YK 9c0653f39e add macro for .parse().unwrap() 2024-11-27 17:02:56 +03:00
YK ce521e996a solution: day 20 2024-11-27 12:37:41 +03:00
YK 9fab2616c5 gold answer for d19 2024-11-27 12:11:48 +03:00
YK 60ef7ce843 solution: day 19 2024-11-27 12:06:53 +03:00
YK 7a65f1431c add prelude re-export for VecDeque 2024-11-27 12:06:39 +03:00
YK f8633a07c4 typo 2024-11-27 03:00:45 +03:00
YK 06e554c53e solution: day 18 2024-11-27 03:00:26 +03:00
YK 350f6ef6eb solution: day 17 2024-11-27 01:51:13 +03:00
YK 728a14855a cleanup: day 15 2024-11-27 00:05:26 +03:00
YK c8a2ebffc4 solution: day 16 2024-11-27 00:05:07 +03:00
YK afba7f543c solution: day 15 2024-11-26 23:39:03 +03:00
YK 1f4e5be195 cleanup: day 14 2024-11-25 17:29:25 +03:00
YK aead1786ba solution: day 14 2024-11-25 17:23:45 +03:00
YK 95122bb4ca solution: day 13 (dfs) 2024-11-25 15:23:15 +03:00
YK 576547bfc8 silence warnings for d11 since it's not done yet 2024-11-25 01:29:39 +03:00
YK 143dabb6f2 solution: day 12 2024-11-25 01:28:27 +03:00
YK c60d0bf3f6 day 11: todo 2024-11-24 22:46:00 +03:00
YK d3c1c39a68 solution: day 10 (ugly) 2024-11-20 13:13:46 +03:00
YK 27501c65ee solution/refactor: day 9 (simulation approach; 1000x faster) 2024-11-20 11:56:37 +03:00
YK 4614df8845 solution: day 9 (expansion approach) 2024-11-20 11:56:07 +03:00
YK c32121a4b2 solution: day 8 2024-11-20 10:00:13 +03:00
YK cd4dab86c8 remove unused code in d2 solution 2024-11-20 10:00:02 +03:00
YK f29621d4ca solution: day 7 2024-11-20 09:31:22 +03:00
YK c046c78f08 add feature flag 2024-11-20 09:31:14 +03:00
YK d13f519c9e add prelude type alias for static str 2024-11-20 09:31:05 +03:00
YK 1cf168ec71 solution: day 6 2024-11-20 00:32:14 +03:00
YK 92d5b89623 solution: day 5 + deps 2024-11-19 23:53:38 +03:00
YK 22cf080338 style 2024-11-19 23:12:00 +03:00
YK 1404c330c7 solution: day 4 2024-11-19 23:09:53 +03:00
YK ea06d5c748 new prelude re-exports 2024-11-19 23:09:45 +03:00
YK 603f07c784 fix template 2024-11-19 22:07:26 +03:00
YK be167e4b2c solution: day 3 2024-11-19 21:45:46 +03:00
YK ad4d777770 remove inputs from repo from now on (as per AoC request) 2024-11-19 21:13:48 +03:00
YK 171282194a improve template 2024-11-19 21:13:09 +03:00
28 changed files with 1596 additions and 28 deletions

No files matched your search

+1
View File
@@ -1 +1,2 @@
/target
/in
Generated
+7
View File
@@ -14,6 +14,7 @@ version = "0.1.0"
dependencies = [
"anyhow",
"itertools",
"md5",
]
[[package]]
@@ -30,3 +31,9 @@ checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186"
dependencies = [
"either",
]
[[package]]
name = "md5"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "490cc448043f947bae3cbee9c203358d62dbee0db12107a74be5c30ccfd09771"
+1
View File
@@ -6,3 +6,4 @@ edition = "2021"
[dependencies]
anyhow = "1.0.93"
itertools = "0.13.0"
md5 = "0.7.0"
+19 -19
View File
@@ -1,24 +1,24 @@
pub mod d1;
pub mod d2;
// pub mod d3;
// pub mod d4;
// pub mod d5;
// pub mod d6;
// pub mod d7;
// pub mod d8;
// pub mod d9;
// pub mod d10;
// pub mod d11;
// pub mod d12;
// pub mod d13;
// pub mod d14;
// pub mod d15;
// pub mod d16;
// pub mod d17;
// pub mod d18;
// pub mod d19;
// pub mod d20;
// pub mod d21;
pub mod d3;
pub mod d4;
pub mod d5;
pub mod d6;
pub mod d7;
pub mod d8;
pub mod d9;
pub mod d10;
pub mod d11;
pub mod d12;
pub mod d13;
pub mod d14;
pub mod d15;
pub mod d16;
pub mod d17;
pub mod d18;
pub mod d19;
pub mod d20;
pub mod d21;
// pub mod d22;
// pub mod d23;
// pub mod d24;
+124
View File
@@ -0,0 +1,124 @@
use crate::prelude::*;
pub type I = (M<SS, Vec<u32>>, M<SS, (SS, SS)>);
pub type O = SS;
fn _parse (data: &'static str) -> I {
let mut initial: M<SS, Vec<u32>> = M::new();
let mut maps = M::new();
for line in data.lines() {
if let Some(sfx) = line.strip_prefix("value ") {
let (val, out) = sfx.split_once(" goes to bot ").map(|(v, b)| (v.parse().unwrap(), b)).unwrap();
initial.entry(out).or_default().push(val);
} else {
let line = line.trim_start_matches("bot ");
let (source, dests) = line.split_once(" gives low to ").unwrap();
let (l, h) = dests.split_once(" and high to ").unwrap();
let l = l.strip_prefix("bot ").unwrap_or(l);
let h = h.strip_prefix("bot ").unwrap_or(h);
maps.insert(source, (l, h));
}
}
(initial, maps)
}
#[allow(suspicious_double_ref_op)]
fn _silver ((mut state, map): I) -> O {
let mut cur = state.iter().find(|e| e.1.len() == 2).unwrap().0.clone();
loop {
let chips = state.entry(cur).or_default();
let a = chips[0];
let b = chips[1];
chips.clear();
if (a == 17 && b == 61) || (b == 17 && a == 61) { return cur; }
let (low, high) = map.get(cur).unwrap();
let l_entry = state.entry(low).or_default();
l_entry.push(if a > b { b } else { a });
let l_len = l_entry.len();
let h_entry = state.entry(high).or_default();
h_entry.push(if a > b { a } else { b });
let h_len = h_entry.len();
cur = if l_len == 2 {
low
} else if h_len == 2 {
high
} else {
state.iter().find(|e| e.1.len() == 2).unwrap().0
}
}
}
#[allow(suspicious_double_ref_op)]
fn _gold ((mut state, map): I) -> u32 {
let mut cur = state.iter().find(|e| e.1.len() == 2).unwrap().0.clone();
loop {
let chips = state.entry(cur).or_default();
let a = chips[0];
let b = chips[1];
chips.clear();
let (low, high) = map.get(cur).unwrap();
let l_entry = state.entry(low).or_default();
l_entry.push(if a > b { b } else { a });
let l_len = l_entry.len();
let h_entry = state.entry(high).or_default();
h_entry.push(if a > b { a } else { b });
let h_len = h_entry.len();
if let Some(a) = state.get("output 0") && let Some(b) = state.get("output 1") && let Some(c) = state.get("output 2") {
return a[0] * b[0] * c[0]
}
cur = if l_len == 2 {
low
} else if h_len == 2 {
high
} else {
state.iter().find(|e| e.1.len() == 2).unwrap().0
}
}
}
#[cfg(test)]
mod test {
use super::*;
fn read () -> I {
let data = inc!(10);
_parse(data)
}
#[test]
fn silver () {
let data = read();
let ans = _silver(data);
assert_eq!(ans, s!(116))
}
#[test]
fn gold () {
let data = read();
let ans = _gold(data);
assert_eq!(ans, 23903)
}
}
+73
View File
@@ -0,0 +1,73 @@
#![allow(dead_code)]
#![allow(unused_variables)]
use crate::prelude::*;
pub type I = Vec<Vec<char>>;
pub type O = i64;
fn _parse (data: &str) -> I {
Default::default()
}
fn _solve (data: &I) -> O {
Default::default()
}
fn _silver (data: &I) -> O {
Default::default()
}
fn _gold (data: &I) -> O {
Default::default()
}
type St = [Vec<char>; 4];
fn is_final_state (state: &St) -> bool {
state[1..].iter().all(|e| e.is_empty())
}
fn is_valid_state (state: &St) -> bool {
state.iter().all(|level| level.len() < 2 || level.iter().all(|e| e.is_lowercase()) || {
let mut chips = level.iter().filter(|e| e.is_uppercase());
while let Some(chip) = chips.next() {
if level.iter().find(|&v| chip.to_ascii_lowercase() == *v).is_none() {
return false;
}
}
true
})
}
fn state_discriminant (state: &St) -> [(usize, usize); 4] {
state.iter().map(|s| (
s.iter().filter(|e| e.is_lowercase()).count(),
s.iter().filter(|e| e.is_uppercase()).count()
)).collect_vec().try_into().unwrap()
}
#[cfg(test)]
mod test {
use super::*;
fn read () -> I {
vec![vec!['a', 'b', 'c', 'd', 'e', 'A', 'B', 'C'], vec!['D', 'E'], vec![], vec![]]
}
#[test]
fn silver () {
let data = read();
let ans = _silver(&data);
assert_eq!(ans, 1)
}
#[test]
fn gold () {
let data = read();
let ans = _gold(&data);
assert_eq!(ans, 1)
}
}
+121
View File
@@ -0,0 +1,121 @@
use crate::prelude::*;
pub type I = Vec<Inst>;
pub type O = i64;
pub enum Val {
Register (char),
Literal (i64),
}
#[allow(dead_code)]
impl Val {
fn new (i: &str) -> Self {
match i.parse::<i64>() {
Ok(s) => Self::Literal(s),
Err(_) => Self::Register(i.chars().next().unwrap())
}
}
}
pub enum Inst {
Jnz (Val, isize),
Cpy (Val, char),
Inc (char),
Dec (char)
}
fn _parse (data: &str) -> I {
data.trim().lines().map(|line| {
let mut split = line.split_ascii_whitespace();
match split.next().unwrap() {
"jnz" => {
Inst::Jnz(Val::new(split.next().unwrap()), split.next().unwrap().parse().unwrap())
},
"cpy" => {
Inst::Cpy(Val::new(split.next().unwrap()), split.next().unwrap().chars().next().unwrap())
},
"inc" => {
Inst::Inc(split.next().unwrap().chars().next().unwrap())
},
"dec" => {
Inst::Dec(split.next().unwrap().chars().next().unwrap())
},
_ => unreachable!()
}
}).collect()
}
fn _solve (data: I, r: &mut M<char, i64>) {
let mut ptr = 0;
while ptr < data.len() {
let i = &data[ptr];
match i {
Inst::Inc(reg) => *r.entry(*reg).or_default() += 1,
Inst::Dec(reg) => *r.entry(*reg).or_default() -= 1,
Inst::Cpy(val, reg) => {
match val {
Val::Literal(val) => *r.entry(*reg).or_default() = *val,
Val::Register(r2) => *r.entry(*reg).or_default() = *r.get(r2).unwrap(),
}
},
Inst::Jnz(val, dest) => {
let val = match val {
Val::Literal(val) => *val,
Val::Register(r2) => *r.get(r2).unwrap(),
};
if val != 0 {
ptr = ptr.checked_add_signed(*dest).unwrap_or(0);
continue;
}
}
}
ptr += 1;
}
}
fn _silver (data: I) -> O {
let mut state = M::from([('a', 0), ('b', 0), ('c', 0), ('d', 0)]);
_solve(data, &mut state);
*state.get(&'a').unwrap()
}
fn _gold (data: I) -> O {
let mut state = M::from([('a', 0), ('b', 0), ('c', 1), ('d', 0)]);
_solve(data, &mut state);
*state.get(&'a').unwrap()
}
#[cfg(test)]
mod test {
use super::*;
fn read () -> I {
let data = inc!(12);
_parse(data)
}
#[test]
fn silver () {
let data = read();
let ans = _silver(data);
assert_eq!(ans, 318007)
}
#[test]
fn gold () {
let data = read();
let ans = _gold(data);
assert_eq!(ans, 9227661)
}
}
+74
View File
@@ -0,0 +1,74 @@
#![allow(dead_code)]
#![allow(non_upper_case_globals)]
use crate::prelude::*;
pub type O = usize;
const INPUT: usize = 1358;
fn is_wall (y: usize, x: usize) -> bool {
((x*x + 3*x + 2*x*y + y + y*y) + INPUT).count_ones() % 2 != 0
}
fn next ((y, x, len): (usize, usize, usize)) -> Vec<(usize, usize, usize)> {
const cand: [(isize, isize); 4] = [(0, -1), (0, 1), (-1, 0), (1, 0)];
cand.into_iter().filter_map(|(dy, dx)| {
let ny = y.checked_add_signed(dy);
let nx = x.checked_add_signed(dx);
match (ny, nx) {
(Some(y), Some(x)) => if is_wall(y, x) { None } else { Some((y, x, len + 1)) },
_ => None
}
}).collect()
}
fn solve (visited: &mut M<(usize, usize), usize>) {
let mut stack = vec![(1, 1, 0)];
while !stack.is_empty() {
let c @ (y, x, len) = stack.pop().unwrap();
*visited.entry((y, x)).or_default() = len;
for n @ (ny, nx, nl) in next(c) {
if !visited.contains_key(&(ny, nx)) || visited.get(&(ny, nx)).is_some_and(|&old| old > nl) {
stack.push(n);
}
}
}
}
fn _silver () -> O {
let mut visited = M::new();
solve(&mut visited);
*visited.get(&(39, 31)).unwrap()
}
fn _gold () -> O {
let mut visited = M::new();
solve(&mut visited);
visited.values().filter(|&&e| e <= 50).count()
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn silver () {
let ans = _silver();
assert_eq!(ans, 96)
}
#[test]
fn gold () {
let ans = _gold();
assert_eq!(ans, 141)
}
}
+72
View File
@@ -0,0 +1,72 @@
use crate::prelude::*;
pub type O = usize;
pub const INPUT: &'static str = "qzyelonm";
fn _solve (extra: u32) -> O {
let mut candidates: M<char, Vec<usize>> = M::new();
let mut done: Vec<usize> = vec![];
for i in 0.. {
let s = format!("{INPUT}{i}");
let mut hash = md5::compute(s);
let mut repr = format!("{:x}", hash);
for _ in 0..extra {
hash = md5::compute(repr);
repr = format!("{:x}", hash);
}
let three = repr.chars().tuple_windows().find(|&(a, b, c)| a == b && b == c);
if let Some((c, _, _)) = three {
if !candidates.is_empty() {
let five = repr.chars().tuple_windows().find(|&(a, b, c, d, e)| a == b && a == c && a == d && a == e);
if let Some((c, _, _, _, _)) = five {
if let Some(candidates) = candidates.get_mut(&c) {
let pos = candidates.iter().position(|pos| pos + 1000 > i);
if let Some(pos) = pos {
done.extend(candidates.drain(pos..));
candidates.clear();
if done.len() >= 100 { // should be pretty solid, but not sure if I can prove this!
done.sort();
return done[63];
}
}
}
}
}
candidates.entry(c).or_default().push(i);
}
}
0
}
fn _silver () -> O {
_solve(0)
}
fn _gold () -> O {
_solve(2016)
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn silver () {
let ans = _silver();
assert_eq!(ans, 15168)
}
#[test]
fn gold () {
let ans = _gold();
assert_eq!(ans, 20864)
}
}
+59
View File
@@ -0,0 +1,59 @@
#![allow(unused_imports)]
use crate::prelude::*;
pub type I = Vec<(usize, usize)>;
pub type O = usize;
fn _parse (data: &str) -> I {
data.lines()
.map(|e| e.trim().trim_matches('.')
.split_once(" has ")
.map(|(_, r)|
r.split_once(" positions; at time=0, it is at position ")
.map(|(n, p)| (n.parse().unwrap(), p.parse().unwrap()))
)
.flatten()
.unwrap()
)
.collect()
}
fn _solve (data: &I) -> O {
let d = |time, (size, position)| (time + position) % size == 0;
(0..).into_iter().find(|t| data.iter().enumerate().all(|(i, &df)| d(t + i + 1, df))).unwrap()
}
fn _silver (data: I) -> O {
_solve(&data)
}
fn _gold (mut data: I) -> O {
data.push((11, 0));
_solve(&data)
}
#[cfg(test)]
mod test {
use super::*;
fn read () -> I {
let data = inc!(15);
_parse(data)
}
#[test]
fn silver () {
let data = read();
let ans = _silver(data);
assert_eq!(ans, 122318)
}
#[test]
fn gold () {
let data = read();
let ans = _gold(data);
assert_eq!(ans, 3208583)
}
}
+44
View File
@@ -0,0 +1,44 @@
#![allow(non_upper_case_globals)]
#![allow(unused_imports)]
#![allow(dead_code)]
use crate::prelude::*;
pub type I = &'static str;
pub type O = String;
fn checksum (data: &str) -> String {
data.chars().array_chunks().map(|[a, b]| if a == b { '1' } else { '0' }).collect()
}
fn _solve (data: I, disk: usize) -> O {
let mut s = data.to_owned();
while s.len() < disk {
s = format!("{}0{}", &s, s.chars().rev().map(|e| if e == '1' { '0' } else { '1' }).collect::<String>());
}
s = (&s[..disk]).to_owned();
let mut chk = checksum(&s);
while chk.len() % 2 == 0 {
chk = checksum(&chk);
}
chk
}
#[cfg(test)]
mod test {
use super::*;
const input: &'static str = "01110110101001000";
#[test]
fn silver () {
let ans = _solve(input, 272);
assert_eq!(ans, s!("11100111011101111"))
}
#[test]
fn gold () {
let ans = _solve(input, 35651584);
assert_eq!(ans, s!("10001110010000110"))
}
}
+94
View File
@@ -0,0 +1,94 @@
#![allow(non_upper_case_globals)]
#![allow(unused_imports)]
use md5::Digest;
use crate::prelude::*;
pub type I = &'static str;
pub type O = String;
pub type O2 = usize;
const _cand: [(isize, isize, char); 4] = [(-1, 0, 'U'), (1, 0, 'D'), (0, -1, 'L'), (0, 1, 'R')];
fn _open_doors ((y, x): (usize, usize), digest: Digest) -> Vec<(usize, usize, char)> {
let repr = format!("{:x}", digest);
let access: [char; 4] = repr.chars().take(4).collect_vec().try_into().unwrap();
_cand.iter()
.enumerate()
.filter_map(|(i, &(dy, dx, n))| match (y.checked_add_signed(dy), x.checked_add_signed(dx)) {
(Some(y), Some(x)) => if y <= 3 && x <= 3 { Some((y, x, n, i)) } else { None },
_ => None
})
.filter(|(_, _, _, idx)| ('b'..='f').contains(&access[*idx]))
.map(|(a, b, c, _)| (a, b, c))
.collect()
}
fn _digest (pass: &'static str, i: &str) -> Digest {
let full = format!("{pass}{i}");
md5::compute(full)
}
fn _dfs (cur: (usize, usize), path: &mut String, pass: &'static str) -> bool {
if cur == (3, 3) { return true; } else {
let digest = _digest(pass, &path);
let doors = _open_doors(cur, digest);
for (ny, nx, p) in doors {
path.push(p);
if _dfs((ny, nx), path, pass) {
return true;
} else {
path.pop();
}
}
return false;
}
}
fn _dfs_max (cur: (usize, usize), path: &mut String, pass: &'static str, max: &mut usize) {
if cur == (3, 3) {
*max = (*max).max(path.len());
} else {
let digest = _digest(pass, &path);
let doors = _open_doors(cur, digest);
for (ny, nx, p) in doors {
path.push(p);
_dfs_max((ny, nx), path, pass, max);
path.pop();
}
}
}
fn _silver (data: I) -> O {
let mut path = String::new();
_dfs((0, 0), &mut path, data);
path
}
fn _gold (data: I) -> O2 {
let mut path = String::new();
let mut max = 0;
_dfs_max((0, 0), &mut path, data, &mut max);
max
}
#[cfg(test)]
mod test {
use super::*;
const pass: &'static str = "edjrjqaa";
#[test]
fn silver () {
let ans = _silver(pass);
assert_eq!(ans, s!("DUDRDLRRRD"))
}
#[test]
fn gold () {
let ans = _gold(pass);
assert_eq!(ans, 502)
}
}
+64
View File
@@ -0,0 +1,64 @@
use crate::prelude::*;
pub type I = &'static str;
pub type O = usize;
// @TODO: try bit mask approach
fn _solve (data: I, rows: usize) -> O {
let w = data.len();
let mut v = vec![data.to_owned()];
while v.len() < rows {
let mut v2 = String::new();
let prev = &v[v.len() - 1].chars().collect_vec();
for i in 0..w {
let next = match (
i.checked_sub(1).map(|i| prev.get(i)).flatten().unwrap_or(&'.'),
prev.get(i).unwrap_or(&'.'),
prev.get(i+1).unwrap_or(&'.')
) {
('^', '^', '.') | ('.', '^', '^') | ('^', '.', '.') | ('.', '.', '^') => '^',
_ => '.'
};
v2.push(next);
}
v.push(v2);
}
v.concat().chars().filter(|&c| c == '.').count()
}
#[cfg(test)]
mod test {
use super::*;
fn read () -> I {
inc!(18).trim()
}
#[test]
fn example () {
let data = ".^^.^.^^^^";
let ans = _solve(data, 10);
assert_eq!(ans, 38);
let data = "..^^.";
let ans = _solve(data, 3);
assert_eq!(ans, 6);
}
#[test]
fn silver () {
let data = read();
let ans = _solve(data, 40);
assert_eq!(ans, 1982)
}
#[test]
fn gold () {
let data = read();
let ans = _solve(data, 400_000);
assert_eq!(ans, 20005203)
}
}
+42
View File
@@ -0,0 +1,42 @@
use crate::prelude::*;
pub type I = u32;
pub type O = u32;
fn _silver (data: I) -> O {
2 * (data - (1 << (32 - data.leading_zeros() - 1))) + 1
}
fn _gold (data: I) -> O {
let mut left = D::from_iter(0..data/2);
let mut right = D::from_iter(data/2..data);
while !left.is_empty() && !right.is_empty() {
if left.len() > right.len() {
left.pop_back();
} else {
right.pop_back();
}
right.push_front(left.pop_front().unwrap());
left.push_back(right.pop_back().unwrap());
}
left[0] + 1
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn silver () {
let ans = _silver(3005290);
assert_eq!(ans, 1816277)
}
#[test]
fn gold () {
let ans = _gold(3005290);
assert_eq!(ans, 1410967)
}
}
-4
View File
@@ -13,10 +13,6 @@ fn _parse (data: &str) -> I {
}).collect()).collect()
}
fn _solve (data: &I) -> O {
Default::default()
}
fn _silver (data: &I) -> O {
let mut out = format!("");
+74
View File
@@ -0,0 +1,74 @@
#![allow(unused_imports)]
use std::ops::RangeInclusive;
use crate::prelude::*;
pub type I = Vec<RangeInclusive<u64>>;
pub type O = u64;
fn _parse (data: &str) -> I {
data.trim().lines().map(|e| e.trim().split_once('-').map(|(a, b)| a.parse().unwrap()..=b.parse().unwrap()).unwrap()).collect()
}
fn _silver (mut data: I) -> O {
data.sort_by_key(|d| *d.start());
let mut start = data[0].end() + 1;
for d in &data[1..] {
if *d.start() <= start {
start = start.max(d.end() + 1);
continue;
}
return start;
}
Default::default()
}
fn _gold (mut data: I) -> O {
data.sort_by_key(|d| *d.start());
let mut start = data[0].end() + 1;
let mut gaps = 0;
for d in &data[1..] {
if *d.start() <= start {
start = start.max(d.end() + 1);
continue;
}
let l = d.start() - start;
gaps += l;
start = d.end() + 1;
}
// doesn't matter in my input, but for a general case where there's a gap between last
// blocked ip and last possible ip will matter. check is here to ensure it won't overflow
if start <= u32::MAX as u64 {
gaps += u32::MAX as u64 - start;
}
gaps
}
#[cfg(test)]
mod test {
use super::*;
fn read () -> I {
let data = inc!(20);
_parse(data)
}
#[test]
fn silver () {
let data = read();
let ans = _silver(data);
assert_eq!(ans, 17348574)
}
#[test]
fn gold () {
let data = read();
let ans = _gold(data);
assert_eq!(ans, 104)
}
}
+151
View File
@@ -0,0 +1,151 @@
#![allow(non_upper_case_globals)]
#![allow(dead_code)]
use crate::prelude::*;
pub type I = Vec<Inst>;
pub type O = String;
#[derive(Debug, Clone)]
pub enum Inst {
SwapPos (usize, usize),
SwapLetter (char, char),
RotateLeft (usize),
RotateRight (usize),
RotatePos (char),
Reverse (usize, usize),
Move (usize, usize)
}
// const _rotations: [usize; 8] = [1,2,3,4,6,7,0,1];
// ↓
const _rotations: [usize; 8] = [1,1,6,2,7,3,0,4];
impl Inst {
fn from_str (i: &str) -> Self {
let i = i.trim();
if let Some(sfx) = i.strip_prefix("swap position ") {
let (l, r) = sfx.split_once(" with position ").unwrap();
Inst::SwapPos(p!(l), p!(r))
} else if let Some(sfx) = i.strip_prefix("swap letter ") {
let (l, r) = sfx.split_once(" with letter ").unwrap();
Inst::SwapLetter(l.trim().chars().next().unwrap(), r.trim().chars().next().unwrap())
} else if let Some(sfx) = i.strip_prefix("rotate left ") {
let (l, _) = sfx.split_once(' ').unwrap();
Inst::RotateLeft(p!(l))
} else if let Some(sfx) = i.strip_prefix("rotate right ") {
let (l, _) = sfx.split_once(' ').unwrap();
Inst::RotateRight(p!(l))
} else if let Some(sfx) = i.strip_prefix("rotate based on position of letter ") {
Inst::RotatePos(sfx.chars().next().unwrap())
} else if let Some(sfx) = i.strip_prefix("reverse positions ") {
let (l, r) = sfx.split_once(" through ").unwrap();
Inst::Reverse(p!(l), p!(r))
} else if let Some(sfx) = i.strip_prefix("move position ") {
let (l, r) = sfx.split_once(" to position ").unwrap();
Inst::Move(p!(l), p!(r))
} else {
panic!("unknown instruction")
}
}
fn apply (self, target: &mut Vec<char>) {
use Inst as I;
match self {
I::SwapPos(l, r) => target.swap(l, r),
I::SwapLetter(l, r) => {
let lpos = target.iter().position(|&c| c == l);
let rpos = target.iter().position(|&c| c == r);
if let Some(lpos) = lpos && let Some(rpos) = rpos {
target.swap(lpos, rpos);
}
},
I::RotateLeft(times) => target.rotate_left(times),
I::RotateRight(times) => target.rotate_right(times),
I::RotatePos(needle) => {
let pos = target.iter().position(|&c| c == needle);
if let Some(pos) = pos {
let rotations = pos + 1 + if pos >= 4 { 1 } else { 0 };
let len = target.len(); // % len is useless in my input but needed for the general correctness of the algorithm
target.rotate_right(rotations % len);
}
},
I::Reverse(from, to) => (&mut target[from..=to]).reverse(),
I::Move(from, to) => {
let val = target.remove(from);
target.insert(to, val);
}
}
}
fn undo (self, target: &mut Vec<char>) {
use Inst as I;
match self {
I::SwapPos(l, r) => target.swap(l, r),
I::SwapLetter(l, r) => {
let lpos = target.iter().position(|&c| c == l);
let rpos = target.iter().position(|&c| c == r);
if let Some(lpos) = lpos && let Some(rpos) = rpos {
target.swap(lpos, rpos);
}
},
I::RotateLeft(times) => target.rotate_right(times),
I::RotateRight(times) => target.rotate_left(times),
I::RotatePos(needle) => {
let pos = target.iter().position(|&c| c == needle);
if let Some(pos) = pos {
target.rotate_left(_rotations[pos]);
}
},
I::Reverse(from, to) => (&mut target[from..=to]).reverse(),
I::Move(from, to) => {
let val = target.remove(to);
target.insert(from, val);
}
}
}
}
fn _parse (data: &str) -> I {
data.trim().lines().map(Inst::from_str).collect()
}
fn _silver (start: &'static str, list: I) -> O {
let mut state = start.chars().collect_vec();
list.into_iter().for_each(|i| i.apply(&mut state));
state.into_iter().collect()
}
fn _gold (start: &'static str, mut list: I) -> O {
list.reverse();
let mut state = start.chars().collect_vec();
list.into_iter().for_each(|i| i.undo(&mut state));
state.into_iter().collect()
}
#[cfg(test)]
mod test {
use super::*;
fn read () -> I {
let data = inc!(21);
_parse(data)
}
#[test]
fn silver () {
let data = read();
let ans = _silver("abcdefgh", data);
assert_eq!(ans, s!("bfheacgd"))
}
#[test]
fn gold () {
let data = read();
let ans = _gold("fbgdceah", data);
assert_eq!(ans, s!("gcehdbfa"))
}
}
+48
View File
@@ -0,0 +1,48 @@
use crate::prelude::*;
pub type I = &'static str;
pub type O = usize;
fn _silver (data: &I) -> O {
data.lines().filter(|line| {
let v = line.trim().split_ascii_whitespace().filter(|part| !part.is_empty()).map(|num| num.trim().parse::<u16>().unwrap()).collect_vec();
v[0] + v[1] > v[2] &&
v[1] + v[2] > v[0] &&
v[0] + v[2] > v[1]
}).count()
}
fn _gold (data: &I) -> O {
let mut counter = 0;
for chunk in &data.lines().chunks(3) {
let v = chunk.map(|line| line.trim().split_ascii_whitespace().filter(|part| !part.is_empty()).map(|num| num.trim().parse::<u16>().unwrap()).collect_vec()).collect_vec();
counter += (0usize..3).into_iter().filter(|&i| v[0][i] + v[1][i] > v[2][i] && v[1][i] + v[2][i] > v[0][i] && v[2][i] + v[0][i] > v[1][i]).count();
}
counter
}
#[cfg(test)]
mod test {
use super::*;
fn read () -> I {
inc!(3)
}
#[test]
fn silver () {
let data = read();
let ans = _silver(&data);
assert_eq!(ans, 993)
}
#[test]
fn gold () {
let data = read();
let ans = _gold(&data);
assert_eq!(ans, 1849)
}
}
+84
View File
@@ -0,0 +1,84 @@
use crate::prelude::*;
pub type T = (String, M<char, usize>, Vec<char>, u32);
pub type I = Vec<T>;
pub type O = u32;
fn _parse (data: &str) -> I {
data.lines().map(|line| {
let mut parts = line.split('-').collect_vec();
let last = parts.pop().unwrap();
let rest = parts.join(" ");
let m = parts.into_iter().map(|p| p.chars()).flatten().counts();
let (number, checksum) = last.split_once('[').unwrap();
let number = number.parse().unwrap();
let checksum = checksum.trim_matches(']').chars().collect();
(rest, m, checksum, number)
}).collect()
}
fn _solve <'a> (data: &'a I) -> impl Iterator<Item = (&'a String, &'a u32)> {
data.into_iter().filter_map(|(st, m, checksum, num)| {
let mut m = m.into_iter().collect_vec();
m.sort_by(|a, b| match b.1.cmp(&a.1) {
Or::Equal => a.0.cmp(&b.0),
e => e
});
if checksum.into_iter().zip(m.into_iter()).all(|(&l, (&r, _))| l == r) {
Some((st, num))
} else {
None
}
})
}
fn _silver (data: &I) -> O {
_solve(data).map(|(_, num)| num).sum()
}
fn _gold (data: &I) -> O {
*_solve(data).find(|(s, num)| {
let mut s = s.chars().collect_vec();
for c in s.iter_mut() {
match *c {
' ' => (),
a => {
*c = ((((a as u8 - b'a') as u32 + *num) % 26) + b'a' as u32) as u8 as char;
}
}
}
let s = s.into_iter().collect::<String>();
s.contains("north")
}).unwrap().1
}
#[cfg(test)]
mod test {
use super::*;
fn read () -> I {
let data = inc!(4);
_parse(data)
}
#[test]
fn silver () {
let data = read();
let ans = _silver(&data);
assert_eq!(ans, 278221)
}
#[test]
fn gold () {
let data = read();
let ans = _gold(&data);
assert_eq!(ans, 267)
}
}
+74
View File
@@ -0,0 +1,74 @@
use crate::prelude::*;
pub type I = &'static str;
pub type O = String;
fn _silver (data: I) -> O {
let mut s = s!("");
for i in 0.. {
let st = format!("{data}{i}");
let hash = md5::compute(st);
let repr = format!("{:x}", hash);
if repr.starts_with("00000") {
s.push_str(&repr[5..=5]);
if s.len() == 8 {
break;
}
}
}
s
}
fn _gold (data: I) -> O {
let mut s = ['\0'; 8];
for i in 0.. {
let st = format!("{data}{i}");
let hash = md5::compute(st);
let repr = format!("{:x}", hash);
if repr.starts_with("00000") {
let idx = (&repr[5..=5]).parse::<usize>().unwrap_or(usize::MAX);
let val = (&repr[6..=6]).chars().next().unwrap();
if idx < s.len() && s[idx] == '\0' {
s[idx] = val;
}
if s.iter().all(|&e| e != '\0') { break; }
}
}
s.into_iter().collect()
}
#[cfg(test)]
mod test {
use super::*;
fn read () -> I {
"abbhdwsy"
}
#[test]
fn silver () {
let data = read();
let ans = _silver(data);
assert_eq!(ans, s!("801b56a7"))
}
#[test]
fn gold () {
let data = read();
let ans = _gold(data);
assert_eq!(ans, s!("424a0197"))
}
}
+55
View File
@@ -0,0 +1,55 @@
use crate::prelude::*;
pub type I = Vec<&'static str>;
pub type O = String;
fn _parse (data: &'static str) -> I {
data.lines().collect()
}
fn _solve (data: &I) -> Vec<M<char, usize>> {
let mut m: Vec<M<char, usize>> = vec![M::new(); data[0].len()];
for line in data {
for (col, c) in line.chars().enumerate() {
*m[col].entry(c).or_default() += 1;
}
}
m
}
fn _silver (data: &I) -> O {
_solve(data).into_iter().map(|v| v.into_iter().max_by_key(|&(_, v)| v).unwrap().0).collect::<String>()
}
fn _gold (data: &I) -> O {
_solve(data).into_iter().map(|v| v.into_iter().min_by_key(|&(_, v)| v).unwrap().0).collect::<String>()
}
#[cfg(test)]
mod test {
use super::*;
fn read () -> I {
let data = inc!(6);
_parse(data)
}
#[test]
fn silver () {
let data = read();
let ans = _silver(&data);
assert_eq!(ans, s!("gebzfnbt"))
}
#[test]
fn gold () {
let data = read();
let ans = _gold(&data);
assert_eq!(ans, s!("fykjtwyn"))
}
}
+81
View File
@@ -0,0 +1,81 @@
use crate::prelude::*;
pub type I = Vec<(SS, Vec<SS>, Vec<SS>)>;
pub type O = usize;
fn _parse (data: &'static str) -> I {
data.lines().map(|line| {
let line = line.trim();
let mut out = vec![];
let mut _in = vec![];
let mut pos = false;
let mut l = 0;
let mut r = 0;
for (idx, c) in line.char_indices() {
match c {
'[' | ']' => {
let v = if pos { &mut _in } else { &mut out };
v.push(&line[l..r]);
pos = !pos;
l = idx + 1;
r = l;
continue;
},
_ => {
r += 1;
}
}
}
let v = if pos { &mut _in } else { &mut out };
v.push(&line[l..r]);
(line, _in, out)
}).collect()
}
fn _silver (data: &I) -> O {
let abba = |(a, b, c, d)| a == d && b == c && b != a;
data.into_iter().filter(|(_, _in, out)| {
!_in.into_iter().any(|part| part.chars().tuple_windows().any(abba)) &&
out.into_iter().any(|part| part.chars().tuple_windows().any(abba))
}).count()
}
fn _gold (data: &I) -> O {
let aba = |(a, b, c)| a == c && b != a;
data.into_iter().filter(|(_, _in, out)| {
let mut abas = S::new();
out.into_iter().for_each(|part| part.chars().tuple_windows().for_each(|x| if aba(x) { abas.insert(x); }));
_in.into_iter().any(|part| part.chars().tuple_windows().any(|x @ (a, b, _)| aba(x) && abas.contains(&(b, a, b))))
}).count()
}
#[cfg(test)]
mod test {
use super::*;
fn read () -> I {
let data = inc!(7);
_parse(data)
}
#[test]
fn silver () {
let data = read();
let ans = _silver(&data);
assert_eq!(ans, 105)
}
#[test]
fn gold () {
let data = read();
let ans = _gold(&data);
assert_eq!(ans, 258)
}
}
+94
View File
@@ -0,0 +1,94 @@
use crate::prelude::*;
pub type I = Vec<Inst>;
pub type O = usize;
#[derive(Clone, Debug)]
pub enum Inst {
Rect(usize, usize),
RotateY(usize, usize),
RotateX(usize, usize),
}
#[allow(dead_code)]
impl Inst {
fn from_line (data: &str) -> Self {
if let Some(suffix) = data.strip_prefix("rotate row y=") && let Some((y, rotation)) = suffix.split_once(" by ") {
Self::RotateY(y.parse().unwrap(), rotation.parse().unwrap())
} else if let Some(suffix) = data.strip_prefix("rotate column x=") && let Some((x, rotation)) = suffix.split_once(" by ") {
Self::RotateX(x.parse().unwrap(), rotation.parse().unwrap())
} else {
let (a, b) = data.strip_prefix("rect ").unwrap().split_once('x').unwrap();
Self::Rect(a.parse().unwrap(), b.parse().unwrap())
}
}
}
fn _parse (data: &str) -> I {
data.lines().map(Inst::from_line).collect()
}
fn _solve (data: &I) -> Vec<Vec<bool>> {
let mut state = vec![vec![false; 50]; 6];
for i in data {
match i {
Inst::Rect(a, b) => {
for y in 0..*b {
for x in 0..*a {
state[y][x] = true;
}
}
},
Inst::RotateY(y, r) => {
state[*y].rotate_right(*r);
},
Inst::RotateX(x, r) => {
let mut view = state.iter().map(|e| e[*x]).collect_vec();
view.rotate_right(*r);
for (y, n) in view.into_iter().enumerate() {
state[y][*x] = n;
}
}
}
}
state
}
fn _silver (data: &I) -> O {
_solve(data).into_iter().flatten().filter(|e| *e).count()
}
fn _gold (data: &I) -> () {
let data = _solve(data).into_iter().map(|line| line.into_iter().map(|f| if f { 'X' } else { '.' }).collect::<String>()).collect_vec();
println!("{:#?}", data);
}
#[cfg(test)]
mod test {
use super::*;
fn read () -> I {
let data = inc!(8);
_parse(data)
}
#[test]
fn silver () {
let data = read();
let ans = _silver(&data);
assert_eq!(ans, 115)
}
#[test]
fn gold () {
let data = read();
let ans = _gold(&data);
// EFEYKFRFIJ
assert_eq!(ans, ())
}
}
+108
View File
@@ -0,0 +1,108 @@
use crate::prelude::*;
pub type I = SS;
pub type O = usize;
fn _expand (mut data: impl Iterator<Item = char>) -> O {
let mut stack = 0;
data.by_ref().take_while(|e| *e != '(').for_each(|_| stack += 1);
let a = data.by_ref().take_while(|c| *c != ')').collect::<String>();
let Some((len, times)) = a.split_once('x') else { return stack };
let len = len.parse::<usize>().unwrap();
let taken = data.by_ref().take(len).filter(|e| !e.is_control() && !e.is_whitespace()).collect::<String>();
let t = if let Some(p) = taken.find("(") {
p + _expand(taken[p..].chars())
} else {
taken.len()
};
let times = times.parse::<usize>().unwrap();
stack += t * times;
stack += _expand(data);
stack
}
fn _silver (data: I) -> O {
let mut stack = String::new();
let mut cs = data.trim().chars();
loop {
let Some(cur) = cs.next() else { break; };
match cur {
'(' => {
let a = cs.by_ref().take_while(|c| *c != ')').collect::<String>();
let (len, times) = a.split_once('x').unwrap();
let len = len.parse::<usize>().unwrap();
let times = times.parse::<usize>().unwrap();
let taken = cs.by_ref().take(len).filter(|e| !e.is_control() && !e.is_whitespace()).collect::<String>();
stack.push_str(&taken.repeat(times));
},
e => stack.push(e),
}
}
stack.len()
}
fn _gold (data: I) -> O {
let stack = _expand(data.trim().chars());
stack
}
#[cfg(test)]
mod test {
use super::*;
fn read () -> I {
inc!(9)
}
#[test]
fn sample () {
let cases = [
("ADVENT", 6),
("X(8x2)(3x3)ABCY", 18),
("A(1x5)BC", 7),
("(3x3)XYZ", 9),
("A(2x2)BCD(2x2)EFG", 11),
("(6x1)(1x3)A", 6),
];
for (i, o) in cases {
assert_eq!(_silver(i), o)
}
let cases_gold = [
("X(8x2)(3x3)ABCY", 20),
("(25x3)(3x3)ABC(2x3)XY(5x2)PQRSTX(18x9)(3x2)TWO(5x7)SEVEN", 445),
];
for (i, o) in cases_gold {
assert_eq!(_gold(i), o)
}
}
#[test]
fn silver () {
let data = read();
let ans = _silver(&data);
assert_eq!(ans, 74532)
}
#[test]
fn gold () {
let data = read();
let ans = _gold(&data);
assert_eq!(ans, 11558231665)
}
}
+12 -4
View File
@@ -1,21 +1,21 @@
use crate::prelude::*;
pub type I = String;
pub type I = Vec<String>;
pub type O = i64;
fn _parse (data: &str) -> I {
Default::default()
}
fn _solve (data: I) -> O {
fn _solve (data: &I) -> O {
Default::default()
}
fn _silver (data: I) -> O {
fn _silver (data: &I) -> O {
Default::default()
}
fn _gold (data: I) -> O {
fn _gold (data: &I) -> O {
Default::default()
}
@@ -28,6 +28,14 @@ mod test {
_parse(data)
}
#[test]
fn sample () {
let data = "";
let data = _parse(data);
assert_eq!(1, 1)
}
#[test]
fn silver () {
let data = read();
+1
View File
@@ -1,5 +1,6 @@
#![feature(let_chains)]
#![feature(decl_macro)]
#![feature(iter_array_chunks)]
pub mod days;
pub mod utils;
+6 -1
View File
@@ -1,4 +1,9 @@
pub use crate::utils::*;
pub use anyhow::Error;
pub use itertools::Itertools;
pub use std::collections::{ HashMap as M, HashSet as S };
pub use std::{
collections::{ HashMap as M, HashSet as S, VecDeque as D },
cmp::Ordering as Or,
};
pub type SS = &'static str;
+13
View File
@@ -9,3 +9,16 @@ pub macro s {
format!("{}", $s)
}
}
pub macro p {
($s: expr) => {
$s.parse().unwrap()
}
}
pub macro n {
($s: expr) => {
$s.next().unwrap()
}
}