Advent of Code solutions
at main 176 lines 5.1 kB view raw
1use advent_core::{day_stuff, ex_for_day, Day}; 2use rayon::iter::{IntoParallelIterator, ParallelIterator}; 3use utils::{grid::Grid, pos::Position, tiles, upos, yippee}; 4 5pub struct Day12; 6 7tiles!(Tile, [ 8 '#' => Filled, 9 '.' => Empty, 10]); 11 12type Present = utils::grid::Grid<Tile>; 13type Input = (Vec<[Vec<Position>; 4]>, Vec<((usize, usize), Vec<usize>)>); 14 15pub const KERNS: [Position; 9] = [ 16 Position::new(0, 0), 17 Position::new(0, -1), 18 Position::new(0, 1), 19 Position::new(1, 0), 20 Position::new(-1, 0), 21 Position::new(1, 1), 22 Position::new(1, -1), 23 Position::new(-1, 1), 24 Position::new(-1, -1), 25]; 26 27fn rot_pos(Position { x, y }: Position) -> Position { 28 let (x, y) = match (x, y) { 29 (0, 0) => (0, 0), 30 (1, 0) => (0, -1), 31 (0, -1) => (-1, 0), 32 (-1, 0) => (0, 1), 33 (0, 1) => (1, 0), 34 (1, 1) => (1, -1), 35 (1, -1) => (-1, -1), 36 (-1, -1) => (-1, 1), 37 (-1, 1) => (1, 1), 38 _ => unreachable!(), 39 }; 40 Position::new(x, y) 41} 42 43fn try_place_at(pos: Position, shape: &[Position], grid: &mut Present) -> bool { 44 let empties = grid 45 .relatives(pos, shape) 46 .take_while(|(_, _, t)| **t == Tile::Empty) 47 .map(|(_, p, _)| p) 48 .collect::<Vec<_>>(); 49 50 if empties.len() == shape.len() { 51 for pos in empties { 52 *(grid.get_mut(pos).unwrap()) = Tile::Filled; 53 } 54 true 55 } else { 56 false 57 } 58} 59 60fn undo_place(pos: Position, shape: &[Position], grid: &mut Present) { 61 let positions = grid 62 .relatives(pos, shape) 63 .map(|(_, p, _)| p) 64 .collect::<Vec<_>>(); 65 66 for pos in positions { 67 *(grid.get_mut(pos).unwrap()) = Tile::Empty; 68 } 69} 70 71fn solve(shapes: &Vec<[Vec<Position>; 4]>, grid: &mut Present, targets: &mut Vec<usize>) -> bool { 72 let next_shape = targets 73 .iter() 74 .enumerate() 75 .find(|(_, amnt)| **amnt != 0) 76 .map(|(f, _)| f); 77 78 let (x, y) = grid.size(); 79 80 if let Some(next_shape) = next_shape { 81 let shape_all_rots = shapes.get(next_shape).unwrap(); 82 for y in 1..(y - 1) { 83 for x in 1..(x - 1) { 84 for shape in shape_all_rots { 85 if try_place_at(upos!(x, y), shape.as_slice(), grid) { 86 targets[next_shape] -= 1; 87 let inner_res = solve(shapes, grid, targets); 88 if inner_res { 89 return inner_res; 90 } else { 91 targets[next_shape] += 1; 92 undo_place(upos!(x, y), shape.as_slice(), grid); 93 } 94 } 95 } 96 } 97 } 98 false 99 } else { 100 true 101 } 102} 103 104impl Day for Day12 { 105 day_stuff!(12, "2", "🥳", Input); 106 107 fn part_1((shapes, targets): Self::Input) -> Option<String> { 108 let ans = targets 109 .into_par_iter() 110 .filter_map(|((x, y), mut avail)| { 111 let area_needed = shapes 112 .iter() 113 .enumerate() 114 .map(|(i, s)| s[0].len() * avail[i]) 115 .sum::<usize>(); 116 if area_needed > (x * y) { 117 return None; 118 } 119 let mut grid = Grid::new(vec![vec![Tile::Empty; x]; y]); 120 let can_fit = solve(&shapes, &mut grid, &mut avail); 121 if can_fit { 122 Some(0) 123 } else { 124 None 125 } 126 }) 127 .count(); 128 129 Some(ans.to_string()) 130 } 131 132 yippee!(); 133 134 fn parse_input(input: &str) -> Self::Input { 135 let sections = input.split("\n\n").collect::<Vec<_>>(); 136 let shapes = sections 137 .iter() 138 .take(sections.len() - 1) 139 .map(|s| { 140 let (_, grid) = s.split_once('\n').unwrap(); 141 let grid = Present::parse(grid); 142 let rels = grid 143 .relatives(Position::new(1, 1), &KERNS) 144 .filter(|(_, _, t)| **t == Tile::Filled) 145 .map(|(kern, _, _)| kern) 146 .collect::<Vec<_>>(); 147 148 let rels90: Vec<_> = rels.iter().copied().map(rot_pos).collect(); 149 150 let rels180: Vec<_> = rels90.iter().copied().map(rot_pos).collect(); 151 152 let rels270: Vec<_> = rels180.iter().copied().map(rot_pos).collect(); 153 154 [rels, rels90, rels180, rels270] 155 }) 156 .collect(); 157 158 let targets = sections 159 .last() 160 .unwrap() 161 .lines() 162 .map(|l| { 163 let (size, amnts) = l.split_once(':').unwrap(); 164 let (x, y) = size.split_once('x').unwrap(); 165 let amnts = amnts 166 .trim() 167 .split(' ') 168 .map(|x| x.parse().unwrap()) 169 .collect(); 170 ((x.parse().unwrap(), y.parse().unwrap()), amnts) 171 }) 172 .collect(); 173 174 (shapes, targets) 175 } 176}