2020: day20: refacto before part 2
This commit is contained in:
parent
44c2536b72
commit
0fbb57f14c
|
@ -18,24 +18,7 @@ fn part1(input: &str) -> Result<u64> {
|
||||||
Ok(tiles
|
Ok(tiles
|
||||||
.iter()
|
.iter()
|
||||||
.filter_map(|tile| {
|
.filter_map(|tile| {
|
||||||
let mut count = 0;
|
let count = tile.neighbours(&tiles).len();
|
||||||
for other in &tiles {
|
|
||||||
if tile == other {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
count += tile
|
|
||||||
.edges
|
|
||||||
.iter()
|
|
||||||
.filter(|e| other.edges.contains(e))
|
|
||||||
.count();
|
|
||||||
|
|
||||||
count += tile
|
|
||||||
.reversed_edges
|
|
||||||
.iter()
|
|
||||||
.filter(|e| other.edges.contains(e))
|
|
||||||
.count();
|
|
||||||
}
|
|
||||||
|
|
||||||
// corners have 2 edges in common
|
// corners have 2 edges in common
|
||||||
if count == 2 {
|
if count == 2 {
|
||||||
|
@ -47,11 +30,182 @@ fn part1(input: &str) -> Result<u64> {
|
||||||
.product())
|
.product())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
enum Rotation {
|
||||||
|
R90,
|
||||||
|
R180,
|
||||||
|
R270,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Represents a transformation of a tile or image.
|
||||||
|
///
|
||||||
|
/// Note: we don't need a horizontal and a vertical flip, these result in the same output as a 180
|
||||||
|
/// degree rotation when combined, so only one is necessary
|
||||||
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
struct Transform {
|
||||||
|
flip: bool,
|
||||||
|
rotation: Option<Rotation>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Transform {
|
||||||
|
fn new(flip: bool, rotation: Option<Rotation>) -> Self {
|
||||||
|
Self { flip, rotation }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn all() -> Vec<Transform> {
|
||||||
|
vec![
|
||||||
|
Transform::new(false, None),
|
||||||
|
Transform::new(false, Some(Rotation::R90)),
|
||||||
|
Transform::new(false, Some(Rotation::R180)),
|
||||||
|
Transform::new(false, Some(Rotation::R270)),
|
||||||
|
Transform::new(true, None),
|
||||||
|
Transform::new(true, Some(Rotation::R90)),
|
||||||
|
Transform::new(true, Some(Rotation::R180)),
|
||||||
|
Transform::new(true, Some(Rotation::R270)),
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for Transform {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
flip: false,
|
||||||
|
rotation: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)]
|
||||||
|
enum Position {
|
||||||
|
Down,
|
||||||
|
Left,
|
||||||
|
Right,
|
||||||
|
Up,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Position {
|
||||||
|
fn opposite(&self) -> Self {
|
||||||
|
match self {
|
||||||
|
Self::Down => Self::Up,
|
||||||
|
Self::Left => Self::Right,
|
||||||
|
Self::Right => Self::Left,
|
||||||
|
Self::Up => Self::Down,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn ordered() -> [Self; 4] {
|
||||||
|
[Self::Down, Self::Left, Self::Right, Self::Up]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const TILE_WIDTH: usize = 10;
|
||||||
|
const TILE_HEIGHT: usize = 10;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
struct Tile {
|
struct Tile {
|
||||||
id: u64,
|
id: u64,
|
||||||
edges: [Vec<bool>; 4],
|
cells: [[bool; TILE_WIDTH]; TILE_HEIGHT],
|
||||||
reversed_edges: [Vec<bool>; 4],
|
transform: Transform,
|
||||||
|
}
|
||||||
|
|
||||||
|
type Borders = [Vec<bool>; 4];
|
||||||
|
|
||||||
|
impl Tile {
|
||||||
|
fn with_transform(&self, transform: Transform) -> Self {
|
||||||
|
let mut res = self.clone();
|
||||||
|
res.transform = transform;
|
||||||
|
res
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the tile's 4 borders, according to its current transformation.
|
||||||
|
///
|
||||||
|
/// See [`Self::borders_with_transform()`] for more details
|
||||||
|
fn borders(&self) -> Borders {
|
||||||
|
self.borders_with_transform(self.transform)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the tile's 4 borders, according to the provided transformation.
|
||||||
|
///
|
||||||
|
/// Each border is associated with its position on the tile
|
||||||
|
fn borders_with_transform(&self, transform: Transform) -> Borders {
|
||||||
|
let mut up = Vec::new();
|
||||||
|
let mut down = Vec::new();
|
||||||
|
for k in 0..TILE_WIDTH {
|
||||||
|
up.push(self.get_with_transform(0, k, transform));
|
||||||
|
down.push(self.get_with_transform(TILE_HEIGHT - 1, k, transform));
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut left = Vec::new();
|
||||||
|
let mut right = Vec::new();
|
||||||
|
for k in 0..TILE_HEIGHT {
|
||||||
|
left.push(self.get_with_transform(k, 0, transform));
|
||||||
|
right.push(self.get_with_transform(k, TILE_WIDTH - 1, transform));
|
||||||
|
}
|
||||||
|
|
||||||
|
// NOTE: the ordering is important, must use the enum's integer representations as indices
|
||||||
|
[down, left, right, up]
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the pixel at indices (i, j) in the tile.
|
||||||
|
///
|
||||||
|
/// Uses the tile's current `self.transform`.
|
||||||
|
fn get(&self, i: usize, j: usize) -> bool {
|
||||||
|
self.get_with_transform(i, j, self.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 {
|
||||||
|
if let Some(rotation) = transform.rotation {
|
||||||
|
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]
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns a list of neighbour tiles, along with the offset where they'd fit compared to the
|
||||||
|
/// current tile (depending on which borders match)
|
||||||
|
fn neighbours(&self, tiles: &[Tile]) -> Vec<(Position, Tile)> {
|
||||||
|
let borders = &self.borders();
|
||||||
|
|
||||||
|
tiles
|
||||||
|
.iter()
|
||||||
|
.filter(|other| *other != self)
|
||||||
|
.flat_map(|other| {
|
||||||
|
Transform::all().into_iter().filter_map(move |transform| {
|
||||||
|
let other_borders = other.borders_with_transform(transform);
|
||||||
|
|
||||||
|
for (bord, pos) in other_borders.iter().zip(Position::ordered().iter()) {
|
||||||
|
let opposite = pos.opposite();
|
||||||
|
|
||||||
|
if bord == &borders[opposite as usize] {
|
||||||
|
return Some((opposite, other.with_transform(transform)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
None
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::cmp::PartialEq for Tile {
|
impl std::cmp::PartialEq for Tile {
|
||||||
|
@ -64,8 +218,6 @@ impl std::str::FromStr for Tile {
|
||||||
type Err = anyhow::Error;
|
type Err = anyhow::Error;
|
||||||
|
|
||||||
fn from_str(s: &str) -> Result<Self> {
|
fn from_str(s: &str) -> Result<Self> {
|
||||||
const LINE_LENGTH: usize = 10;
|
|
||||||
|
|
||||||
let mut lines = s.lines();
|
let mut lines = s.lines();
|
||||||
|
|
||||||
let title = lines.next().context("couldn't find line with tile ID")?;
|
let title = lines.next().context("couldn't find line with tile ID")?;
|
||||||
|
@ -73,7 +225,7 @@ impl std::str::FromStr for Tile {
|
||||||
let colon = title.find(':').unwrap();
|
let colon = title.find(':').unwrap();
|
||||||
let id = title[(space + 1)..colon].parse()?;
|
let id = title[(space + 1)..colon].parse()?;
|
||||||
|
|
||||||
let mut edges = [vec![], vec![], vec![], vec![]];
|
let mut cells = [[false; TILE_WIDTH]; TILE_HEIGHT];
|
||||||
|
|
||||||
lines
|
lines
|
||||||
.enumerate()
|
.enumerate()
|
||||||
|
@ -85,18 +237,7 @@ impl std::str::FromStr for Tile {
|
||||||
_ => return Err(anyhow!("unknown char `{}` while parsing tile", c)),
|
_ => return Err(anyhow!("unknown char `{}` while parsing tile", c)),
|
||||||
};
|
};
|
||||||
|
|
||||||
if i == 0 {
|
cells[i][j] = c;
|
||||||
edges[0].push(c);
|
|
||||||
}
|
|
||||||
if j == 0 {
|
|
||||||
edges[1].push(c);
|
|
||||||
}
|
|
||||||
if i == (LINE_LENGTH - 1) {
|
|
||||||
edges[2].push(c);
|
|
||||||
}
|
|
||||||
if j == (LINE_LENGTH - 1) {
|
|
||||||
edges[3].push(c);
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
})?;
|
})?;
|
||||||
|
@ -104,15 +245,10 @@ impl std::str::FromStr for Tile {
|
||||||
Ok(())
|
Ok(())
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
let mut reversed_edges = [vec![], vec![], vec![], vec![]];
|
|
||||||
for (i, edge) in edges.iter().enumerate() {
|
|
||||||
reversed_edges[i] = edge.iter().copied().rev().collect();
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(Tile {
|
Ok(Tile {
|
||||||
id,
|
id,
|
||||||
edges,
|
cells,
|
||||||
reversed_edges,
|
transform: Transform::default(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue