Added Day 20 part 1
This commit is contained in:
parent
f866ad67b3
commit
66fa7e34c8
File diff suppressed because it is too large
Load Diff
152
inputs/test.txt
152
inputs/test.txt
|
@ -1,47 +1,107 @@
|
||||||
42: 9 14 | 10 1
|
Tile 2311:
|
||||||
9: 14 27 | 1 26
|
..##.#..#.
|
||||||
10: 23 14 | 28 1
|
##..#.....
|
||||||
1: "a"
|
#...##..#.
|
||||||
11: 42 31
|
####.#...#
|
||||||
5: 1 14 | 15 1
|
##.##.###.
|
||||||
19: 14 1 | 14 14
|
##...#.###
|
||||||
12: 24 14 | 19 1
|
.#.#.#..##
|
||||||
16: 15 1 | 14 14
|
..#....#..
|
||||||
31: 14 17 | 1 13
|
###...#.#.
|
||||||
6: 14 14 | 1 14
|
..###..###
|
||||||
2: 1 24 | 14 4
|
|
||||||
0: 8 11
|
|
||||||
13: 14 3 | 1 12
|
|
||||||
15: 1 | 14
|
|
||||||
17: 14 2 | 1 7
|
|
||||||
23: 25 1 | 22 14
|
|
||||||
28: 16 1
|
|
||||||
4: 1 1
|
|
||||||
20: 14 14 | 1 15
|
|
||||||
3: 5 14 | 16 1
|
|
||||||
27: 1 6 | 14 18
|
|
||||||
14: "b"
|
|
||||||
21: 14 1 | 1 14
|
|
||||||
25: 1 1 | 1 14
|
|
||||||
22: 14 14
|
|
||||||
8: 42
|
|
||||||
26: 14 22 | 1 20
|
|
||||||
18: 15 15
|
|
||||||
7: 14 5 | 1 21
|
|
||||||
24: 14 1
|
|
||||||
|
|
||||||
abbbbbabbbaaaababbaabbbbabababbbabbbbbbabaaaa
|
Tile 1951:
|
||||||
bbabbbbaabaabba
|
#.##...##.
|
||||||
babbbbaabbbbbabbbbbbaabaaabaaa
|
#.####...#
|
||||||
aaabbbbbbaaaabaababaabababbabaaabbababababaaa
|
.....#..##
|
||||||
bbbbbbbaaaabbbbaaabbabaaa
|
#...######
|
||||||
bbbababbbbaaaaaaaabbababaaababaabab
|
.##.#....#
|
||||||
ababaaaaaabaaab
|
.###.#####
|
||||||
ababaaaaabbbaba
|
###.##.##.
|
||||||
baabbaaaabbaaaababbaababb
|
.###....#.
|
||||||
abbbbabbbbaaaababbbbbbaaaababb
|
..#.#..#.#
|
||||||
aaaaabbaabaaaaababaa
|
#...##.#..
|
||||||
aaaabbaaaabbaaa
|
|
||||||
aaaabbaabbaaaaaaabbbabbbaaabbaabaaa
|
Tile 1171:
|
||||||
babaaabbbaaabaababbaabababaaab
|
####...##.
|
||||||
aabbbbbaabbbaaaaaabbbbbababaaaaabbaaabba
|
#..##.#..#
|
||||||
|
##.#..#.#.
|
||||||
|
.###.####.
|
||||||
|
..###.####
|
||||||
|
.##....##.
|
||||||
|
.#...####.
|
||||||
|
#.##.####.
|
||||||
|
####..#...
|
||||||
|
.....##...
|
||||||
|
|
||||||
|
Tile 1427:
|
||||||
|
###.##.#..
|
||||||
|
.#..#.##..
|
||||||
|
.#.##.#..#
|
||||||
|
#.#.#.##.#
|
||||||
|
....#...##
|
||||||
|
...##..##.
|
||||||
|
...#.#####
|
||||||
|
.#.####.#.
|
||||||
|
..#..###.#
|
||||||
|
..##.#..#.
|
||||||
|
|
||||||
|
Tile 1489:
|
||||||
|
##.#.#....
|
||||||
|
..##...#..
|
||||||
|
.##..##...
|
||||||
|
..#...#...
|
||||||
|
#####...#.
|
||||||
|
#..#.#.#.#
|
||||||
|
...#.#.#..
|
||||||
|
##.#...##.
|
||||||
|
..##.##.##
|
||||||
|
###.##.#..
|
||||||
|
|
||||||
|
Tile 2473:
|
||||||
|
#....####.
|
||||||
|
#..#.##...
|
||||||
|
#.##..#...
|
||||||
|
######.#.#
|
||||||
|
.#...#.#.#
|
||||||
|
.#########
|
||||||
|
.###.#..#.
|
||||||
|
########.#
|
||||||
|
##...##.#.
|
||||||
|
..###.#.#.
|
||||||
|
|
||||||
|
Tile 2971:
|
||||||
|
..#.#....#
|
||||||
|
#...###...
|
||||||
|
#.#.###...
|
||||||
|
##.##..#..
|
||||||
|
.#####..##
|
||||||
|
.#..####.#
|
||||||
|
#..#.#..#.
|
||||||
|
..####.###
|
||||||
|
..#.#.###.
|
||||||
|
...#.#.#.#
|
||||||
|
|
||||||
|
Tile 2729:
|
||||||
|
...#.#.#.#
|
||||||
|
####.#....
|
||||||
|
..#.#.....
|
||||||
|
....#..#.#
|
||||||
|
.##..##.#.
|
||||||
|
.#.####...
|
||||||
|
####.#.#..
|
||||||
|
##.####...
|
||||||
|
##..#.##..
|
||||||
|
#.##...##.
|
||||||
|
|
||||||
|
Tile 3079:
|
||||||
|
#.#.#####.
|
||||||
|
.#..######
|
||||||
|
..#.......
|
||||||
|
######....
|
||||||
|
####.#..#.
|
||||||
|
.#...#.##.
|
||||||
|
#.#####.##
|
||||||
|
..#.###...
|
||||||
|
..#.......
|
||||||
|
..#.###...
|
||||||
|
|
|
@ -0,0 +1,395 @@
|
||||||
|
use std::collections::HashSet;
|
||||||
|
use std::error::Error;
|
||||||
|
use std::fs::File;
|
||||||
|
use std::io::{self, BufRead};
|
||||||
|
use std::vec::Vec;
|
||||||
|
|
||||||
|
#[derive(PartialEq, Clone, Debug, Hash, Eq)]
|
||||||
|
struct Tile {
|
||||||
|
id: u64,
|
||||||
|
pixels: Vec<Vec<bool>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Tile {
|
||||||
|
fn flip_hor(&self) -> Tile {
|
||||||
|
Tile {
|
||||||
|
id: self.id,
|
||||||
|
pixels: self.pixels.iter().cloned().rev().collect(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn flip_vert(&self) -> Tile {
|
||||||
|
Tile {
|
||||||
|
id: self.id,
|
||||||
|
pixels: self
|
||||||
|
.pixels
|
||||||
|
.iter()
|
||||||
|
.map(|l| l.iter().cloned().rev().collect())
|
||||||
|
.collect(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn rotate_90(&self) -> Tile {
|
||||||
|
let len = self.pixels.len();
|
||||||
|
let mut rot_pixels = Vec::new();
|
||||||
|
for rot_y in 0..len {
|
||||||
|
let mut rot_line = Vec::new();
|
||||||
|
for rot_x in 0..len {
|
||||||
|
rot_line.push(self.pixels[len - rot_x - 1][rot_y])
|
||||||
|
}
|
||||||
|
rot_pixels.push(rot_line);
|
||||||
|
}
|
||||||
|
|
||||||
|
Tile {
|
||||||
|
id: self.id,
|
||||||
|
pixels: rot_pixels,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn rotate_180(&self) -> Tile {
|
||||||
|
self.rotate_90().rotate_90()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn rotate_270(&self) -> Tile {
|
||||||
|
self.rotate_180().rotate_90()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn all_orientations(&self) -> Vec<Tile> {
|
||||||
|
let mut res = Vec::new();
|
||||||
|
res.push(self.clone());
|
||||||
|
res.push(self.flip_hor());
|
||||||
|
//res.push(self.flip_vert());
|
||||||
|
|
||||||
|
res.push(self.rotate_90());
|
||||||
|
res.push(self.rotate_90().flip_hor());
|
||||||
|
//res.push(self.rotate_90().flip_vert());
|
||||||
|
|
||||||
|
res.push(self.rotate_180());
|
||||||
|
res.push(self.rotate_180().flip_hor());
|
||||||
|
//res.push(self.rotate_180().flip_vert());
|
||||||
|
|
||||||
|
res.push(self.rotate_270());
|
||||||
|
res.push(self.rotate_270().flip_hor());
|
||||||
|
//res.push(self.rotate_270().flip_vert());
|
||||||
|
|
||||||
|
res
|
||||||
|
}
|
||||||
|
|
||||||
|
fn matches_below(&self, other: &Tile) -> bool {
|
||||||
|
let other_bottom = other.pixels.iter().last().unwrap();
|
||||||
|
let my_top = self.pixels.iter().next().unwrap();
|
||||||
|
for i in 0..my_top.len() {
|
||||||
|
if my_top[i] != other_bottom[i] {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn matches_right_of(&self, other: &Tile) -> bool {
|
||||||
|
for y in 0..self.pixels.len() {
|
||||||
|
let other_pixel = other.pixels[y].iter().last().unwrap();
|
||||||
|
let my_pixel = self.pixels[y].iter().next().unwrap();
|
||||||
|
if my_pixel != other_pixel {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn complete_puzzle(
|
||||||
|
puzzle: Vec<Vec<Tile>>,
|
||||||
|
tiles: &HashSet<Tile>,
|
||||||
|
x: usize,
|
||||||
|
y: usize,
|
||||||
|
len: usize,
|
||||||
|
) -> Option<Vec<Vec<Tile>>> {
|
||||||
|
if tiles.len() == 0 {
|
||||||
|
return Some(puzzle);
|
||||||
|
} else if x == 0 && y == 0 {
|
||||||
|
for tile in tiles.iter() {
|
||||||
|
//println!("Trying {} as first tile", tile.id);
|
||||||
|
|
||||||
|
let mut next_tiles = tiles.clone();
|
||||||
|
next_tiles.remove(tile);
|
||||||
|
|
||||||
|
for next_tile in tile.all_orientations() {
|
||||||
|
if let Some(p) = complete_puzzle(vec![vec![next_tile]], &next_tiles, 1, 0, len) {
|
||||||
|
return Some(p);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if y == 0 {
|
||||||
|
let other = &puzzle[y][x - 1];
|
||||||
|
|
||||||
|
let next_x = if x + 1 < len { x + 1 } else { 0 };
|
||||||
|
let next_y = if x + 1 < len { 0 } else { 1 };
|
||||||
|
|
||||||
|
for tile in tiles.iter() {
|
||||||
|
let mut next_tiles = tiles.clone();
|
||||||
|
next_tiles.remove(tile);
|
||||||
|
|
||||||
|
for next_tile in tile.all_orientations() {
|
||||||
|
if next_tile.matches_right_of(other) {
|
||||||
|
//println!("{} matches right of {}", next_tile.id, other.id);
|
||||||
|
let mut next_puzzle = puzzle.clone();
|
||||||
|
next_puzzle[0].push(next_tile);
|
||||||
|
if let Some(p) = complete_puzzle(next_puzzle, &next_tiles, next_x, next_y, len)
|
||||||
|
{
|
||||||
|
return Some(p);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if x == 0 {
|
||||||
|
let other = &puzzle[y - 1][0];
|
||||||
|
|
||||||
|
for tile in tiles.iter() {
|
||||||
|
let mut next_tiles = tiles.clone();
|
||||||
|
next_tiles.remove(tile);
|
||||||
|
|
||||||
|
for next_tile in tile.all_orientations() {
|
||||||
|
if next_tile.matches_below(other) {
|
||||||
|
//println!("{} matches below of {}", next_tile.id, other.id);
|
||||||
|
let mut next_puzzle = puzzle.clone();
|
||||||
|
next_puzzle.push(vec![next_tile]);
|
||||||
|
if let Some(p) = complete_puzzle(next_puzzle, &next_tiles, 1, y, len) {
|
||||||
|
return Some(p);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
let other_top = &puzzle[y - 1][x];
|
||||||
|
let other_left = &puzzle[y][x - 1];
|
||||||
|
|
||||||
|
let next_x = if x + 1 < len { x + 1 } else { 0 };
|
||||||
|
let next_y = if x + 1 < len { y } else { y + 1 };
|
||||||
|
|
||||||
|
for tile in tiles.iter() {
|
||||||
|
let mut next_tiles = tiles.clone();
|
||||||
|
next_tiles.remove(tile);
|
||||||
|
|
||||||
|
for next_tile in tile.all_orientations() {
|
||||||
|
if next_tile.matches_below(other_top) && next_tile.matches_right_of(other_left) {
|
||||||
|
/*println!(
|
||||||
|
"{} matches right of {} and below {}",
|
||||||
|
next_tile.id, other_top.id, other_top.id
|
||||||
|
);*/
|
||||||
|
let mut next_puzzle = puzzle.clone();
|
||||||
|
next_puzzle[y].push(next_tile);
|
||||||
|
if let Some(p) = complete_puzzle(next_puzzle, &next_tiles, next_x, next_y, len)
|
||||||
|
{
|
||||||
|
return Some(p);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//println!("No match for {} {}", x, y);
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() -> Result<(), Box<dyn Error>> {
|
||||||
|
let file = File::open("inputs/day20.txt")?;
|
||||||
|
let mut lines = io::BufReader::new(file).lines().map(|l| l.unwrap());
|
||||||
|
|
||||||
|
let mut tiles: HashSet<Tile> = HashSet::new();
|
||||||
|
|
||||||
|
let mut tile_id: u64 = 0;
|
||||||
|
let mut tile_pixels: Vec<Vec<bool>> = Vec::new();
|
||||||
|
while let Some(line) = lines.next() {
|
||||||
|
if line == "" {
|
||||||
|
tiles.insert(Tile {
|
||||||
|
id: tile_id,
|
||||||
|
pixels: tile_pixels,
|
||||||
|
});
|
||||||
|
tile_pixels = Vec::new();
|
||||||
|
} else if line.starts_with("Tile ") {
|
||||||
|
tile_id = line
|
||||||
|
.strip_prefix("Tile ")
|
||||||
|
.expect("Tile keyword")
|
||||||
|
.strip_suffix(":")
|
||||||
|
.expect(": missing in tile line.")
|
||||||
|
.parse()
|
||||||
|
.expect("Unable to parse tile id");
|
||||||
|
println!("Tile ID: {}", tile_id);
|
||||||
|
} else {
|
||||||
|
let tile_line: Vec<bool> = line.chars().map(|c| c == '#').collect();
|
||||||
|
tile_pixels.push(tile_line);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
tiles.insert(Tile {
|
||||||
|
id: tile_id,
|
||||||
|
pixels: tile_pixels,
|
||||||
|
});
|
||||||
|
|
||||||
|
println!("Found {} tiles", tiles.len());
|
||||||
|
let len = (tiles.len() as f64).sqrt() as usize;
|
||||||
|
println!("Images is {} x {} tiles", len, len);
|
||||||
|
|
||||||
|
if let Some(p) = complete_puzzle(Vec::new(), &tiles, 0, 0, len) {
|
||||||
|
println!("Found a solution");
|
||||||
|
for y in 0..len {
|
||||||
|
let line = p[y]
|
||||||
|
.iter()
|
||||||
|
.map(|t| t.id.to_string())
|
||||||
|
.fold(String::new(), |x, acc| acc + " " + &x);
|
||||||
|
println!("{}", line);
|
||||||
|
}
|
||||||
|
|
||||||
|
let answer = p[0][0].id * p[0][len - 1].id * p[len - 1][0].id * p[len - 1][len - 1].id;
|
||||||
|
println!("Answer: {}", answer);
|
||||||
|
} else {
|
||||||
|
println!("Sorry nope");
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use crate::*;
|
||||||
|
#[test]
|
||||||
|
fn test_rot() {
|
||||||
|
let t = Tile {
|
||||||
|
id: 5,
|
||||||
|
pixels: vec![
|
||||||
|
vec![true, true, true],
|
||||||
|
vec![false, true, false],
|
||||||
|
vec![true, false, false],
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
let result_90 = Tile {
|
||||||
|
id: 5,
|
||||||
|
pixels: vec![
|
||||||
|
vec![true, false, true],
|
||||||
|
vec![false, true, true],
|
||||||
|
vec![false, false, true],
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
let result_180 = Tile {
|
||||||
|
id: 5,
|
||||||
|
pixels: vec![
|
||||||
|
vec![false, false, true],
|
||||||
|
vec![false, true, false],
|
||||||
|
vec![true, true, true],
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
let result_270 = Tile {
|
||||||
|
id: 5,
|
||||||
|
pixels: vec![
|
||||||
|
vec![true, false, false],
|
||||||
|
vec![true, true, false],
|
||||||
|
vec![true, false, true],
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
assert_eq!(t.rotate_90(), result_90);
|
||||||
|
assert_eq!(t.rotate_180(), result_180);
|
||||||
|
assert_eq!(t.rotate_270(), result_270);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_matches_below() {
|
||||||
|
let t1 = Tile {
|
||||||
|
id: 5,
|
||||||
|
pixels: vec![
|
||||||
|
vec![false, false, false],
|
||||||
|
vec![false, false, false],
|
||||||
|
vec![true, false, true],
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
let t2 = Tile {
|
||||||
|
id: 5,
|
||||||
|
pixels: vec![
|
||||||
|
vec![true, false, true],
|
||||||
|
vec![false, false, false],
|
||||||
|
vec![false, true, false],
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
assert!(!t1.matches_below(&t1));
|
||||||
|
assert!(!t2.matches_below(&t2));
|
||||||
|
assert!(!t1.matches_below(&t2));
|
||||||
|
assert!(t2.matches_below(&t1));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_matches_right_off() {
|
||||||
|
let t1 = Tile {
|
||||||
|
id: 5,
|
||||||
|
pixels: vec![
|
||||||
|
vec![false, false, true],
|
||||||
|
vec![false, false, false],
|
||||||
|
vec![false, false, true],
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
let t2 = Tile {
|
||||||
|
id: 5,
|
||||||
|
pixels: vec![
|
||||||
|
vec![true, false, true],
|
||||||
|
vec![false, false, true],
|
||||||
|
vec![true, false, false],
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
assert!(!t1.matches_right_of(&t1));
|
||||||
|
assert!(!t2.matches_right_of(&t2));
|
||||||
|
assert!(!t1.matches_right_of(&t2));
|
||||||
|
assert!(t2.matches_right_of(&t1));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_flip_hor() {
|
||||||
|
let t1 = Tile {
|
||||||
|
id: 5,
|
||||||
|
pixels: vec![
|
||||||
|
vec![false, false, false],
|
||||||
|
vec![false, true, false],
|
||||||
|
vec![true, true, true],
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
let result = Tile {
|
||||||
|
id: 5,
|
||||||
|
pixels: vec![
|
||||||
|
vec![true, true, true],
|
||||||
|
vec![false, true, false],
|
||||||
|
vec![false, false, false],
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
assert_eq!(t1.flip_hor(), result);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_flip_vert() {
|
||||||
|
let t1 = Tile {
|
||||||
|
id: 5,
|
||||||
|
pixels: vec![
|
||||||
|
vec![true, false, false],
|
||||||
|
vec![true, true, false],
|
||||||
|
vec![true, false, false],
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
let result = Tile {
|
||||||
|
id: 5,
|
||||||
|
pixels: vec![
|
||||||
|
vec![false, false, true],
|
||||||
|
vec![false, true, true],
|
||||||
|
vec![false, false, true],
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
assert_eq!(t1.flip_vert(), result);
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue