This commit is contained in:
2025-12-12 20:58:10 +00:00
parent 26d64ba4c9
commit 71db768227
6 changed files with 1734 additions and 336 deletions

1030
src/day12/act.txt Normal file

File diff suppressed because it is too large Load Diff

448
src/day12/main.rs Normal file
View 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
View 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