2020: day20: part 2

This commit is contained in:
Antoine Martin 2020-12-31 15:09:00 +01:00
parent a4747e0b94
commit 27054d60f9
2 changed files with 138 additions and 27 deletions

View file

@ -0,0 +1,3 @@
#
# ## ## ###
# # # # # #

View file

@ -5,11 +5,13 @@ use anyhow::{anyhow, Context, Result};
const INPUT: &str = include_str!("../input/day20.txt"); const INPUT: &str = include_str!("../input/day20.txt");
const SNAKE: &str = include_str!("../input/day20_snake.txt");
pub fn run() -> Result<String> { pub fn run() -> Result<String> {
let mut res = String::with_capacity(128); let mut res = String::with_capacity(128);
writeln!(res, "part 1: {}", part1(INPUT)?)?; writeln!(res, "part 1: {}", part1(INPUT)?)?;
writeln!(res, "part 2:\n{}", part2(INPUT)?)?; writeln!(res, "part 2: {}", part2(INPUT)?)?;
Ok(res) Ok(res)
} }
@ -32,12 +34,18 @@ fn part1(input: &str) -> Result<u64> {
.product()) .product())
} }
fn part2(input: &str) -> Result<String> { fn part2(input: &str) -> Result<usize> {
let tiles: Vec<Tile> = input.split("\n\n").map(str::parse).collect::<Result<_>>()?; let tiles: Vec<Tile> = input.split("\n\n").map(str::parse).collect::<Result<_>>()?;
let image = Image::from_tiles(&tiles); let image = Image::from_tiles(&tiles);
let snake: Pattern = SNAKE.parse()?;
Ok(format!("{}", image)) let snake_number = image.count_pattern(&snake);
let snake_pixels = snake.offsets.len() * snake_number;
let pixels_number = image.count_pixels();
Ok(pixels_number - snake_pixels)
} }
#[derive(Debug, Clone, Copy)] #[derive(Debug, Clone, Copy)]
@ -74,6 +82,39 @@ impl Transform {
Transform::new(true, Some(Rotation::R270)), Transform::new(true, Some(Rotation::R270)),
] ]
} }
fn apply(
&self,
mut i: usize,
mut j: usize,
max_width: usize,
max_height: usize,
) -> (usize, usize) {
if let Some(rotation) = self.rotation {
match rotation {
Rotation::R90 => {
let prev_i = i;
i = j;
j = (max_width - 1) - prev_i;
}
Rotation::R180 => {
i = (max_height - 1) - i;
j = (max_width - 1) - j;
}
Rotation::R270 => {
let prev_j = j;
j = i;
i = (max_height - 1) - prev_j;
}
}
}
if self.flip {
i = (max_height - 1) - i;
}
(i, j)
}
} }
impl Default for Transform { impl Default for Transform {
@ -177,30 +218,8 @@ impl Tile {
} }
/// Returns the pixel at indices (i, j) in the tile, using the provided transform. /// Returns the pixel at indices (i, j) in the tile, using the provided transform.
fn get_with_transform(&self, mut i: usize, mut j: usize, transform: Transform) -> bool { fn get_with_transform(&self, i: usize, j: usize, transform: Transform) -> bool {
if let Some(rotation) = transform.rotation { let (i, j) = transform.apply(i, j, TILE_WIDTH, TILE_HEIGHT);
match rotation {
Rotation::R90 => {
let prev_i = i;
i = j;
j = (TILE_WIDTH - 1) - prev_i;
}
Rotation::R180 => {
i = (TILE_HEIGHT - 1) - i;
j = (TILE_WIDTH - 1) - j;
}
Rotation::R270 => {
let prev_j = j;
j = i;
i = (TILE_HEIGHT - 1) - prev_j;
}
}
}
if transform.flip {
i = (TILE_HEIGHT - 1) - i;
}
self.cells[i][j] self.cells[i][j]
} }
@ -351,6 +370,46 @@ impl Image {
pixels, pixels,
} }
} }
fn get_with_transform(&self, i: usize, j: usize, transform: &Transform) -> bool {
let (i, j) = transform.apply(i, j, self.width, self.height);
self.pixels[i][j]
}
fn count_pixels(&self) -> usize {
self.pixels
.iter()
.flat_map(|line| {
line.iter()
.filter_map(|pix| if *pix { Some(()) } else { None })
})
.count()
}
fn has_pattern_at(&self, i: usize, j: usize, transform: &Transform, pattern: &Pattern) -> bool {
pattern
.offsets
.iter()
.all(|(di, dj)| self.get_with_transform(i + di, j + dj, transform))
}
fn count_pattern(&self, pattern: &Pattern) -> usize {
Transform::all()
.into_iter()
.map(|transform| {
let mut count = 0;
for i in 0..(self.height - pattern.height) {
for j in 0..(self.width - pattern.width) {
if self.has_pattern_at(i, j, &transform, pattern) {
count += 1;
}
}
}
count
})
.max()
.unwrap()
}
} }
impl std::fmt::Display for Image { impl std::fmt::Display for Image {
@ -367,6 +426,45 @@ impl std::fmt::Display for Image {
} }
} }
struct Pattern {
height: usize,
width: usize,
offsets: Vec<(usize, usize)>,
}
impl Pattern {
fn from_offsets(offsets: Vec<(usize, usize)>) -> Self {
let height = *offsets.iter().map(|(x, _)| x).max().unwrap_or(&0);
let width = *offsets.iter().map(|(_, y)| y).max().unwrap_or(&0);
Self {
height,
width,
offsets,
}
}
}
impl std::str::FromStr for Pattern {
type Err = anyhow::Error;
fn from_str(s: &str) -> Result<Self> {
let offsets = s
.lines()
.enumerate()
.flat_map(|(i, line)| {
line.chars().enumerate().filter_map(move |(j, c)| match c {
'#' => Some(Ok((i, j))),
' ' => None,
_ => Some(Err(anyhow!("unexpected character in Pattern: `{}`", c))),
})
})
.collect::<Result<_>>()?;
Ok(Self::from_offsets(offsets))
}
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
@ -382,4 +480,14 @@ mod tests {
fn part1_real() { fn part1_real() {
assert_eq!(part1(INPUT).unwrap(), 5_775_714_912_743); assert_eq!(part1(INPUT).unwrap(), 5_775_714_912_743);
} }
#[test]
fn part2_provided() {
assert_eq!(part2(PROVIDED).unwrap(), 273);
}
#[test]
fn part2_real() {
assert_eq!(part2(INPUT).unwrap(), 1836);
}
} }