This commit is contained in:
Jan-Bulthuis 2024-12-06 07:46:04 +01:00
parent 7feae9f1aa
commit d3c428c84b
7 changed files with 388 additions and 34 deletions

7
Cargo.lock generated
View File

@ -48,6 +48,7 @@ dependencies = [
"aoc-runner-derive",
"nom",
"regex",
"topological-sort",
]
[[package]]
@ -185,6 +186,12 @@ dependencies = [
"unicode-ident",
]
[[package]]
name = "topological-sort"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ea68304e134ecd095ac6c3574494fc62b909f416c4fca77e440530221e549d3d"
[[package]]
name = "unicode-ident"
version = "1.0.14"

View File

@ -8,3 +8,4 @@ aoc-runner = "0.3.0"
aoc-runner-derive = "0.3.0"
nom = "7.1.3"
regex = "1.11.1"
topological-sort = "0.2.2"

View File

@ -1 +1 @@
max_width = 200
# max_width = 1000

View File

@ -1,21 +1,39 @@
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()
input
.split("\n")
.map(|line| line.chars().collect())
.collect()
}
#[aoc(day4, part1)]
fn part1(d: &[Vec<char>]) -> usize {
(0..d.len())
.flat_map(|i| (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)))
.filter(|p| d[p.1 as usize][p.0 as usize] == 'X')
.flat_map(|p| {
(-1..=1)
.flat_map(|dx| (-1..=1).filter(move |i| dx != 0 || *i != 0).map(move |i| (dx, i)))
.filter(move |(a, b)| (*a == 0 || (*a == -1 && p.2) || (*a == 1 && p.3)) && (*b == 0 || (*b == -1 && p.4) || (*b == 1 && p.5)))
.filter(move |r| (1..=3).zip("MAS".chars()).all(|v| d[(p.1 + v.0 * r.1) as usize][(p.0 + v.0 * r.0) as usize] == v.1))
(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
})
})
})
})
.count()
}
fn _part1(input: &[Vec<char>]) -> u64 {
@ -25,36 +43,72 @@ fn _part1(input: &[Vec<char>]) -> u64 {
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' {
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' {
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' {
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' {
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' {
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' {
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' {
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' {
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
}
@ -72,8 +126,10 @@ fn part2(input: &[Vec<char>]) -> u64 {
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');
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;
}
@ -89,22 +145,12 @@ mod tests {
#[test]
fn part1_example() {
assert_eq!(
part1(&parse(
"MMMSXXMASM\nMSAMXMSMSA\nAMXSXMAAMM\nMSAMASMSMX\nXMASAMXAMM\nXXAMMXXAMA\nSMSMSASXSS\nSAXAMASAAA\nMAMMMXMMMM\nMXMXAXMASX"
)),
18
);
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
);
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
src/day5.rs Normal file
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);
}
}

173
src/day6.rs Normal file
View File

@ -0,0 +1,173 @@
type Input = ((usize, usize), Vec<Vec<u8>>);
use aoc_runner_derive::{aoc, aoc_generator};
#[aoc_generator(day6)]
fn parse(input: &str) -> Input {
let lines = input.split("\n");
let mut pos = (0, 0);
let map = lines
.enumerate()
.map(|(y, line)| {
line.chars()
.enumerate()
.map(|(x, c)| match c {
'.' => 0,
'#' => 1,
'^' => {
pos = (x, y);
0
}
_ => todo!(),
})
.collect()
})
.collect();
(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;
let mut dir = (0, -1);
let mut count = 0;
loop {
if map[pos.1][pos.0] == 0 {
map[pos.1][pos.0] = 2;
count += 1;
};
if pos.0 as isize + dir.0 < 0
|| pos.0 as isize + dir.0 >= w as isize
|| pos.1 as isize + dir.1 < 0
|| pos.1 as isize + dir.1 >= h as isize
{
break;
}
while map[(pos.1 as isize + dir.1) as usize][(pos.0 as isize + dir.0) as usize] == 1 {
if dir.0 == 1 {
dir = (0, 1);
} else if dir.0 == -1 {
dir = (0, -1);
} else if dir.1 == 1 {
dir = (-1, 0);
} else if dir.1 == -1 {
dir = (1, 0);
}
}
pos = (
(pos.0 as isize + dir.0) as usize,
(pos.1 as isize + dir.1) as usize,
)
}
count
}
#[aoc(day6, part2)]
fn part2(input: &Input) -> u64 {
let initial = input.0;
let h = input.1.len();
let w = input.1[0].len();
let mut map = input.1.clone();
let mut pos = input.0;
let mut dir = (0, -1);
let mut loops = vec![];
loop {
if pos.0 as isize + dir.0 < 0
|| pos.0 as isize + dir.0 >= w as isize
|| pos.1 as isize + dir.1 < 0
|| pos.1 as isize + dir.1 >= h as isize
{
break;
}
while map[(pos.1 as isize + dir.1) as usize][(pos.0 as isize + dir.0) as usize] == 1 {
if dir.0 == 1 {
dir = (0, 1);
} else if dir.0 == -1 {
dir = (0, -1);
} else if dir.1 == 1 {
dir = (-1, 0);
} else if dir.1 == -1 {
dir = (1, 0);
}
}
let next_pos = (
(pos.0 as isize + dir.0) as usize,
(pos.1 as isize + dir.1) as usize,
);
if map[next_pos.1][next_pos.0] != 1 {
map[next_pos.1][next_pos.0] = 1;
if check_loop(initial, (0, -1), &map) {
// count += 1;
if !(loops.contains(&next_pos)) && next_pos != initial {
loops.push(next_pos);
}
// println!("{} {}", nexts_pos.0 + 1, next_pos.1 + 1);
}
map[next_pos.1][next_pos.0] = 0;
}
pos = next_pos;
}
loops.len() as u64
}
fn check_loop(pos: (usize, usize), dir: (isize, isize), map: &[Vec<u8>]) -> bool {
let mut visited = vec![];
let h = map.len();
let w = map[0].len();
let mut dir = dir;
let mut pos = pos;
loop {
if pos.0 as isize + dir.0 < 0
|| pos.0 as isize + dir.0 >= w as isize
|| pos.1 as isize + dir.1 < 0
|| pos.1 as isize + dir.1 >= h as isize
{
break;
}
let mut turns = 0;
while map[(pos.1 as isize + dir.1) as usize][(pos.0 as isize + dir.0) as usize] == 1 {
if dir.0 == 1 {
dir = (0, 1);
} else if dir.0 == -1 {
dir = (0, -1);
} else if dir.1 == 1 {
dir = (-1, 0);
} else if dir.1 == -1 {
dir = (1, 0);
}
turns += 1;
}
pos = (
(pos.0 as isize + dir.0) as usize,
(pos.1 as isize + dir.1) as usize,
);
if visited.contains(&(pos.0, pos.1, dir.0, dir.1)) {
return true;
} else if turns > 0 {
visited.push((pos.0, pos.1, dir.0, dir.1));
}
}
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);
}
#[test]
fn part2_example() {
assert_eq!(part2(&parse("....#.....\n.........#\n..........\n..#.......\n.......#..\n..........\n.#..^.....\n........#.\n#.........\n......#...")), 6);
}
}

View File

@ -1,3 +1,5 @@
mod day6;
mod day5;
mod day4;
mod day1;
mod day2;