1
0
Fork 0
aoc2020/src/bin/day24.rs

141 lines
4.0 KiB
Rust

use std::collections::HashSet;
const INPUT: &str = include_str!("../../data/day24");
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
enum Direction {
East,
SouthEast,
SouthWest,
West,
NorthWest,
NorthEast,
}
impl Direction {
const ALL: [Direction; 6] = [
Self::East,
Self::SouthEast,
Self::SouthWest,
Self::West,
Self::NorthWest,
Self::NorthEast,
];
fn symbol(self) -> &'static str {
match self {
Self::East => "e",
Self::SouthEast => "se",
Self::SouthWest => "sw",
Self::West => "w",
Self::NorthWest => "nw",
Self::NorthEast => "ne",
}
}
fn parse_list(line: &str) -> Vec<Self> {
let mut line = line.trim();
let mut result = Vec::new();
'outer: while !line.is_empty() {
for &d in Self::ALL.iter() {
if let Some(rem) = line.strip_prefix(d.symbol()) {
result.push(d);
line = rem;
continue 'outer;
}
}
panic!("Invalid directions: {:?}", line);
}
result
}
// -> (row, col)
fn as_offset(self) -> (i32, i32) {
match self {
Self::East => (0, 1),
Self::SouthEast => (1, 1),
Self::SouthWest => (1, 0),
Self::West => (0, -1),
Self::NorthWest => (-1, -1),
Self::NorthEast => (-1, 0),
}
}
}
#[derive(Clone, Copy, PartialEq, Eq, Hash, Default, Debug)]
struct Position {
row: i32,
// the "0"-column goes from south-west to north-east through (0/0)
col: i32,
}
impl Position {
fn adjacent(self) -> impl Iterator<Item=Self> {
Direction::ALL.iter().map(move |&d| self + d)
}
}
impl std::ops::Add<Direction> for Position {
type Output = Position;
fn add(self, rhs: Direction) -> Self::Output {
let (row, col) = rhs.as_offset();
Self {
row: self.row + row,
col: self.col + col,
}
}
}
fn daily_flip(black_tiles: HashSet<Position>) -> HashSet<Position> {
let mut new_black_tiles: HashSet<Position> = HashSet::new();
let mut dont_flip_to_black: HashSet<Position> = HashSet::new();
for &tile in &black_tiles {
let mut count_adj_black = 0;
for adj_tile in tile.adjacent() {
if black_tiles.contains(&adj_tile) {
count_adj_black += 1;
} else if !new_black_tiles.contains(&adj_tile) && !dont_flip_to_black.contains(&adj_tile) {
// white adjacent: maybe needs to be flipped to black
let mut white_count_adj_black = 0;
for adj_tile_2 in adj_tile.adjacent() {
if black_tiles.contains(&adj_tile_2) {
white_count_adj_black += 1;
if white_count_adj_black > 2 { break; }
}
}
if white_count_adj_black == 2 {
new_black_tiles.insert(adj_tile);
} else {
// don't check again
dont_flip_to_black.insert(adj_tile);
}
}
}
if count_adj_black == 1 || count_adj_black == 2 {
new_black_tiles.insert(tile);
}
}
new_black_tiles
}
fn main() {
let directions = INPUT.lines().map(Direction::parse_list).collect::<Vec<_>>();
let flip_tiles = directions.iter().map(|tile| {
tile.iter().fold(Position::default(), |tile, &dir| tile + dir)
}).collect::<Vec<_>>();
let mut black_tiles = HashSet::new();
for &tile in &flip_tiles {
if black_tiles.contains(&tile) {
black_tiles.remove(&tile);
} else {
black_tiles.insert(tile);
}
}
println!("Black tiles: {}", black_tiles.len());
for _ in 0..100 {
black_tiles = daily_flip(black_tiles);
}
println!("Black tiles: {}", black_tiles.len());
}