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::io::{self, BufRead}; |
||||
|
||||
mod game; |
||||
mod locations; |
||||
|
||||
use crate::game::Game; |
||||
|
||||
const MAX_NUMBER_OF_STEPS: usize = 100; |
||||
|
||||
fn main() { |
||||
let stdin = io::stdin(); |
||||
let mut changed_tiles = HashSet::new(); |
||||
let mut alive_tiles = HashSet::new(); |
||||
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 mut x = 0; |
||||
let mut y = 0; |
||||
let processed_line = line_result.unwrap().replace("nw", "n").replace("se", "s"); |
||||
let mut x = 0i16; |
||||
let mut y = 0i16; |
||||
for ch in processed_line.chars() { |
||||
x += match ch { |
||||
'e' => 2, |
||||
'w' => -2, |
||||
'9' | '3' => 1, |
||||
'7' | '1' => -1, |
||||
'e' => 1, |
||||
'w' => -1, |
||||
_ => 0, |
||||
}; |
||||
y += match ch { |
||||
'9' | '7' => 1, |
||||
'3' | '1' => -1, |
||||
'n' => 1, |
||||
's' => -1, |
||||
_ => 0, |
||||
}; |
||||
} |
||||
|
||||
let tile = (x, y); |
||||
if changed_tiles.contains(&tile) { |
||||
changed_tiles.remove(&tile); |
||||
if alive_tiles.contains(&tile) { |
||||
alive_tiles.remove(&tile); |
||||
} 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