parent
b6ff7fb54b
commit
574e1422af
@ -0,0 +1,121 @@ |
|||||||
|
use ndarray::Array2; |
||||||
|
|
||||||
|
use crate::locations::{CellLocation, MINICUBE_DIFF_ARRAY}; |
||||||
|
|
||||||
|
struct CellInfo { |
||||||
|
is_alive: bool, // alive = black, dead = white
|
||||||
|
alive_minicube_count: usize, |
||||||
|
} |
||||||
|
|
||||||
|
impl Default for CellInfo { |
||||||
|
fn default() -> CellInfo { |
||||||
|
CellInfo { |
||||||
|
is_alive: false, |
||||||
|
alive_minicube_count: 0, |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
impl CellInfo { |
||||||
|
pub fn set_alive(&mut self) { |
||||||
|
self.is_alive = true; |
||||||
|
} |
||||||
|
|
||||||
|
pub fn add_alive_minicube(&mut self) { |
||||||
|
self.alive_minicube_count += 1; |
||||||
|
} |
||||||
|
|
||||||
|
pub fn set_dead(&mut self) { |
||||||
|
self.is_alive = false; |
||||||
|
} |
||||||
|
|
||||||
|
pub fn add_dead_minicube(&mut self) { |
||||||
|
self.alive_minicube_count -= 1; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
pub struct Game { |
||||||
|
board: Array2<CellInfo>, |
||||||
|
corner: CellLocation, |
||||||
|
} |
||||||
|
|
||||||
|
impl Game { |
||||||
|
fn make_alive(&mut self, cell_location: CellLocation) { |
||||||
|
self.board[cell_location].set_alive(); |
||||||
|
for &diff in &MINICUBE_DIFF_ARRAY { |
||||||
|
self.board[cell_location + diff].add_alive_minicube(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
fn make_dead(&mut self, cell_location: CellLocation) { |
||||||
|
self.board[cell_location].set_dead(); |
||||||
|
for &diff in &MINICUBE_DIFF_ARRAY { |
||||||
|
self.board[cell_location + diff].add_dead_minicube(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
pub fn next_step(&mut self) -> usize { |
||||||
|
let mut new_alive: Vec<_> = vec![]; |
||||||
|
let mut new_dead: Vec<_> = vec![]; |
||||||
|
for x in 0..self.corner.x { |
||||||
|
for y in 0..self.corner.y { |
||||||
|
let location = CellLocation { x, y }; |
||||||
|
match &self.board[location] { |
||||||
|
CellInfo { is_alive: true, alive_minicube_count } if *alive_minicube_count == 0 || *alive_minicube_count > 2 => { |
||||||
|
new_dead.push(location); |
||||||
|
}, |
||||||
|
CellInfo { is_alive: false, alive_minicube_count: 2 } => { |
||||||
|
new_alive.push(location); |
||||||
|
}, |
||||||
|
_ => {} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
let changes_count = new_alive.len() + new_dead.len(); |
||||||
|
for location in new_alive { |
||||||
|
self.make_alive(location); |
||||||
|
} |
||||||
|
for location in new_dead { |
||||||
|
self.make_dead(location); |
||||||
|
} |
||||||
|
|
||||||
|
changes_count |
||||||
|
} |
||||||
|
|
||||||
|
pub fn from_input(input_data: &[(i16, i16)], max_number_of_steps: usize) -> Self { |
||||||
|
let min_x = input_data.iter().map(|(x, _y)| *x).min().unwrap(); |
||||||
|
let max_x = input_data.iter().map(|(x, _y)| *x).max().unwrap(); |
||||||
|
let min_y = input_data.iter().map(|(_x, y)| *y).min().unwrap(); |
||||||
|
let max_y = input_data.iter().map(|(_x, y)| *y).max().unwrap(); |
||||||
|
let input_size_x = max_x - min_x; |
||||||
|
let input_size_y = max_y - min_y; |
||||||
|
let size_x = (input_size_x as usize) + max_number_of_steps * 2; |
||||||
|
let size_y = (input_size_y as usize) + max_number_of_steps * 2; |
||||||
|
let offset_x = (max_number_of_steps as i16) - min_x; |
||||||
|
let offset_y = (max_number_of_steps as i16) - min_y; |
||||||
|
let corner = CellLocation { |
||||||
|
x: size_x, |
||||||
|
y: size_y, |
||||||
|
}; |
||||||
|
let board = Array2::default((corner.x, corner.y)); |
||||||
|
let mut game = Game { |
||||||
|
board, |
||||||
|
corner, |
||||||
|
}; |
||||||
|
|
||||||
|
for (input_x, input_y) in input_data { |
||||||
|
game.make_alive(CellLocation { |
||||||
|
x: (input_x + offset_x) as usize, |
||||||
|
y: (input_y + offset_y) as usize, |
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
return game; |
||||||
|
} |
||||||
|
|
||||||
|
pub fn get_alive_count(&self) -> usize { |
||||||
|
(&self.board).iter().filter(|&cell| cell.is_alive).count() |
||||||
|
} |
||||||
|
} |
||||||
|
|
@ -0,0 +1,49 @@ |
|||||||
|
use std::ops::{Add, Index, IndexMut}; |
||||||
|
|
||||||
|
use ndarray::Array2; |
||||||
|
|
||||||
|
#[derive(Copy, Clone)] |
||||||
|
pub struct CellDiff { |
||||||
|
x: i8, |
||||||
|
y: i8, |
||||||
|
} |
||||||
|
|
||||||
|
pub const MINICUBE_DIFF_ARRAY: [CellDiff; 6] = [ |
||||||
|
CellDiff { x: -1, y: 0 }, |
||||||
|
CellDiff { x: 0, y: 1 }, |
||||||
|
CellDiff { x: 1, y: 1 }, |
||||||
|
CellDiff { x: 1, y: 0 }, |
||||||
|
CellDiff { x: 0, y: -1 }, |
||||||
|
CellDiff { x: -1, y: -1 }, |
||||||
|
]; |
||||||
|
|
||||||
|
#[derive(Copy, Clone)] |
||||||
|
pub struct CellLocation { |
||||||
|
pub x: usize, |
||||||
|
pub y: usize, |
||||||
|
} |
||||||
|
|
||||||
|
impl Add<CellDiff> for CellLocation { |
||||||
|
type Output = Self; |
||||||
|
|
||||||
|
fn add(self, other: CellDiff) -> Self { |
||||||
|
CellLocation { |
||||||
|
x: (self.x as isize + other.x as isize) as usize, |
||||||
|
y: (self.y as isize + other.y as isize) as usize, |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
impl<T> Index<CellLocation> for Array2<T> { |
||||||
|
type Output = T; |
||||||
|
fn index<'a>(&'a self, cell_location: CellLocation) -> &'a T { |
||||||
|
&self[[cell_location.x, cell_location.y]] |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
impl<T> IndexMut<CellLocation> for Array2<T> { |
||||||
|
fn index_mut<'a>(&'a mut self, cell_location: CellLocation) -> &'a mut T { |
||||||
|
&mut self[[cell_location.x, cell_location.y]] |
||||||
|
} |
||||||
|
} |
||||||
|
|
@ -1,35 +1,47 @@ |
|||||||
use std::collections::HashSet; |
use std::collections::HashSet; |
||||||
use std::io::{self, BufRead}; |
use std::io::{self, BufRead}; |
||||||
|
|
||||||
|
mod game; |
||||||
|
mod locations; |
||||||
|
|
||||||
|
use crate::game::Game; |
||||||
|
|
||||||
|
const MAX_NUMBER_OF_STEPS: usize = 100; |
||||||
|
|
||||||
fn main() { |
fn main() { |
||||||
let stdin = io::stdin(); |
let stdin = io::stdin(); |
||||||
let mut changed_tiles = HashSet::new(); |
let mut alive_tiles = HashSet::new(); |
||||||
for line_result in stdin.lock().lines() { |
for line_result in stdin.lock().lines() { |
||||||
let processed_line = line_result.unwrap().replace("nw", "7").replace("ne", "9").replace("sw", "1").replace("se", "3"); |
let processed_line = line_result.unwrap().replace("nw", "n").replace("se", "s"); |
||||||
let mut x = 0; |
let mut x = 0i16; |
||||||
let mut y = 0; |
let mut y = 0i16; |
||||||
for ch in processed_line.chars() { |
for ch in processed_line.chars() { |
||||||
x += match ch { |
x += match ch { |
||||||
'e' => 2, |
'e' => 1, |
||||||
'w' => -2, |
'w' => -1, |
||||||
'9' | '3' => 1, |
|
||||||
'7' | '1' => -1, |
|
||||||
_ => 0, |
_ => 0, |
||||||
}; |
}; |
||||||
y += match ch { |
y += match ch { |
||||||
'9' | '7' => 1, |
'n' => 1, |
||||||
'3' | '1' => -1, |
's' => -1, |
||||||
_ => 0, |
_ => 0, |
||||||
}; |
}; |
||||||
} |
} |
||||||
|
|
||||||
let tile = (x, y); |
let tile = (x, y); |
||||||
if changed_tiles.contains(&tile) { |
if alive_tiles.contains(&tile) { |
||||||
changed_tiles.remove(&tile); |
alive_tiles.remove(&tile); |
||||||
} else { |
} else { |
||||||
changed_tiles.insert(tile); |
alive_tiles.insert(tile); |
||||||
} |
} |
||||||
} |
} |
||||||
|
|
||||||
println!("{}", changed_tiles.len()); |
let mut game = Game::from_input(&alive_tiles.iter().cloned().collect::<Vec<_>>(), MAX_NUMBER_OF_STEPS); |
||||||
|
|
||||||
|
println!("{}", game.get_alive_count()); |
||||||
|
|
||||||
|
for i in 1..=MAX_NUMBER_OF_STEPS { |
||||||
|
game.next_step(); |
||||||
|
println!("Day {}: {}", i, game.get_alive_count()); |
||||||
|
} |
||||||
} |
} |
||||||
|
Loading…
Reference in new issue