day12: cleanup and speedup
This commit is contained in:
212
src/day12.rs
212
src/day12.rs
@@ -1,101 +1,64 @@
|
|||||||
use std::iter::repeat_n;
|
|
||||||
use std::{collections::BinaryHeap, f64::consts, fmt::Display};
|
|
||||||
|
|
||||||
use aoc_runner_derive::{aoc, aoc_generator};
|
use aoc_runner_derive::{aoc, aoc_generator};
|
||||||
use grid::{Coord2d, Grid, MirrorAxis};
|
use grid::{Coord2d, Grid, MirrorAxis};
|
||||||
use itertools::{Format, Itertools};
|
use itertools::Itertools;
|
||||||
|
use std::collections::HashSet;
|
||||||
|
use std::fmt::Display;
|
||||||
|
use std::iter::repeat_n;
|
||||||
|
|
||||||
|
type CellType = bool;
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
struct PresentShape {
|
struct PresentShape {
|
||||||
idx: u8,
|
variants: Vec<Grid<CellType>>,
|
||||||
shape: Grid<u8>,
|
|
||||||
variants: Vec<Grid<u8>>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<&str> for PresentShape {
|
impl From<&str> for PresentShape {
|
||||||
fn from(value: &str) -> Self {
|
fn from(value: &str) -> Self {
|
||||||
let (idx, rest) = value.split_once(":\n").unwrap();
|
let (_idx, rest) = value.split_once(":\n").unwrap();
|
||||||
|
|
||||||
let shape: Grid<u8> = rest.parse().unwrap();
|
let shape_raw: Grid<u8> = rest.parse().unwrap();
|
||||||
|
let mut shape: Grid<CellType> = shape_raw.same_shape(false);
|
||||||
|
for pos in shape_raw.find_all(&b'#') {
|
||||||
|
shape.set(&pos, true);
|
||||||
|
}
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
idx: idx.parse().unwrap(),
|
|
||||||
variants: (0..4)
|
variants: (0..4)
|
||||||
.map(|rot| shape.rotated(rot))
|
.map(|rot| shape.rotated(rot))
|
||||||
.chain([MirrorAxis::X, MirrorAxis::Y].map(|axis| shape.mirrored(axis)))
|
.chain([MirrorAxis::X, MirrorAxis::Y].map(|axis| shape.mirrored(axis)))
|
||||||
.unique()
|
.unique()
|
||||||
.collect(),
|
.collect(),
|
||||||
shape,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Display for PresentShape {
|
impl Display for PresentShape {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
f.write_fmt(format_args!("{}:\n", self.idx))?;
|
f.write_fmt(format_args!("{}", self.variants[0]))
|
||||||
f.write_fmt(format_args!("{}", self.shape))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PresentShape {
|
impl PresentShape {
|
||||||
fn make(&self) -> Present {
|
|
||||||
Present {
|
|
||||||
shape: self.clone(),
|
|
||||||
location: None,
|
|
||||||
variant: 0,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fn popcount(&self) -> u64 {
|
fn popcount(&self) -> u64 {
|
||||||
self.shape.data.iter().filter(|c| **c != b'.').count() as u64
|
self.variants[0].count(&true) as u64
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
struct Present {
|
struct Present {
|
||||||
shape: PresentShape,
|
shape: usize,
|
||||||
location: Option<Coord2d>,
|
|
||||||
variant: usize,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
struct ChristmasTree {
|
struct ChristmasTree {
|
||||||
area: Grid<u8>,
|
area: Grid<CellType>,
|
||||||
presents: Vec<Present>,
|
presents: Vec<Present>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ChristmasTree {}
|
|
||||||
|
|
||||||
impl Display for ChristmasTree {
|
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
||||||
f.write_fmt(format_args!("{}", self.area))?;
|
|
||||||
for (i, p) in self.presents.iter().enumerate() {
|
|
||||||
f.write_fmt(format_args!(" {i}: @{:?} rot {}\n", p.location, p.variant))?;
|
|
||||||
f.write_fmt(format_args!("{}", p.shape.variants[p.variant]))?;
|
|
||||||
}
|
|
||||||
std::fmt::Result::Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
struct PresentsProblem {
|
struct PresentsProblem {
|
||||||
shapes: Vec<PresentShape>,
|
|
||||||
trees: Vec<ChristmasTree>,
|
trees: Vec<ChristmasTree>,
|
||||||
}
|
shapes: Vec<PresentShape>,
|
||||||
|
|
||||||
impl PresentsProblem {
|
|
||||||
fn present(&self, idx: usize) -> Present {
|
|
||||||
Present {
|
|
||||||
shape: self.shapes[idx].clone(),
|
|
||||||
location: None,
|
|
||||||
variant: 0,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fn add_tree(&mut self, w: usize, h: usize, presents: Vec<Present>) {
|
|
||||||
self.trees.push(ChristmasTree {
|
|
||||||
area: Grid::with_shape(w, h, b'.'),
|
|
||||||
presents,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[aoc_generator(day12)]
|
#[aoc_generator(day12)]
|
||||||
@@ -116,88 +79,113 @@ fn parse(input: &str) -> PresentsProblem {
|
|||||||
let presents = present_counts
|
let presents = present_counts
|
||||||
.iter()
|
.iter()
|
||||||
.enumerate()
|
.enumerate()
|
||||||
.flat_map(|(i, count)| repeat_n(shapes[i].make(), *count))
|
.flat_map(|(i, count)| repeat_n(Present { shape: i }, *count))
|
||||||
.collect();
|
.collect();
|
||||||
trees.push(ChristmasTree {
|
trees.push(ChristmasTree {
|
||||||
area: Grid::with_shape(w.parse().unwrap(), h.parse().unwrap(), b'.'),
|
area: Grid::with_shape(w.parse().unwrap(), h.parse().unwrap(), false),
|
||||||
presents,
|
presents,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
PresentsProblem { shapes, trees }
|
PresentsProblem { trees, shapes }
|
||||||
}
|
}
|
||||||
|
|
||||||
// place b on a and test if any # overlap any non-.
|
// place b on a and test if any # overlap any non-.
|
||||||
fn overlaps(a: &Grid<u8>, b: &Grid<u8>, p: &Coord2d) -> bool {
|
// fn overlaps(a: &Grid<CellType>, b: &Grid<CellType>, p: &Coord2d) -> bool {
|
||||||
b.find_all(&b'#')
|
// b.find_all(&true)
|
||||||
.any(|c| a.get(&(c + p)).is_some_and(|c| *c != b'.'))
|
// .any(|c| a.get(&(c + p)).is_some_and(|c| *c))
|
||||||
}
|
// }
|
||||||
|
|
||||||
fn place_shape(
|
impl PresentsProblem {
|
||||||
mut t: ChristmasTree,
|
fn place_shape(
|
||||||
idx: usize,
|
&self,
|
||||||
pos: Coord2d,
|
mut t: ChristmasTree,
|
||||||
variant: usize,
|
idx: usize,
|
||||||
) -> Option<ChristmasTree> {
|
pos: Coord2d,
|
||||||
let variant = &t.presents[idx].shape.variants[variant];
|
variant: usize,
|
||||||
|
) -> Option<ChristmasTree> {
|
||||||
|
let variant = &self.shapes[t.presents[idx].shape].variants[variant];
|
||||||
|
|
||||||
if !overlaps(&t.area, variant, &pos) {
|
for xy in variant.find_all(&true) {
|
||||||
for xy in variant.find_all(&b'#') {
|
if let Some(was) = t.area.set(&(pos + &(xy)), true)
|
||||||
t.area.set(&(pos + &(xy)), b'0' + idx as u8).unwrap();
|
&& was
|
||||||
|
{
|
||||||
|
// overlapped
|
||||||
|
return None;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Some(t)
|
Some(t)
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
fn place_next(mut t: ChristmasTree, cur: usize) -> Option<ChristmasTree> {
|
fn place_next(
|
||||||
if cur == t.presents.len() {
|
&self,
|
||||||
return Some(t);
|
t: ChristmasTree,
|
||||||
}
|
cur: usize,
|
||||||
let variants = &t.presents[cur].shape.variants;
|
cache: &mut HashSet<(usize, Grid<CellType>)>, // impossible shapes
|
||||||
'variant: for (i, _v) in variants.iter().enumerate() {
|
) -> Option<ChristmasTree> {
|
||||||
//make sure the longest 'bar' can fit
|
if cur == t.presents.len() {
|
||||||
for y in 0..&t.area.height() - 2 {
|
// done
|
||||||
for x in 0..&t.area.width() - 2 {
|
return Some(t);
|
||||||
if let Some(new_t) = place_shape(
|
}
|
||||||
t.clone(),
|
if cache.contains(&(cur, t.area.clone())) {
|
||||||
cur,
|
// doomed
|
||||||
Coord2d {
|
return None;
|
||||||
x: x as i64,
|
}
|
||||||
y: y as i64,
|
let variants = &self.shapes[t.presents[cur].shape].variants;
|
||||||
},
|
for (i, _v) in variants.iter().enumerate() {
|
||||||
i,
|
for y in 0..&t.area.height() - 2 {
|
||||||
) {
|
for x in 0..&t.area.width() - 2 {
|
||||||
if let Some(solution) = place_next(new_t, cur + 1) {
|
if let Some(new_t) = self.place_shape(
|
||||||
return Some(solution);
|
t.clone(),
|
||||||
|
cur,
|
||||||
|
Coord2d {
|
||||||
|
x: x as i64,
|
||||||
|
y: y as i64,
|
||||||
|
},
|
||||||
|
i,
|
||||||
|
) {
|
||||||
|
if let Some(solution) = self.place_next(new_t, cur + 1, cache) {
|
||||||
|
return Some(solution);
|
||||||
|
} else {
|
||||||
|
cache.insert((cur, t.area.clone()));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
None
|
||||||
}
|
}
|
||||||
None
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[aoc(day12, part1)]
|
#[aoc(day12, part1)]
|
||||||
fn part1(input: &PresentsProblem) -> u64 {
|
fn part1(input: &PresentsProblem) -> u64 {
|
||||||
let input = input.clone();
|
let input = input.clone();
|
||||||
let mut count = 0;
|
let mut count = 0;
|
||||||
// for t in input.trees[0..2].iter() {
|
|
||||||
// println!("trying:\n{t}");
|
|
||||||
// if let Some(r) = place_next(t.clone(), 0) {
|
|
||||||
// count += 1;
|
|
||||||
// println!("ok!\n{r}");
|
|
||||||
// } else {
|
|
||||||
// println!("failed :(");
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
for t in input.trees.iter() {
|
for t in input.trees.iter() {
|
||||||
let area = (t.area.width() * t.area.height()) as u64;
|
let area = (t.area.width() * t.area.height()) as u64;
|
||||||
let occupied_area = t.presents.iter().map(|p| p.shape.popcount()).sum::<u64>();
|
let (occupied_area, present_count) = t
|
||||||
if occupied_area <= area {
|
.presents
|
||||||
count += 1
|
.iter()
|
||||||
|
.map(|p| (input.shapes[p.shape].popcount(), 1))
|
||||||
|
.reduce(|(o_a, pc_a), (o_b, pc_b)| (o_a + o_b, pc_a + pc_b))
|
||||||
|
.unwrap();
|
||||||
|
if occupied_area > area {
|
||||||
|
// definitely impossible
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
let available_3x3s = (t.area.width() / 3) * (t.area.height() / 3);
|
||||||
|
if available_3x3s >= present_count {
|
||||||
|
// definitely_possible
|
||||||
|
count += 1;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if input
|
||||||
|
.place_next(t.clone(), 0, &mut HashSet::new())
|
||||||
|
.is_some()
|
||||||
|
{
|
||||||
|
count += 1;
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -205,7 +193,7 @@ fn part1(input: &PresentsProblem) -> u64 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[aoc(day12, part2)]
|
#[aoc(day12, part2)]
|
||||||
fn part2(input: &PresentsProblem) -> u64 {
|
fn part2(_input: &PresentsProblem) -> u64 {
|
||||||
0
|
0
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -251,12 +239,12 @@ mod tests {
|
|||||||
#[rstest]
|
#[rstest]
|
||||||
#[case(EXAMPLE, 0)]
|
#[case(EXAMPLE, 0)]
|
||||||
fn part1_example(#[case] input: &str, #[case] expected: u64) {
|
fn part1_example(#[case] input: &str, #[case] expected: u64) {
|
||||||
assert_eq!(part1(&parse(EXAMPLE)), 2);
|
assert_eq!(part1(&parse(input)), expected);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[rstest]
|
#[rstest]
|
||||||
#[case(EXAMPLE, 0)]
|
#[case(EXAMPLE, 0)]
|
||||||
fn part2_example(#[case] input: &str, #[case] expected: u64) {
|
fn part2_example(#[case] input: &str, #[case] expected: u64) {
|
||||||
assert_eq!(part2(&parse(EXAMPLE)), 0);
|
assert_eq!(part2(&parse(input)), expected);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user