mirror of
https://github.com/bertptrs/adventofcode.git
synced 2025-12-25 21:00:31 +01:00
Finally a correct implementation
This commit is contained in:
@@ -2,13 +2,42 @@ use std::collections::HashMap;
|
|||||||
use std::collections::HashSet;
|
use std::collections::HashSet;
|
||||||
use std::io::Read;
|
use std::io::Read;
|
||||||
|
|
||||||
|
use regex::bytes::Regex;
|
||||||
|
|
||||||
use crate::common::Lines;
|
use crate::common::Lines;
|
||||||
use crate::Solution;
|
use crate::Solution;
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
struct Tile {
|
struct Tile {
|
||||||
grid: Vec<Vec<bool>>,
|
grid: Vec<Vec<bool>>,
|
||||||
id: u64,
|
id: u64,
|
||||||
|
// NESW order
|
||||||
sides: [Vec<bool>; 4],
|
sides: [Vec<bool>; 4],
|
||||||
|
rotations: u8,
|
||||||
|
flipped: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Tile {
|
||||||
|
/// Rotate the slice once to the left. Affects
|
||||||
|
fn rotate(&mut self) {
|
||||||
|
self.rotations = (self.rotations + 1) % 4;
|
||||||
|
self.sides.rotate_right(1);
|
||||||
|
self.sides[0].reverse();
|
||||||
|
self.sides[2].reverse();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Horizontally flip the tile
|
||||||
|
fn flip(&mut self) {
|
||||||
|
debug_assert!(!self.flipped);
|
||||||
|
self.flipped = !self.flipped;
|
||||||
|
self.sides[0].reverse();
|
||||||
|
self.sides[2].reverse();
|
||||||
|
self.sides.swap(1, 3);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn len(&self) -> usize {
|
||||||
|
self.grid.len() - 2
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn read_input(input: &mut dyn Read) -> HashMap<u64, Tile> {
|
fn read_input(input: &mut dyn Read) -> HashMap<u64, Tile> {
|
||||||
@@ -36,7 +65,13 @@ fn read_input(input: &mut dyn Read) -> HashMap<u64, Tile> {
|
|||||||
grid.iter().map(|v| v[0]).collect(),
|
grid.iter().map(|v| v[0]).collect(),
|
||||||
];
|
];
|
||||||
|
|
||||||
let tile = Tile { grid, sides, id };
|
let tile = Tile {
|
||||||
|
grid,
|
||||||
|
sides,
|
||||||
|
id,
|
||||||
|
rotations: 0,
|
||||||
|
flipped: false,
|
||||||
|
};
|
||||||
|
|
||||||
tiles.insert(id, tile);
|
tiles.insert(id, tile);
|
||||||
}
|
}
|
||||||
@@ -96,6 +131,219 @@ fn rev_eq(a: &[bool], b: &[bool]) -> bool {
|
|||||||
a.iter().zip(b.iter().rev()).all(|(&a, &b)| a == b)
|
a.iter().zip(b.iter().rev()).all(|(&a, &b)| a == b)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn complete_row(
|
||||||
|
mut first: u64,
|
||||||
|
neighbours: &HashMap<u64, Vec<u64>>,
|
||||||
|
tiles: &mut HashMap<u64, Tile>,
|
||||||
|
used_tiles: &mut HashSet<u64>,
|
||||||
|
) -> Vec<u64> {
|
||||||
|
let mut row = vec![first];
|
||||||
|
used_tiles.insert(first);
|
||||||
|
|
||||||
|
loop {
|
||||||
|
let last = first;
|
||||||
|
let mut next = None;
|
||||||
|
|
||||||
|
for n in &neighbours[&last] {
|
||||||
|
if used_tiles.contains(n) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
for _ in 0..4 {
|
||||||
|
if &tiles[&last].sides[1] == &tiles[n].sides[3] {
|
||||||
|
break;
|
||||||
|
} else if rev_eq(&tiles[&last].sides[1], &tiles[n].sides[3]) {
|
||||||
|
let tile = tiles.get_mut(n).unwrap();
|
||||||
|
|
||||||
|
// Vertical flip == horizontal flip + 180
|
||||||
|
tile.rotate();
|
||||||
|
tile.rotate();
|
||||||
|
tile.flip();
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
tiles.get_mut(&n).unwrap().rotate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if &tiles[&last].sides[1] == &tiles[n].sides[3] {
|
||||||
|
// This tile matches, add it
|
||||||
|
next = Some(*n);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(next) = next {
|
||||||
|
row.push(next);
|
||||||
|
used_tiles.insert(next);
|
||||||
|
first = next;
|
||||||
|
} else {
|
||||||
|
return row;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn compute_image(
|
||||||
|
neighbours: &HashMap<u64, Vec<u64>>,
|
||||||
|
matching: HashMap<Vec<bool>, Vec<u64>>,
|
||||||
|
tiles: &mut HashMap<u64, Tile>,
|
||||||
|
) -> Vec<Vec<u64>> {
|
||||||
|
let mut corner = Vec::new();
|
||||||
|
|
||||||
|
for (&id, connected) in neighbours {
|
||||||
|
if connected.len() == 2 && !corner.contains(&id) {
|
||||||
|
corner.push(id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Randomly put down the first corner
|
||||||
|
let mut used_tiles = HashSet::new();
|
||||||
|
|
||||||
|
let corner_tile = tiles.get_mut(&corner[0]).unwrap();
|
||||||
|
|
||||||
|
// Rotate it until it fits
|
||||||
|
while matching[&corner_tile.sides[2]].len() != 2 {
|
||||||
|
corner_tile.rotate();
|
||||||
|
}
|
||||||
|
|
||||||
|
if matching[&corner_tile.sides[1]].len() != 2 {
|
||||||
|
corner_tile.flip();
|
||||||
|
}
|
||||||
|
|
||||||
|
debug_assert_eq!(matching[&corner_tile.sides[1]].len(), 2);
|
||||||
|
debug_assert_eq!(matching[&corner_tile.sides[2]].len(), 2);
|
||||||
|
|
||||||
|
let rotation = corner_tile.rotations;
|
||||||
|
let sides = corner_tile.sides.clone();
|
||||||
|
|
||||||
|
let mut rows = vec![complete_row(corner[0], &neighbours, tiles, &mut used_tiles)];
|
||||||
|
|
||||||
|
debug_assert_eq!(rotation, tiles[&rows[0][0]].rotations);
|
||||||
|
debug_assert_eq!(sides, tiles[&rows[0][0]].sides);
|
||||||
|
|
||||||
|
while used_tiles.len() < tiles.len() {
|
||||||
|
let prev = rows.last().unwrap()[0];
|
||||||
|
|
||||||
|
// Should be just one tile that can go there.
|
||||||
|
let next = neighbours[&prev]
|
||||||
|
.iter()
|
||||||
|
.filter(|&n| !used_tiles.contains(n))
|
||||||
|
.next()
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
for _ in 0..4 {
|
||||||
|
if &tiles[&prev].sides[2] == &tiles[next].sides[0] {
|
||||||
|
break;
|
||||||
|
} else if rev_eq(&tiles[&prev].sides[2], &tiles[next].sides[0]) {
|
||||||
|
tiles.get_mut(next).unwrap().flip();
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
tiles.get_mut(next).unwrap().rotate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
debug_assert_eq!(&tiles[&prev].sides[2], &tiles[next].sides[0]);
|
||||||
|
|
||||||
|
rows.push(complete_row(*next, &neighbours, tiles, &mut used_tiles));
|
||||||
|
}
|
||||||
|
|
||||||
|
rows
|
||||||
|
}
|
||||||
|
|
||||||
|
fn combine_tiles(rows: &[Vec<u64>], tiles: &mut HashMap<u64, Tile>) -> Vec<Vec<u8>> {
|
||||||
|
// Fix orientation
|
||||||
|
for tile in tiles.values_mut() {
|
||||||
|
let to_rotate = tile.rotations;
|
||||||
|
|
||||||
|
for _ in 0..to_rotate {
|
||||||
|
tile.grid = rotate(&tile.grid);
|
||||||
|
}
|
||||||
|
|
||||||
|
if tile.flipped {
|
||||||
|
reverse(&mut tile.grid);
|
||||||
|
}
|
||||||
|
|
||||||
|
tile.rotations = 0;
|
||||||
|
tile.flipped = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
rows.iter()
|
||||||
|
.flat_map(|row| {
|
||||||
|
let len = tiles[row.first().unwrap()].len();
|
||||||
|
|
||||||
|
let mut rows = vec![Vec::new(); len];
|
||||||
|
|
||||||
|
for id in row {
|
||||||
|
let tile = &tiles[id];
|
||||||
|
for (r, line) in tile.grid.iter().skip(1).take(len).enumerate() {
|
||||||
|
rows[r].extend(
|
||||||
|
line.iter()
|
||||||
|
.skip(1)
|
||||||
|
.take(len)
|
||||||
|
.map(|&b| if b { b'#' } else { b'.' }),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
rows
|
||||||
|
})
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn monster_regex() -> [Regex; 3] {
|
||||||
|
[
|
||||||
|
Regex::new(&" # ".replace(' ', ".")).unwrap(),
|
||||||
|
Regex::new(&"# ## ## ###".replace(' ', ".")).unwrap(),
|
||||||
|
Regex::new(&" # # # # # # ".replace(' ', ".")).unwrap(),
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
fn rotate<T: Copy>(image: &[Vec<T>]) -> Vec<Vec<T>> {
|
||||||
|
let mut new = vec![Vec::new(); image[0].len()];
|
||||||
|
|
||||||
|
for (c, target) in new.iter_mut().enumerate() {
|
||||||
|
target.extend(image.iter().rev().map(|r| r[c]));
|
||||||
|
}
|
||||||
|
|
||||||
|
new
|
||||||
|
}
|
||||||
|
|
||||||
|
fn reverse<T>(image: &mut [Vec<T>]) {
|
||||||
|
for row in image {
|
||||||
|
row.reverse();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn replace_monster(image: &mut [Vec<u8>]) {
|
||||||
|
let searchers = monster_regex();
|
||||||
|
|
||||||
|
for i in 1..(image.len() - 1) {
|
||||||
|
let mut start = 0;
|
||||||
|
|
||||||
|
while let Some(found) = searchers[1].find_at(&image[i], start) {
|
||||||
|
start = found.start() + 1;
|
||||||
|
let range = found.range();
|
||||||
|
|
||||||
|
if searchers[2].is_match(&image[i + 1][range.clone()])
|
||||||
|
&& searchers[0].is_match(&image[i - 1][range.clone()])
|
||||||
|
{
|
||||||
|
image[(i - 1)..=(i + 1)]
|
||||||
|
.iter_mut()
|
||||||
|
.zip(&searchers)
|
||||||
|
.for_each(|(line, expr)| {
|
||||||
|
line[range.clone()]
|
||||||
|
.iter_mut()
|
||||||
|
.zip(expr.as_str().as_bytes().iter())
|
||||||
|
.for_each(|(b, &r)| {
|
||||||
|
if *b == r {
|
||||||
|
*b = b'O';
|
||||||
|
}
|
||||||
|
})
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct Day20;
|
pub struct Day20;
|
||||||
|
|
||||||
@@ -111,6 +359,34 @@ impl Solution for Day20 {
|
|||||||
.fold(1, |a, b| a * b)
|
.fold(1, |a, b| a * b)
|
||||||
.to_string()
|
.to_string()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn part2(&mut self, input: &mut dyn Read) -> String {
|
||||||
|
let mut tiles = read_input(input);
|
||||||
|
let matching = compute_matching(tiles.values());
|
||||||
|
let neighbours = compute_neighbours(matching.values());
|
||||||
|
|
||||||
|
let rows = compute_image(&neighbours, matching, &mut tiles);
|
||||||
|
|
||||||
|
let mut image = combine_tiles(&rows, &mut tiles);
|
||||||
|
|
||||||
|
for _ in 0..4 {
|
||||||
|
replace_monster(&mut image);
|
||||||
|
image = rotate(&image);
|
||||||
|
}
|
||||||
|
|
||||||
|
reverse(&mut image);
|
||||||
|
|
||||||
|
for _ in 0..4 {
|
||||||
|
replace_monster(&mut image);
|
||||||
|
image = rotate(&image);
|
||||||
|
}
|
||||||
|
|
||||||
|
image
|
||||||
|
.iter()
|
||||||
|
.map(|b| bytecount::count(&b, b'#'))
|
||||||
|
.sum::<usize>()
|
||||||
|
.to_string()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
@@ -125,4 +401,84 @@ mod tests {
|
|||||||
fn sample_part1() {
|
fn sample_part1() {
|
||||||
test_implementation!(Day20, 1, SAMPLE, 20899048083289u64);
|
test_implementation!(Day20, 1, SAMPLE, 20899048083289u64);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn sample_part2() {
|
||||||
|
test_implementation!(Day20, 2, SAMPLE, 273);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_tile_rotate() {
|
||||||
|
let mut tile = Tile {
|
||||||
|
grid: Vec::new(),
|
||||||
|
id: 1,
|
||||||
|
sides: [
|
||||||
|
vec![false, true],
|
||||||
|
vec![false, false, true],
|
||||||
|
vec![false, true],
|
||||||
|
vec![true, true, false],
|
||||||
|
],
|
||||||
|
rotations: 0,
|
||||||
|
flipped: false,
|
||||||
|
};
|
||||||
|
|
||||||
|
tile.rotate();
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
tile.sides,
|
||||||
|
[
|
||||||
|
vec![false, true, true],
|
||||||
|
vec![false, true],
|
||||||
|
vec![true, false, false],
|
||||||
|
vec![false, true],
|
||||||
|
]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_tile_flip() {
|
||||||
|
let mut tile = Tile {
|
||||||
|
grid: Vec::new(),
|
||||||
|
id: 1,
|
||||||
|
sides: [
|
||||||
|
vec![false, true],
|
||||||
|
vec![false, false, true],
|
||||||
|
vec![false, true],
|
||||||
|
vec![true, true, false],
|
||||||
|
],
|
||||||
|
rotations: 0,
|
||||||
|
flipped: false,
|
||||||
|
};
|
||||||
|
|
||||||
|
tile.flip();
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
tile.sides,
|
||||||
|
[
|
||||||
|
vec![true, false],
|
||||||
|
vec![true, true, false],
|
||||||
|
vec![true, false],
|
||||||
|
vec![false, false, true],
|
||||||
|
]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_rotate() {
|
||||||
|
let sample: Vec<Vec<u8>> = vec![
|
||||||
|
b"###".as_ref().to_owned(),
|
||||||
|
b" #".as_ref().to_owned(),
|
||||||
|
b"# #".as_ref().to_owned(),
|
||||||
|
];
|
||||||
|
|
||||||
|
let rotated = rotate(&sample);
|
||||||
|
|
||||||
|
let correct: Vec<Vec<u8>> = vec![
|
||||||
|
b"# #".as_ref().to_owned(),
|
||||||
|
b" #".as_ref().to_owned(),
|
||||||
|
b"###".as_ref().to_owned(),
|
||||||
|
];
|
||||||
|
|
||||||
|
assert_eq!(correct, rotated);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user