Reorganization

This commit is contained in:
Jan-Bulthuis committed 2024-12-06 19:00:58 +01:00
1 parent 02fe9aea97
commit 85ce1c4641
13 files changed
+49 -34

No files matched your search

+54
View File
@@ -0,0 +1,54 @@
use aoc_runner_derive::{aoc, aoc_generator};
use nom::character::complete::multispace0;
use nom::multi::many1;
use nom::{character::complete::u64 as parse_u64, IResult};
#[aoc_generator(day1)]
pub fn generator(input: &str) -> Vec<(u64, u64)> {
many1(parse_line)(input).unwrap().1
}
fn parse_line(input: &str) -> IResult<&str, (u64, u64)> {
let (input, left) = parse_u64(input)?;
let (input, _) = multispace0(input)?;
let (input, right) = parse_u64(input)?;
let (input, _) = multispace0(input)?;
Ok((input, (left, right)))
}
#[aoc(day1, part1)]
pub fn part1(input: &[(u64, u64)]) -> u64 {
let (mut left, mut right): (Vec<u64>, Vec<u64>) = input.iter().copied().unzip();
left.sort();
right.sort();
left.iter()
.zip(right.iter())
.map(|(l, r)| (*l).abs_diff(*r))
.sum()
}
#[aoc(day1, part2)]
pub fn part2(input: &[(u64, u64)]) -> usize {
let (mut left, mut right): (Vec<u64>, Vec<u64>) = input.iter().copied().unzip();
left.sort();
right.sort();
let mut l;
let mut r = 0;
let mut score = 0usize;
for value in left {
l = r;
while l < right.len() && right[l] < value {
l += 1
}
r = l;
while r < right.len() && right[r] <= value {
r += 1
}
score += (value as usize) * (r - l);
if r == right.len() - 1 {
break;
};
}
score
}
+86
View File
@@ -0,0 +1,86 @@
use aoc_runner_derive::{aoc, aoc_generator};
use nom::character::complete::{newline, space0};
use nom::multi::{many0, many1};
use nom::{character::complete::i64 as parse_usize, IResult};
#[aoc_generator(day2)]
pub fn generator(input: &str) -> Vec<Vec<isize>> {
many1(parse_line)(input).unwrap().1
}
fn parse_level(input: &str) -> IResult<&str, isize> {
let (input, res) = parse_usize(input)?;
let (input, _) = space0(input)?;
Ok((input, res as isize))
}
fn parse_line(input: &str) -> IResult<&str, Vec<isize>> {
let (input, levels) = many1(parse_level)(input)?;
let (input, _) = many0(newline)(input)?;
Ok((input, levels))
}
#[aoc(day2, part1)]
pub fn part1(input: &[Vec<isize>]) -> usize {
input.iter().filter(|vec| is_valid(vec)).count()
}
fn is_valid(input: &[isize]) -> bool {
let mut direction = 0;
for i in 0..(input.len() - 1) {
let diff = input[i + 1] - input[i];
if diff == 0 {
return false;
} else if (1..=3).contains(&diff) {
if direction == -1 {
return false;
}
direction = 1;
} else if (1..=3).contains(&-diff) {
if direction == 1 {
return false;
}
direction = -1;
} else {
return false;
}
}
true
}
#[aoc(day2, part2)]
pub fn part2(input: &[Vec<isize>]) -> usize {
input
.iter()
.filter(|rec| (0..rec.len()).any(|i| is_valid2(rec, i)))
.count()
}
fn is_valid2(input: &[isize], skip: usize) -> bool {
let mut direction = 0;
for i in 0..(input.len() - 2) {
let diff = if skip <= i {
input[i + 2] - input[i + 1]
} else if skip == i + 1 {
input[i + 2] - input[i]
} else {
input[i + 1] - input[i]
};
if diff == 0 {
return false;
} else if (1..=3).contains(&diff) {
if direction == -1 {
return false;
}
direction = 1;
} else if (1..=3).contains(&-diff) {
if direction == 1 {
return false;
}
direction = -1;
} else {
return false;
}
}
true
}
+66
View File
@@ -0,0 +1,66 @@
use aoc_runner_derive::{aoc, aoc_generator};
use regex::Regex;
#[aoc_generator(day3)]
fn parse(input: &str) -> String {
input.to_string()
}
#[aoc(day3, part1)]
fn part1(input: &str) -> i64 {
Regex::new(r"mul\((?<a>[0-9]{1,3}),(?<b>[0-9]{1,3})\)")
.unwrap()
.captures_iter(input)
.fold(0, |a, c| {
a + c.get(1).unwrap().as_str().parse::<i64>().unwrap()
* c.get(2).unwrap().as_str().parse::<i64>().unwrap()
})
}
#[aoc(day3, part2)]
fn part2(input: &str) -> i64 {
Regex::new(r"mul\((?<a>[0-9]{1,3}),(?<b>[0-9]{1,3})\)|do\(\)|don't\(\)")
.unwrap()
.captures_iter(input)
.fold((true, 0i64), |a, c| match c.get(0).unwrap().as_str() {
"do()" => (true, a.1),
"don't()" => (false, a.1),
_ => {
if a.0 {
(
a.0,
a.1 + c.get(1).unwrap().as_str().parse::<i64>().unwrap()
* c.get(2).unwrap().as_str().parse::<i64>().unwrap(),
)
} else {
a
}
}
})
.1
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn part1_example() {
assert_eq!(
part1(&parse(
"xmul(2,4)%&mul[3,7]!@^do_not_mul(5,5)+mul(32,64]then(mul(11,8)mul(8,5))
"
)),
161
);
}
#[test]
fn part2_example() {
assert_eq!(
part2(&parse(
"xmul(2,4)&mul[3,7]!^don't()_mul(5,5)+mul(32,64](mul(11,8)undo()?mul(8,5))"
)),
48
);
}
}
+156
View File
@@ -0,0 +1,156 @@
use aoc_runner_derive::{aoc, aoc_generator};
#[aoc_generator(day4)]
fn parse(input: &str) -> Vec<Vec<char>> {
input
.split("\n")
.map(|line| line.chars().collect())
.collect()
}
#[aoc(day4, part1)]
fn part1(d: &[Vec<char>]) -> usize {
(0..d.len()).fold(0, |z, i| {
z + (0..d[0].len())
.map(move |j| {
(
j as i64,
i as i64,
j > 2,
j < d.len() - 3,
i > 2,
i < d[0].len() - 3,
)
})
.fold(0, |z, p| {
z + (-1..=1).fold(0, |z, f| {
z + (-1..=1).fold(0, move |z, g| {
z + ((f != 0 || g != 0)
&& (f == 0 || (f == -1 && p.2) || (f == 1 && p.3))
&& (g == 0 || (g == -1 && p.4) || (g == 1 && p.5))
&& (0..=3).zip("XMAS".chars()).all(|v| {
d[(p.1 + v.0 * g) as usize][(p.0 + v.0 * f) as usize] == v.1
})) as usize
})
})
})
})
}
fn _part1(input: &[Vec<char>]) -> u64 {
let h = input.len();
let w = input[0].len();
let mut sum = 0;
for y in 0..h {
for x in 0..w {
if input[y][x] == 'S' {
if x < w - 3
&& input[y][x + 1] == 'A'
&& input[y][x + 2] == 'M'
&& input[y][x + 3] == 'X'
{
// println!("rev X {} {}", x, y);
sum += 1
}
if y < h - 3
&& input[y + 1][x] == 'A'
&& input[y + 2][x] == 'M'
&& input[y + 3][x] == 'X'
{
// println!("rev Y {} {}", x, y);
sum += 1
}
if x < w - 3
&& y < h - 3
&& input[y + 1][x + 1] == 'A'
&& input[y + 2][x + 2] == 'M'
&& input[y + 3][x + 3] == 'X'
{
// println!("rdi Y {} {}", x, y);
sum += 1
}
if x >= 3
&& y < h - 3
&& input[y + 1][x - 1] == 'A'
&& input[y + 2][x - 2] == 'M'
&& input[y + 3][x - 3] == 'X'
{
// println!("rdi Y {} {}", x, y);
sum += 1
}
} else if input[y][x] == 'X' {
if x < w - 3
&& input[y][x + 1] == 'M'
&& input[y][x + 2] == 'A'
&& input[y][x + 3] == 'S'
{
// println!(" X {} {}", x, y);
sum += 1
}
if y < h - 3
&& input[y + 1][x] == 'M'
&& input[y + 2][x] == 'A'
&& input[y + 3][x] == 'S'
{
// println!(" Y {} {}", x, y);
sum += 1
}
if x < w - 3
&& y < h - 3
&& input[y + 1][x + 1] == 'M'
&& input[y + 2][x + 2] == 'A'
&& input[y + 3][x + 3] == 'S'
{
// println!("dia Y {} {}", x, y);
sum += 1
}
if x >= 3
&& y < h - 3
&& input[y + 1][x - 1] == 'M'
&& input[y + 2][x - 2] == 'A'
&& input[y + 3][x - 3] == 'S'
{
// println!("dia Y {} {}", x, y);
sum += 1
}
}
}
}
sum
}
#[aoc(day4, part2)]
fn part2(input: &[Vec<char>]) -> u64 {
let h = input.len();
let w = input[0].len();
let mut sum = 0;
for y in 1..h - 1 {
for x in 1..w - 1 {
if input[y][x] == 'A' {
let diag1 = (input[y - 1][x - 1] == 'M' && input[y + 1][x + 1] == 'S')
|| (input[y - 1][x - 1] == 'S' && input[y + 1][x + 1] == 'M');
let diag2 = (input[y - 1][x + 1] == 'M' && input[y + 1][x - 1] == 'S')
|| (input[y - 1][x + 1] == 'S' && input[y + 1][x - 1] == 'M');
if diag1 && diag2 {
sum += 1;
}
}
}
}
sum
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn part1_example() {
assert_eq!(part1(&parse("MMMSXXMASM\nMSAMXMSMSA\nAMXSXMAAMM\nMSAMASMSMX\nXMASAMXAMM\nXXAMMXXAMA\nSMSMSASXSS\nSAXAMASAAA\nMAMMMXMMMM\nMXMXAXMASX")), 18);
assert_eq!(part1(&parse(include_str!("../input/2024/day4.txt"))), 2406);
}
#[test]
fn part2_example() {
assert_eq!(part2(&parse(".M.S......\n..A..MSMS.\n.M.S.MAA..\n..A.ASMSM.\n.M.S.M....\n..........\nS.S.S.S.S.\n.A.A.A.A..\nM.M.M.M.M.\n..........")), 9);
}
}
+125
View File
@@ -0,0 +1,125 @@
use std::collections::HashMap;
use aoc_runner_derive::{aoc, aoc_generator};
use nom::character::complete::u64 as parse_u64;
use nom::multi::separated_list1;
use nom::IResult;
use nom::{
bytes::complete::tag, character::complete::newline, multi::separated_list0,
sequence::separated_pair,
};
use topological_sort::TopologicalSort;
type Input = (Vec<(u64, u64)>, Vec<Vec<u64>>);
#[aoc_generator(day5)]
fn parse(input: &str) -> Input {
parse_lines(input).unwrap().1
}
fn parse_lines(input: &str) -> IResult<&str, Input> {
let (input, rules) =
separated_list0(newline, separated_pair(parse_u64, tag("|"), parse_u64))(input)?;
let (input, _) = newline(input)?;
let (input, _) = newline(input)?;
let (input, records) = separated_list0(newline, separated_list1(tag(","), parse_u64))(input)?;
Ok((input, (rules, records)))
}
#[aoc(day5, part1)]
fn part1(input: &Input) -> u64 {
let rules = input.0.clone();
let records = input.1.clone();
let mut rules_map: HashMap<u64, Vec<u64>> = HashMap::new();
rules.iter().for_each(|rule| {
if let std::collections::hash_map::Entry::Vacant(e) = rules_map.entry(rule.1) {
e.insert(vec![rule.0]);
} else {
rules_map.get_mut(&rule.1).unwrap().push(rule.0);
rules_map.get_mut(&rule.1).unwrap().sort();
}
});
let mut correct = 0;
records.iter().for_each(|record| {
let mut forbidden = vec![];
let valid = record.iter().all(|page| {
let allowed = !forbidden.contains(page);
if rules_map.contains_key(page) {
let mut new_forbidden = rules_map.get(page).unwrap().clone();
forbidden.append(&mut new_forbidden);
}
allowed
});
if valid {
correct += record[record.len() >> 1]
}
});
correct
}
#[aoc(day5, part2)]
fn part2(input: &Input) -> u64 {
let rules = input.0.clone();
let records = input.1.clone();
let mut rules_map: HashMap<u64, Vec<u64>> = HashMap::new();
rules.iter().for_each(|rule| {
if let std::collections::hash_map::Entry::Vacant(e) = rules_map.entry(rule.1) {
e.insert(vec![rule.0]);
} else {
rules_map.get_mut(&rule.1).unwrap().push(rule.0);
rules_map.get_mut(&rule.1).unwrap().sort();
}
});
let mut correct = 0;
records.iter().for_each(|record| {
let mut forbidden = vec![];
let valid = record.iter().all(|page| {
let allowed = !forbidden.contains(page);
if rules_map.contains_key(page) {
let mut new_forbidden = rules_map.get(page).unwrap().clone();
forbidden.append(&mut new_forbidden);
}
allowed
});
if !valid {
// dbg!(&record);
let mut ts = TopologicalSort::<u64>::new();
for i in record {
ts.insert(*i);
if let Some(rules) = rules_map.get(i) {
for rule in rules {
ts.add_dependency(*rule, *i);
}
}
}
let sorted = ts
.clone()
.filter(|a| record.contains(a))
.collect::<Vec<u64>>();
correct += sorted[sorted.len() >> 1]
}
});
correct
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn part1_example() {
assert_eq!(part1(&parse("47|53\n97|13\n97|61\n97|47\n75|29\n61|13\n75|53\n29|13\n97|29\n53|29\n61|53\n97|53\n61|29\n47|13\n75|47\n97|75\n47|61\n75|61\n47|29\n75|13\n53|13\n\n75,47,61,53,29\n97,61,53,29,13\n75,29,13\n75,97,47,61,53\n61,13,29\n97,13,75,29,47")), 143);
}
#[test]
fn part2_example() {
assert_eq!(part2(&parse("47|53\n97|13\n97|61\n97|47\n75|29\n61|13\n75|53\n29|13\n97|29\n53|29\n61|53\n97|53\n61|29\n47|13\n75|47\n97|75\n47|61\n75|61\n47|29\n75|13\n53|13\n\n75,47,61,53,29\n97,61,53,29,13\n75,29,13\n75,97,47,61,53\n61,13,29\n97,13,75,29,47")), 123);
}
}
+231
View File
@@ -0,0 +1,231 @@
use std::collections::VecDeque;
use aoc_runner_derive::{aoc, aoc_generator};
// type Input = ((usize, usize), Vec<Vec<u8>>);
type Input = ((usize, usize), Vec<Vec<u8>>, Vec<Vec<[u8; 5]>>);
const UP: u8 = 0;
const DOWN: u8 = 1;
const LEFT: u8 = 2;
const RIGHT: u8 = 4;
const EMPTY: u8 = 8;
const WALL: u8 = 16;
const PASSED: u8 = 32;
const DIR: [(isize, isize); 5] = [(0, -1), (0, 1), (-1, 0), (0, 0), (1, 0)];
const NEXT_DIR: [usize; 5] = [RIGHT as usize, LEFT as usize, UP as usize, 0, DOWN as usize];
#[aoc_generator(day6)]
fn parse(input: &str) -> Input {
let lines = input.split("\n");
let mut pos = (0, 0);
let mut stones = VecDeque::new();
let map: Vec<Vec<u8>> = lines
.enumerate()
.map(|(y, line)| {
line.chars()
.enumerate()
.map(|(x, c)| match c {
'.' => EMPTY,
'#' => {
stones.push_back((x, y));
WALL
}
'^' => {
pos = (x, y);
EMPTY
}
_ => todo!(),
})
.collect()
})
.collect();
let mut jumps = vec![vec![[u8::MAX; 5]; map[0].len()]; map.len()];
while let Some((x, y)) = stones.pop_front() {
for dir in NEXT_DIR {
let mut d = 1;
loop {
let pos = (x as isize - d * DIR[dir].0, y as isize - d * DIR[dir].1);
if pos.0 < 0
|| pos.0 >= map[0].len() as isize
|| pos.1 < 0
|| pos.1 >= map.len() as isize
|| map[pos.1 as usize][pos.0 as usize] == WALL
{
break;
}
jumps[pos.1 as usize][pos.0 as usize][dir] = (d - 1) as u8;
d += 1;
}
}
}
(pos, map, jumps)
// (pos, map)
}
#[aoc(day6, part1)]
fn part1(input: &Input) -> u64 {
let h = input.1.len();
let w = input.1[0].len();
let mut map = input.1.clone();
let mut pos = ((input.0).0 as isize, (input.0).1 as isize);
let mut dir = UP as usize;
let mut count = 0;
loop {
if map[pos.1 as usize][pos.0 as usize] == EMPTY {
count += 1;
map[pos.1 as usize][pos.0 as usize] = PASSED;
}
if pos.0 + DIR[dir].0 < 0
|| pos.0 + DIR[dir].0 >= w as isize
|| pos.1 + DIR[dir].1 < 0
|| pos.1 + DIR[dir].1 >= h as isize
{
break;
}
while map[(pos.1 + DIR[dir].1) as usize][(pos.0 + DIR[dir].0) as usize] == WALL {
dir = NEXT_DIR[dir];
}
let next_pos = (pos.0 + DIR[dir].0, pos.1 + DIR[dir].1);
pos = next_pos;
}
count
}
#[aoc(day6, part2)]
fn part2(input: &Input) -> u64 {
let h = input.1.len();
let w = input.1[0].len();
let initial = ((input.0).0 as isize, (input.0).1 as isize);
let mut map = input.1.clone();
let jumps = &input.2;
let mut pos = initial;
let mut dir = UP as usize;
let mut count = 0;
loop {
if pos.0 + DIR[dir].0 < 0
|| pos.0 + DIR[dir].0 >= w as isize
|| pos.1 + DIR[dir].1 < 0
|| pos.1 + DIR[dir].1 >= h as isize
{
break;
}
while map[(pos.1 + DIR[dir].1) as usize][(pos.0 + DIR[dir].0) as usize] == WALL {
dir = NEXT_DIR[dir];
}
let next_pos = (pos.0 + DIR[dir].0, pos.1 + DIR[dir].1);
if next_pos != initial && map[next_pos.1 as usize][next_pos.0 as usize] == EMPTY {
map[next_pos.1 as usize][next_pos.0 as usize] = WALL;
if check_loop(pos, next_pos, dir, &mut map, jumps) {
count += 1;
}
map[next_pos.1 as usize][next_pos.0 as usize] = PASSED;
}
pos = next_pos;
}
count
}
fn check_loop(
mut pos: (isize, isize),
stone: (isize, isize),
mut dir: usize,
map: &mut [Vec<u8>],
jumps: &[Vec<[u8; 5]>],
) -> bool {
let h = map.len();
let w = map[0].len();
let mut changed = Vec::with_capacity(w * h * 4);
loop {
if pos.0 + DIR[dir].0 < 0
|| pos.0 + DIR[dir].0 >= w as isize
|| pos.1 + DIR[dir].1 < 0
|| pos.1 + DIR[dir].1 >= h as isize
{
break;
}
let mut turn = false;
while map[(pos.1 + DIR[dir].1) as usize][(pos.0 + DIR[dir].0) as usize] == WALL {
dir = NEXT_DIR[dir];
turn = true;
}
let next_pos = if (pos.0 == stone.0) || (pos.1 == stone.1) {
(pos.0 + DIR[dir].0, pos.1 + DIR[dir].1)
} else {
let d = jumps[pos.1 as usize][pos.0 as usize][dir] as isize;
if d == u8::MAX as isize {
changed.into_iter().for_each(|pos: (isize, isize)| {
map[pos.1 as usize][pos.0 as usize] &= !(UP | DOWN | LEFT | RIGHT);
});
return false;
}
(pos.0 + d * DIR[dir].0, pos.1 + d * DIR[dir].1)
};
if turn {
if map[next_pos.1 as usize][next_pos.0 as usize] & dir as u8 != 0 {
changed.into_iter().for_each(|pos: (isize, isize)| {
map[pos.1 as usize][pos.0 as usize] &= !(UP | DOWN | LEFT | RIGHT);
});
return true;
} else {
map[next_pos.1 as usize][next_pos.0 as usize] |= dir as u8;
changed.push(next_pos);
}
}
pos = next_pos;
}
changed.into_iter().for_each(|pos: (isize, isize)| {
map[pos.1 as usize][pos.0 as usize] &= !(UP | DOWN | LEFT | RIGHT);
});
false
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn part1_example() {
assert_eq!(part1(&parse("....#.....\n.........#\n..........\n..#.......\n.......#..\n..........\n.#..^.....\n........#.\n#.........\n......#...")), 41);
assert_eq!(part1(&parse(include_str!("../input/2024/day6.txt"))), 4977);
}
#[test]
fn part2_example() {
assert_eq!(part2(&parse("....#.....\n.........#\n..........\n..#.......\n.......#..\n..........\n.#..^.....\n........#.\n#.........\n......#...")), 6);
assert_eq!(part2(&parse(include_str!("../input/2024/day6.txt"))), 1729);
}
}
+13
View File
@@ -0,0 +1,13 @@
mod day6;
mod day5;
mod day4;
mod day1;
mod day2;
mod day3;
extern crate aoc_runner;
#[macro_use]
extern crate aoc_runner_derive;
aoc_lib! {year = 2024}