generated from janezicmatej/aoc-template
	Compare commits
	
		
			1 Commits
		
	
	
		
			day20
			...
			0000015049
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 0000015049 | 
| @@ -1,5 +0,0 @@ | |||||||
| 32T3K 765 |  | ||||||
| T55J5 684 |  | ||||||
| KK677 28 |  | ||||||
| KTJJT 220 |  | ||||||
| QQQJA 483 |  | ||||||
| @@ -1,5 +0,0 @@ | |||||||
| LLR |  | ||||||
|  |  | ||||||
| AAA = (BBB, BBB) |  | ||||||
| BBB = (AAA, ZZZ) |  | ||||||
| ZZZ = (ZZZ, ZZZ) |  | ||||||
| @@ -1,10 +0,0 @@ | |||||||
| LR |  | ||||||
|  |  | ||||||
| 11A = (11B, XXX) |  | ||||||
| 11B = (XXX, 11Z) |  | ||||||
| 11Z = (11B, XXX) |  | ||||||
| 22A = (22B, XXX) |  | ||||||
| 22B = (22C, 22C) |  | ||||||
| 22C = (22Z, 22Z) |  | ||||||
| 22Z = (22B, 22B) |  | ||||||
| XXX = (XXX, XXX) |  | ||||||
| @@ -1,3 +0,0 @@ | |||||||
| 0 3 6 9 12 15 |  | ||||||
| 1 3 6 10 15 21 |  | ||||||
| 10 13 16 21 30 45 |  | ||||||
| @@ -1,5 +0,0 @@ | |||||||
| ..F7. |  | ||||||
| .FJ|. |  | ||||||
| SJ.L7 |  | ||||||
| |F--J |  | ||||||
| LJ... |  | ||||||
| @@ -1,10 +0,0 @@ | |||||||
| FF7FSF7F7F7F7F7F---7 |  | ||||||
| L|LJ||||||||||||F--J |  | ||||||
| FL-7LJLJ||||||LJL-77 |  | ||||||
| F--JF--7||LJLJ7F7FJ- |  | ||||||
| L---JF-JLJ.||-FJLJJ7 |  | ||||||
| |F|F-JF---7F7-L7L|7| |  | ||||||
| |FFJF7L7F-JF7|JL---7 |  | ||||||
| 7-L-JL7||F7|L7F-7F7| |  | ||||||
| L.L7LFJ|||||FJL7||LJ |  | ||||||
| L7JLJL-JLJLJL--JLJ.L |  | ||||||
| @@ -1,10 +0,0 @@ | |||||||
| ...#...... |  | ||||||
| .......#.. |  | ||||||
| #......... |  | ||||||
| .......... |  | ||||||
| ......#... |  | ||||||
| .#........ |  | ||||||
| .........# |  | ||||||
| .......... |  | ||||||
| .......#.. |  | ||||||
| #...#..... |  | ||||||
| @@ -1,6 +0,0 @@ | |||||||
| ???.### 1,1,3 |  | ||||||
| .??..??...?##. 1,1,3 |  | ||||||
| ?#?#?#?#?#?#?#? 1,3,1,6 |  | ||||||
| ????.#...#... 4,1,1 |  | ||||||
| ????.######..#####. 1,6,5 |  | ||||||
| ?###???????? 3,2,1 |  | ||||||
| @@ -1,15 +0,0 @@ | |||||||
| #.##..##. |  | ||||||
| ..#.##.#. |  | ||||||
| ##......# |  | ||||||
| ##......# |  | ||||||
| ..#.##.#. |  | ||||||
| ..##..##. |  | ||||||
| #.#.##.#. |  | ||||||
|  |  | ||||||
| #...##..# |  | ||||||
| #....#..# |  | ||||||
| ..##..### |  | ||||||
| #####.##. |  | ||||||
| #####.##. |  | ||||||
| ..##..### |  | ||||||
| #....#..# |  | ||||||
| @@ -1,10 +0,0 @@ | |||||||
| O....#.... |  | ||||||
| O.OO#....# |  | ||||||
| .....##... |  | ||||||
| OO.#O....O |  | ||||||
| .O.....O#. |  | ||||||
| O.#..O.#.# |  | ||||||
| ..O..#O..O |  | ||||||
| .......O.. |  | ||||||
| #....###.. |  | ||||||
| #OO..#.... |  | ||||||
| @@ -1 +0,0 @@ | |||||||
| rn=1,cm-,qp=3,cm=2,qp-,pc=4,ot=9,ab=5,pc-,pc=6,ot=7 |  | ||||||
| @@ -1,10 +0,0 @@ | |||||||
| .|...\.... |  | ||||||
| |.-.\..... |  | ||||||
| .....|-... |  | ||||||
| ........|. |  | ||||||
| .......... |  | ||||||
| .........\ |  | ||||||
| ..../.\\.. |  | ||||||
| .-.-/..|.. |  | ||||||
| .|....-|.\ |  | ||||||
| ..//.|.... |  | ||||||
| @@ -1,13 +0,0 @@ | |||||||
| 2413432311323 |  | ||||||
| 3215453535623 |  | ||||||
| 3255245654254 |  | ||||||
| 3446585845452 |  | ||||||
| 4546657867536 |  | ||||||
| 1438598798454 |  | ||||||
| 4457876987766 |  | ||||||
| 3637877979653 |  | ||||||
| 4654967986887 |  | ||||||
| 4564679986453 |  | ||||||
| 1224686865563 |  | ||||||
| 2546548887735 |  | ||||||
| 4322674655533 |  | ||||||
| @@ -1,14 +0,0 @@ | |||||||
| R 6 (#70c710) |  | ||||||
| D 5 (#0dc571) |  | ||||||
| L 2 (#5713f0) |  | ||||||
| D 2 (#d2c081) |  | ||||||
| R 2 (#59c680) |  | ||||||
| D 2 (#411b91) |  | ||||||
| L 5 (#8ceee2) |  | ||||||
| U 2 (#caa173) |  | ||||||
| L 1 (#1b58a2) |  | ||||||
| U 2 (#caa171) |  | ||||||
| R 2 (#7807d2) |  | ||||||
| U 3 (#a77fa3) |  | ||||||
| L 2 (#015232) |  | ||||||
| U 2 (#7a21e3) |  | ||||||
| @@ -1,17 +0,0 @@ | |||||||
| px{a<2006:qkq,m>2090:A,rfg} |  | ||||||
| pv{a>1716:R,A} |  | ||||||
| lnx{m>1548:A,A} |  | ||||||
| rfg{s<537:gd,x>2440:R,A} |  | ||||||
| qs{s>3448:A,lnx} |  | ||||||
| qkq{x<1416:A,crn} |  | ||||||
| crn{x>2662:A,R} |  | ||||||
| in{s<1351:px,qqz} |  | ||||||
| qqz{s>2770:qs,m<1801:hdj,R} |  | ||||||
| gd{a>3333:R,R} |  | ||||||
| hdj{m>838:A,pv} |  | ||||||
|  |  | ||||||
| {x=787,m=2655,a=1222,s=2876} |  | ||||||
| {x=1679,m=44,a=2067,s=496} |  | ||||||
| {x=2036,m=264,a=79,s=2244} |  | ||||||
| {x=2461,m=1339,a=466,s=291} |  | ||||||
| {x=2127,m=1623,a=2188,s=1013} |  | ||||||
| @@ -1,5 +0,0 @@ | |||||||
| broadcaster -> a, b, c |  | ||||||
| %a -> b |  | ||||||
| %b -> c |  | ||||||
| %c -> inv |  | ||||||
| &inv -> a |  | ||||||
| @@ -1,33 +1,32 @@ | |||||||
| use std::{ | use std::{ | ||||||
|     cmp::{max, min}, |     cmp::{max, min}, | ||||||
|     fmt::Debug, |     ops::Range, | ||||||
|     ops::RangeInclusive, |  | ||||||
|     str::FromStr, |     str::FromStr, | ||||||
| }; | }; | ||||||
|  |  | ||||||
| use aoc::parsers::to_vec; | use aoc::parsers::to_vec; | ||||||
|  |  | ||||||
| trait RangeInclusiveExt { | trait RangeExt { | ||||||
|     fn overlaps(&self, other: &Self) -> bool; |     fn overlaps(&self, other: &Self) -> bool; | ||||||
| } | } | ||||||
|  |  | ||||||
| impl<T> RangeInclusiveExt for RangeInclusive<T> | impl<T> RangeExt for Range<T> | ||||||
| where | where | ||||||
|     T: PartialOrd, |     T: PartialOrd, | ||||||
| { | { | ||||||
|     fn overlaps(&self, other: &Self) -> bool { |     fn overlaps(&self, other: &Self) -> bool { | ||||||
|         self.contains(other.start()) || self.contains(other.end()) |         self.contains(&other.start) || self.contains(&other.end) | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| fn build_range(start: u64, range: u64) -> RangeInclusive<u64> { | fn build_range(start: u64, range: u64) -> Range<u64> { | ||||||
|     start..=(start + range - 1) |     start..(start + range) | ||||||
| } | } | ||||||
|  |  | ||||||
| #[derive(Debug)] | #[derive(Debug)] | ||||||
| struct Mapping { | struct Mapping { | ||||||
|     pub source: RangeInclusive<u64>, |     destination: Range<u64>, | ||||||
|     pub destination: RangeInclusive<u64>, |     source: Range<u64>, | ||||||
| } | } | ||||||
|  |  | ||||||
| struct ParseMappingError; | struct ParseMappingError; | ||||||
| @@ -38,43 +37,41 @@ impl FromStr for Mapping { | |||||||
|         let nums: Vec<u64> = to_vec(s, ' '); |         let nums: Vec<u64> = to_vec(s, ' '); | ||||||
|  |  | ||||||
|         Ok(Self { |         Ok(Self { | ||||||
|             destination: build_range(nums[0], nums[2]), |             destination: build_range(nums[2], nums[0]), | ||||||
|             source: build_range(nums[1], nums[2]), |             source: build_range(nums[1], nums[0]), | ||||||
|         }) |         }) | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| impl Mapping { | impl Mapping { | ||||||
|     fn map(&self, n: u64) -> u64 { |     fn map(&self, n: u64) -> u64 { | ||||||
|         let shift = n - self.source.start(); |         let shift = n - self.source.start; | ||||||
|         self.destination.start() + shift |         self.destination.start + shift | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     fn map_range(&self, r: RangeInclusive<u64>) -> RangeInclusive<u64> { |     fn split_range(&self, r: Range<u64>) -> [Option<Range<u64>>; 3] { | ||||||
|         self.map(*r.start())..=(self.map(*r.end())) |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     fn split_range(&self, r: RangeInclusive<u64>) -> [Option<RangeInclusive<u64>>; 3] { |  | ||||||
|         let mut fences = [ |         let mut fences = [ | ||||||
|             *r.start(), |             r.start, | ||||||
|             *r.end() + 1, |             r.end, | ||||||
|             max(*self.source.start(), *r.start()), |             max(self.source.start, r.start), | ||||||
|             min(*self.source.end() + 1, *r.end() + 1), |             min(self.source.end, r.end), | ||||||
|         ]; |         ]; | ||||||
|  |  | ||||||
|         fences.sort(); |         fences.sort(); | ||||||
|  |  | ||||||
|         const ARRAY_REPEAT_VALUE: Option<RangeInclusive<u64>> = None; |         let mut v = Vec::new(); | ||||||
|         let mut v = [ARRAY_REPEAT_VALUE; 3]; |  | ||||||
|  |  | ||||||
|         for i in 0..3 { |         for i in 0..3 { | ||||||
|             let f = fences[i]; |             let f = fences[i]; | ||||||
|             let nf = fences[i + 1]; |             let nf = fences[i + 1]; | ||||||
|             if f != nf { |             if f != nf { | ||||||
|                 v[i] = Some(f..=(nf - 1)) |                 v.push(Some(f..(nf - f))) | ||||||
|  |             } else { | ||||||
|  |                 v.push(None) | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|         v |  | ||||||
|  |         [v[0], v[1], v[2]] | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -105,7 +102,7 @@ pub fn part_one(input: &str) -> Option<u64> { | |||||||
|         let mut s = *seed; |         let mut s = *seed; | ||||||
|         'maps: for map in maps.iter() { |         'maps: for map in maps.iter() { | ||||||
|             for inner in map.iter() { |             for inner in map.iter() { | ||||||
|                 if inner.source.contains(&s) { |                 if inner.contains(s) { | ||||||
|                     s = inner.map(s); |                     s = inner.map(s); | ||||||
|                     continue 'maps; |                     continue 'maps; | ||||||
|                 } |                 } | ||||||
| @@ -121,19 +118,22 @@ pub fn part_one(input: &str) -> Option<u64> { | |||||||
| pub fn part_two(input: &str) -> Option<u64> { | pub fn part_two(input: &str) -> Option<u64> { | ||||||
|     let (first, rest) = input.split_once("\n\n").unwrap(); |     let (first, rest) = input.split_once("\n\n").unwrap(); | ||||||
|     let maps = parse_map(rest); |     let maps = parse_map(rest); | ||||||
|     let mut seeds_ranges: Vec<_> = to_vec::<u64, _>(first.strip_prefix("seeds: ").unwrap(), ' ') |     let seeds = to_vec::<u64, _>(first.strip_prefix("seeds: ").unwrap(), ' '); | ||||||
|         .chunks(2) |     let mut seeds_ranges: Vec<_> = Vec::new(); | ||||||
|         .map(|x| build_range(x[0], x[1])) |     for s in seeds.chunks(2) { | ||||||
|         .collect(); |         seeds_ranges.push((s[0], s[1])) | ||||||
|  |     } | ||||||
|  |  | ||||||
|     for mapping in maps.iter() { |     for mapping in maps.iter() { | ||||||
|         let mut new_ranges = Vec::new(); |         let mut new_ranges = Vec::new(); | ||||||
|  |  | ||||||
|         'queue: while let Some(rng) = seeds_ranges.pop() { |         'queue: while let Some((s, r)) = seeds_ranges.pop() { | ||||||
|             for map in mapping { |             for map in mapping { | ||||||
|                 if map.source.overlaps(&rng) { |                 if map.contains_any((s, r)) { | ||||||
|                     let [pre, to_map, post] = map.split_range(rng); |                     let [pre, to_map, post] = map.split_range((s, r)); | ||||||
|                     new_ranges.push(map.map_range(to_map.unwrap())); |                     let to_map = to_map.unwrap(); | ||||||
|  |                     let mapped = (map.map(to_map.0), to_map.1); | ||||||
|  |                     new_ranges.push(mapped); | ||||||
|                     for r in [pre, post].into_iter().flatten() { |                     for r in [pre, post].into_iter().flatten() { | ||||||
|                         seeds_ranges.push(r); |                         seeds_ranges.push(r); | ||||||
|                     } |                     } | ||||||
| @@ -141,17 +141,13 @@ pub fn part_two(input: &str) -> Option<u64> { | |||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
|  |  | ||||||
|             new_ranges.push(rng); |             new_ranges.push((s, r)); | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         seeds_ranges = new_ranges; |         seeds_ranges = new_ranges; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     seeds_ranges |     seeds_ranges.iter().map(|(x, _)| *x).min() | ||||||
|         .iter() |  | ||||||
|         .map(RangeInclusive::start) |  | ||||||
|         .min() |  | ||||||
|         .copied() |  | ||||||
| } | } | ||||||
|  |  | ||||||
| aoc::solution!(5); | aoc::solution!(5); | ||||||
|   | |||||||
							
								
								
									
										191
									
								
								src/bin/07.rs
									
									
									
									
									
								
							
							
						
						
									
										191
									
								
								src/bin/07.rs
									
									
									
									
									
								
							| @@ -1,191 +0,0 @@ | |||||||
| use std::{cmp::Ordering, collections::HashMap, str::FromStr}; |  | ||||||
|  |  | ||||||
| #[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Hash)] |  | ||||||
| enum Label { |  | ||||||
|     Ace = 14, |  | ||||||
|     King = 13, |  | ||||||
|     Queen = 12, |  | ||||||
|     Jack = 11, |  | ||||||
|     Ten = 10, |  | ||||||
|     Nine = 9, |  | ||||||
|     Eight = 8, |  | ||||||
|     Seven = 7, |  | ||||||
|     Six = 6, |  | ||||||
|     Five = 5, |  | ||||||
|     Four = 4, |  | ||||||
|     Three = 3, |  | ||||||
|     Two = 2, |  | ||||||
|     Joker = 1, |  | ||||||
| } |  | ||||||
|  |  | ||||||
| impl From<char> for Label { |  | ||||||
|     fn from(value: char) -> Self { |  | ||||||
|         use Label::*; |  | ||||||
|         match value { |  | ||||||
|             'A' => Ace, |  | ||||||
|             'K' => King, |  | ||||||
|             'Q' => Queen, |  | ||||||
|             'J' => Jack, |  | ||||||
|             'T' => Ten, |  | ||||||
|             '9' => Nine, |  | ||||||
|             '8' => Eight, |  | ||||||
|             '7' => Seven, |  | ||||||
|             '6' => Six, |  | ||||||
|             '5' => Five, |  | ||||||
|             '4' => Four, |  | ||||||
|             '3' => Three, |  | ||||||
|             '2' => Two, |  | ||||||
|             'X' => Joker, |  | ||||||
|             _ => unreachable!(), |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| #[derive(Debug)] |  | ||||||
| struct ParseHandError; |  | ||||||
|  |  | ||||||
| #[derive(Debug, PartialEq, Eq, Clone, Copy)] |  | ||||||
| struct Hand { |  | ||||||
|     labels: [Label; 5], |  | ||||||
| } |  | ||||||
|  |  | ||||||
| impl FromStr for Hand { |  | ||||||
|     type Err = ParseHandError; |  | ||||||
|     fn from_str(s: &str) -> Result<Self, Self::Err> { |  | ||||||
|         let labels = s |  | ||||||
|             .chars() |  | ||||||
|             .map(Label::from) |  | ||||||
|             .collect::<Vec<_>>() |  | ||||||
|             .try_into() |  | ||||||
|             .map_err(|_| ParseHandError)?; |  | ||||||
|         Ok(Hand { labels }) |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| impl PartialOrd for Hand { |  | ||||||
|     fn partial_cmp(&self, other: &Self) -> Option<Ordering> { |  | ||||||
|         Some(self.cmp(other)) |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| impl Ord for Hand { |  | ||||||
|     fn cmp(&self, other: &Self) -> Ordering { |  | ||||||
|         let sf = HandType::from(*self); |  | ||||||
|         let so = HandType::from(*other); |  | ||||||
|  |  | ||||||
|         sf.cmp(&so).then({ |  | ||||||
|             let mut c = Ordering::Equal; |  | ||||||
|             for i in 0..5 { |  | ||||||
|                 c = self.labels[i].cmp(&other.labels[i]); |  | ||||||
|                 if c != Ordering::Equal { |  | ||||||
|                     break; |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
|             c |  | ||||||
|         }) |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| #[derive(Debug, PartialEq, Eq, PartialOrd, Ord)] |  | ||||||
| enum HandType { |  | ||||||
|     Five = 6, |  | ||||||
|     Four = 5, |  | ||||||
|     FullHouse = 4, |  | ||||||
|     Three = 3, |  | ||||||
|     TwoPair = 2, |  | ||||||
|     Pair = 1, |  | ||||||
|     HighCard = 0, |  | ||||||
| } |  | ||||||
|  |  | ||||||
| impl From<Hand> for HandType { |  | ||||||
|     fn from(value: Hand) -> Self { |  | ||||||
|         let mut map = HashMap::new(); |  | ||||||
|  |  | ||||||
|         for c in value.labels { |  | ||||||
|             *map.entry(c).or_insert(0) += 1; |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         let joker = map.remove(&Label::Joker).unwrap_or(0); |  | ||||||
|  |  | ||||||
|         let is_n_k = |n| joker == n || map.values().filter(|&x| *x == n - joker).count() > 0; |  | ||||||
|  |  | ||||||
|         if is_n_k(5) { |  | ||||||
|             return Self::Five; |  | ||||||
|         } |  | ||||||
|         if is_n_k(4) { |  | ||||||
|             return Self::Four; |  | ||||||
|         } |  | ||||||
|         // full house |  | ||||||
|         if map.values().count() <= 2 { |  | ||||||
|             return Self::FullHouse; |  | ||||||
|         } |  | ||||||
|         if is_n_k(3) { |  | ||||||
|             return Self::Three; |  | ||||||
|         } |  | ||||||
|         // two pair |  | ||||||
|         if map.values().count() <= 3 { |  | ||||||
|             return Self::TwoPair; |  | ||||||
|         } |  | ||||||
|         if is_n_k(2) { |  | ||||||
|             return Self::Pair; |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         Self::HighCard |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| pub fn part_one(input: &str) -> Option<u32> { |  | ||||||
|     let mut v = input |  | ||||||
|         .lines() |  | ||||||
|         .filter_map(|l| l.split_once(' ')) |  | ||||||
|         .map(|(f, s)| (f.parse::<Hand>().unwrap(), s.parse::<u32>().unwrap())) |  | ||||||
|         .collect::<Vec<_>>(); |  | ||||||
|  |  | ||||||
|     v.sort_by(|f, o| f.0.cmp(&o.0)); |  | ||||||
|  |  | ||||||
|     Some( |  | ||||||
|         v.into_iter() |  | ||||||
|             .enumerate() |  | ||||||
|             .map(|(i, x)| (i as u32 + 1) * x.1) |  | ||||||
|             .sum::<u32>(), |  | ||||||
|     ) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| pub fn part_two(input: &str) -> Option<u32> { |  | ||||||
|     let mut v = input |  | ||||||
|         .replace('J', "X") |  | ||||||
|         .lines() |  | ||||||
|         .filter_map(|l| l.split_once(' ')) |  | ||||||
|         .map(|(f, s)| (f.parse::<Hand>().unwrap(), s.parse::<u32>().unwrap())) |  | ||||||
|         .collect::<Vec<_>>(); |  | ||||||
|  |  | ||||||
|     v.sort_by(|f, o| f.0.cmp(&o.0)); |  | ||||||
|  |  | ||||||
|     Some( |  | ||||||
|         v.into_iter() |  | ||||||
|             .enumerate() |  | ||||||
|             .map(|(i, x)| (i as u32 + 1) * x.1) |  | ||||||
|             .sum::<u32>(), |  | ||||||
|     ) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| aoc::solution!(7); |  | ||||||
|  |  | ||||||
| #[cfg(test)] |  | ||||||
| mod tests { |  | ||||||
|     use super::*; |  | ||||||
|     #[test] |  | ||||||
|     fn test_part_one() { |  | ||||||
|         assert_eq!( |  | ||||||
|             part_one(&aoc::template::read_file("examples", 7)), |  | ||||||
|             Some(6440) |  | ||||||
|         ); |  | ||||||
|     } |  | ||||||
|     #[test] |  | ||||||
|     fn test_part_two() { |  | ||||||
|         assert_eq!( |  | ||||||
|             part_two(&aoc::template::read_file("examples", 7)), |  | ||||||
|             Some(5905) |  | ||||||
|         ); |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @@ -1,95 +0,0 @@ | |||||||
| use std::collections::HashMap; |  | ||||||
|  |  | ||||||
| use aoc::lcm; |  | ||||||
|  |  | ||||||
| pub fn part_one(input: &str) -> Option<u32> { |  | ||||||
|     let mut map = HashMap::new(); |  | ||||||
|  |  | ||||||
|     let (directions, map_data) = input.split_once("\n\n")?; |  | ||||||
|     for line in map_data.lines() { |  | ||||||
|         let (k, v) = line.split_once(" = ")?; |  | ||||||
|         let v = v.strip_prefix('(')?; |  | ||||||
|         let v = v.strip_suffix(')')?; |  | ||||||
|         let (l, r) = v.split_once(", ")?; |  | ||||||
|  |  | ||||||
|         *map.entry(k).or_default() = (l, r); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     let mut counter = 0; |  | ||||||
|     let mut loc = "AAA"; |  | ||||||
|  |  | ||||||
|     for d in directions.chars().cycle() { |  | ||||||
|         if loc == "ZZZ" { |  | ||||||
|             break; |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         counter += 1; |  | ||||||
|         let (l, r) = map[loc]; |  | ||||||
|  |  | ||||||
|         match d { |  | ||||||
|             'L' => loc = l, |  | ||||||
|             'R' => loc = r, |  | ||||||
|             _ => unimplemented!(), |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     Some(counter) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| pub fn part_two(input: &str) -> Option<u64> { |  | ||||||
|     let mut map = HashMap::new(); |  | ||||||
|  |  | ||||||
|     let (directions, map_data) = input.split_once("\n\n")?; |  | ||||||
|     for line in map_data.lines() { |  | ||||||
|         let (k, v) = line.split_once(" = ")?; |  | ||||||
|         let v = v.strip_prefix('(')?; |  | ||||||
|         let v = v.strip_suffix(')')?; |  | ||||||
|         let (l, r) = v.split_once(", ")?; |  | ||||||
|  |  | ||||||
|         *map.entry(k).or_default() = (l, r); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     let mut res = 1; |  | ||||||
|  |  | ||||||
|     for k in map.keys().filter(|x| x.ends_with('A')) { |  | ||||||
|         let mut location = *k; |  | ||||||
|  |  | ||||||
|         for (i, d) in directions.chars().cycle().enumerate() { |  | ||||||
|             let (l, r) = map[location]; |  | ||||||
|  |  | ||||||
|             location = match d { |  | ||||||
|                 'L' => l, |  | ||||||
|                 'R' => r, |  | ||||||
|                 _ => unimplemented!(), |  | ||||||
|             }; |  | ||||||
|  |  | ||||||
|             if location.ends_with('Z') { |  | ||||||
|                 res = lcm(res, i + 1); |  | ||||||
|                 break; |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     Some(res as u64) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| aoc::solution!(8); |  | ||||||
|  |  | ||||||
| #[cfg(test)] |  | ||||||
| mod tests { |  | ||||||
|     use super::*; |  | ||||||
|     #[test] |  | ||||||
|     fn test_part_one() { |  | ||||||
|         assert_eq!( |  | ||||||
|             part_one(&aoc::template::read_file_part("examples", 8, 1)), |  | ||||||
|             Some(6) |  | ||||||
|         ); |  | ||||||
|     } |  | ||||||
|     #[test] |  | ||||||
|     fn test_part_two() { |  | ||||||
|         assert_eq!( |  | ||||||
|             part_two(&aoc::template::read_file_part("examples", 8, 2)), |  | ||||||
|             Some(6) |  | ||||||
|         ); |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @@ -1,90 +0,0 @@ | |||||||
| use std::collections::VecDeque; |  | ||||||
|  |  | ||||||
| fn find_history(v: VecDeque<i32>) -> VecDeque<VecDeque<i32>> { |  | ||||||
|     let mut s = VecDeque::new(); |  | ||||||
|     s.push_back(v); |  | ||||||
|  |  | ||||||
|     loop { |  | ||||||
|         let mut all_zeros = true; |  | ||||||
|  |  | ||||||
|         let last = s.back().unwrap(); |  | ||||||
|         let mut new = VecDeque::new(); |  | ||||||
|         for i in 0..(last.len() - 1) { |  | ||||||
|             let diff = last[i + 1] - last[i]; |  | ||||||
|             if diff != 0 { |  | ||||||
|                 all_zeros = false; |  | ||||||
|             } |  | ||||||
|  |  | ||||||
|             new.push_back(diff); |  | ||||||
|         } |  | ||||||
|         s.push_back(new); |  | ||||||
|  |  | ||||||
|         if all_zeros { |  | ||||||
|             break; |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     s |  | ||||||
| } |  | ||||||
|  |  | ||||||
| fn extrapolate_forward(v: VecDeque<i32>) -> i32 { |  | ||||||
|     let mut s = find_history(v); |  | ||||||
|  |  | ||||||
|     for i in (1..s.len()).rev() { |  | ||||||
|         let adder = *s[i].back().unwrap(); |  | ||||||
|         let last = *s[i - 1].back().unwrap(); |  | ||||||
|         s[i - 1].push_back(last + adder); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     *s[0].back().unwrap() |  | ||||||
| } |  | ||||||
|  |  | ||||||
| fn extrapolate_back(v: VecDeque<i32>) -> i32 { |  | ||||||
|     let mut s = find_history(v); |  | ||||||
|  |  | ||||||
|     for i in (1..s.len()).rev() { |  | ||||||
|         let adder = *s[i].front().unwrap(); |  | ||||||
|         let first = *s[i - 1].front().unwrap(); |  | ||||||
|         s[i - 1].push_front(first - adder); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     *s[0].front().unwrap() |  | ||||||
| } |  | ||||||
|  |  | ||||||
| pub fn part_one(input: &str) -> Option<i32> { |  | ||||||
|     Some( |  | ||||||
|         input |  | ||||||
|             .lines() |  | ||||||
|             .map(|x| x.split(' ').filter_map(|y| y.parse().ok()).collect()) |  | ||||||
|             .map(extrapolate_forward) |  | ||||||
|             .sum(), |  | ||||||
|     ) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| pub fn part_two(input: &str) -> Option<i32> { |  | ||||||
|     Some( |  | ||||||
|         input |  | ||||||
|             .lines() |  | ||||||
|             .map(|x| x.split(' ').filter_map(|y| y.parse().ok()).collect()) |  | ||||||
|             .map(extrapolate_back) |  | ||||||
|             .sum(), |  | ||||||
|     ) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| aoc::solution!(9); |  | ||||||
|  |  | ||||||
| #[cfg(test)] |  | ||||||
| mod tests { |  | ||||||
|     use super::*; |  | ||||||
|     #[test] |  | ||||||
|     fn test_part_one() { |  | ||||||
|         assert_eq!( |  | ||||||
|             part_one(&aoc::template::read_file("examples", 9)), |  | ||||||
|             Some(114) |  | ||||||
|         ); |  | ||||||
|     } |  | ||||||
|     #[test] |  | ||||||
|     fn test_part_two() { |  | ||||||
|         assert_eq!(part_two(&aoc::template::read_file("examples", 9)), Some(2)); |  | ||||||
|     } |  | ||||||
| } |  | ||||||
							
								
								
									
										158
									
								
								src/bin/10.rs
									
									
									
									
									
								
							
							
						
						
									
										158
									
								
								src/bin/10.rs
									
									
									
									
									
								
							| @@ -1,158 +0,0 @@ | |||||||
| use std::collections::HashSet; |  | ||||||
|  |  | ||||||
| const DIRS: [(isize, isize); 4] = [(1, 0), (0, 1), (-1, 0), (0, -1)]; |  | ||||||
|  |  | ||||||
| fn next_move(dy: isize, dx: isize, c: char) -> Option<(isize, isize)> { |  | ||||||
|     Some(match (dy, dx) { |  | ||||||
|         (1, 0) => match c { |  | ||||||
|             '|' => (1, 0), |  | ||||||
|             'J' => (0, -1), |  | ||||||
|             'L' => (0, 1), |  | ||||||
|             _ => None?, |  | ||||||
|         }, |  | ||||||
|         (0, 1) => match c { |  | ||||||
|             '-' => (0, 1), |  | ||||||
|             '7' => (1, 0), |  | ||||||
|             'J' => (-1, 0), |  | ||||||
|             _ => None?, |  | ||||||
|         }, |  | ||||||
|         (-1, 0) => match c { |  | ||||||
|             '|' => (-1, 0), |  | ||||||
|             '7' => (0, -1), |  | ||||||
|             'F' => (0, 1), |  | ||||||
|             _ => None?, |  | ||||||
|         }, |  | ||||||
|         (0, -1) => match c { |  | ||||||
|             '-' => (0, -1), |  | ||||||
|             'F' => (1, 0), |  | ||||||
|             'L' => (-1, 0), |  | ||||||
|             _ => None?, |  | ||||||
|         }, |  | ||||||
|         _ => unreachable!(), |  | ||||||
|     }) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| fn parse_input(input: &str) -> (Vec<Vec<char>>, (isize, isize)) { |  | ||||||
|     let pipes: Vec<Vec<char>> = input.lines().map(|x| x.chars().collect()).collect(); |  | ||||||
|  |  | ||||||
|     let start = pipes |  | ||||||
|         .iter() |  | ||||||
|         .enumerate() |  | ||||||
|         .find_map(|(i, x)| { |  | ||||||
|             x.iter() |  | ||||||
|                 .enumerate() |  | ||||||
|                 .map(|(j, y)| (i, j, y)) |  | ||||||
|                 .find(|(_, _, &x)| x == 'S') |  | ||||||
|         }) |  | ||||||
|         .map(|(j, i, _)| (j as isize, i as isize)) |  | ||||||
|         .unwrap(); |  | ||||||
|  |  | ||||||
|     (pipes, start) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| pub fn part_one(input: &str) -> Option<u32> { |  | ||||||
|     let (pipes, start) = parse_input(input); |  | ||||||
|  |  | ||||||
|     'start_dir: for d in DIRS { |  | ||||||
|         let (mut dy, mut dx) = d; |  | ||||||
|         let (mut ly, mut lx) = (start.0 + d.0, start.1 + d.1); |  | ||||||
|         let mut counter = 0; |  | ||||||
|  |  | ||||||
|         while start != (ly, lx) { |  | ||||||
|             let p = match pipes.get(ly as usize).and_then(|x| x.get(lx as usize)) { |  | ||||||
|                 Some(x) => *x, |  | ||||||
|                 None => continue 'start_dir, |  | ||||||
|             }; |  | ||||||
|  |  | ||||||
|             match next_move(dy, dx, p) { |  | ||||||
|                 Some((ndy, ndx)) => (dy, dx) = (ndy, ndx), |  | ||||||
|                 None => continue 'start_dir, |  | ||||||
|             } |  | ||||||
|  |  | ||||||
|             counter += 1; |  | ||||||
|             (ly, lx) = (ly + dy, lx + dx); |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         return Some((counter + 1) / 2); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     None |  | ||||||
| } |  | ||||||
|  |  | ||||||
| pub fn part_two(input: &str) -> Option<u32> { |  | ||||||
|     let (pipes, start) = parse_input(input); |  | ||||||
|  |  | ||||||
|     let mut border = HashSet::with_capacity(pipes.len() * pipes[0].len()); |  | ||||||
|  |  | ||||||
|     'start_dir: for d in DIRS { |  | ||||||
|         border.clear(); |  | ||||||
|         border.insert(start); |  | ||||||
|  |  | ||||||
|         let (mut dy, mut dx) = d; |  | ||||||
|         let (mut ly, mut lx) = (start.0 + d.0, start.1 + d.1); |  | ||||||
|  |  | ||||||
|         while start != (ly, lx) { |  | ||||||
|             let p = match pipes.get(ly as usize).and_then(|x| x.get(lx as usize)) { |  | ||||||
|                 Some(x) => *x, |  | ||||||
|                 None => continue 'start_dir, |  | ||||||
|             }; |  | ||||||
|  |  | ||||||
|             match next_move(dy, dx, p) { |  | ||||||
|                 Some((ndx, ndy)) => { |  | ||||||
|                     (dy, dx) = (ndx, ndy); |  | ||||||
|                     border.insert((ly, lx)); |  | ||||||
|                 } |  | ||||||
|                 None => continue 'start_dir, |  | ||||||
|             } |  | ||||||
|  |  | ||||||
|             (ly, lx) = (ly + dy, lx + dx); |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         break; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     let mut counter = 0; |  | ||||||
|  |  | ||||||
|     for (sy, sx) in (1..pipes.len()) |  | ||||||
|         .map(|x| (x, 0)) |  | ||||||
|         .chain((0..pipes[0].len()).map(|x| (0, x))) |  | ||||||
|     { |  | ||||||
|         let mut inside = false; |  | ||||||
|         for range in 0.. { |  | ||||||
|             if let Some(c) = pipes.get(sy + range).and_then(|x| x.get(sx + range)) { |  | ||||||
|                 let is_border = border.contains(&((sy + range) as isize, (sx + range) as isize)); |  | ||||||
|                 if is_border && !['7', 'L'].contains(c) { |  | ||||||
|                     inside = !inside; |  | ||||||
|                 } |  | ||||||
|                 if !is_border && inside { |  | ||||||
|                     counter += 1; |  | ||||||
|                 } |  | ||||||
|             } else { |  | ||||||
|                 break; |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     Some(counter) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| aoc::solution!(10); |  | ||||||
|  |  | ||||||
| #[cfg(test)] |  | ||||||
| mod tests { |  | ||||||
|     use super::*; |  | ||||||
|     #[test] |  | ||||||
|     fn test_part_one() { |  | ||||||
|         assert_eq!( |  | ||||||
|             part_one(&aoc::template::read_file_part("examples", 10, 1)), |  | ||||||
|             Some(8) |  | ||||||
|         ); |  | ||||||
|     } |  | ||||||
|     #[test] |  | ||||||
|     fn test_part_two() { |  | ||||||
|         assert_eq!( |  | ||||||
|             part_two(&aoc::template::read_file_part("examples", 10, 2)), |  | ||||||
|             Some(10) |  | ||||||
|         ); |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @@ -1,74 +0,0 @@ | |||||||
| use std::collections::HashSet; |  | ||||||
|  |  | ||||||
| fn parse_input(input: &str) -> (Vec<(usize, usize)>, HashSet<usize>, HashSet<usize>) { |  | ||||||
|     let mut galaxies = Vec::new(); |  | ||||||
|     let mut rows = HashSet::from_iter(0..input.lines().count()); |  | ||||||
|     let mut columns = HashSet::from_iter(0..input.lines().next().unwrap().len()); |  | ||||||
|  |  | ||||||
|     for (y, line) in input.lines().enumerate() { |  | ||||||
|         for (x, c) in line.chars().enumerate() { |  | ||||||
|             if c == '#' { |  | ||||||
|                 galaxies.push((y, x)); |  | ||||||
|                 rows.remove(&y); |  | ||||||
|                 columns.remove(&x); |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     (galaxies, rows, columns) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| fn solve(input: &str, explode: usize) -> Option<usize> { |  | ||||||
|     let (galaxies, rows, columns) = parse_input(input); |  | ||||||
|  |  | ||||||
|     let mut counter = 0; |  | ||||||
|  |  | ||||||
|     for ((y1, x1), (y2, x2)) in galaxies |  | ||||||
|         .iter() |  | ||||||
|         .copied() |  | ||||||
|         .enumerate() |  | ||||||
|         .flat_map(|(i, x)| galaxies.iter().skip(i + 1).copied().map(move |y| (x, y))) |  | ||||||
|     { |  | ||||||
|         let y_abs = y1.abs_diff(y2); |  | ||||||
|         let x_abs = x1.abs_diff(x2); |  | ||||||
|         let x_extra = columns |  | ||||||
|             .iter() |  | ||||||
|             .filter(|x| (x1..=x2).contains(x) || (x2..=x1).contains(x)) |  | ||||||
|             .count(); |  | ||||||
|         let y_extra = rows |  | ||||||
|             .iter() |  | ||||||
|             .filter(|y| (y1..=y2).contains(y) || (y2..=y1).contains(y)) |  | ||||||
|             .count(); |  | ||||||
|         counter += x_abs + y_abs + (x_extra + y_extra) * (explode - 1); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     Some(counter) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| pub fn part_one(input: &str) -> Option<usize> { |  | ||||||
|     solve(input, 2) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| pub fn part_two(input: &str) -> Option<usize> { |  | ||||||
|     solve(input, 1_000_000) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| aoc::solution!(11); |  | ||||||
|  |  | ||||||
| #[cfg(test)] |  | ||||||
| mod tests { |  | ||||||
|     use super::*; |  | ||||||
|     #[test] |  | ||||||
|     fn test_part_one() { |  | ||||||
|         assert_eq!( |  | ||||||
|             part_one(&aoc::template::read_file("examples", 11)), |  | ||||||
|             Some(374) |  | ||||||
|         ); |  | ||||||
|     } |  | ||||||
|     #[test] |  | ||||||
|     fn test_part_two() { |  | ||||||
|         let input = aoc::template::read_file("examples", 11); |  | ||||||
|         assert_eq!(solve(&input, 10), Some(1030)); |  | ||||||
|         assert_eq!(solve(&input, 100), Some(8410)); |  | ||||||
|     } |  | ||||||
| } |  | ||||||
							
								
								
									
										114
									
								
								src/bin/12.rs
									
									
									
									
									
								
							
							
						
						
									
										114
									
								
								src/bin/12.rs
									
									
									
									
									
								
							| @@ -1,114 +0,0 @@ | |||||||
| use std::{collections::HashMap, iter::once}; |  | ||||||
|  |  | ||||||
| fn is_valid(sequence: &[char], ptr: usize, group: usize) -> bool { |  | ||||||
|     let edges_front = *once(&'.').chain(sequence.iter()).nth(ptr).unwrap() != '#'; |  | ||||||
|     let edges_back = *sequence.iter().chain(once(&'.')).nth(ptr + group).unwrap() != '#'; |  | ||||||
|  |  | ||||||
|     let filled = sequence |  | ||||||
|         .iter() |  | ||||||
|         .chain(once(&'.').cycle()) |  | ||||||
|         .skip(ptr) |  | ||||||
|         .take(group) |  | ||||||
|         .all(|x| *x != '.'); |  | ||||||
|  |  | ||||||
|     edges_front && edges_back && filled |  | ||||||
| } |  | ||||||
|  |  | ||||||
| fn count( |  | ||||||
|     memo: &mut HashMap<(usize, usize), usize>, |  | ||||||
|     sequence: &[char], |  | ||||||
|     groups: &[usize], |  | ||||||
|     ptr: usize, |  | ||||||
| ) -> usize { |  | ||||||
|     match groups.split_first() { |  | ||||||
|         None => !sequence.iter().skip(ptr).any(|c| *c == '#') as usize, |  | ||||||
|         Some((group, r_groups)) => { |  | ||||||
|             let remaining = r_groups.iter().sum(); |  | ||||||
|             let mut total = 0; |  | ||||||
|  |  | ||||||
|             for idx in ptr..(sequence.len() - group - remaining + 1) { |  | ||||||
|                 if is_valid(sequence, idx, *group) { |  | ||||||
|                     let next = idx + *group + 1; |  | ||||||
|                     match memo.get(&(remaining, next)) { |  | ||||||
|                         Some(m) => total += m, |  | ||||||
|                         None => { |  | ||||||
|                             let count = count(memo, sequence, r_groups, next); |  | ||||||
|                             memo.insert((remaining, next), count); |  | ||||||
|                             total += count |  | ||||||
|                         } |  | ||||||
|                     } |  | ||||||
|                 } |  | ||||||
|  |  | ||||||
|                 if sequence[idx] == '#' { |  | ||||||
|                     break; |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
|             total |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| fn parse_line(line: &str) -> (Vec<char>, Vec<usize>) { |  | ||||||
|     let (str_seq, str_grp) = line.split_once(' ').unwrap(); |  | ||||||
|     let sequence = str_seq.chars().collect(); |  | ||||||
|     let groups = str_grp.split(',').filter_map(|x| x.parse().ok()).collect(); |  | ||||||
|  |  | ||||||
|     (sequence, groups) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| fn unfold(sequence: Vec<char>, groups: Vec<usize>, n: usize) -> (Vec<char>, Vec<usize>) { |  | ||||||
|     let seq_len = sequence.len(); |  | ||||||
|     let grp_len = groups.len(); |  | ||||||
|  |  | ||||||
|     let new_sequence = sequence |  | ||||||
|         .into_iter() |  | ||||||
|         .chain(once('?')) |  | ||||||
|         .cycle() |  | ||||||
|         .take(seq_len * n + n - 1) |  | ||||||
|         .collect(); |  | ||||||
|     let new_groups = groups.into_iter().cycle().take(grp_len * n).collect(); |  | ||||||
|  |  | ||||||
|     (new_sequence, new_groups) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| pub fn part_one(input: &str) -> Option<usize> { |  | ||||||
|     Some( |  | ||||||
|         input |  | ||||||
|             .lines() |  | ||||||
|             .map(parse_line) |  | ||||||
|             .map(|(sequence, groups)| count(&mut HashMap::new(), &sequence, &groups, 0)) |  | ||||||
|             .sum(), |  | ||||||
|     ) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| pub fn part_two(input: &str) -> Option<usize> { |  | ||||||
|     Some( |  | ||||||
|         input |  | ||||||
|             .lines() |  | ||||||
|             .map(parse_line) |  | ||||||
|             .map(|(sequence, groups)| unfold(sequence, groups, 5)) |  | ||||||
|             .map(|(sequence, groups)| count(&mut HashMap::new(), &sequence, &groups, 0)) |  | ||||||
|             .sum(), |  | ||||||
|     ) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| aoc::solution!(12); |  | ||||||
|  |  | ||||||
| #[cfg(test)] |  | ||||||
| mod tests { |  | ||||||
|     use super::*; |  | ||||||
|     #[test] |  | ||||||
|     fn test_part_one() { |  | ||||||
|         assert_eq!( |  | ||||||
|             part_one(&aoc::template::read_file("examples", 12)), |  | ||||||
|             Some(21) |  | ||||||
|         ); |  | ||||||
|     } |  | ||||||
|     #[test] |  | ||||||
|     fn test_part_two() { |  | ||||||
|         assert_eq!( |  | ||||||
|             part_two(&aoc::template::read_file("examples", 12)), |  | ||||||
|             Some(525152) |  | ||||||
|         ); |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @@ -1,64 +0,0 @@ | |||||||
| fn mirror_h(shape: &[Vec<char>], smudges: usize) -> Option<usize> { |  | ||||||
|     (1..shape.len()).find(|&i| { |  | ||||||
|         shape |  | ||||||
|             .iter() |  | ||||||
|             .skip(i) |  | ||||||
|             .zip(shape.iter().take(i).rev()) |  | ||||||
|             .map(|(x, y)| { |  | ||||||
|                 x.iter() |  | ||||||
|                     .zip(y.iter()) |  | ||||||
|                     .map(|(xx, yy)| (xx != yy) as usize) |  | ||||||
|                     .sum::<usize>() |  | ||||||
|             }) |  | ||||||
|             .sum::<usize>() |  | ||||||
|             == smudges |  | ||||||
|     }) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| fn mirror_v(shape: &[Vec<char>], smudges: usize) -> Option<usize> { |  | ||||||
|     let shape: Vec<Vec<char>> = (0..shape[0].len()) |  | ||||||
|         .map(|col| (0..shape.len()).map(|row| shape[row][col]).collect()) |  | ||||||
|         .collect(); |  | ||||||
|  |  | ||||||
|     mirror_h(&shape, smudges) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| fn solve(input: &str, smudges: usize) -> usize { |  | ||||||
|     input |  | ||||||
|         .split("\n\n") |  | ||||||
|         .map(|x| x.lines().map(|line| line.chars().collect()).collect()) |  | ||||||
|         .map(|shape: Vec<Vec<char>>| { |  | ||||||
|             mirror_v(&shape, smudges).unwrap_or_default() |  | ||||||
|                 + mirror_h(&shape, smudges).unwrap_or_default() * 100 |  | ||||||
|         }) |  | ||||||
|         .sum() |  | ||||||
| } |  | ||||||
|  |  | ||||||
| pub fn part_one(input: &str) -> Option<usize> { |  | ||||||
|     Some(solve(input, 0)) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| pub fn part_two(input: &str) -> Option<usize> { |  | ||||||
|     Some(solve(input, 1)) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| aoc::solution!(13); |  | ||||||
|  |  | ||||||
| #[cfg(test)] |  | ||||||
| mod tests { |  | ||||||
|     use super::*; |  | ||||||
|     #[test] |  | ||||||
|     fn test_part_one() { |  | ||||||
|         assert_eq!( |  | ||||||
|             part_one(&aoc::template::read_file("examples", 13)), |  | ||||||
|             Some(405) |  | ||||||
|         ); |  | ||||||
|     } |  | ||||||
|     #[test] |  | ||||||
|     fn test_part_two() { |  | ||||||
|         assert_eq!( |  | ||||||
|             part_two(&aoc::template::read_file("examples", 13)), |  | ||||||
|             Some(400) |  | ||||||
|         ); |  | ||||||
|     } |  | ||||||
| } |  | ||||||
							
								
								
									
										115
									
								
								src/bin/14.rs
									
									
									
									
									
								
							
							
						
						
									
										115
									
								
								src/bin/14.rs
									
									
									
									
									
								
							| @@ -1,115 +0,0 @@ | |||||||
| use std::{collections::HashMap, iter::once}; |  | ||||||
|  |  | ||||||
| enum Tilt { |  | ||||||
|     North, |  | ||||||
|     West, |  | ||||||
|     South, |  | ||||||
|     East, |  | ||||||
| } |  | ||||||
|  |  | ||||||
| fn get_load(floor: &[Vec<char>]) -> usize { |  | ||||||
|     floor |  | ||||||
|         .iter() |  | ||||||
|         .enumerate() |  | ||||||
|         .flat_map(|(i, x)| x.iter().map(move |y| (i, y))) |  | ||||||
|         .filter(|x| *x.1 == 'O') |  | ||||||
|         .map(|x| floor.len() - x.0) |  | ||||||
|         .sum() |  | ||||||
| } |  | ||||||
|  |  | ||||||
| fn swap<T: Copy>(floor: &mut [Vec<T>], from: (usize, usize), to: (usize, usize)) { |  | ||||||
|     let a = floor[from.0][from.1]; |  | ||||||
|     let b = floor[to.0][to.1]; |  | ||||||
|     floor[from.0][from.1] = b; |  | ||||||
|     floor[to.0][to.1] = a; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| fn tilt(floor: &mut [Vec<char>], tilt: Tilt) { |  | ||||||
|     let (inner, outer) = match tilt { |  | ||||||
|         Tilt::North | Tilt::South => (floor[0].len(), floor.len()), |  | ||||||
|         Tilt::West | Tilt::East => (floor.len(), floor[0].len()), |  | ||||||
|     }; |  | ||||||
|     let inx_n = |(i, j)| (j, i); |  | ||||||
|     let inx_s = |(i, j)| (inner - 1 - j, i); |  | ||||||
|     let inx_w = |(i, j)| (i, j); |  | ||||||
|     let inx_e = |(i, j)| (i, inner - 1 - j); |  | ||||||
|     for i in 0..outer { |  | ||||||
|         let mut ptr = 0; |  | ||||||
|         for j in 0..inner { |  | ||||||
|             let ((ii, jj), (pi, pj)) = match tilt { |  | ||||||
|                 Tilt::North => (inx_n((i, j)), inx_n((i, ptr))), |  | ||||||
|                 Tilt::South => (inx_s((i, j)), inx_s((i, ptr))), |  | ||||||
|                 Tilt::East => (inx_e((i, j)), inx_e((i, ptr))), |  | ||||||
|                 Tilt::West => (inx_w((i, j)), inx_w((i, ptr))), |  | ||||||
|             }; |  | ||||||
|             match floor[ii][jj] { |  | ||||||
|                 'O' => { |  | ||||||
|                     swap(floor, (ii, jj), (pi, pj)); |  | ||||||
|                     ptr += 1; |  | ||||||
|                 } |  | ||||||
|                 '#' => ptr = j + 1, |  | ||||||
|                 _ => (), |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| fn tilt_cycle(floor: &mut [Vec<char>]) { |  | ||||||
|     use Tilt::*; |  | ||||||
|     tilt(floor, North); |  | ||||||
|     tilt(floor, West); |  | ||||||
|     tilt(floor, South); |  | ||||||
|     tilt(floor, East); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| pub fn part_one(input: &str) -> Option<usize> { |  | ||||||
|     let mut f: Vec<Vec<_>> = input.lines().map(|x| x.chars().collect()).collect(); |  | ||||||
|     tilt(&mut f, Tilt::North); |  | ||||||
|     Some(get_load(&f)) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| pub fn part_two(input: &str) -> Option<usize> { |  | ||||||
|     let mut f: Vec<Vec<_>> = input.lines().map(|x| x.chars().collect()).collect(); |  | ||||||
|     let mut memo: HashMap<String, usize> = HashMap::new(); |  | ||||||
|  |  | ||||||
|     for i in 1.. { |  | ||||||
|         tilt_cycle(&mut f); |  | ||||||
|         let repr = f |  | ||||||
|             .iter() |  | ||||||
|             .flat_map(|x| x.iter().chain(once(&'\n'))) |  | ||||||
|             .collect::<String>(); |  | ||||||
|  |  | ||||||
|         if let Some(ii) = memo.insert(repr, i) { |  | ||||||
|             let m = i - ii; |  | ||||||
|             let shift = (1_000_000_000 - ii) % m; |  | ||||||
|  |  | ||||||
|             for _ in 0..shift { |  | ||||||
|                 tilt_cycle(&mut f); |  | ||||||
|             } |  | ||||||
|             break; |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     Some(get_load(&f)) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| aoc::solution!(14); |  | ||||||
|  |  | ||||||
| #[cfg(test)] |  | ||||||
| mod tests { |  | ||||||
|     use super::*; |  | ||||||
|     #[test] |  | ||||||
|     fn test_part_one() { |  | ||||||
|         assert_eq!( |  | ||||||
|             part_one(&aoc::template::read_file("examples", 14)), |  | ||||||
|             Some(136) |  | ||||||
|         ); |  | ||||||
|     } |  | ||||||
|     #[test] |  | ||||||
|     fn test_part_two() { |  | ||||||
|         assert_eq!( |  | ||||||
|             part_two(&aoc::template::read_file("examples", 14)), |  | ||||||
|             Some(64) |  | ||||||
|         ); |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @@ -1,68 +0,0 @@ | |||||||
| fn hash(s: &str) -> usize { |  | ||||||
|     let mut total = 0; |  | ||||||
|     for c in s.chars().map(|x| x as usize) { |  | ||||||
|         total += c; |  | ||||||
|         total *= 17; |  | ||||||
|         total %= 256; |  | ||||||
|     } |  | ||||||
|     total |  | ||||||
| } |  | ||||||
|  |  | ||||||
| pub fn part_one(input: &str) -> Option<usize> { |  | ||||||
|     Some(input.lines().flat_map(|x| x.split(',')).map(hash).sum()) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| pub fn part_two(input: &str) -> Option<usize> { |  | ||||||
|     let mut map = vec![Vec::<(&str, usize)>::new(); 256]; |  | ||||||
|  |  | ||||||
|     for instruction in input.lines().flat_map(|x| x.split(',')) { |  | ||||||
|         let (label, n) = instruction.split_once(|x| x == '-' || x == '=').unwrap(); |  | ||||||
|         let hash = hash(label); |  | ||||||
|         let indexed_map = map.get_mut(hash).unwrap(); |  | ||||||
|  |  | ||||||
|         if instruction.contains('-') { |  | ||||||
|             if let Some((i, _)) = indexed_map.iter().enumerate().find(|(_, &x)| x.0 == label) { |  | ||||||
|                 indexed_map.remove(i); |  | ||||||
|             } |  | ||||||
|         } else { |  | ||||||
|             let nbr = n.parse().unwrap(); |  | ||||||
|             if let Some((i, _)) = indexed_map.iter().enumerate().find(|(_, &x)| x.0 == label) { |  | ||||||
|                 indexed_map[i] = (label, nbr); |  | ||||||
|             } else { |  | ||||||
|                 indexed_map.push((label, nbr)) |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     Some( |  | ||||||
|         map.into_iter() |  | ||||||
|             .enumerate() |  | ||||||
|             .flat_map(|(i, x)| { |  | ||||||
|                 x.into_iter() |  | ||||||
|                     .enumerate() |  | ||||||
|                     .map(move |(j, y)| (i + 1) * (j + 1) * y.1) |  | ||||||
|             }) |  | ||||||
|             .sum(), |  | ||||||
|     ) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| aoc::solution!(15); |  | ||||||
|  |  | ||||||
| #[cfg(test)] |  | ||||||
| mod tests { |  | ||||||
|     use super::*; |  | ||||||
|     #[test] |  | ||||||
|     fn test_part_one() { |  | ||||||
|         assert_eq!( |  | ||||||
|             part_one(&aoc::template::read_file("examples", 15)), |  | ||||||
|             Some(1320) |  | ||||||
|         ); |  | ||||||
|     } |  | ||||||
|     #[test] |  | ||||||
|     fn test_part_two() { |  | ||||||
|         assert_eq!( |  | ||||||
|             part_two(&aoc::template::read_file("examples", 15)), |  | ||||||
|             Some(145) |  | ||||||
|         ); |  | ||||||
|     } |  | ||||||
| } |  | ||||||
							
								
								
									
										173
									
								
								src/bin/16.rs
									
									
									
									
									
								
							
							
						
						
									
										173
									
								
								src/bin/16.rs
									
									
									
									
									
								
							| @@ -1,173 +0,0 @@ | |||||||
| use std::collections::HashSet; |  | ||||||
|  |  | ||||||
| #[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)] |  | ||||||
| enum Direction { |  | ||||||
|     Up, |  | ||||||
|     Down, |  | ||||||
|     Left, |  | ||||||
|     Right, |  | ||||||
| } |  | ||||||
|  |  | ||||||
| impl Direction { |  | ||||||
|     fn next(&self, (y, x): (usize, usize)) -> (usize, usize) { |  | ||||||
|         use Direction::*; |  | ||||||
|         match self { |  | ||||||
|             Up => (y.checked_sub(1).unwrap_or(usize::MAX), x), |  | ||||||
|             Down => (y + 1, x), |  | ||||||
|             Left => (y, x.checked_sub(1).unwrap_or(usize::MAX)), |  | ||||||
|             Right => (y, x + 1), |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| #[derive(Debug, Clone, Copy)] |  | ||||||
| enum Mirror { |  | ||||||
|     Vertical, |  | ||||||
|     Horizontal, |  | ||||||
|     EvenSymmetric, |  | ||||||
|     OddSymmetric, |  | ||||||
| } |  | ||||||
|  |  | ||||||
| struct ParseMirrorError; |  | ||||||
|  |  | ||||||
| impl TryFrom<u8> for Mirror { |  | ||||||
|     type Error = ParseMirrorError; |  | ||||||
|     fn try_from(value: u8) -> Result<Self, Self::Error> { |  | ||||||
|         Ok(match value { |  | ||||||
|             b'|' => Self::Vertical, |  | ||||||
|             b'-' => Self::Horizontal, |  | ||||||
|             b'\\' => Self::EvenSymmetric, |  | ||||||
|             b'/' => Self::OddSymmetric, |  | ||||||
|             _ => return Err(ParseMirrorError), |  | ||||||
|         }) |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| impl Mirror { |  | ||||||
|     fn bounce(&self, d: &Direction) -> (Direction, Option<Direction>) { |  | ||||||
|         use Direction::*; |  | ||||||
|         use Mirror::*; |  | ||||||
|  |  | ||||||
|         match self { |  | ||||||
|             Vertical => match d { |  | ||||||
|                 Up | Down => (*d, None), |  | ||||||
|                 Left | Right => (Up, Some(Down)), |  | ||||||
|             }, |  | ||||||
|             Horizontal => match d { |  | ||||||
|                 Up | Down => (Left, Some(Right)), |  | ||||||
|                 Left | Right => (*d, None), |  | ||||||
|             }, |  | ||||||
|             EvenSymmetric => ( |  | ||||||
|                 match d { |  | ||||||
|                     Up => Left, |  | ||||||
|                     Down => Right, |  | ||||||
|                     Left => Up, |  | ||||||
|                     Right => Down, |  | ||||||
|                 }, |  | ||||||
|                 None, |  | ||||||
|             ), |  | ||||||
|             OddSymmetric => ( |  | ||||||
|                 match d { |  | ||||||
|                     Up => Right, |  | ||||||
|                     Down => Left, |  | ||||||
|                     Left => Down, |  | ||||||
|                     Right => Up, |  | ||||||
|                 }, |  | ||||||
|                 None, |  | ||||||
|             ), |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| fn solve_with_start(layout: &[Vec<Option<Mirror>>], start: (usize, usize, Direction)) -> usize { |  | ||||||
|     let mut queue = vec![start]; |  | ||||||
|     let mut visited = HashSet::new(); |  | ||||||
|  |  | ||||||
|     while let Some((y, x, d)) = queue.pop() { |  | ||||||
|         let point = layout.get(y).and_then(|row| row.get(x)); |  | ||||||
|  |  | ||||||
|         if point.is_none() { |  | ||||||
|             continue; |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         if !visited.insert((y, x, d)) { |  | ||||||
|             continue; |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         if let Some(Some(m)) = point { |  | ||||||
|             let (new_d, opt_new_d) = m.bounce(&d); |  | ||||||
|             let (ny, nx) = new_d.next((y, x)); |  | ||||||
|             queue.push((ny, nx, new_d)); |  | ||||||
|  |  | ||||||
|             if let Some(od) = opt_new_d { |  | ||||||
|                 let (ony, onx) = od.next((y, x)); |  | ||||||
|                 queue.push((ony, onx, od)); |  | ||||||
|             } |  | ||||||
|         } else { |  | ||||||
|             let (ny, nx) = d.next((y, x)); |  | ||||||
|             queue.push((ny, nx, d)); |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     HashSet::<(usize, usize)>::from_iter(visited.into_iter().map(|(y, x, _)| (y, x))).len() |  | ||||||
| } |  | ||||||
|  |  | ||||||
| pub fn part_one(input: &str) -> Option<usize> { |  | ||||||
|     let layout: Vec<Vec<_>> = input |  | ||||||
|         .lines() |  | ||||||
|         .map(|x| x.as_bytes().iter().map(|&x| x.try_into().ok()).collect()) |  | ||||||
|         .collect(); |  | ||||||
|  |  | ||||||
|     Some(solve_with_start(&layout, (0, 0, Direction::Right))) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| pub fn part_two(input: &str) -> Option<usize> { |  | ||||||
|     let layout: Vec<Vec<_>> = input |  | ||||||
|         .lines() |  | ||||||
|         .map(|x| x.as_bytes().iter().map(|&x| x.try_into().ok()).collect()) |  | ||||||
|         .collect(); |  | ||||||
|  |  | ||||||
|     let h = layout.len(); |  | ||||||
|     let w = layout[0].len(); |  | ||||||
|  |  | ||||||
|     let mut scores = Vec::with_capacity(2 * (h + w) + 1); |  | ||||||
|  |  | ||||||
|     for hh in 0..h { |  | ||||||
|         let left = solve_with_start(&layout, (hh, 0, Direction::Right)); |  | ||||||
|         let right = solve_with_start(&layout, (hh, w - 1, Direction::Left)); |  | ||||||
|  |  | ||||||
|         scores.push(left); |  | ||||||
|         scores.push(right); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     for ww in 0..w { |  | ||||||
|         let downward = solve_with_start(&layout, (0, ww, Direction::Down)); |  | ||||||
|         let upward = solve_with_start(&layout, (h - 1, ww, Direction::Up)); |  | ||||||
|  |  | ||||||
|         scores.push(downward); |  | ||||||
|         scores.push(upward); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     scores.into_iter().max() |  | ||||||
| } |  | ||||||
|  |  | ||||||
| aoc::solution!(16); |  | ||||||
|  |  | ||||||
| #[cfg(test)] |  | ||||||
| mod tests { |  | ||||||
|     use super::*; |  | ||||||
|     #[test] |  | ||||||
|     fn test_part_one() { |  | ||||||
|         assert_eq!( |  | ||||||
|             part_one(&aoc::template::read_file("examples", 16)), |  | ||||||
|             Some(46) |  | ||||||
|         ); |  | ||||||
|     } |  | ||||||
|     #[test] |  | ||||||
|     fn test_part_two() { |  | ||||||
|         assert_eq!( |  | ||||||
|             part_two(&aoc::template::read_file("examples", 16)), |  | ||||||
|             Some(51) |  | ||||||
|         ); |  | ||||||
|     } |  | ||||||
| } |  | ||||||
							
								
								
									
										192
									
								
								src/bin/17.rs
									
									
									
									
									
								
							
							
						
						
									
										192
									
								
								src/bin/17.rs
									
									
									
									
									
								
							| @@ -1,192 +0,0 @@ | |||||||
| use std::{ |  | ||||||
|     collections::{BinaryHeap, HashMap}, |  | ||||||
|     ops::Neg, |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] |  | ||||||
| enum Direction { |  | ||||||
|     Up, |  | ||||||
|     Down, |  | ||||||
|     Left, |  | ||||||
|     Right, |  | ||||||
| } |  | ||||||
|  |  | ||||||
| impl Direction { |  | ||||||
|     const ALL: [Direction; 4] = [ |  | ||||||
|         Direction::Up, |  | ||||||
|         Direction::Down, |  | ||||||
|         Direction::Left, |  | ||||||
|         Direction::Right, |  | ||||||
|     ]; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| impl Neg for Direction { |  | ||||||
|     type Output = Self; |  | ||||||
|     fn neg(self) -> Self::Output { |  | ||||||
|         use Direction::*; |  | ||||||
|         match self { |  | ||||||
|             Up => Down, |  | ||||||
|             Down => Up, |  | ||||||
|             Left => Right, |  | ||||||
|             Right => Left, |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| #[derive(Debug, PartialEq, Eq)] |  | ||||||
| struct State { |  | ||||||
|     heat: usize, |  | ||||||
|     position: (usize, usize), |  | ||||||
|     direction: Direction, |  | ||||||
|     steps: usize, |  | ||||||
| } |  | ||||||
|  |  | ||||||
| impl PartialOrd for State { |  | ||||||
|     fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> { |  | ||||||
|         Some(self.cmp(other)) |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| impl Ord for State { |  | ||||||
|     fn cmp(&self, other: &Self) -> std::cmp::Ordering { |  | ||||||
|         other |  | ||||||
|             .heat |  | ||||||
|             .cmp(&self.heat) |  | ||||||
|             .then_with(|| self.position.cmp(&other.position)) |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| fn find_path( |  | ||||||
|     grid: &[Vec<usize>], |  | ||||||
|     start: (usize, usize), |  | ||||||
|     target: (usize, usize), |  | ||||||
|     min_steps: usize, |  | ||||||
|     max_steps: usize, |  | ||||||
| ) -> Option<usize> { |  | ||||||
|     let valid_indexing = |y: usize, x: usize| grid.get(y).and_then(|row| row.get(x)).is_some(); |  | ||||||
|  |  | ||||||
|     let mut heap = BinaryHeap::new(); |  | ||||||
|     heap.push(State { |  | ||||||
|         heat: 0, |  | ||||||
|         position: (0, 0), |  | ||||||
|         direction: Direction::Right, |  | ||||||
|         steps: 0, |  | ||||||
|     }); |  | ||||||
|  |  | ||||||
|     let mut heatmap = HashMap::new(); |  | ||||||
|     heatmap.insert((start, Direction::Down, 0), 0); |  | ||||||
|     heatmap.insert((start, Direction::Up, 0), 0); |  | ||||||
|  |  | ||||||
|     while let Some(State { |  | ||||||
|         heat, |  | ||||||
|         position, |  | ||||||
|         direction, |  | ||||||
|         steps, |  | ||||||
|     }) = heap.pop() |  | ||||||
|     { |  | ||||||
|         if position == target { |  | ||||||
|             return Some(heat); |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         if *heatmap |  | ||||||
|             .get(&(position, direction, steps)) |  | ||||||
|             .unwrap_or(&usize::MAX) |  | ||||||
|             < heat |  | ||||||
|         { |  | ||||||
|             continue; |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         let (y, x) = position; |  | ||||||
|         for d in Direction::ALL.iter().filter(|&x| *x != -direction) { |  | ||||||
|             if steps < min_steps && *d != direction { |  | ||||||
|                 continue; |  | ||||||
|             } |  | ||||||
|  |  | ||||||
|             let (ny, nx) = match d { |  | ||||||
|                 Direction::Up => (y.wrapping_sub(1), x), |  | ||||||
|                 Direction::Down => (y + 1, x), |  | ||||||
|                 Direction::Left => (y, x.wrapping_sub(1)), |  | ||||||
|                 Direction::Right => (y, x + 1), |  | ||||||
|             }; |  | ||||||
|  |  | ||||||
|             if !valid_indexing(ny, nx) { |  | ||||||
|                 continue; |  | ||||||
|             } |  | ||||||
|  |  | ||||||
|             let new_steps = if *d == direction { steps + 1 } else { 1 }; |  | ||||||
|  |  | ||||||
|             if new_steps > max_steps { |  | ||||||
|                 continue; |  | ||||||
|             } |  | ||||||
|  |  | ||||||
|             let state = State { |  | ||||||
|                 heat: heat + grid[ny][nx], |  | ||||||
|                 position: (ny, nx), |  | ||||||
|                 direction: *d, |  | ||||||
|                 steps: new_steps, |  | ||||||
|             }; |  | ||||||
|  |  | ||||||
|             if *heatmap |  | ||||||
|                 .get(&((ny, nx), *d, new_steps)) |  | ||||||
|                 .unwrap_or(&usize::MAX) |  | ||||||
|                 > state.heat |  | ||||||
|             { |  | ||||||
|                 heatmap.insert(((ny, nx), *d, new_steps), state.heat); |  | ||||||
|                 heap.push(state); |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     None |  | ||||||
| } |  | ||||||
|  |  | ||||||
| pub fn part_one(input: &str) -> Option<usize> { |  | ||||||
|     let grid: Vec<Vec<_>> = input |  | ||||||
|         .lines() |  | ||||||
|         .map(|x| { |  | ||||||
|             x.chars() |  | ||||||
|                 .filter_map(|x| x.to_digit(10).map(|x| x as usize)) |  | ||||||
|                 .collect() |  | ||||||
|         }) |  | ||||||
|         .collect(); |  | ||||||
|  |  | ||||||
|     let target = (grid.len() - 1, grid[0].len() - 1); |  | ||||||
|  |  | ||||||
|     find_path(&grid, (0, 0), target, 0, 3) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| pub fn part_two(input: &str) -> Option<usize> { |  | ||||||
|     let grid: Vec<Vec<_>> = input |  | ||||||
|         .lines() |  | ||||||
|         .map(|x| { |  | ||||||
|             x.chars() |  | ||||||
|                 .filter_map(|x| x.to_digit(10).map(|x| x as usize)) |  | ||||||
|                 .collect() |  | ||||||
|         }) |  | ||||||
|         .collect(); |  | ||||||
|  |  | ||||||
|     let target = (grid.len() - 1, grid[0].len() - 1); |  | ||||||
|  |  | ||||||
|     find_path(&grid, (0, 0), target, 4, 10) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| aoc::solution!(17); |  | ||||||
|  |  | ||||||
| #[cfg(test)] |  | ||||||
| mod tests { |  | ||||||
|     use super::*; |  | ||||||
|     #[test] |  | ||||||
|     fn test_part_one() { |  | ||||||
|         assert_eq!( |  | ||||||
|             part_one(&aoc::template::read_file("examples", 17)), |  | ||||||
|             Some(102) |  | ||||||
|         ); |  | ||||||
|     } |  | ||||||
|     #[test] |  | ||||||
|     fn test_part_two() { |  | ||||||
|         assert_eq!( |  | ||||||
|             part_two(&aoc::template::read_file("examples", 17)), |  | ||||||
|             Some(94) |  | ||||||
|         ); |  | ||||||
|     } |  | ||||||
| } |  | ||||||
							
								
								
									
										126
									
								
								src/bin/18.rs
									
									
									
									
									
								
							
							
						
						
									
										126
									
								
								src/bin/18.rs
									
									
									
									
									
								
							| @@ -1,126 +0,0 @@ | |||||||
| use std::str::FromStr; |  | ||||||
|  |  | ||||||
| #[derive(Debug, Clone, Copy)] |  | ||||||
| enum Direction { |  | ||||||
|     Up, |  | ||||||
|     Down, |  | ||||||
|     Left, |  | ||||||
|     Right, |  | ||||||
| } |  | ||||||
|  |  | ||||||
| #[derive(Debug)] |  | ||||||
| struct ParseDirectionError; |  | ||||||
|  |  | ||||||
| impl FromStr for Direction { |  | ||||||
|     type Err = ParseDirectionError; |  | ||||||
|     fn from_str(s: &str) -> Result<Self, Self::Err> { |  | ||||||
|         use Direction::*; |  | ||||||
|  |  | ||||||
|         Ok(match s { |  | ||||||
|             "U" | "3" => Up, |  | ||||||
|             "D" | "1" => Down, |  | ||||||
|             "L" | "2" => Left, |  | ||||||
|             "R" | "0" => Right, |  | ||||||
|             _ => return Err(ParseDirectionError), |  | ||||||
|         }) |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| impl From<Direction> for (isize, isize) { |  | ||||||
|     fn from(value: Direction) -> Self { |  | ||||||
|         use Direction::*; |  | ||||||
|         match value { |  | ||||||
|             Up => (-1, 0), |  | ||||||
|             Down => (1, 0), |  | ||||||
|             Left => (0, -1), |  | ||||||
|             Right => (0, 1), |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| fn get_area(border: &[(isize, isize)], border_length: isize) -> isize { |  | ||||||
|     // get area with shoelace formula (trapezoid variant) |  | ||||||
|     // https://en.wikipedia.org/wiki/Shoelace_formula |  | ||||||
|     let mut shoelace: isize = 0; |  | ||||||
|     for n in 0..border.len() { |  | ||||||
|         let (y1, x1) = border[n]; |  | ||||||
|         let (y2, x2) = border[(n + 1) % border.len()]; |  | ||||||
|         shoelace += (y1 + y2) * (x1 - x2); |  | ||||||
|     } |  | ||||||
|     let area = shoelace / 2; |  | ||||||
|  |  | ||||||
|     // get interior by inverting pick's theorem formula |  | ||||||
|     // https://en.wikipedia.org/wiki/Pick%27s_theorem |  | ||||||
|     let interior = area + 1 - border_length / 2; |  | ||||||
|     interior + border_length |  | ||||||
| } |  | ||||||
|  |  | ||||||
| fn get_border(instructions: &[(Direction, isize)]) -> (Vec<(isize, isize)>, isize) { |  | ||||||
|     let mut border = Vec::new(); |  | ||||||
|     let mut border_length = 0; |  | ||||||
|     let (mut sy, mut sx) = (0, 0); |  | ||||||
|     border.push((sy, sx)); |  | ||||||
|  |  | ||||||
|     for (d, l) in instructions.iter().copied() { |  | ||||||
|         let (dy, dx) = d.into(); |  | ||||||
|         (sy, sx) = (sy + l * dy, sx + l * dx); |  | ||||||
|         border.push((sy, sx)); |  | ||||||
|         border_length += l; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     border.pop(); |  | ||||||
|  |  | ||||||
|     (border, border_length) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| pub fn part_one(input: &str) -> Option<isize> { |  | ||||||
|     let instructions: Vec<(Direction, isize)> = input |  | ||||||
|         .lines() |  | ||||||
|         .map(|line| { |  | ||||||
|             let [d, l, _] = line.splitn(3, ' ').collect::<Vec<_>>().try_into().unwrap(); |  | ||||||
|             (d.parse().unwrap(), l.parse::<isize>().unwrap()) |  | ||||||
|         }) |  | ||||||
|         .collect(); |  | ||||||
|  |  | ||||||
|     let (border, border_length) = get_border(&instructions); |  | ||||||
|     Some(get_area(&border, border_length)) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| fn join_option_tuple<T, U>((a, b): (Option<T>, Option<U>)) -> Option<(T, U)> { |  | ||||||
|     Some((a?, b?)) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| pub fn part_two(input: &str) -> Option<isize> { |  | ||||||
|     let instructions: Vec<(Direction, isize)> = input |  | ||||||
|         .lines() |  | ||||||
|         .filter_map(|line| line.split_once(" (#")) |  | ||||||
|         .filter_map(|(_, h)| h.strip_suffix(')')) |  | ||||||
|         .map(|h| h.split_at(h.len() - 1)) |  | ||||||
|         .map(|(hex, dir)| (dir.parse().ok(), isize::from_str_radix(hex, 16).ok())) |  | ||||||
|         .filter_map(join_option_tuple) |  | ||||||
|         .collect(); |  | ||||||
|  |  | ||||||
|     let (border, border_length) = get_border(&instructions); |  | ||||||
|     Some(get_area(&border, border_length)) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| aoc::solution!(18); |  | ||||||
|  |  | ||||||
| #[cfg(test)] |  | ||||||
| mod tests { |  | ||||||
|     use super::*; |  | ||||||
|     #[test] |  | ||||||
|     fn test_part_one() { |  | ||||||
|         assert_eq!( |  | ||||||
|             part_one(&aoc::template::read_file("examples", 18)), |  | ||||||
|             Some(62) |  | ||||||
|         ); |  | ||||||
|     } |  | ||||||
|     #[test] |  | ||||||
|     fn test_part_two() { |  | ||||||
|         assert_eq!( |  | ||||||
|             part_two(&aoc::template::read_file("examples", 18)), |  | ||||||
|             Some(952408144115) |  | ||||||
|         ); |  | ||||||
|     } |  | ||||||
| } |  | ||||||
							
								
								
									
										387
									
								
								src/bin/19.rs
									
									
									
									
									
								
							
							
						
						
									
										387
									
								
								src/bin/19.rs
									
									
									
									
									
								
							| @@ -1,387 +0,0 @@ | |||||||
| use std::{ |  | ||||||
|     collections::HashMap, |  | ||||||
|     ops::{Index, IndexMut}, |  | ||||||
|     str::FromStr, |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| #[derive(Debug, Hash, PartialEq, Eq, Clone, Copy)] |  | ||||||
| enum Gear { |  | ||||||
|     X, |  | ||||||
|     M, |  | ||||||
|     A, |  | ||||||
|     S, |  | ||||||
| } |  | ||||||
|  |  | ||||||
| #[derive(Debug)] |  | ||||||
| struct GearParseError; |  | ||||||
|  |  | ||||||
| impl TryFrom<&str> for Gear { |  | ||||||
|     type Error = GearParseError; |  | ||||||
|     fn try_from(value: &str) -> Result<Self, Self::Error> { |  | ||||||
|         use Gear::*; |  | ||||||
|         Ok(match value { |  | ||||||
|             "x" => X, |  | ||||||
|             "m" => M, |  | ||||||
|             "a" => A, |  | ||||||
|             "s" => S, |  | ||||||
|             _ => return Err(GearParseError), |  | ||||||
|         }) |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| #[derive(Debug, Hash, PartialEq, Eq)] |  | ||||||
| enum Comparator { |  | ||||||
|     Lt, |  | ||||||
|     Gt, |  | ||||||
| } |  | ||||||
|  |  | ||||||
| #[derive(Debug)] |  | ||||||
| struct ComparatorParseError; |  | ||||||
|  |  | ||||||
| impl TryFrom<char> for Comparator { |  | ||||||
|     type Error = ComparatorParseError; |  | ||||||
|     fn try_from(value: char) -> Result<Self, Self::Error> { |  | ||||||
|         use Comparator::*; |  | ||||||
|         Ok(match value { |  | ||||||
|             '<' => Lt, |  | ||||||
|             '>' => Gt, |  | ||||||
|             _ => return Err(ComparatorParseError), |  | ||||||
|         }) |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| #[derive(Debug, Hash, PartialEq, Eq, Clone)] |  | ||||||
| enum Resolver { |  | ||||||
|     Accepted, |  | ||||||
|     Rejected, |  | ||||||
|     Delegated(String), |  | ||||||
| } |  | ||||||
|  |  | ||||||
| impl From<&str> for Resolver { |  | ||||||
|     fn from(value: &str) -> Self { |  | ||||||
|         use Resolver::*; |  | ||||||
|         match value { |  | ||||||
|             "A" => Accepted, |  | ||||||
|             "R" => Rejected, |  | ||||||
|             x => Delegated(x.to_string()), |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| #[derive(Debug, Hash, PartialEq, Eq)] |  | ||||||
| struct Workflow { |  | ||||||
|     workflows: Vec<WorkflowInner>, |  | ||||||
| } |  | ||||||
|  |  | ||||||
| #[derive(Debug)] |  | ||||||
| struct ParseWorkflowError; |  | ||||||
|  |  | ||||||
| impl TryFrom<&str> for Workflow { |  | ||||||
|     type Error = ParseWorkflowError; |  | ||||||
|     fn try_from(value: &str) -> Result<Self, Self::Error> { |  | ||||||
|         Ok(Self { |  | ||||||
|             workflows: value.split(',').filter_map(|x| x.try_into().ok()).collect(), |  | ||||||
|         }) |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| #[derive(Debug, Hash, PartialEq, Eq)] |  | ||||||
| enum WorkflowInner { |  | ||||||
|     Resolver(Resolver), |  | ||||||
|     Rule((Gear, Comparator, usize, Resolver)), |  | ||||||
| } |  | ||||||
|  |  | ||||||
| #[derive(Debug)] |  | ||||||
| struct ParseWorkflowInnerError; |  | ||||||
|  |  | ||||||
| impl TryFrom<&str> for WorkflowInner { |  | ||||||
|     type Error = ParseWorkflowInnerError; |  | ||||||
|     fn try_from(value: &str) -> Result<Self, Self::Error> { |  | ||||||
|         if !value.contains(':') { |  | ||||||
|             return Ok(WorkflowInner::Resolver(value.into())); |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         let (rest, resolver) = value.split_once(':').unwrap(); |  | ||||||
|         let resolver = resolver.into(); |  | ||||||
|  |  | ||||||
|         let (gear, number) = rest.split_once(|x| x == '<' || x == '>').unwrap(); |  | ||||||
|         let gear = gear.try_into().map_err(|_| ParseWorkflowInnerError)?; |  | ||||||
|         let number = number.parse().map_err(|_| ParseWorkflowInnerError)?; |  | ||||||
|  |  | ||||||
|         let comparator = if value.contains('<') { '<' } else { '>' }; |  | ||||||
|         let comparator = comparator.try_into().map_err(|_| ParseWorkflowInnerError)?; |  | ||||||
|  |  | ||||||
|         Ok(WorkflowInner::Rule((gear, comparator, number, resolver))) |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| #[derive(Debug)] |  | ||||||
| struct Xmas { |  | ||||||
|     x: usize, |  | ||||||
|     m: usize, |  | ||||||
|     a: usize, |  | ||||||
|     s: usize, |  | ||||||
| } |  | ||||||
|  |  | ||||||
| #[derive(Debug)] |  | ||||||
| struct ParseXmasError; |  | ||||||
|  |  | ||||||
| impl FromStr for Xmas { |  | ||||||
|     type Err = ParseXmasError; |  | ||||||
|     fn from_str(s: &str) -> Result<Self, Self::Err> { |  | ||||||
|         let s = s.strip_prefix('{').ok_or(ParseXmasError)?; |  | ||||||
|         let s = s.strip_suffix('}').ok_or(ParseXmasError)?; |  | ||||||
|         let xmas: Vec<_> = s |  | ||||||
|             .split(',') |  | ||||||
|             .filter_map(|x| x.split_once('=')) |  | ||||||
|             .map(|x| x.1) |  | ||||||
|             .filter_map(|x| x.parse().ok()) |  | ||||||
|             .collect(); |  | ||||||
|  |  | ||||||
|         Ok(Self { |  | ||||||
|             x: xmas[0], |  | ||||||
|             m: xmas[1], |  | ||||||
|             a: xmas[2], |  | ||||||
|             s: xmas[3], |  | ||||||
|         }) |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| impl Index<Gear> for Xmas { |  | ||||||
|     type Output = usize; |  | ||||||
|     fn index(&self, index: Gear) -> &Self::Output { |  | ||||||
|         use Gear::*; |  | ||||||
|         match index { |  | ||||||
|             X => &self.x, |  | ||||||
|             M => &self.m, |  | ||||||
|             A => &self.a, |  | ||||||
|             S => &self.s, |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| impl IndexMut<Gear> for Xmas { |  | ||||||
|     fn index_mut(&mut self, index: Gear) -> &mut Self::Output { |  | ||||||
|         use Gear::*; |  | ||||||
|         match index { |  | ||||||
|             X => &mut self.x, |  | ||||||
|             M => &mut self.m, |  | ||||||
|             A => &mut self.a, |  | ||||||
|             S => &mut self.s, |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| impl Xmas { |  | ||||||
|     fn sum(&self) -> usize { |  | ||||||
|         self.x + self.m + self.a + self.s |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     fn apply(&self, workflow: &Workflow) -> Resolver { |  | ||||||
|         for w in workflow.workflows.iter() { |  | ||||||
|             match w { |  | ||||||
|                 WorkflowInner::Resolver(x) => { |  | ||||||
|                     return x.clone(); |  | ||||||
|                 } |  | ||||||
|                 WorkflowInner::Rule((g, c, n, r)) => { |  | ||||||
|                     let is_match = match c { |  | ||||||
|                         Comparator::Gt => self[*g] > *n, |  | ||||||
|                         Comparator::Lt => self[*g] < *n, |  | ||||||
|                     }; |  | ||||||
|                     if is_match { |  | ||||||
|                         return r.clone(); |  | ||||||
|                     } |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         unreachable!() |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| #[derive(Debug, Clone, Copy)] |  | ||||||
| struct XmasRange { |  | ||||||
|     x: (usize, usize), |  | ||||||
|     m: (usize, usize), |  | ||||||
|     a: (usize, usize), |  | ||||||
|     s: (usize, usize), |  | ||||||
| } |  | ||||||
|  |  | ||||||
| impl Index<Gear> for XmasRange { |  | ||||||
|     type Output = (usize, usize); |  | ||||||
|     fn index(&self, index: Gear) -> &Self::Output { |  | ||||||
|         use Gear::*; |  | ||||||
|         match index { |  | ||||||
|             X => &self.x, |  | ||||||
|             M => &self.m, |  | ||||||
|             A => &self.a, |  | ||||||
|             S => &self.s, |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| impl IndexMut<Gear> for XmasRange { |  | ||||||
|     fn index_mut(&mut self, index: Gear) -> &mut Self::Output { |  | ||||||
|         use Gear::*; |  | ||||||
|         match index { |  | ||||||
|             X => &mut self.x, |  | ||||||
|             M => &mut self.m, |  | ||||||
|             A => &mut self.a, |  | ||||||
|             S => &mut self.s, |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| impl XmasRange { |  | ||||||
|     fn size(&self) -> usize { |  | ||||||
|         (self.x.1 - self.x.0 + 1) |  | ||||||
|             * (self.m.1 - self.m.0 + 1) |  | ||||||
|             * (self.a.1 - self.a.0 + 1) |  | ||||||
|             * (self.s.1 - self.s.0 + 1) |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     fn divide(self, workflow: &Workflow) -> Vec<(Self, Resolver)> { |  | ||||||
|         let mut processed = Vec::new(); |  | ||||||
|         let mut rest = vec![self]; |  | ||||||
|  |  | ||||||
|         for w in workflow.workflows.iter() { |  | ||||||
|             let mut new_rest = Vec::new(); |  | ||||||
|  |  | ||||||
|             while let Some(xmas_range) = rest.pop() { |  | ||||||
|                 match w { |  | ||||||
|                     WorkflowInner::Resolver(r) => processed.push((xmas_range, r.clone())), |  | ||||||
|                     WorkflowInner::Rule((g, c, n, r)) => { |  | ||||||
|                         let compare = |x: usize, y: usize| match c { |  | ||||||
|                             Comparator::Gt => x > y, |  | ||||||
|                             Comparator::Lt => x < y, |  | ||||||
|                         }; |  | ||||||
|  |  | ||||||
|                         let mut min_ok = usize::MAX; |  | ||||||
|                         let mut max_ok = usize::MIN; |  | ||||||
|                         let mut min_e = usize::MAX; |  | ||||||
|                         let mut max_e = usize::MIN; |  | ||||||
|  |  | ||||||
|                         for i in xmas_range[*g].0..=xmas_range[*g].1 { |  | ||||||
|                             if compare(i, *n) { |  | ||||||
|                                 max_ok = if max_ok < i { i } else { max_ok }; |  | ||||||
|                                 min_ok = if min_ok > i { i } else { min_ok }; |  | ||||||
|                             } else { |  | ||||||
|                                 max_e = if max_e < i { i } else { max_e }; |  | ||||||
|                                 min_e = if min_e > i { i } else { min_e }; |  | ||||||
|                             } |  | ||||||
|                         } |  | ||||||
|  |  | ||||||
|                         if min_e <= max_e { |  | ||||||
|                             let mut r = xmas_range; |  | ||||||
|                             r[*g] = (min_e, max_e); |  | ||||||
|                             new_rest.push(r); |  | ||||||
|                         } |  | ||||||
|                         if min_ok <= max_ok { |  | ||||||
|                             let mut p = xmas_range; |  | ||||||
|                             p[*g] = (min_ok, max_ok); |  | ||||||
|                             processed.push((p, r.clone())); |  | ||||||
|                         } |  | ||||||
|                     } |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
|  |  | ||||||
|             rest = new_rest; |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         processed |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| fn build_map(input: &str) -> HashMap<&str, Workflow> { |  | ||||||
|     let mut map = HashMap::new(); |  | ||||||
|     for (s, w) in input |  | ||||||
|         .split("\n\n") |  | ||||||
|         .next() |  | ||||||
|         .unwrap() |  | ||||||
|         .lines() |  | ||||||
|         .filter_map(|x| x.strip_suffix('}')) |  | ||||||
|         .filter_map(|x| x.split_once('{')) |  | ||||||
|         .filter_map(|(s, w)| w.try_into().ok().map(|x| (s, x))) |  | ||||||
|     { |  | ||||||
|         map.insert(s, w); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     map |  | ||||||
| } |  | ||||||
|  |  | ||||||
| pub fn part_one(input: &str) -> Option<usize> { |  | ||||||
|     let map = build_map(input); |  | ||||||
|     let xmas_vec: Vec<Xmas> = input |  | ||||||
|         .split("\n\n") |  | ||||||
|         .nth(1)? |  | ||||||
|         .lines() |  | ||||||
|         .filter_map(|x| x.parse().ok()) |  | ||||||
|         .collect(); |  | ||||||
|  |  | ||||||
|     let mut total = 0; |  | ||||||
|  |  | ||||||
|     'outer: for xmas in xmas_vec.into_iter() { |  | ||||||
|         let mut workflow = &map["in"]; |  | ||||||
|         'apply: loop { |  | ||||||
|             match xmas.apply(workflow) { |  | ||||||
|                 Resolver::Accepted => break 'apply, |  | ||||||
|                 Resolver::Rejected => continue 'outer, |  | ||||||
|                 Resolver::Delegated(x) => workflow = &map[x.as_str()], |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         total += xmas.sum(); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     Some(total) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| pub fn part_two(input: &str) -> Option<usize> { |  | ||||||
|     let map = build_map(input); |  | ||||||
|  |  | ||||||
|     let mut finished = Vec::new(); |  | ||||||
|     let mut stack = vec![( |  | ||||||
|         XmasRange { |  | ||||||
|             x: (1, 4000), |  | ||||||
|             m: (1, 4000), |  | ||||||
|             a: (1, 4000), |  | ||||||
|             s: (1, 4000), |  | ||||||
|         }, |  | ||||||
|         Resolver::Delegated("in".to_string()), |  | ||||||
|     )]; |  | ||||||
|  |  | ||||||
|     while let Some((xmas_range, resolver)) = stack.pop() { |  | ||||||
|         match resolver { |  | ||||||
|             Resolver::Accepted => finished.push(xmas_range), |  | ||||||
|             Resolver::Rejected => { |  | ||||||
|                 continue; |  | ||||||
|             } |  | ||||||
|             Resolver::Delegated(x) => { |  | ||||||
|                 let workflow = &map[x.as_str()]; |  | ||||||
|                 stack.append(&mut xmas_range.divide(workflow)) |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     Some(finished.iter().map(XmasRange::size).sum()) |  | ||||||
| } |  | ||||||
| aoc::solution!(19); |  | ||||||
|  |  | ||||||
| #[cfg(test)] |  | ||||||
| mod tests { |  | ||||||
|     use super::*; |  | ||||||
|     #[test] |  | ||||||
|     fn test_part_one() { |  | ||||||
|         assert_eq!( |  | ||||||
|             part_one(&aoc::template::read_file("examples", 19)), |  | ||||||
|             Some(19114) |  | ||||||
|         ); |  | ||||||
|     } |  | ||||||
|     #[test] |  | ||||||
|     fn test_part_two() { |  | ||||||
|         assert_eq!( |  | ||||||
|             part_two(&aoc::template::read_file("examples", 19)), |  | ||||||
|             Some(167409079868000) |  | ||||||
|         ); |  | ||||||
|     } |  | ||||||
| } |  | ||||||
							
								
								
									
										187
									
								
								src/bin/20.rs
									
									
									
									
									
								
							
							
						
						
									
										187
									
								
								src/bin/20.rs
									
									
									
									
									
								
							| @@ -1,187 +0,0 @@ | |||||||
| use std::{ |  | ||||||
|     collections::{HashMap, VecDeque}, |  | ||||||
|     mem::swap, |  | ||||||
|     str::FromStr, |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] |  | ||||||
| enum Module { |  | ||||||
|     Broadcaster, |  | ||||||
|     FlipFlop(bool), |  | ||||||
|     Conjuction, |  | ||||||
| } |  | ||||||
|  |  | ||||||
| #[derive(Debug)] |  | ||||||
| struct ParseModuleError; |  | ||||||
|  |  | ||||||
| impl FromStr for Module { |  | ||||||
|     type Err = ParseModuleError; |  | ||||||
|     fn from_str(s: &str) -> Result<Self, Self::Err> { |  | ||||||
|         Ok(match s { |  | ||||||
|             "%" => Module::FlipFlop(false), |  | ||||||
|             "&" => Module::Conjuction, |  | ||||||
|             _ => Module::Broadcaster, |  | ||||||
|         }) |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| #[derive(Debug)] |  | ||||||
| struct Node { |  | ||||||
|     index: usize, |  | ||||||
|     module: Module, |  | ||||||
|     inputs: Vec<usize>, |  | ||||||
|     outputs: Vec<usize>, |  | ||||||
| } |  | ||||||
|  |  | ||||||
| fn parse_input(input: &str) -> Vec<Node> { |  | ||||||
|     let mut nodes = Vec::new(); |  | ||||||
|     let mut mapper: HashMap<String, usize> = HashMap::new(); |  | ||||||
|  |  | ||||||
|     for line in input.lines() { |  | ||||||
|         let (from, _) = line.split_once(" -> ").unwrap(); |  | ||||||
|         let (module, mut name) = from.split_at(1); |  | ||||||
|         let module: Module = module.parse().unwrap(); |  | ||||||
|         if module == Module::Broadcaster { |  | ||||||
|             name = from; |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         *mapper.entry(name.to_string()).or_default() = nodes.len(); |  | ||||||
|  |  | ||||||
|         let l = Node { |  | ||||||
|             module, |  | ||||||
|             index: nodes.len(), |  | ||||||
|             inputs: Vec::new(), |  | ||||||
|             outputs: Vec::new(), |  | ||||||
|         }; |  | ||||||
|  |  | ||||||
|         nodes.push(l); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     for line in input.lines() { |  | ||||||
|         let (from, to) = line.split_once(" -> ").unwrap(); |  | ||||||
|         let (module, mut name) = from.split_at(1); |  | ||||||
|         let module: Module = module.parse().unwrap(); |  | ||||||
|         if module == Module::Broadcaster { |  | ||||||
|             name = from; |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         let index = mapper[name]; |  | ||||||
|         for destination in to.split(", ") { |  | ||||||
|             let to_index = mapper[destination]; |  | ||||||
|             nodes[index].outputs.push(to_index); |  | ||||||
|             nodes[to_index].inputs.push(index); |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     nodes |  | ||||||
| } |  | ||||||
|  |  | ||||||
| pub fn part_one(input: &str) -> Option<usize> { |  | ||||||
|     let mut nodes = parse_input(input); |  | ||||||
|  |  | ||||||
|     let mut graph = vec![vec![None; nodes.len()]; nodes.len()]; |  | ||||||
|  |  | ||||||
|     let mut highs = 0; |  | ||||||
|     let mut lows = 0; |  | ||||||
|  |  | ||||||
|     let broadcaster = nodes |  | ||||||
|         .iter() |  | ||||||
|         .find(|x| x.module == Module::Broadcaster)? |  | ||||||
|         .index; |  | ||||||
|  |  | ||||||
|     for _ in 0..1000 { |  | ||||||
|         let mut stack = VecDeque::from([broadcaster]); |  | ||||||
|         while !stack.is_empty() { |  | ||||||
|             let mut new_stack = VecDeque::new(); |  | ||||||
|             let mut new_graph = graph.clone(); |  | ||||||
|  |  | ||||||
|             while let Some(index) = stack.pop_front() { |  | ||||||
|                 let node = &nodes[index]; |  | ||||||
|                 let mut new_module = None; |  | ||||||
|  |  | ||||||
|                 match node.module { |  | ||||||
|                     Module::Broadcaster => { |  | ||||||
|                         for dest_index in node.outputs.iter() { |  | ||||||
|                             new_graph[index][*dest_index] = Some(false); |  | ||||||
|                             new_stack.push_back(*dest_index); |  | ||||||
|                             lows += 1; |  | ||||||
|                         } |  | ||||||
|                     } |  | ||||||
|                     Module::FlipFlop(high) => { |  | ||||||
|                         let mut swapper = None; |  | ||||||
|                         for in_index in nodes[index].inputs.iter() { |  | ||||||
|                             let value = &mut graph[*in_index][index]; |  | ||||||
|                             if value.is_some() { |  | ||||||
|                                 debug_assert!(swapper.is_none()); |  | ||||||
|                                 swap(&mut swapper, value); |  | ||||||
|                             } |  | ||||||
|                         } |  | ||||||
|  |  | ||||||
|                         if !swapper.unwrap() { |  | ||||||
|                             let signal = !high; |  | ||||||
|  |  | ||||||
|                             for dest_index in nodes[index].outputs.iter() { |  | ||||||
|                                 new_graph[index][*dest_index] = Some(signal); |  | ||||||
|                                 new_stack.push_back(*dest_index); |  | ||||||
|                                 if signal { |  | ||||||
|                                     highs += 1; |  | ||||||
|                                 } else { |  | ||||||
|                                     lows += 1; |  | ||||||
|                                 } |  | ||||||
|                             } |  | ||||||
|  |  | ||||||
|                             new_module = Some(Module::FlipFlop(signal)); |  | ||||||
|                         } |  | ||||||
|                     } |  | ||||||
|                     Module::Conjuction => { |  | ||||||
|                         let mut all = true; |  | ||||||
|  |  | ||||||
|                         for in_index in nodes[index].inputs.iter() { |  | ||||||
|                             let value = graph[*in_index][index]; |  | ||||||
|                             all &= value.unwrap_or(false); |  | ||||||
|                         } |  | ||||||
|  |  | ||||||
|                         for dest_index in nodes[index].outputs.iter() { |  | ||||||
|                             new_graph[index][*dest_index] = Some(!all); |  | ||||||
|                             new_stack.push_back(*dest_index); |  | ||||||
|                             if !all { |  | ||||||
|                                 highs += 1; |  | ||||||
|                             } else { |  | ||||||
|                                 lows += 1; |  | ||||||
|                             } |  | ||||||
|                         } |  | ||||||
|                     } |  | ||||||
|                 } |  | ||||||
|  |  | ||||||
|                 let node = &mut nodes[index]; |  | ||||||
|                 if let Some(module) = new_module { |  | ||||||
|                     node.module = module; |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
|  |  | ||||||
|             stack = new_stack; |  | ||||||
|             graph = new_graph; |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     Some(highs * lows) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| pub fn part_two(input: &str) -> Option<u32> { |  | ||||||
|     None |  | ||||||
| } |  | ||||||
|  |  | ||||||
| aoc::solution!(20); |  | ||||||
|  |  | ||||||
| #[cfg(test)] |  | ||||||
| mod tests { |  | ||||||
|     use super::*; |  | ||||||
|     #[test] |  | ||||||
|     fn test_part_one() { |  | ||||||
|         assert_eq!(part_one(&aoc::template::read_file("examples", 20)), None); |  | ||||||
|     } |  | ||||||
|     #[test] |  | ||||||
|     fn test_part_two() { |  | ||||||
|         assert_eq!(part_two(&aoc::template::read_file("examples", 20)), None); |  | ||||||
|     } |  | ||||||
| } |  | ||||||
							
								
								
									
										24
									
								
								src/lib.rs
									
									
									
									
									
								
							
							
						
						
									
										24
									
								
								src/lib.rs
									
									
									
									
									
								
							| @@ -2,27 +2,3 @@ | |||||||
|  |  | ||||||
| pub mod parsers; | pub mod parsers; | ||||||
| pub mod template; | pub mod template; | ||||||
|  |  | ||||||
| use std::mem::swap; |  | ||||||
|  |  | ||||||
| pub fn lcm(first: usize, second: usize) -> usize { |  | ||||||
|     first * second / gcd(first, second) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| pub fn gcd(first: usize, second: usize) -> usize { |  | ||||||
|     let mut max = first; |  | ||||||
|     let mut min = second; |  | ||||||
|     if min > max { |  | ||||||
|         swap(&mut min, &mut max) |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     loop { |  | ||||||
|         let res = max % min; |  | ||||||
|         if res == 0 { |  | ||||||
|             return min; |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         max = min; |  | ||||||
|         min = res; |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|   | |||||||
| @@ -54,14 +54,3 @@ pub fn read_file(folder: &str, day: u8) -> String { | |||||||
|     let f = fs::read_to_string(filepath); |     let f = fs::read_to_string(filepath); | ||||||
|     f.expect("could not open input file").trim().to_string() |     f.expect("could not open input file").trim().to_string() | ||||||
| } | } | ||||||
|  |  | ||||||
| #[must_use] |  | ||||||
| pub fn read_file_part(folder: &str, day: u8, part: u8) -> String { |  | ||||||
|     let cwd = env::current_dir().unwrap(); |  | ||||||
|     let filepath = cwd |  | ||||||
|         .join("data") |  | ||||||
|         .join(folder) |  | ||||||
|         .join(format!("{day:02}-{part}.txt")); |  | ||||||
|     let f = fs::read_to_string(filepath); |  | ||||||
|     f.expect("could not open input file").trim().to_string() |  | ||||||
| } |  | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user