Day 12
This commit is contained in:
1030
src/day12/act.txt
Normal file
1030
src/day12/act.txt
Normal file
File diff suppressed because it is too large
Load Diff
448
src/day12/main.rs
Normal file
448
src/day12/main.rs
Normal file
@@ -0,0 +1,448 @@
|
||||
use anyhow::Result;
|
||||
use std::{
|
||||
collections::{HashMap, HashSet},
|
||||
fmt::Debug,
|
||||
fs,
|
||||
};
|
||||
|
||||
#[derive(Debug)]
|
||||
struct Area {
|
||||
width: u64,
|
||||
height: u64,
|
||||
reqs: Vec<usize>,
|
||||
hash: HashMap<usize, usize>,
|
||||
}
|
||||
|
||||
impl Into<Area> for &str {
|
||||
fn into(self) -> Area {
|
||||
let mut niter = self.split(":");
|
||||
let mut positer = niter.next().unwrap().split("x");
|
||||
let width: u64 = positer.next().unwrap().parse().unwrap();
|
||||
let height: u64 = positer.next().unwrap().parse().unwrap();
|
||||
let (reqs, hash) = niter.next().unwrap().trim().split(" ").enumerate().fold(
|
||||
(Vec::new(), HashMap::new()),
|
||||
|(mut v, mut h), (i, to_num)| {
|
||||
h.insert(i, to_num.parse().unwrap());
|
||||
for _ in 0..to_num.parse().unwrap() {
|
||||
v.push(i);
|
||||
}
|
||||
(v, h)
|
||||
},
|
||||
);
|
||||
Area {
|
||||
width,
|
||||
height,
|
||||
reqs,
|
||||
hash,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn try_all_vars(
|
||||
area: &Area,
|
||||
shapes: &HashMap<usize, Vec<StrictShape>>,
|
||||
list_of_reqs: Vec<usize>,
|
||||
cur_shape: StrictShape,
|
||||
) -> bool {
|
||||
if list_of_reqs.len() == 0 {
|
||||
return true;
|
||||
}
|
||||
let mut reqs = list_of_reqs.clone().into_iter();
|
||||
let cur_idx = reqs.next().unwrap();
|
||||
let reqs: Vec<usize> = reqs.collect();
|
||||
|
||||
// TODO check if the current shape fits in the box
|
||||
|
||||
// We don't have to loop because if we can not fit the first
|
||||
// we will never be able to fit any way
|
||||
for s in shapes.get(&cur_idx).unwrap().iter() {
|
||||
let possibles = cur_shape.try_to_attach_all(area, &s);
|
||||
for p in possibles {
|
||||
if try_all_vars(area, shapes, reqs.clone(), p) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
impl Area {
|
||||
fn valid(&self, shapes: &HashMap<usize, Vec<StrictShape>>) -> bool {
|
||||
let mut reqs = self.reqs.clone().into_iter();
|
||||
let cur_idx = reqs.next().unwrap();
|
||||
let reqs: Vec<usize> = reqs.collect();
|
||||
|
||||
// TODO check if the current shape fits in the box
|
||||
|
||||
// We don't have to loop because if we can not fit the first
|
||||
// we will never be able to fit any way
|
||||
// also we only need to test one here and this one "sets" the orientaion of the world
|
||||
if try_all_vars(
|
||||
self,
|
||||
shapes,
|
||||
reqs.clone(),
|
||||
shapes.get(&cur_idx).unwrap().iter().next().unwrap().clone(),
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Eq, PartialEq, Clone, Hash)]
|
||||
struct Shape {
|
||||
lines: Vec<Vec<bool>>,
|
||||
}
|
||||
|
||||
#[derive(Eq, PartialEq, Clone, Hash)]
|
||||
// It can not rotate or flip but it's faster for comparions
|
||||
struct StrictShape {
|
||||
lines: Vec<u64>,
|
||||
width: i64,
|
||||
}
|
||||
|
||||
const ALL_ONES: u64 = 0b1111111111111111111111111111111111111111111111111111111111111111;
|
||||
|
||||
impl StrictShape {
|
||||
fn get_attach_poins(&self) -> Vec<(i64, i64)> {
|
||||
let mut attach_points = Vec::new();
|
||||
for (y, line) in self.lines.iter().enumerate() {
|
||||
let mut line = *line ^ ALL_ONES;
|
||||
while line != 0 {
|
||||
let i = line.trailing_zeros();
|
||||
line ^= 1 << i;
|
||||
if 64 - i < self.width as u32 {
|
||||
attach_points.push((y as i64, 64 - i as i64));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
attach_points
|
||||
}
|
||||
|
||||
fn get_size(&self) -> (i64, i64) {
|
||||
(self.lines.len() as i64, self.width)
|
||||
}
|
||||
|
||||
fn try_to_attach(&self, to_attach: &StrictShape, y: i64, x: i64) -> Option<StrictShape> {
|
||||
// Calculate the matrix
|
||||
let (s_h, s_w) = self.get_size();
|
||||
let (a_h, a_w) = to_attach.get_size();
|
||||
let height = if y < 0 {
|
||||
(y.abs() + s_h).max(a_h + y)
|
||||
} else {
|
||||
s_h.max(a_h + y)
|
||||
};
|
||||
let width = if x < 0 {
|
||||
(x.abs() + s_w).max(a_w + x)
|
||||
} else {
|
||||
s_w.max(a_w + x)
|
||||
};
|
||||
|
||||
let mut lines = Vec::new();
|
||||
// i_<> is the offset into the to_attach shape it can go out of bounds on the positive side
|
||||
// o_<> is the offset into the self shape it can go out of bound both sides
|
||||
for i_y in 0..height {
|
||||
let self_y = i_y + y.min(0);
|
||||
let to_y = i_y - y.max(0);
|
||||
|
||||
if (self_y < 0 || self_y >= s_h) && (to_y < 0 || to_y >= a_h) {
|
||||
lines.push(0);
|
||||
} else if self_y < 0 || self_y >= s_h {
|
||||
lines.push(to_attach.lines[to_y as usize] >> x.max(0));
|
||||
} else if to_y < 0 || to_y >= a_h {
|
||||
lines.push(self.lines[self_y as usize] >> x.min(0).abs());
|
||||
} else {
|
||||
let a_line = to_attach.lines[to_y as usize] >> x.max(0);
|
||||
let self_line = self.lines[self_y as usize] >> x.min(0).abs();
|
||||
// There is an itersection
|
||||
if a_line & self_line > 0 {
|
||||
return None;
|
||||
}
|
||||
lines.push(a_line | self_line);
|
||||
}
|
||||
}
|
||||
|
||||
Some(StrictShape { lines, width })
|
||||
}
|
||||
|
||||
fn try_to_attach_all(&self, area: &Area, to_attach: &StrictShape) -> Vec<StrictShape> {
|
||||
// println!(
|
||||
// "Trying to attach {:?} \n{:?}\n with \n{:?}\n",
|
||||
// area, self, to_attach
|
||||
// );
|
||||
let (s_y, s_x) = self.get_size();
|
||||
let (a_y, a_x) = to_attach.get_size();
|
||||
|
||||
let min_x = (-(a_x as i64)).max(-(area.width as i64 - s_x));
|
||||
let max_x = (s_x + a_x).min(area.width as i64 - a_x as i64);
|
||||
|
||||
let min_y = (-(a_y as i64)).max(-(area.height as i64 - s_y));
|
||||
let max_y = (s_y + a_y).min(area.height as i64 - a_y as i64);
|
||||
|
||||
if min_x > max_x || min_y > max_y {
|
||||
//println!("Could never attach");
|
||||
return Vec::new();
|
||||
}
|
||||
|
||||
// println!("found bounds {} {} -> {} {}", min_y, min_x, max_y, max_x);
|
||||
// Try edges
|
||||
let mut new_possible_shapes = Vec::new();
|
||||
for y in min_y..0 {
|
||||
for x in min_x..=max_x {
|
||||
if let Some(merged) = self.try_to_attach(to_attach, y, x) {
|
||||
// println!("Found shape {y} {x} {merged:?} ");
|
||||
new_possible_shapes.push(merged);
|
||||
} else {
|
||||
// println!("Could not find shape {y} {x}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for y in s_y..=max_y {
|
||||
for x in min_x..max_x {
|
||||
if let Some(merged) = self.try_to_attach(to_attach, y, x) {
|
||||
// println!("Found shape {y} {x} {merged:?} ");
|
||||
new_possible_shapes.push(merged);
|
||||
} else {
|
||||
// println!("Could not find shape {y} {x}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for y in 0..=(s_y).min(max_y) {
|
||||
for x in min_x..0 {
|
||||
if let Some(merged) = self.try_to_attach(to_attach, y, x) {
|
||||
// println!("Found shape {y} {x} {merged:?} ");
|
||||
new_possible_shapes.push(merged);
|
||||
} else {
|
||||
// println!("Could not find shape {y} {x}");
|
||||
}
|
||||
}
|
||||
for x in s_x..max_x {
|
||||
if let Some(merged) = self.try_to_attach(to_attach, y, x) {
|
||||
// println!("Found shape {y} {x} {merged:?} ");
|
||||
new_possible_shapes.push(merged);
|
||||
} else {
|
||||
// println!("Could not find shape {y} {x}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let r_y = min_y..=max_y;
|
||||
let r_x = min_x..=max_x;
|
||||
|
||||
for (p_y, p_x) in self.get_attach_poins() {
|
||||
// println!("in {p_y} {p_x}");
|
||||
if r_y.contains(&p_y) && r_x.contains(&p_x) {
|
||||
if let Some(merged) = self.try_to_attach(to_attach, p_y, p_x) {
|
||||
// println!("Found shape {p_y} {p_x}{merged:?}");
|
||||
new_possible_shapes.push(merged);
|
||||
} else {
|
||||
// println!("Could not find shape {p_y} {p_x}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
new_possible_shapes.sort_by(|a, b| {
|
||||
(a.width * a.lines.len() as i64).cmp(&(b.width * b.lines.len() as i64))
|
||||
});
|
||||
|
||||
// if new_possible_shapes > 0 {
|
||||
// println!("Best shape {:?}", new_possible_shapes[0]);
|
||||
// }
|
||||
|
||||
new_possible_shapes
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for StrictShape {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "\n")?;
|
||||
for line in self.lines.iter() {
|
||||
let line = format!("{line:064b}");
|
||||
let line = line[0..(self.width as usize)]
|
||||
.to_string()
|
||||
.replace("0", ".")
|
||||
.replace("1", "#");
|
||||
write!(f, "{}\n", line)?;
|
||||
}
|
||||
write!(f, "")
|
||||
}
|
||||
}
|
||||
|
||||
impl Shape {
|
||||
fn new() -> Shape {
|
||||
Shape { lines: Vec::new() }
|
||||
}
|
||||
|
||||
fn add_line(&mut self, text: &str) {
|
||||
let mut line = Vec::new();
|
||||
for c in text.chars() {
|
||||
match c {
|
||||
'#' => line.push(true),
|
||||
'.' => line.push(false),
|
||||
c => panic!("Not sure what to do {}", c),
|
||||
}
|
||||
}
|
||||
self.lines.push(line);
|
||||
}
|
||||
|
||||
fn rotate_right(&self) -> Self {
|
||||
let mut lines = Vec::new();
|
||||
let height = self.lines.len();
|
||||
assert!(height > 0, "Something is wrong {:?}", self);
|
||||
let width = self.lines[0].len();
|
||||
for x in (0..width).rev() {
|
||||
let mut new_line = Vec::new();
|
||||
for y in 0..height {
|
||||
new_line.push(self.lines[y][x]);
|
||||
}
|
||||
lines.push(new_line);
|
||||
}
|
||||
Self { lines }
|
||||
}
|
||||
|
||||
fn flip(&self) -> Self {
|
||||
let mut lines = Vec::new();
|
||||
let height = self.lines.len();
|
||||
let width = self.lines[0].len();
|
||||
for y in (0..height).rev() {
|
||||
let mut new_line = Vec::new();
|
||||
for x in (0..width).rev() {
|
||||
new_line.push(self.lines[y][x]);
|
||||
}
|
||||
lines.push(new_line);
|
||||
}
|
||||
Self { lines }
|
||||
}
|
||||
|
||||
fn to_strict(self) -> StrictShape {
|
||||
let mut lines = Vec::new();
|
||||
for line in self.lines.iter() {
|
||||
let mut n_line = 0;
|
||||
for (i, c) in line.iter().enumerate() {
|
||||
n_line = n_line | (*c as u64) << 63 - i;
|
||||
}
|
||||
lines.push(n_line);
|
||||
}
|
||||
|
||||
StrictShape {
|
||||
lines,
|
||||
width: self.lines[0].len() as i64,
|
||||
}
|
||||
}
|
||||
|
||||
fn get_varients(self) -> Vec<StrictShape> {
|
||||
let mut shapes_list = HashSet::new();
|
||||
let shape1 = self.rotate_right();
|
||||
let shape2 = shape1.rotate_right();
|
||||
let shape3 = shape2.rotate_right();
|
||||
shapes_list.insert(shape1.to_strict());
|
||||
shapes_list.insert(shape2.to_strict());
|
||||
shapes_list.insert(shape3.to_strict());
|
||||
let fliped = self.flip();
|
||||
let shape1 = fliped.rotate_right();
|
||||
let shape2 = shape1.rotate_right();
|
||||
let shape3 = shape2.rotate_right();
|
||||
shapes_list.insert(self.to_strict());
|
||||
shapes_list.insert(fliped.to_strict());
|
||||
shapes_list.insert(shape1.to_strict());
|
||||
shapes_list.insert(shape2.to_strict());
|
||||
shapes_list.insert(shape3.to_strict());
|
||||
shapes_list.into_iter().collect()
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for Shape {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "\n")?;
|
||||
for line in self.lines.iter() {
|
||||
for c in line {
|
||||
write!(f, "{}", if *c { "#" } else { "." })?;
|
||||
}
|
||||
write!(f, "\n")?;
|
||||
}
|
||||
write!(f, "")
|
||||
}
|
||||
}
|
||||
|
||||
fn main() -> Result<()> {
|
||||
let text = fs::read_to_string("src/day12/act.txt")?;
|
||||
|
||||
let mut areas: Vec<Area> = Vec::new();
|
||||
let mut shapes = HashMap::new();
|
||||
let mut cur_shape = Shape::new();
|
||||
|
||||
for line in text.split("\n") {
|
||||
if line == "" {
|
||||
continue;
|
||||
} else if line.contains("x") {
|
||||
if cur_shape.lines.len() != 0 {
|
||||
let vars = cur_shape.get_varients();
|
||||
println!("Shape: {:?} {}", vars[0], vars.len());
|
||||
shapes.insert(shapes.values().len(), vars);
|
||||
cur_shape = Shape::new();
|
||||
}
|
||||
areas.push(line.into());
|
||||
} else if line.contains(":") {
|
||||
if cur_shape.lines.len() == 0 {
|
||||
continue;
|
||||
}
|
||||
let vars = cur_shape.get_varients();
|
||||
println!("Shape: {:?} {}", vars[0], vars.len());
|
||||
shapes.insert(shapes.values().len(), vars);
|
||||
cur_shape = Shape::new();
|
||||
} else {
|
||||
cur_shape.add_line(line);
|
||||
}
|
||||
}
|
||||
|
||||
let mut valid = 0;
|
||||
for a in areas.iter() {
|
||||
let mut count_of_1 = *a.hash.get(&1).unwrap();
|
||||
let by_6 = count_of_1 / 6;
|
||||
let left_over = count_of_1 % 6;
|
||||
|
||||
let mut area = a.width / 3 * a.height / 3;
|
||||
if (a.width / 12 * a.height / 3) as usize > by_6
|
||||
|| (a.width / 3 * a.height / 12) as usize > by_6
|
||||
{
|
||||
count_of_1 = left_over;
|
||||
area -= (by_6 * 4) as u64;
|
||||
}
|
||||
|
||||
let mut sum: u64 = 0;
|
||||
for (i, v) in a.hash.iter() {
|
||||
if *i == 1 {
|
||||
sum += count_of_1 as u64;
|
||||
} else {
|
||||
sum += *v as u64;
|
||||
}
|
||||
}
|
||||
|
||||
if area >= sum {
|
||||
valid += 1;
|
||||
}
|
||||
}
|
||||
println!("sol1: {}", valid);
|
||||
|
||||
panic!("mad");
|
||||
|
||||
// We know that 3 only nicly packs with itself
|
||||
|
||||
let mut valid = 0;
|
||||
for (i, a) in areas.iter().enumerate() {
|
||||
if a.valid(&shapes) {
|
||||
println!("Found Valid ans {i}\n\n\n\n\n\n");
|
||||
valid += 1;
|
||||
} else {
|
||||
println!("Could not Find valid answer {i}\n\n\n\n\n\n");
|
||||
}
|
||||
}
|
||||
|
||||
println!("sol1: {}", valid);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
33
src/day12/test.txt
Normal file
33
src/day12/test.txt
Normal file
@@ -0,0 +1,33 @@
|
||||
0:
|
||||
###
|
||||
##.
|
||||
##.
|
||||
|
||||
1:
|
||||
###
|
||||
##.
|
||||
.##
|
||||
|
||||
2:
|
||||
.##
|
||||
###
|
||||
##.
|
||||
|
||||
3:
|
||||
##.
|
||||
###
|
||||
##.
|
||||
|
||||
4:
|
||||
###
|
||||
#..
|
||||
###
|
||||
|
||||
5:
|
||||
###
|
||||
.#.
|
||||
###
|
||||
|
||||
4x4: 0 0 0 0 2 0
|
||||
12x5: 1 0 1 0 2 2
|
||||
12x5: 1 0 1 0 3 2
|
||||
Reference in New Issue
Block a user