660 lines
22 KiB
Rust
660 lines
22 KiB
Rust
|
use crate::common::AdventOfCodeDay;
|
||
|
|
||
|
use regex::Regex;
|
||
|
use strum::IntoEnumIterator;
|
||
|
use strum_macros::EnumIter;
|
||
|
use std::convert::TryInto;
|
||
|
use std::collections::HashMap;
|
||
|
use std::collections::HashSet;
|
||
|
|
||
|
|
||
|
#[derive(Debug, EnumIter, Clone, Copy, PartialEq, Eq, Hash)]
|
||
|
enum Compass { North, East, South, West }
|
||
|
|
||
|
impl Compass {
|
||
|
pub fn transform_back(&self, tf: Transform) -> (Self, bool) {
|
||
|
return match tf {
|
||
|
|
||
|
Transform::None => match self {
|
||
|
Compass::North => (Compass::North, false),
|
||
|
Compass::East => (Compass::East, false),
|
||
|
Compass::South => (Compass::South, false),
|
||
|
Compass::West => (Compass::West, false),
|
||
|
},
|
||
|
|
||
|
Transform::RotCW090 => match self {
|
||
|
Compass::North => (Compass::West, false),
|
||
|
Compass::East => (Compass::North, false),
|
||
|
Compass::South => (Compass::East, false),
|
||
|
Compass::West => (Compass::South, false),
|
||
|
},
|
||
|
|
||
|
Transform::RotCW180 => match self {
|
||
|
Compass::North => (Compass::South, false),
|
||
|
Compass::East => (Compass::West, false),
|
||
|
Compass::South => (Compass::North, false),
|
||
|
Compass::West => (Compass::East, false),
|
||
|
},
|
||
|
|
||
|
Transform::RotCW270 => match self {
|
||
|
Compass::North => (Compass::East, false),
|
||
|
Compass::East => (Compass::South, false),
|
||
|
Compass::South => (Compass::West, false),
|
||
|
Compass::West => (Compass::North, false),
|
||
|
},
|
||
|
|
||
|
Transform::Flipped => match self {
|
||
|
Compass::North => (Compass::West, true),
|
||
|
Compass::East => (Compass::South, true),
|
||
|
Compass::South => (Compass::East, true),
|
||
|
Compass::West => (Compass::North, true),
|
||
|
},
|
||
|
|
||
|
Transform::RotCW090Flipped => match self {
|
||
|
Compass::North => (Compass::North, true),
|
||
|
Compass::East => (Compass::West, true),
|
||
|
Compass::South => (Compass::South, true),
|
||
|
Compass::West => (Compass::East, true),
|
||
|
},
|
||
|
|
||
|
Transform::RotCW180Flipped => match self {
|
||
|
Compass::North => (Compass::East, true),
|
||
|
Compass::East => (Compass::North, true),
|
||
|
Compass::South => (Compass::West, true),
|
||
|
Compass::West => (Compass::South, true),
|
||
|
},
|
||
|
|
||
|
Transform::RotCW270Flipped => match self {
|
||
|
Compass::North => (Compass::South, true),
|
||
|
Compass::East => (Compass::East, true),
|
||
|
Compass::South => (Compass::North, true),
|
||
|
Compass::West => (Compass::West, true),
|
||
|
},
|
||
|
};
|
||
|
}
|
||
|
}
|
||
|
|
||
|
#[derive(Debug, EnumIter, Clone, Copy, PartialEq, Eq, Hash)]
|
||
|
enum Transform {
|
||
|
None,
|
||
|
RotCW090,
|
||
|
RotCW180,
|
||
|
RotCW270,
|
||
|
Flipped,
|
||
|
RotCW090Flipped,
|
||
|
RotCW180Flipped,
|
||
|
RotCW270Flipped,
|
||
|
}
|
||
|
|
||
|
struct Tile {
|
||
|
id: u32,
|
||
|
bitmap: [[bool;10];10],
|
||
|
sides: HashMap<(Compass, bool), (u32,u32)>,
|
||
|
}
|
||
|
|
||
|
pub struct Day20 {
|
||
|
input: [[Tile;12];12],
|
||
|
}
|
||
|
|
||
|
fn new_tile_array() -> [[Tile;12];12] {
|
||
|
let mut vec = Vec::<[Tile;12]>::with_capacity(12);
|
||
|
|
||
|
for _ in 0..12 {
|
||
|
let mut inner = Vec::<Tile>::with_capacity(12);
|
||
|
for _ in 0..12 {
|
||
|
inner.push(Tile{ id: 0, bitmap: [[false;10];10], sides: HashMap::with_capacity(4*2) });
|
||
|
}
|
||
|
vec.push(inner.try_into().unwrap_or_else(|_|panic!()));
|
||
|
}
|
||
|
return vec.try_into().unwrap_or_else(|_|panic!())
|
||
|
}
|
||
|
|
||
|
impl Day20 {
|
||
|
pub fn new() -> Self {
|
||
|
let input_bytes = include_bytes!("../res/20_input.txt");
|
||
|
let input_str = String::from_utf8_lossy(input_bytes);
|
||
|
|
||
|
let rex = Regex::new(r"Tile (?P<id>[0-9]+):\n(?P<bmp>([.#]{10}\n){10})").unwrap();
|
||
|
|
||
|
let mut tiles = new_tile_array();
|
||
|
|
||
|
let mut i = 0;
|
||
|
for cap in rex.captures_iter(&input_str)
|
||
|
{
|
||
|
tiles[i/12][i%12].id = cap.name("id").unwrap().as_str().parse::<u32>().unwrap();
|
||
|
|
||
|
let raw = cap.name("bmp").unwrap().as_str().lines().map(|l| l.chars().collect::<Vec<char>>()).collect::<Vec<Vec<char>>>();
|
||
|
|
||
|
for y in 0..10 {
|
||
|
for x in 0..10 {
|
||
|
tiles[i/12][i%12].bitmap[y][x] = raw[y][x]=='#';
|
||
|
}
|
||
|
}
|
||
|
|
||
|
tiles[i/12][i%12].gen_cache();
|
||
|
|
||
|
i+=1;
|
||
|
}
|
||
|
|
||
|
Self {
|
||
|
input: tiles
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
impl Day20 {
|
||
|
|
||
|
fn format_ids(tiles: &[[Tile;12];12]) -> String {
|
||
|
let mut r = String::new();
|
||
|
for y in 0..12 {
|
||
|
for x in 0..12 {
|
||
|
let pad = format!(" {}", tiles[y][x].id);
|
||
|
r.push_str(&pad);
|
||
|
}
|
||
|
r.push('\n');
|
||
|
}
|
||
|
return r;
|
||
|
}
|
||
|
|
||
|
|
||
|
fn format_bitmaps(tiles: &[[Tile;12];12]) -> String {
|
||
|
let mut r = String::with_capacity(12*12*12*12 + 1000);
|
||
|
|
||
|
for y in 0..(12 * 11) {
|
||
|
for x in 0..(12 * 11) {
|
||
|
|
||
|
let gx = x / 11;
|
||
|
let gy = y / 11;
|
||
|
|
||
|
let ix = x % 11;
|
||
|
let iy = y % 11;
|
||
|
|
||
|
if ix==10 || iy == 10 { r.push(' '); continue; }
|
||
|
|
||
|
r.push(match tiles[gy][gx].bitmap[iy][ix] {
|
||
|
true => '#',
|
||
|
false => '.',
|
||
|
});
|
||
|
}
|
||
|
r.push('\n');
|
||
|
}
|
||
|
|
||
|
return r;
|
||
|
}
|
||
|
|
||
|
fn corner_to_transform_tl(tfs: Vec<(Compass, bool)>) -> Vec<Transform> {
|
||
|
|
||
|
let rtf = tfs.iter().map(|(c,_)| *c).collect::<Vec<Compass>>();
|
||
|
|
||
|
if rtf.contains(&Compass::West) && rtf.contains(&Compass::North) { return vec![Transform::RotCW180, Transform::RotCW180Flipped]; }
|
||
|
if rtf.contains(&Compass::North) && rtf.contains(&Compass::East) { return vec![Transform::RotCW270, Transform::RotCW270Flipped]; }
|
||
|
if rtf.contains(&Compass::East) && rtf.contains(&Compass::South) { return vec![Transform::None, Transform::Flipped]; }
|
||
|
if rtf.contains(&Compass::South) && rtf.contains(&Compass::West) { return vec![Transform::RotCW090, Transform::RotCW090Flipped]; }
|
||
|
|
||
|
panic!();
|
||
|
}
|
||
|
|
||
|
fn is_valid_neigbour_horz(left: &(&Tile, Transform), right: &(&Tile, Transform)) -> bool {
|
||
|
let s1 = left.0.get_side_after_transform(Compass::East, left.1);
|
||
|
let s2 = right.0.get_side_after_transform(Compass::West, right.1);
|
||
|
|
||
|
return s1.0 == s2.1;
|
||
|
}
|
||
|
|
||
|
fn is_valid_neigbour_vert(top: &(&Tile, Transform), bottom: &(&Tile, Transform)) -> bool {
|
||
|
let s1 = top.0.get_side_after_transform(Compass::South, top.1);
|
||
|
let s2 = bottom.0.get_side_after_transform(Compass::North, bottom.1);
|
||
|
|
||
|
return s1.0 == s2.1;
|
||
|
}
|
||
|
|
||
|
fn is_valid_candidate(x: usize, y: usize, tile: &Tile, transform: Transform, map: &HashMap::<(usize, usize), Vec<(&Tile, Transform)>>) -> bool {
|
||
|
|
||
|
if x > 0 {
|
||
|
if !map.get(&(x-1, y)).unwrap().iter().any(|t| Self::is_valid_neigbour_horz(t, &(tile, transform))) {
|
||
|
return false;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if x < 11 {
|
||
|
if !map.get(&(x+1, y)).unwrap().iter().any(|t| Self::is_valid_neigbour_horz(&(tile, transform), t)) {
|
||
|
return false;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if y > 0 {
|
||
|
if !map.get(&(x, y-1)).unwrap().iter().any(|t| Self::is_valid_neigbour_vert(t, &(tile, transform))) {
|
||
|
return false;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if y < 11 {
|
||
|
if !map.get(&(x, y+1)).unwrap().iter().any(|t| Self::is_valid_neigbour_vert(&(tile, transform), t)) {
|
||
|
return false;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
fn reconstruct(tiles: &Vec<&Tile>) -> [[Tile;12];12] {
|
||
|
|
||
|
let mut candidates = HashMap::<(usize, usize), Vec<(&Tile, Transform)>>::with_capacity(12*12);
|
||
|
for y in 0..12 {
|
||
|
for x in 0..12 {
|
||
|
candidates.insert((x,y), tiles.iter().flat_map(|tile| Transform::iter().map(move |tf| (*tile, tf))).collect());
|
||
|
}
|
||
|
}
|
||
|
|
||
|
let corners_tl = tiles.iter()
|
||
|
.map(|t| (t, t.matching_sides(&tiles)))
|
||
|
.filter(|(_,s)| s.len()==2)
|
||
|
.map(|(t,s)| (*t, Self::corner_to_transform_tl(s)))
|
||
|
.flat_map(|(t,s)| s.iter().map(|tf| (t, *tf)).collect::<Vec<(&Tile, Transform)>>() )
|
||
|
.collect::<Vec<(&Tile, Transform)>>();
|
||
|
|
||
|
let corner_tl = corners_tl.iter().skip(5).nth(0).unwrap();
|
||
|
|
||
|
verboseln!("Define [0,0] := ({}, {:?}):", corner_tl.0.id, corner_tl.1);
|
||
|
verboseln!("{}", corner_tl.0.transform(corner_tl.1).format_bitmap());
|
||
|
|
||
|
candidates.insert((0, 0), vec![*corner_tl]);
|
||
|
|
||
|
for yy in 0..12 {
|
||
|
for xx in 0..12 {
|
||
|
if xx== 0 && yy == 0 { continue; }
|
||
|
let other = candidates.get_mut(&(xx,yy)).unwrap();
|
||
|
other.retain(|p| p.0.id != corner_tl.0.id);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
loop {
|
||
|
|
||
|
let mut ok = 0;
|
||
|
for y in 0..12 {
|
||
|
for x in 0..12 {
|
||
|
let cand = candidates.get(&(x,y)).unwrap();
|
||
|
if cand.len() == 1 { ok+=1; continue; }
|
||
|
|
||
|
let oldlen = cand.len();
|
||
|
|
||
|
let mut cand_clone = cand.clone();
|
||
|
cand_clone.retain(|(tile, tf)| Day20::is_valid_candidate(x, y, tile, *tf, &candidates));
|
||
|
|
||
|
|
||
|
if cand_clone.len() == 0 {
|
||
|
verboseln!("Reduced [{},{}] from {} to {} candidates", x, y, oldlen, cand_clone.len());
|
||
|
panic!("No more candidates after [is_valid_candidate]");
|
||
|
}
|
||
|
else if oldlen != cand_clone.len() {
|
||
|
if cand_clone.len() == 1 {
|
||
|
verboseln!(" > Found tile for [{},{}] from {} candidates := ({}, {:?}):", x, y, oldlen, cand_clone[0].0.id, cand_clone[0].1);
|
||
|
verboseln!("{}", cand_clone[0].0.transform(cand_clone[0].1).format_bitmap());
|
||
|
verboseln!();
|
||
|
} else {
|
||
|
verboseln!("Reduced [{},{}] from {} to {} candidates", x, y, oldlen, cand_clone.len());
|
||
|
}
|
||
|
|
||
|
if cand_clone.len() == 1 {
|
||
|
for yy in 0..12 {
|
||
|
for xx in 0..12 {
|
||
|
if x==xx && y==yy { continue; }
|
||
|
|
||
|
let other = candidates.get_mut(&(xx,yy)).unwrap();
|
||
|
|
||
|
let other_oldlen = other.len();
|
||
|
other.retain(|p| p.0.id != cand_clone[0].0.id);
|
||
|
|
||
|
if other_oldlen != other.len() {
|
||
|
verboseln!("Auto-force reduced [{},{}] from {} to {} candidates (triggered by [{},{}])", xx, yy, other_oldlen, other.len(), x, y);
|
||
|
}
|
||
|
|
||
|
if other.len() == 0 { panic!("No more candidates after [retain] in [{},{}]", xx, yy); }
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
candidates.insert((x, y), cand_clone);
|
||
|
}
|
||
|
else {
|
||
|
verboseln!("No changes on [{},{}] ({} candidates)", x, y, oldlen);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if ok == 12*12 { break; }
|
||
|
}
|
||
|
|
||
|
let mut tiles = new_tile_array();
|
||
|
for y in 0..12 {
|
||
|
for x in 0..12 {
|
||
|
tiles[y][x].id = candidates[&(x,y)][0].0.id;
|
||
|
tiles[y][x].bitmap = Tile::apply_transform_10(&candidates[&(x,y)][0].0.bitmap, candidates[&(x,y)][0].1);
|
||
|
tiles[y][x].gen_cache();
|
||
|
}
|
||
|
}
|
||
|
return tiles;
|
||
|
}
|
||
|
|
||
|
pub fn find_monsters(sea: &[[bool;8*12];8*12], str_blueprint: String) -> (Vec<(usize, usize)>, Vec<(usize, usize)>) {
|
||
|
let bp_width = str_blueprint.lines().nth(0).unwrap().len();
|
||
|
let bp_height = str_blueprint.lines().count();
|
||
|
let blueprint = str_blueprint
|
||
|
.lines()
|
||
|
.enumerate()
|
||
|
.flat_map(|(y,l)| l.chars().enumerate().filter(|(_,v)| *v=='#').map(move |(x,_)| (x,y)))
|
||
|
.collect::<Vec<_>>();
|
||
|
|
||
|
verboseln!("Monster: {:?}", blueprint);
|
||
|
|
||
|
let mut r = Vec::new();
|
||
|
|
||
|
for y in 0..(8*12-bp_height) {
|
||
|
for x in 0..(8*12-bp_width) {
|
||
|
if blueprint.iter().all(|(dx,dy)| sea[y + *dy][x + *dx]) { r.push((x, y)); }
|
||
|
}
|
||
|
}
|
||
|
|
||
|
let mk = r.iter()
|
||
|
.flat_map(|(sx,sy)| blueprint.iter().map(move |(dx,dy)| (*sx+*dx, *sy+*dy)))
|
||
|
.collect::<HashSet<_>>()
|
||
|
.iter()
|
||
|
.map(|p|*p)
|
||
|
.collect::<Vec<_>>();
|
||
|
|
||
|
return (r, mk);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
impl Tile {
|
||
|
fn format_bitmap(&self) -> String {
|
||
|
let mut r = String::with_capacity(10*11);
|
||
|
|
||
|
for y in 0..10 {
|
||
|
for x in 0..10 {
|
||
|
r.push(match self.bitmap[y][x] {
|
||
|
true => '#',
|
||
|
false => '.',
|
||
|
});
|
||
|
}
|
||
|
r.push('\n');
|
||
|
}
|
||
|
return r;
|
||
|
}
|
||
|
|
||
|
fn transform(&self, tf: Transform) -> Self {
|
||
|
let mut r = Self {
|
||
|
id: self.id,
|
||
|
bitmap: Self::apply_transform_10(&self.bitmap, tf),
|
||
|
sides: HashMap::new(),
|
||
|
};
|
||
|
|
||
|
r.gen_cache();
|
||
|
|
||
|
return r;
|
||
|
}
|
||
|
|
||
|
fn apply_transform_10(src: &[[bool;10];10], tf: Transform) -> [[bool;10];10] {
|
||
|
let mut r = [[false;10];10];
|
||
|
|
||
|
for src_y in 0..10 {
|
||
|
for src_x in 0..10 {
|
||
|
|
||
|
let dst_x: usize;
|
||
|
let dst_y: usize;
|
||
|
|
||
|
match tf {
|
||
|
Transform::None => { dst_x = 0 + src_x; dst_y = 0 + src_y; },
|
||
|
Transform::RotCW090 => { dst_x = 9 - src_y; dst_y = 0 + src_x; },
|
||
|
Transform::RotCW180 => { dst_x = 9 - src_x; dst_y = 9 - src_y; },
|
||
|
Transform::RotCW270 => { dst_x = 0 + src_y; dst_y = 9 - src_x; },
|
||
|
|
||
|
Transform::Flipped => { dst_x = 0 + src_y; dst_y = 0 + src_x; },
|
||
|
Transform::RotCW090Flipped => { dst_x = 9 - src_x; dst_y = 0 + src_y; },
|
||
|
Transform::RotCW180Flipped => { dst_x = 9 - src_y; dst_y = 9 - src_x; },
|
||
|
Transform::RotCW270Flipped => { dst_x = 0 + src_x; dst_y = 9 - src_y; },
|
||
|
};
|
||
|
|
||
|
r[dst_y][dst_x] = src[src_y][src_x];
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return r;
|
||
|
}
|
||
|
|
||
|
fn apply_transform_96(src: &[[bool;8*12];8*12], tf: Transform) -> [[bool;8*12];8*12] {
|
||
|
let mut r = [[false;8*12];8*12];
|
||
|
|
||
|
for src_y in 0..(8*12) {
|
||
|
for src_x in 0..(8*12) {
|
||
|
|
||
|
let dst_x: usize;
|
||
|
let dst_y: usize;
|
||
|
|
||
|
match tf {
|
||
|
Transform::None => { dst_x = 0 + src_x; dst_y = 0 + src_y; },
|
||
|
Transform::RotCW090 => { dst_x = 95 - src_y; dst_y = 0 + src_x; },
|
||
|
Transform::RotCW180 => { dst_x = 95 - src_x; dst_y = 95 - src_y; },
|
||
|
Transform::RotCW270 => { dst_x = 0 + src_y; dst_y = 95 - src_x; },
|
||
|
|
||
|
Transform::Flipped => { dst_x = 0 + src_y; dst_y = 0 + src_x; },
|
||
|
Transform::RotCW090Flipped => { dst_x = 95 - src_x; dst_y = 0 + src_y; },
|
||
|
Transform::RotCW180Flipped => { dst_x = 95 - src_y; dst_y = 95 - src_x; },
|
||
|
Transform::RotCW270Flipped => { dst_x = 0 + src_x; dst_y = 95 - src_y; },
|
||
|
};
|
||
|
|
||
|
r[dst_y][dst_x] = src[src_y][src_x];
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return r;
|
||
|
}
|
||
|
|
||
|
fn side_to_int(s: &[bool;10]) -> (u32,u32) {
|
||
|
let mut u1=0;
|
||
|
for i in 0..10 {
|
||
|
u1 *= 2;
|
||
|
if s[i] { u1 += 1; }
|
||
|
}
|
||
|
let mut u2=0;
|
||
|
for i in 0..10 {
|
||
|
u2 *= 2;
|
||
|
if s[9-i] { u2 += 1; }
|
||
|
}
|
||
|
return (u1,u2);
|
||
|
}
|
||
|
|
||
|
fn get_side(&self, d: Compass, flipped: bool) -> (u32,u32) {
|
||
|
return *self.sides.get(&(d,flipped)).unwrap();
|
||
|
}
|
||
|
|
||
|
fn get_side_after_transform(&self, d: Compass, tf: Transform) -> (u32,u32) {
|
||
|
return *self.sides.get(&d.transform_back(tf)).unwrap();
|
||
|
}
|
||
|
|
||
|
fn calc_side(&self, d: Compass, flipped: bool) -> [bool;10] {
|
||
|
let mut r = [false;10];
|
||
|
|
||
|
match flipped {
|
||
|
false =>
|
||
|
{
|
||
|
match d {
|
||
|
Compass::North =>
|
||
|
{
|
||
|
for i in 0..10 { r[i] = self.bitmap[0][i]; }
|
||
|
return r;
|
||
|
},
|
||
|
Compass::East =>
|
||
|
{
|
||
|
for i in 0..10 { r[i] = self.bitmap[i][9]; }
|
||
|
return r;
|
||
|
},
|
||
|
Compass::South =>
|
||
|
{
|
||
|
for i in 0..10 { r[i] = self.bitmap[9][9-i]; }
|
||
|
return r;
|
||
|
},
|
||
|
Compass::West =>
|
||
|
{
|
||
|
for i in 0..10 { r[i] = self.bitmap[9-i][0]; }
|
||
|
return r;
|
||
|
},
|
||
|
}
|
||
|
},
|
||
|
true =>
|
||
|
{
|
||
|
match d {
|
||
|
Compass::North =>
|
||
|
{
|
||
|
for i in 0..10 { r[i] = self.bitmap[0][9-i]; }
|
||
|
return r;
|
||
|
},
|
||
|
Compass::East =>
|
||
|
{
|
||
|
for i in 0..10 { r[i] = self.bitmap[9-i][9]; }
|
||
|
return r;
|
||
|
},
|
||
|
Compass::South =>
|
||
|
{
|
||
|
for i in 0..10 { r[i] = self.bitmap[9][i]; }
|
||
|
return r;
|
||
|
},
|
||
|
Compass::West =>
|
||
|
{
|
||
|
for i in 0..10 { r[i] = self.bitmap[i][0]; }
|
||
|
return r;
|
||
|
},
|
||
|
}
|
||
|
},
|
||
|
}
|
||
|
}
|
||
|
|
||
|
fn matching_sides(&self, tiles: &Vec<&Tile>) -> Vec<(Compass, bool)> {
|
||
|
|
||
|
let mut r = Vec::<(Compass, bool)>::new();
|
||
|
|
||
|
for d in Compass::iter() {
|
||
|
for f in &[true, false] {
|
||
|
let side = self.get_side(d, *f);
|
||
|
|
||
|
let mut c = 0;
|
||
|
for tile in tiles.iter().filter(|t| t.id != self.id) {
|
||
|
for d2 in Compass::iter() {
|
||
|
if side.0 == tile.get_side(d2, true).0 {
|
||
|
c+=1;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
if c > 0 {
|
||
|
r.push((d, *f))
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return r;
|
||
|
}
|
||
|
|
||
|
fn gen_cache(&mut self) {
|
||
|
for c in Compass::iter() {
|
||
|
self.sides.insert((c, false), Self::side_to_int(&self.calc_side(c, false)));
|
||
|
self.sides.insert((c, true), Self::side_to_int(&self.calc_side(c, true)));
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
impl AdventOfCodeDay for Day20 {
|
||
|
|
||
|
fn task_1(&self) -> String {
|
||
|
|
||
|
verboseln!("{}", Day20::format_ids(&self.input));
|
||
|
verboseln!("{}", Day20::format_bitmaps(&self.input));
|
||
|
|
||
|
let tiles = self.input.iter().flat_map(|p| p.iter()).collect::<Vec<&Tile>>();
|
||
|
|
||
|
if is_verbose!() {
|
||
|
for t in tiles.iter().filter(|t| t.matching_sides(&tiles).len() == 2) {
|
||
|
verboseln!("{}", t.format_bitmap());
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return tiles.iter().filter(|t| t.matching_sides(&tiles).len() == 2).map(|p| p.id as u128).product::<u128>().to_string();
|
||
|
}
|
||
|
|
||
|
fn task_2(&self) -> String {
|
||
|
let tiles = self.input.iter().flat_map(|p| p.iter()).collect::<Vec<&Tile>>();
|
||
|
|
||
|
let bitmap_r = Day20::reconstruct(&tiles);
|
||
|
|
||
|
verboseln!("Reconstructed:");
|
||
|
verboseln!("{}", Day20::format_bitmaps(&bitmap_r));
|
||
|
|
||
|
let mut bitmap_full: [[bool;8*12];8*12] = [[false;8*12];8*12];
|
||
|
for gy in 0..12 {
|
||
|
for gx in 0..12 {
|
||
|
for iy in 1..9 {
|
||
|
for ix in 1..9 {
|
||
|
bitmap_full[gy*8+iy-1][gx*8+ix-1] = bitmap_r[gy][gx].bitmap[iy][ix];
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if is_verbose!() {
|
||
|
verboseln!("Raw:");
|
||
|
let mut ostr = String::with_capacity(8*12*8*12);
|
||
|
for y in 0..(8*12) {
|
||
|
for x in 0..(8*12) {
|
||
|
ostr.push(if bitmap_full[y][x] {'#'} else {'.'})
|
||
|
}
|
||
|
ostr.push('\n');
|
||
|
}
|
||
|
verboseln!("{}", ostr);
|
||
|
}
|
||
|
|
||
|
let monster_blueprint = "".to_owned() +
|
||
|
" # " + "\n" +
|
||
|
"# ## ## ###" + "\n" +
|
||
|
" # # # # # # ";
|
||
|
|
||
|
let mut monster_parts = Vec::new();
|
||
|
let mut monster_bitmap: [[bool;8*12];8*12] = [[false;8*12];8*12];
|
||
|
for tf in Transform::iter() {
|
||
|
|
||
|
let bitmap_tf = Tile::apply_transform_96(&bitmap_full, tf);
|
||
|
|
||
|
let (monsters, markers) = Day20::find_monsters(&bitmap_tf, monster_blueprint.clone());
|
||
|
verboseln!("Monsters in {:?}: {:?}", tf, monsters);
|
||
|
|
||
|
if !monsters.is_empty() {
|
||
|
monster_parts = markers;
|
||
|
monster_bitmap = bitmap_tf;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
let mut roughness = 0;
|
||
|
for x in 0..(8*12) {
|
||
|
for y in 0..(8*12) {
|
||
|
if monster_bitmap[y][x] && !monster_parts.contains(&(x,y)) { roughness += 1; }
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if is_verbose!() {
|
||
|
verboseln!("Analyzed:");
|
||
|
let mut ostr = String::with_capacity(8*12*8*12);
|
||
|
for y in 0..(8*12) {
|
||
|
for x in 0..(8*12) {
|
||
|
if monster_parts.contains(&(x,y)) {
|
||
|
ostr.push('O')
|
||
|
} else {
|
||
|
ostr.push(if monster_bitmap[y][x] {'#'} else {'.'})
|
||
|
}
|
||
|
}
|
||
|
ostr.push('\n');
|
||
|
}
|
||
|
verboseln!("{}", ostr);
|
||
|
}
|
||
|
|
||
|
return roughness.to_string();
|
||
|
}
|
||
|
}
|