parent
dc69aea676
commit
e43fb3f11c
@ -0,0 +1,10 @@ |
||||
[package] |
||||
name = "day17-hard" |
||||
version = "0.1.0" |
||||
authors = ["inga-lovinde <52715130+inga-lovinde@users.noreply.github.com>"] |
||||
edition = "2018" |
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html |
||||
|
||||
[dependencies] |
||||
ndarray = "0.14.0" |
@ -0,0 +1,124 @@ |
||||
use ndarray::Array4; |
||||
|
||||
use crate::locations::{CellLocation, MINICUBE_DIFF_ARRAY}; |
||||
|
||||
struct CellInfo { |
||||
is_alive: bool, |
||||
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 const MAX_NUMBER_OF_STEPS: usize = 30; |
||||
|
||||
pub struct Game { |
||||
board: Array4<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 { |
||||
for z in 0..self.corner.z { |
||||
for w in 0..self.corner.w { |
||||
let location = CellLocation { x, y, z, w }; |
||||
match &self.board[location] { |
||||
CellInfo { is_alive: true, alive_minicube_count } if *alive_minicube_count < 3 || *alive_minicube_count > 4 => { |
||||
new_dead.push(location); |
||||
}, |
||||
CellInfo { is_alive: false, alive_minicube_count: 3 } => { |
||||
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: &[String]) -> Self { |
||||
let rows = input_data.len(); |
||||
let columns = input_data[0].len(); |
||||
let corner = CellLocation { |
||||
x: rows + 2*MAX_NUMBER_OF_STEPS, |
||||
y: columns + 2*MAX_NUMBER_OF_STEPS, |
||||
z: 1 + 2*MAX_NUMBER_OF_STEPS, |
||||
w: 1 + 2*MAX_NUMBER_OF_STEPS, |
||||
}; |
||||
let board = Array4::default((corner.x, corner.y, corner.z, corner.w)); |
||||
let mut game = Game { |
||||
board, |
||||
corner, |
||||
}; |
||||
|
||||
for row in 0..rows { |
||||
let chars = input_data[row].chars().collect::<Vec<_>>(); |
||||
for column in 0..columns { |
||||
let ch = chars[column]; |
||||
if ch == '#' { |
||||
game.make_alive(CellLocation { x: row + MAX_NUMBER_OF_STEPS, y: column + MAX_NUMBER_OF_STEPS, z: MAX_NUMBER_OF_STEPS, w: MAX_NUMBER_OF_STEPS }); |
||||
} |
||||
} |
||||
} |
||||
|
||||
return game; |
||||
} |
||||
|
||||
pub fn get_alive_count(&self) -> usize { |
||||
(&self.board).iter().filter(|&cell| cell.is_alive).count() |
||||
} |
||||
} |
||||
|
@ -0,0 +1,78 @@ |
||||
use std::ops::{Add, Index, IndexMut}; |
||||
|
||||
use ndarray::Array4; |
||||
|
||||
#[derive(Copy, Clone)] |
||||
pub struct CellDiff { |
||||
x: i8, |
||||
y: i8, |
||||
z: i8, |
||||
w: i8, |
||||
} |
||||
|
||||
const fn build_minicube_diff_array() -> [CellDiff; 81] { |
||||
let mut result: [CellDiff; 81] = [CellDiff { x: 0, y: 0, z: 0, w: 0 }; 81]; |
||||
let mut x = -1; |
||||
let mut y = -1; |
||||
let mut z = -1; |
||||
let mut w = -1; |
||||
loop { |
||||
result[((x*27+y*9+z*3+w) + 40) as usize] = CellDiff {x, y, z, w }; |
||||
|
||||
w += 1; |
||||
if w == 2 { |
||||
w -= 3; |
||||
z += 1; |
||||
} |
||||
if z == 2 { |
||||
z -= 3; |
||||
y += 1; |
||||
} |
||||
if y == 2 { |
||||
y -= 3; |
||||
x += 1; |
||||
} |
||||
if x == 2 { |
||||
break; |
||||
} |
||||
} |
||||
|
||||
result |
||||
} |
||||
|
||||
pub const MINICUBE_DIFF_ARRAY: [CellDiff; 81] = build_minicube_diff_array(); |
||||
|
||||
#[derive(Copy, Clone)] |
||||
pub struct CellLocation { |
||||
pub x: usize, |
||||
pub y: usize, |
||||
pub z: usize, |
||||
pub w: 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, |
||||
z: (self.z as isize + other.z as isize) as usize, |
||||
w: (self.w as isize + other.w as isize) as usize, |
||||
} |
||||
} |
||||
} |
||||
|
||||
impl<T> Index<CellLocation> for Array4<T> { |
||||
type Output = T; |
||||
fn index<'a>(&'a self, cell_location: CellLocation) -> &'a T { |
||||
&self[[cell_location.x, cell_location.y, cell_location.z, cell_location.w]] |
||||
} |
||||
} |
||||
|
||||
impl<T> IndexMut<CellLocation> for Array4<T> { |
||||
fn index_mut<'a>(&'a mut self, cell_location: CellLocation) -> &'a mut T { |
||||
&mut self[[cell_location.x, cell_location.y, cell_location.z, cell_location.w]] |
||||
} |
||||
} |
||||
|
@ -0,0 +1,21 @@ |
||||
use std::io::{self, BufRead}; |
||||
|
||||
mod game; |
||||
mod locations; |
||||
|
||||
use crate::game::{Game, MAX_NUMBER_OF_STEPS}; |
||||
|
||||
fn main() { |
||||
let stdin = io::stdin(); |
||||
let lines: Vec<_> = stdin.lock().lines().map(|line| line.unwrap()).collect(); |
||||
|
||||
let mut game = Game::from_input(&lines); |
||||
|
||||
for i in 1..MAX_NUMBER_OF_STEPS { |
||||
let changes_count = game.next_step(); |
||||
println!("Iteration {}; changed cells: {}; alive_cells: {}", i, changes_count, game.get_alive_count()); |
||||
if changes_count == 0 { |
||||
break; |
||||
} |
||||
} |
||||
} |
Loading…
Reference in new issue