Added Day 20 part 1

This commit is contained in:
Sebastian 2020-12-20 17:36:45 +01:00
parent f866ad67b3
commit 66fa7e34c8
3 changed files with 2228 additions and 46 deletions

1727
inputs/day20.txt Normal file

File diff suppressed because it is too large Load Diff

View File

@ -1,47 +1,107 @@
42: 9 14 | 10 1
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
Tile 2311:
..##.#..#.
##..#.....
#...##..#.
####.#...#
##.##.###.
##...#.###
.#.#.#..##
..#....#..
###...#.#.
..###..###
abbbbbabbbaaaababbaabbbbabababbbabbbbbbabaaaa
bbabbbbaabaabba
babbbbaabbbbbabbbbbbaabaaabaaa
aaabbbbbbaaaabaababaabababbabaaabbababababaaa
bbbbbbbaaaabbbbaaabbabaaa
bbbababbbbaaaaaaaabbababaaababaabab
ababaaaaaabaaab
ababaaaaabbbaba
baabbaaaabbaaaababbaababb
abbbbabbbbaaaababbbbbbaaaababb
aaaaabbaabaaaaababaa
aaaabbaaaabbaaa
aaaabbaabbaaaaaaabbbabbbaaabbaabaaa
babaaabbbaaabaababbaabababaaab
aabbbbbaabbbaaaaaabbbbbababaaaaabbaaabba
Tile 1951:
#.##...##.
#.####...#
.....#..##
#...######
.##.#....#
.###.#####
###.##.##.
.###....#.
..#.#..#.#
#...##.#..
Tile 1171:
####...##.
#..##.#..#
##.#..#.#.
.###.####.
..###.####
.##....##.
.#...####.
#.##.####.
####..#...
.....##...
Tile 1427:
###.##.#..
.#..#.##..
.#.##.#..#
#.#.#.##.#
....#...##
...##..##.
...#.#####
.#.####.#.
..#..###.#
..##.#..#.
Tile 1489:
##.#.#....
..##...#..
.##..##...
..#...#...
#####...#.
#..#.#.#.#
...#.#.#..
##.#...##.
..##.##.##
###.##.#..
Tile 2473:
#....####.
#..#.##...
#.##..#...
######.#.#
.#...#.#.#
.#########
.###.#..#.
########.#
##...##.#.
..###.#.#.
Tile 2971:
..#.#....#
#...###...
#.#.###...
##.##..#..
.#####..##
.#..####.#
#..#.#..#.
..####.###
..#.#.###.
...#.#.#.#
Tile 2729:
...#.#.#.#
####.#....
..#.#.....
....#..#.#
.##..##.#.
.#.####...
####.#.#..
##.####...
##..#.##..
#.##...##.
Tile 3079:
#.#.#####.
.#..######
..#.......
######....
####.#..#.
.#...#.##.
#.#####.##
..#.###...
..#.......
..#.###...

395
src/bin/day20.rs Normal file
View File

@ -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);
}
}