Compare commits
	
		
			2 Commits
		
	
	
		
			4ce7184b1e
			...
			0ecea97752
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|   | 0ecea97752 | ||
|   | dc5f488212 | 
							
								
								
									
										287
									
								
								aoc_2024/src/day16.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										287
									
								
								aoc_2024/src/day16.rs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,287 @@ | |||||||
|  | use std::{ | ||||||
|  |     cmp::{Ordering, Reverse}, | ||||||
|  |     collections::BinaryHeap, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | use aoc_runner_derive::{aoc, aoc_generator}; | ||||||
|  | use hashbrown::HashSet; | ||||||
|  | 
 | ||||||
|  | #[derive(Debug, Clone, Copy, PartialEq, Eq)] | ||||||
|  | enum Cell { | ||||||
|  |     Empty, | ||||||
|  |     Wall, | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | type Input = ((usize, usize), (usize, usize), Vec<Vec<Cell>>); | ||||||
|  | 
 | ||||||
|  | #[aoc_generator(day16)] | ||||||
|  | fn parse(input: &str) -> Input { | ||||||
|  |     let mut pos = (0, 0); | ||||||
|  |     let mut end = (0, 0); | ||||||
|  |     let map = input | ||||||
|  |         .lines() | ||||||
|  |         .enumerate() | ||||||
|  |         .map(|(y, line)| { | ||||||
|  |             line.chars() | ||||||
|  |                 .enumerate() | ||||||
|  |                 .map(|(x, c)| match c { | ||||||
|  |                     '#' => Cell::Wall, | ||||||
|  |                     '.' => Cell::Empty, | ||||||
|  |                     'E' => { | ||||||
|  |                         end = (x, y); | ||||||
|  |                         Cell::Empty | ||||||
|  |                     } | ||||||
|  |                     'S' => { | ||||||
|  |                         pos = (x, y); | ||||||
|  |                         Cell::Empty | ||||||
|  |                     } | ||||||
|  |                     _ => unreachable!(), | ||||||
|  |                 }) | ||||||
|  |                 .collect() | ||||||
|  |         }) | ||||||
|  |         .collect(); | ||||||
|  |     (pos, end, map) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #[aoc(day16, part1)] | ||||||
|  | fn part1(input: &Input) -> i64 { | ||||||
|  |     let start = (input.0 .0 as i64, input.0 .1 as i64); | ||||||
|  |     let end = (input.1 .0 as i64, input.1 .1 as i64); | ||||||
|  |     let map = input.2.clone(); | ||||||
|  |     let w = map[0].len(); | ||||||
|  |     let h = map.len(); | ||||||
|  |     let mut d = vec![vec![[i64::MAX; 4]; w]; h]; | ||||||
|  | 
 | ||||||
|  |     #[allow(clippy::type_complexity)] | ||||||
|  |     let mut queue: BinaryHeap<Reverse<(i64, i64, i64, i64, i64)>> = BinaryHeap::new(); | ||||||
|  |     d[start.1 as usize][start.0 as usize][1] = 0; | ||||||
|  |     queue.push(Reverse((0, start.0, start.1, 1, 0))); | ||||||
|  | 
 | ||||||
|  |     while let Some(Reverse((dist, pos_x, pos_y, dir_x, dir_y))) = queue.pop() { | ||||||
|  |         let dir_num = match (dir_x, dir_y) { | ||||||
|  |             (0, -1) => 0, | ||||||
|  |             (1, 0) => 1, | ||||||
|  |             (0, 1) => 2, | ||||||
|  |             (-1, 0) => 3, | ||||||
|  |             _ => unreachable!(), | ||||||
|  |         }; | ||||||
|  | 
 | ||||||
|  |         if d[pos_y as usize][pos_x as usize][dir_num] < dist { | ||||||
|  |             continue; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         if (pos_x, pos_y) == end { | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         if map[(pos_y + dir_y) as usize][(pos_x + dir_x) as usize] == Cell::Empty { | ||||||
|  |             let dist_next = &mut d[(pos_y + dir_y) as usize][(pos_x + dir_x) as usize][dir_num]; | ||||||
|  |             if (dist + 1).cmp(dist_next) == Ordering::Less { | ||||||
|  |                 *dist_next = dist + 1; | ||||||
|  |                 queue.push(Reverse(( | ||||||
|  |                     *dist_next, | ||||||
|  |                     pos_x + dir_x, | ||||||
|  |                     pos_y + dir_y, | ||||||
|  |                     dir_x, | ||||||
|  |                     dir_y, | ||||||
|  |                 ))); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         { | ||||||
|  |             let dist_next = &mut d[pos_y as usize][pos_x as usize][(dir_num + 3) % 4]; | ||||||
|  |             if (dist + 1000).cmp(dist_next) == Ordering::Less { | ||||||
|  |                 *dist_next = dist + 1000; | ||||||
|  |                 queue.push(Reverse((*dist_next, pos_x, pos_y, dir_y, -dir_x))); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         { | ||||||
|  |             let dist_next = &mut d[pos_y as usize][pos_x as usize][(dir_num + 1) % 4]; | ||||||
|  |             if (dist + 1000).cmp(dist_next) == Ordering::Less { | ||||||
|  |                 *dist_next = dist + 1000; | ||||||
|  |                 queue.push(Reverse((*dist_next, pos_x, pos_y, -dir_y, dir_x))); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     *d[end.1 as usize][end.0 as usize].iter().min().unwrap() | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #[aoc(day16, part2)] | ||||||
|  | fn part2(input: &Input) -> i64 { | ||||||
|  |     let start = (input.0 .0 as i64, input.0 .1 as i64); | ||||||
|  |     let end = (input.1 .0 as i64, input.1 .1 as i64); | ||||||
|  |     let map = input.2.clone(); | ||||||
|  |     let w = map[0].len(); | ||||||
|  |     let h = map.len(); | ||||||
|  |     let mut d = vec![vec![[i64::MAX; 4]; w]; h]; | ||||||
|  |     let mut b = vec![vec![[vec![], vec![], vec![], vec![]]; w]; h]; | ||||||
|  | 
 | ||||||
|  |     #[allow(clippy::type_complexity)] | ||||||
|  |     let mut queue: BinaryHeap<Reverse<(i64, i64, i64, i64, i64)>> = BinaryHeap::new(); | ||||||
|  |     d[start.1 as usize][start.0 as usize][1] = 0; | ||||||
|  |     queue.push(Reverse((0, start.0, start.1, 1, 0))); | ||||||
|  | 
 | ||||||
|  |     while let Some(Reverse((dist, pos_x, pos_y, dir_x, dir_y))) = queue.pop() { | ||||||
|  |         let dir_num = match (dir_x, dir_y) { | ||||||
|  |             (0, -1) => 0, | ||||||
|  |             (1, 0) => 1, | ||||||
|  |             (0, 1) => 2, | ||||||
|  |             (-1, 0) => 3, | ||||||
|  |             _ => unreachable!(), | ||||||
|  |         }; | ||||||
|  | 
 | ||||||
|  |         if d[pos_y as usize][pos_x as usize][dir_num] < dist { | ||||||
|  |             continue; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         if (pos_x, pos_y) == end { | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         if map[(pos_y + dir_y) as usize][(pos_x + dir_x) as usize] == Cell::Empty { | ||||||
|  |             let dist_next = &mut d[(pos_y + dir_y) as usize][(pos_x + dir_x) as usize][dir_num]; | ||||||
|  |             let back_next = &mut b[(pos_y + dir_y) as usize][(pos_x + dir_x) as usize][dir_num]; | ||||||
|  |             if (dist + 1).cmp(dist_next) == Ordering::Less { | ||||||
|  |                 *dist_next = dist + 1; | ||||||
|  |                 back_next.clear(); | ||||||
|  |                 back_next.push((pos_x, pos_y, dir_num)); | ||||||
|  |                 queue.push(Reverse(( | ||||||
|  |                     *dist_next, | ||||||
|  |                     pos_x + dir_x, | ||||||
|  |                     pos_y + dir_y, | ||||||
|  |                     dir_x, | ||||||
|  |                     dir_y, | ||||||
|  |                 ))); | ||||||
|  |             } else if (dist + 1).cmp(dist_next) == Ordering::Equal { | ||||||
|  |                 back_next.push((pos_x, pos_y, dir_num)); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         { | ||||||
|  |             let dist_next = &mut d[pos_y as usize][pos_x as usize][(dir_num + 3) % 4]; | ||||||
|  |             let back_next = &mut b[pos_y as usize][pos_x as usize][(dir_num + 3) % 4]; | ||||||
|  |             if (dist + 1000).cmp(dist_next) == Ordering::Less { | ||||||
|  |                 *dist_next = dist + 1000; | ||||||
|  |                 back_next.clear(); | ||||||
|  |                 back_next.push((pos_x, pos_y, dir_num)); | ||||||
|  |                 queue.push(Reverse((*dist_next, pos_x, pos_y, dir_y, -dir_x))); | ||||||
|  |             } else if (dist + 1000).cmp(dist_next) == Ordering::Equal { | ||||||
|  |                 back_next.push((pos_x, pos_y, dir_num)); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         { | ||||||
|  |             let dist_next = &mut d[pos_y as usize][pos_x as usize][(dir_num + 1) % 4]; | ||||||
|  |             let back_next = &mut b[pos_y as usize][pos_x as usize][(dir_num + 1) % 4]; | ||||||
|  |             if (dist + 1000).cmp(dist_next) == Ordering::Less { | ||||||
|  |                 *dist_next = dist + 1000; | ||||||
|  |                 back_next.clear(); | ||||||
|  |                 back_next.push((pos_x, pos_y, dir_num)); | ||||||
|  |                 queue.push(Reverse((*dist_next, pos_x, pos_y, -dir_y, dir_x))); | ||||||
|  |             } else if (dist + 1000).cmp(dist_next) == Ordering::Equal { | ||||||
|  |                 back_next.push((pos_x, pos_y, dir_num)); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     let mut visited = HashSet::new(); | ||||||
|  |     let dist_min = *d[end.1 as usize][end.0 as usize].iter().min().unwrap(); | ||||||
|  |     let mut stack = vec![]; | ||||||
|  |     d[end.1 as usize][end.0 as usize] | ||||||
|  |         .iter() | ||||||
|  |         .enumerate() | ||||||
|  |         .for_each(|(i, d)| { | ||||||
|  |             if *d == dist_min { | ||||||
|  |                 stack.push((end.0, end.1, i)); | ||||||
|  |             } | ||||||
|  |         }); | ||||||
|  | 
 | ||||||
|  |     while let Some((pos_x, pos_y, dir_num)) = stack.pop() { | ||||||
|  |         visited.insert((pos_x, pos_y)); | ||||||
|  |         stack.append(&mut b[pos_y as usize][pos_x as usize][dir_num]); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     visited.len() as i64 | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #[cfg(test)] | ||||||
|  | mod tests { | ||||||
|  |     use super::*; | ||||||
|  | 
 | ||||||
|  |     #[test] | ||||||
|  |     fn part1_example() { | ||||||
|  |         assert_eq!( | ||||||
|  |             part1(&parse( | ||||||
|  |                 "###############
 | ||||||
|  | #.......#....E# | ||||||
|  | #.#.###.#.###.# | ||||||
|  | #.....#.#...#.# | ||||||
|  | #.###.#####.#.# | ||||||
|  | #.#.#.......#.# | ||||||
|  | #.#.#####.###.# | ||||||
|  | #...........#.# | ||||||
|  | ###.#.#####.#.# | ||||||
|  | #...#.....#.#.# | ||||||
|  | #.#.#.###.#.#.# | ||||||
|  | #.....#...#.#.# | ||||||
|  | #.###.#.#.#.#.# | ||||||
|  | #S..#.....#...# | ||||||
|  | ###############" | ||||||
|  |             )), | ||||||
|  |             7036 | ||||||
|  |         ); | ||||||
|  |         // assert_eq!(
 | ||||||
|  |         //     part1(&parse(include_str!("../input/2024/day16.txt"))),
 | ||||||
|  |         //     85420
 | ||||||
|  |         // );
 | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     #[test] | ||||||
|  |     fn part2_example() { | ||||||
|  |         assert_eq!( | ||||||
|  |             part2(&parse( | ||||||
|  |                 "###############
 | ||||||
|  | #.......#....E# | ||||||
|  | #.#.###.#.###.# | ||||||
|  | #.....#.#...#.# | ||||||
|  | #.###.#####.#.# | ||||||
|  | #.#.#.......#.# | ||||||
|  | #.#.#####.###.# | ||||||
|  | #...........#.# | ||||||
|  | ###.#.#####.#.# | ||||||
|  | #...#.....#.#.# | ||||||
|  | #.#.#.###.#.#.# | ||||||
|  | #.....#...#.#.# | ||||||
|  | #.###.#.#.#.#.# | ||||||
|  | #S..#.....#...# | ||||||
|  | ###############" | ||||||
|  |             )), | ||||||
|  |             45 | ||||||
|  |         ); | ||||||
|  |         assert_eq!( | ||||||
|  |             part2(&parse( | ||||||
|  |                 "#################
 | ||||||
|  | #...#...#...#..E# | ||||||
|  | #.#.#.#.#.#.#.#.# | ||||||
|  | #.#.#.#...#...#.# | ||||||
|  | #.#.#.#.###.#.#.# | ||||||
|  | #...#.#.#.....#.# | ||||||
|  | #.#.#.#.#.#####.# | ||||||
|  | #.#...#.#.#.....# | ||||||
|  | #.#.#####.#.###.# | ||||||
|  | #.#.#.......#...# | ||||||
|  | #.#.###.#####.### | ||||||
|  | #.#.#...#.....#.# | ||||||
|  | #.#.#.#####.###.# | ||||||
|  | #.#.#.........#.# | ||||||
|  | #.#.#.#########.# | ||||||
|  | #S#.............# | ||||||
|  | #################" | ||||||
|  |             )), | ||||||
|  |             64 | ||||||
|  |         ); | ||||||
|  |         // assert_eq!(part2(&parse(include_str!("../input/2024/day16.txt"))), 492);
 | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										126
									
								
								aoc_2024/src/day17.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										126
									
								
								aoc_2024/src/day17.rs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,126 @@ | |||||||
|  | use aoc_runner_derive::{aoc, aoc_generator}; | ||||||
|  | use nom::bytes::complete::tag; | ||||||
|  | use nom::character::complete::i64 as parse_i64; | ||||||
|  | use nom::error::Error; | ||||||
|  | use nom::multi::separated_list1; | ||||||
|  | use nom::sequence::{preceded, separated_pair, tuple}; | ||||||
|  | 
 | ||||||
|  | type Input = ((i64, i64, i64), Vec<i64>); | ||||||
|  | 
 | ||||||
|  | #[aoc_generator(day17)] | ||||||
|  | fn parse(input: &str) -> Input { | ||||||
|  |     separated_pair( | ||||||
|  |         tuple(( | ||||||
|  |             preceded(tag("Register A: "), parse_i64::<&str, Error<&str>>), | ||||||
|  |             preceded(tag("\nRegister B: "), parse_i64), | ||||||
|  |             preceded(tag("\nRegister C: "), parse_i64), | ||||||
|  |         )), | ||||||
|  |         tag("\n\n"), | ||||||
|  |         preceded(tag("Program: "), separated_list1(tag(","), parse_i64)), | ||||||
|  |     )(input) | ||||||
|  |     .unwrap() | ||||||
|  |     .1 | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #[aoc(day17, part1)] | ||||||
|  | fn part1(input: &Input) -> String { | ||||||
|  |     let mut registers = input.0; | ||||||
|  |     let program = input.1.clone(); | ||||||
|  | 
 | ||||||
|  |     let mut pc = 0; | ||||||
|  | 
 | ||||||
|  |     let mut output = Vec::new(); | ||||||
|  | 
 | ||||||
|  |     loop { | ||||||
|  |         if pc >= program.len() { | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         let instruction = program[pc]; | ||||||
|  |         let literal_value = program[pc + 1]; | ||||||
|  |         let combo_value = match program[pc + 1] { | ||||||
|  |             0 => 0, | ||||||
|  |             1 => 1, | ||||||
|  |             2 => 2, | ||||||
|  |             3 => 3, | ||||||
|  |             4 => registers.0, | ||||||
|  |             5 => registers.1, | ||||||
|  |             6 => registers.2, | ||||||
|  |             _ => unreachable!(), | ||||||
|  |         }; | ||||||
|  | 
 | ||||||
|  |         match instruction { | ||||||
|  |             0 => registers.0 >>= combo_value, | ||||||
|  |             1 => registers.1 ^= literal_value, | ||||||
|  |             2 => registers.1 = combo_value % 8, | ||||||
|  |             3 => { | ||||||
|  |                 if registers.0 != 0 { | ||||||
|  |                     pc = literal_value as usize; | ||||||
|  |                     continue; | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |             4 => registers.1 ^= registers.2, | ||||||
|  |             5 => output.push(combo_value % 8), | ||||||
|  |             6 => registers.1 = registers.0 >> combo_value, | ||||||
|  |             7 => registers.2 = registers.0 >> combo_value, | ||||||
|  |             _ => unreachable!(), | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         pc += 2; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     output | ||||||
|  |         .iter() | ||||||
|  |         .map(|x| x.to_string()) | ||||||
|  |         .collect::<Vec<String>>() | ||||||
|  |         .join(",") | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #[aoc(day17, part2)] | ||||||
|  | fn part2(input: &Input) -> i64 { | ||||||
|  |     let program = input.1.clone(); | ||||||
|  |     let mut candidates = vec![0]; | ||||||
|  | 
 | ||||||
|  |     for target in program.iter().rev() { | ||||||
|  |         let mut new_candidates = vec![]; | ||||||
|  |         for candidate in candidates { | ||||||
|  |             let a = candidate; | ||||||
|  |             new_candidates.extend( | ||||||
|  |                 (0..8) | ||||||
|  |                     .filter(|b| { | ||||||
|  |                         let a = (a << 3) + b; | ||||||
|  |                         let b = a % 8; | ||||||
|  |                         let b = b ^ 5; | ||||||
|  |                         let c = a >> b; | ||||||
|  |                         let b = b ^ c; | ||||||
|  |                         let b = b ^ 6; | ||||||
|  | 
 | ||||||
|  |                         b % 8 == *target | ||||||
|  |                     }) | ||||||
|  |                     .map(|b| (a << 3) + b), | ||||||
|  |             ); | ||||||
|  |         } | ||||||
|  |         candidates = new_candidates; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     candidates[0] | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #[cfg(test)] | ||||||
|  | mod tests { | ||||||
|  |     use super::*; | ||||||
|  | 
 | ||||||
|  |     #[test] | ||||||
|  |     fn part1_example() { | ||||||
|  |         assert_eq!( | ||||||
|  |             part1(&parse( | ||||||
|  |                 "Register A: 729
 | ||||||
|  | Register B: 0 | ||||||
|  | Register C: 0 | ||||||
|  | 
 | ||||||
|  | Program: 0,1,5,4,3,0" | ||||||
|  |             )), | ||||||
|  |             "4,6,3,5,6,3,5,2,1,0" | ||||||
|  |         ); | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										205
									
								
								aoc_2024/src/day18.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										205
									
								
								aoc_2024/src/day18.rs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,205 @@ | |||||||
|  | use std::collections::VecDeque; | ||||||
|  | 
 | ||||||
|  | use aoc_runner_derive::{aoc, aoc_generator}; | ||||||
|  | use nom::{ | ||||||
|  |     bytes::complete::tag, | ||||||
|  |     character::complete::{i64 as parse_i64, newline}, | ||||||
|  |     error::Error, | ||||||
|  |     multi::separated_list0, | ||||||
|  |     sequence::separated_pair, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | #[derive(Debug, Clone, Copy, PartialEq, Eq)] | ||||||
|  | enum Cell { | ||||||
|  |     Empty, | ||||||
|  |     Byte, | ||||||
|  |     Visited, | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | type Input = Vec<(i64, i64)>; | ||||||
|  | 
 | ||||||
|  | #[aoc_generator(day18)] | ||||||
|  | fn parse(input: &str) -> Input { | ||||||
|  |     separated_list0( | ||||||
|  |         newline::<&str, Error<&str>>, | ||||||
|  |         separated_pair(parse_i64, tag(","), parse_i64), | ||||||
|  |     )(input) | ||||||
|  |     .unwrap() | ||||||
|  |     .1 | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #[aoc(day18, part1)] | ||||||
|  | fn part1(input: &Input) -> usize { | ||||||
|  |     #[cfg(not(test))] | ||||||
|  |     let (w, h, bytes) = (71, 71, &input[..1024]); | ||||||
|  |     #[cfg(test)] | ||||||
|  |     let (w, h, bytes) = (7, 7, &input[..12]); | ||||||
|  | 
 | ||||||
|  |     let mut map = vec![vec![Cell::Empty; w]; h]; | ||||||
|  | 
 | ||||||
|  |     bytes | ||||||
|  |         .iter() | ||||||
|  |         .for_each(|(x, y)| map[*y as usize][*x as usize] = Cell::Byte); | ||||||
|  | 
 | ||||||
|  |     let mut deque = VecDeque::new(); | ||||||
|  |     deque.push_back((0, 0, 0)); | ||||||
|  | 
 | ||||||
|  |     while let Some((x, y, depth)) = deque.pop_front() { | ||||||
|  |         if x == w - 1 && y == h - 1 { | ||||||
|  |             return depth; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         if map[y][x] == Cell::Visited { | ||||||
|  |             continue; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         map[y][x] = Cell::Visited; | ||||||
|  | 
 | ||||||
|  |         if x >= 1 && map[y][x - 1] == Cell::Empty { | ||||||
|  |             deque.push_back((x - 1, y, depth + 1)); | ||||||
|  |         } | ||||||
|  |         if x < w - 1 && map[y][x + 1] == Cell::Empty { | ||||||
|  |             deque.push_back((x + 1, y, depth + 1)); | ||||||
|  |         } | ||||||
|  |         if y >= 1 && map[y - 1][x] == Cell::Empty { | ||||||
|  |             deque.push_back((x, y - 1, depth + 1)); | ||||||
|  |         } | ||||||
|  |         if y < h - 1 && map[y + 1][x] == Cell::Empty { | ||||||
|  |             deque.push_back((x, y + 1, depth + 1)); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     unreachable!() | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #[aoc(day18, part2)] | ||||||
|  | fn part2(input: &Input) -> String { | ||||||
|  |     #[cfg(not(test))] | ||||||
|  |     let (w, h, min_idx) = (71, 71, 1024); | ||||||
|  |     #[cfg(test)] | ||||||
|  |     let (w, h, min_idx) = (7, 7, 12); | ||||||
|  | 
 | ||||||
|  |     let (mut l, mut r) = (min_idx - 1, input.len() - 1); | ||||||
|  | 
 | ||||||
|  |     while r - l > 1 { | ||||||
|  |         let mut possible = false; | ||||||
|  | 
 | ||||||
|  |         let mut map = vec![vec![Cell::Empty; w]; h]; | ||||||
|  | 
 | ||||||
|  |         let bytes = (l + r) >> 1; | ||||||
|  | 
 | ||||||
|  |         input[0..bytes] | ||||||
|  |             .iter() | ||||||
|  |             .for_each(|(x, y)| map[*y as usize][*x as usize] = Cell::Byte); | ||||||
|  | 
 | ||||||
|  |         let mut deque = VecDeque::new(); | ||||||
|  |         deque.push_back((0, 0, 0)); | ||||||
|  | 
 | ||||||
|  |         while let Some((x, y, depth)) = deque.pop_front() { | ||||||
|  |             if x == w - 1 && y == h - 1 { | ||||||
|  |                 possible = true; | ||||||
|  |                 break; | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             if map[y][x] == Cell::Visited { | ||||||
|  |                 continue; | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             map[y][x] = Cell::Visited; | ||||||
|  | 
 | ||||||
|  |             if x >= 1 && map[y][x - 1] == Cell::Empty { | ||||||
|  |                 deque.push_back((x - 1, y, depth + 1)); | ||||||
|  |             } | ||||||
|  |             if x < w - 1 && map[y][x + 1] == Cell::Empty { | ||||||
|  |                 deque.push_back((x + 1, y, depth + 1)); | ||||||
|  |             } | ||||||
|  |             if y >= 1 && map[y - 1][x] == Cell::Empty { | ||||||
|  |                 deque.push_back((x, y - 1, depth + 1)); | ||||||
|  |             } | ||||||
|  |             if y < h - 1 && map[y + 1][x] == Cell::Empty { | ||||||
|  |                 deque.push_back((x, y + 1, depth + 1)); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         if possible { | ||||||
|  |             l = bytes; | ||||||
|  |         } else { | ||||||
|  |             r = bytes; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     format!("{},{}", input[l].0, input[l].1).to_string() | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #[cfg(test)] | ||||||
|  | mod tests { | ||||||
|  |     use super::*; | ||||||
|  | 
 | ||||||
|  |     #[test] | ||||||
|  |     fn part1_example() { | ||||||
|  |         assert_eq!( | ||||||
|  |             part1(&parse( | ||||||
|  |                 "5,4
 | ||||||
|  | 4,2 | ||||||
|  | 4,5 | ||||||
|  | 3,0 | ||||||
|  | 2,1 | ||||||
|  | 6,3 | ||||||
|  | 2,4 | ||||||
|  | 1,5 | ||||||
|  | 0,6 | ||||||
|  | 3,3 | ||||||
|  | 2,6 | ||||||
|  | 5,1 | ||||||
|  | 1,2 | ||||||
|  | 5,5 | ||||||
|  | 2,5 | ||||||
|  | 6,5 | ||||||
|  | 1,4 | ||||||
|  | 0,4 | ||||||
|  | 6,4 | ||||||
|  | 1,1 | ||||||
|  | 6,1 | ||||||
|  | 1,0 | ||||||
|  | 0,5 | ||||||
|  | 1,6 | ||||||
|  | 2,0" | ||||||
|  |             )), | ||||||
|  |             22 | ||||||
|  |         ); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     #[test] | ||||||
|  |     fn part2_example() { | ||||||
|  |         assert_eq!( | ||||||
|  |             part2(&parse( | ||||||
|  |                 "5,4
 | ||||||
|  | 4,2 | ||||||
|  | 4,5 | ||||||
|  | 3,0 | ||||||
|  | 2,1 | ||||||
|  | 6,3 | ||||||
|  | 2,4 | ||||||
|  | 1,5 | ||||||
|  | 0,6 | ||||||
|  | 3,3 | ||||||
|  | 2,6 | ||||||
|  | 5,1 | ||||||
|  | 1,2 | ||||||
|  | 5,5 | ||||||
|  | 2,5 | ||||||
|  | 6,5 | ||||||
|  | 1,4 | ||||||
|  | 0,4 | ||||||
|  | 6,4 | ||||||
|  | 1,1 | ||||||
|  | 6,1 | ||||||
|  | 1,0 | ||||||
|  | 0,5 | ||||||
|  | 1,6 | ||||||
|  | 2,0" | ||||||
|  |             )), | ||||||
|  |             "6,1" | ||||||
|  |         ); | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										120
									
								
								aoc_2024/src/day19.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										120
									
								
								aoc_2024/src/day19.rs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,120 @@ | |||||||
|  | use aoc_runner_derive::{aoc, aoc_generator}; | ||||||
|  | use nom::{ | ||||||
|  |     bytes::complete::tag, | ||||||
|  |     character::complete::{newline, one_of}, | ||||||
|  |     combinator::map, | ||||||
|  |     multi::{many1, separated_list1}, | ||||||
|  |     sequence::separated_pair, | ||||||
|  |     IResult, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | #[derive(Debug, Clone, Copy, PartialEq, Eq)] | ||||||
|  | enum Color { | ||||||
|  |     White, | ||||||
|  |     Blue, | ||||||
|  |     Black, | ||||||
|  |     Red, | ||||||
|  |     Green, | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | type Input = (Vec<Vec<Color>>, Vec<Vec<Color>>); | ||||||
|  | 
 | ||||||
|  | #[aoc_generator(day19)] | ||||||
|  | fn parse(input: &str) -> Input { | ||||||
|  |     separated_pair( | ||||||
|  |         separated_list1(tag(", "), many1(parse_color)), | ||||||
|  |         tag("\n\n"), | ||||||
|  |         separated_list1(newline, many1(parse_color)), | ||||||
|  |     )(input) | ||||||
|  |     .unwrap() | ||||||
|  |     .1 | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | fn parse_color(input: &str) -> IResult<&str, Color> { | ||||||
|  |     map(one_of("wubrg"), |c| match c { | ||||||
|  |         'w' => Color::White, | ||||||
|  |         'u' => Color::Blue, | ||||||
|  |         'b' => Color::Black, | ||||||
|  |         'r' => Color::Red, | ||||||
|  |         'g' => Color::Green, | ||||||
|  |         _ => unreachable!(), | ||||||
|  |     })(input) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #[aoc(day19, part1)] | ||||||
|  | fn part1(input: &Input) -> usize { | ||||||
|  |     let towels = &input.0; | ||||||
|  |     let patterns = &input.1; | ||||||
|  | 
 | ||||||
|  |     patterns | ||||||
|  |         .iter() | ||||||
|  |         .map(|pattern| { | ||||||
|  |             let mut m = vec![0u64; pattern.len() + 1]; | ||||||
|  |             m[0] = 1; | ||||||
|  | 
 | ||||||
|  |             for i in 1..=pattern.len() { | ||||||
|  |                 for towel in towels { | ||||||
|  |                     if towel.len() <= i && &pattern[i - towel.len()..i] == towel { | ||||||
|  |                         m[i] += m[i - towel.len()]; | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             m[pattern.len()] | ||||||
|  |         }) | ||||||
|  |         .filter(|n| *n > 0) | ||||||
|  |         .count() | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #[aoc(day19, part2)] | ||||||
|  | fn part2(input: &Input) -> u64 { | ||||||
|  |     let towels = &input.0; | ||||||
|  |     let patterns = &input.1; | ||||||
|  | 
 | ||||||
|  |     patterns | ||||||
|  |         .iter() | ||||||
|  |         .map(|pattern| { | ||||||
|  |             let mut m = vec![0u64; pattern.len() + 1]; | ||||||
|  |             m[0] = 1; | ||||||
|  | 
 | ||||||
|  |             for i in 1..=pattern.len() { | ||||||
|  |                 for towel in towels { | ||||||
|  |                     if towel.len() <= i && &pattern[i - towel.len()..i] == towel { | ||||||
|  |                         m[i] += m[i - towel.len()]; | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             m[pattern.len()] | ||||||
|  |         }) | ||||||
|  |         .sum() | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #[cfg(test)] | ||||||
|  | mod tests { | ||||||
|  |     use super::*; | ||||||
|  | 
 | ||||||
|  |     #[test] | ||||||
|  |     fn part1_example() { | ||||||
|  |         assert_eq!( | ||||||
|  |             part1(&parse( | ||||||
|  |                 "r, wr, b, g, bwu, rb, gb, br
 | ||||||
|  | 
 | ||||||
|  | brwrr | ||||||
|  | bggr | ||||||
|  | gbbr | ||||||
|  | rrbgbr | ||||||
|  | ubwu | ||||||
|  | bwurrg | ||||||
|  | brgr | ||||||
|  | bbrgwb" | ||||||
|  |             )), | ||||||
|  |             6 | ||||||
|  |         ); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     // #[test]
 | ||||||
|  |     // fn part2_example() {
 | ||||||
|  |     //     assert_eq!(part2(&parse("<EXAMPLE>")), 0);
 | ||||||
|  |     // }
 | ||||||
|  | } | ||||||
| @ -1,3 +1,6 @@ | |||||||
|  | mod day19; | ||||||
|  | mod day18; | ||||||
|  | mod day17; | ||||||
| mod day1; | mod day1; | ||||||
| mod day10; | mod day10; | ||||||
| mod day11; | mod day11; | ||||||
| @ -5,6 +8,7 @@ mod day12; | |||||||
| mod day13; | mod day13; | ||||||
| mod day14; | mod day14; | ||||||
| mod day15; | mod day15; | ||||||
|  | mod day16; | ||||||
| mod day2; | mod day2; | ||||||
| mod day3; | mod day3; | ||||||
| mod day4; | mod day4; | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user