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::{ | ||||
|     cmp::{max, min}, | ||||
|     fmt::Debug, | ||||
|     ops::RangeInclusive, | ||||
|     ops::Range, | ||||
|     str::FromStr, | ||||
| }; | ||||
|  | ||||
| use aoc::parsers::to_vec; | ||||
|  | ||||
| trait RangeInclusiveExt { | ||||
| trait RangeExt { | ||||
|     fn overlaps(&self, other: &Self) -> bool; | ||||
| } | ||||
|  | ||||
| impl<T> RangeInclusiveExt for RangeInclusive<T> | ||||
| impl<T> RangeExt for Range<T> | ||||
| where | ||||
|     T: PartialOrd, | ||||
| { | ||||
|     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> { | ||||
|     start..=(start + range - 1) | ||||
| fn build_range(start: u64, range: u64) -> Range<u64> { | ||||
|     start..(start + range) | ||||
| } | ||||
|  | ||||
| #[derive(Debug)] | ||||
| struct Mapping { | ||||
|     pub source: RangeInclusive<u64>, | ||||
|     pub destination: RangeInclusive<u64>, | ||||
|     destination: Range<u64>, | ||||
|     source: Range<u64>, | ||||
| } | ||||
|  | ||||
| struct ParseMappingError; | ||||
| @@ -38,43 +37,41 @@ impl FromStr for Mapping { | ||||
|         let nums: Vec<u64> = to_vec(s, ' '); | ||||
|  | ||||
|         Ok(Self { | ||||
|             destination: build_range(nums[0], nums[2]), | ||||
|             source: build_range(nums[1], nums[2]), | ||||
|             destination: build_range(nums[2], nums[0]), | ||||
|             source: build_range(nums[1], nums[0]), | ||||
|         }) | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl Mapping { | ||||
|     fn map(&self, n: u64) -> u64 { | ||||
|         let shift = n - self.source.start(); | ||||
|         self.destination.start() + shift | ||||
|         let shift = n - self.source.start; | ||||
|         self.destination.start + shift | ||||
|     } | ||||
|  | ||||
|     fn map_range(&self, r: RangeInclusive<u64>) -> RangeInclusive<u64> { | ||||
|         self.map(*r.start())..=(self.map(*r.end())) | ||||
|     } | ||||
|  | ||||
|     fn split_range(&self, r: RangeInclusive<u64>) -> [Option<RangeInclusive<u64>>; 3] { | ||||
|     fn split_range(&self, r: Range<u64>) -> [Option<Range<u64>>; 3] { | ||||
|         let mut fences = [ | ||||
|             *r.start(), | ||||
|             *r.end() + 1, | ||||
|             max(*self.source.start(), *r.start()), | ||||
|             min(*self.source.end() + 1, *r.end() + 1), | ||||
|             r.start, | ||||
|             r.end, | ||||
|             max(self.source.start, r.start), | ||||
|             min(self.source.end, r.end), | ||||
|         ]; | ||||
|  | ||||
|         fences.sort(); | ||||
|  | ||||
|         const ARRAY_REPEAT_VALUE: Option<RangeInclusive<u64>> = None; | ||||
|         let mut v = [ARRAY_REPEAT_VALUE; 3]; | ||||
|         let mut v = Vec::new(); | ||||
|  | ||||
|         for i in 0..3 { | ||||
|             let f = fences[i]; | ||||
|             let nf = fences[i + 1]; | ||||
|             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; | ||||
|         'maps: for map in maps.iter() { | ||||
|             for inner in map.iter() { | ||||
|                 if inner.source.contains(&s) { | ||||
|                 if inner.contains(s) { | ||||
|                     s = inner.map(s); | ||||
|                     continue 'maps; | ||||
|                 } | ||||
| @@ -121,19 +118,22 @@ pub fn part_one(input: &str) -> Option<u64> { | ||||
| pub fn part_two(input: &str) -> Option<u64> { | ||||
|     let (first, rest) = input.split_once("\n\n").unwrap(); | ||||
|     let maps = parse_map(rest); | ||||
|     let mut seeds_ranges: Vec<_> = to_vec::<u64, _>(first.strip_prefix("seeds: ").unwrap(), ' ') | ||||
|         .chunks(2) | ||||
|         .map(|x| build_range(x[0], x[1])) | ||||
|         .collect(); | ||||
|     let seeds = to_vec::<u64, _>(first.strip_prefix("seeds: ").unwrap(), ' '); | ||||
|     let mut seeds_ranges: Vec<_> = Vec::new(); | ||||
|     for s in seeds.chunks(2) { | ||||
|         seeds_ranges.push((s[0], s[1])) | ||||
|     } | ||||
|  | ||||
|     for mapping in maps.iter() { | ||||
|         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 { | ||||
|                 if map.source.overlaps(&rng) { | ||||
|                     let [pre, to_map, post] = map.split_range(rng); | ||||
|                     new_ranges.push(map.map_range(to_map.unwrap())); | ||||
|                 if map.contains_any((s, r)) { | ||||
|                     let [pre, to_map, post] = map.split_range((s, r)); | ||||
|                     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() { | ||||
|                         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 | ||||
|         .iter() | ||||
|         .map(RangeInclusive::start) | ||||
|         .min() | ||||
|         .copied() | ||||
|     seeds_ranges.iter().map(|(x, _)| *x).min() | ||||
| } | ||||
|  | ||||
| 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 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); | ||||
|     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