2020: day20: assemble image for part 2

This commit is contained in:
Antoine Martin 2020-12-31 01:45:49 +01:00
parent 0fbb57f14c
commit a4747e0b94

View file

@ -1,3 +1,4 @@
use std::collections::HashMap;
use std::fmt::Write;
use anyhow::{anyhow, Context, Result};
@ -8,6 +9,7 @@ pub fn run() -> Result<String> {
let mut res = String::with_capacity(128);
writeln!(res, "part 1: {}", part1(INPUT)?)?;
writeln!(res, "part 2:\n{}", part2(INPUT)?)?;
Ok(res)
}
@ -30,6 +32,14 @@ fn part1(input: &str) -> Result<u64> {
.product())
}
fn part2(input: &str) -> Result<String> {
let tiles: Vec<Tile> = input.split("\n\n").map(str::parse).collect::<Result<_>>()?;
let image = Image::from_tiles(&tiles);
Ok(format!("{}", image))
}
#[derive(Debug, Clone, Copy)]
enum Rotation {
R90,
@ -96,6 +106,19 @@ impl Position {
fn ordered() -> [Self; 4] {
[Self::Down, Self::Left, Self::Right, Self::Up]
}
fn apply(&self, (i, j): (i64, i64)) -> (i64, i64) {
let (mut di, mut dj) = (0, 0);
match self {
Self::Down => di = 1,
Self::Left => dj = -1,
Self::Right => dj = 1,
Self::Up => di = -1,
}
(i + di, j + dj)
}
}
const TILE_WIDTH: usize = 10;
@ -253,6 +276,97 @@ impl std::str::FromStr for Tile {
}
}
struct Image {
width: usize,
height: usize,
pixels: Vec<Vec<bool>>,
}
impl Image {
fn from_tiles(tiles: &[Tile]) -> Self {
let mut todo: Vec<(i64, i64)> = vec![(0, 0)];
let mut image_positions = HashMap::new();
image_positions.insert((0, 0), tiles[0].clone());
// compute each image position depending on its neighbours
while !todo.is_empty() {
let pos = todo.pop().unwrap();
let tile = &image_positions[&pos];
for (direction, other_tile) in tile.neighbours(tiles) {
let new_pos = direction.apply(pos);
#[allow(clippy::map_entry)]
if !image_positions.contains_key(&new_pos) {
image_positions.insert(new_pos, other_tile);
todo.push(new_pos);
}
}
}
let image_positions = image_positions.into_iter().collect::<Vec<_>>();
let i_min = *image_positions.iter().map(|((i, _), _)| i).min().unwrap();
let j_min = *image_positions.iter().map(|((_, j), _)| j).min().unwrap();
let image_positions = image_positions
.into_iter()
.map(|((i, j), tile)| {
(
((i + i_min.abs()) as usize, (j + j_min.abs()) as usize),
tile,
)
})
.collect::<Vec<((usize, usize), Tile)>>();
const IMAGE_TILE_HEIGHT: usize = TILE_HEIGHT - 2;
const IMAGE_TILE_WIDTH: usize = TILE_WIDTH - 2;
let height = *image_positions.iter().map(|((i, _), _)| i).max().unwrap() as usize + 1;
let height = height * IMAGE_TILE_HEIGHT;
let width = *image_positions.iter().map(|((_, j), _)| j).max().unwrap() as usize + 1;
let width = width * IMAGE_TILE_HEIGHT;
let mut pixels = Vec::new();
for _ in 0..height {
let mut line: Vec<bool> = Vec::new();
line.resize_with(width, Default::default);
pixels.push(line);
}
for (pos, tile) in image_positions {
let begin_i = IMAGE_TILE_HEIGHT * pos.0 as usize;
let begin_j = IMAGE_TILE_WIDTH * pos.1 as usize;
for i in 0..IMAGE_TILE_HEIGHT {
for j in 0..IMAGE_TILE_WIDTH {
// + 1 in the tile to skip the border
pixels[begin_i + i][begin_j + j] = tile.get(i + 1, j + 1);
}
}
}
Self {
width,
height,
pixels,
}
}
}
impl std::fmt::Display for Image {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
for i in 0..self.height {
for j in 0..self.width {
let c = if self.pixels[i][j] { '#' } else { '.' };
write!(f, "{}", c)?;
}
writeln!(f)?;
}
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;