You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
161 lines
5.3 KiB
161 lines
5.3 KiB
use enum_map::EnumMap;
|
|
use ndarray::Array2;
|
|
use strum::IntoEnumIterator;
|
|
|
|
use crate::binary::CellState;
|
|
use crate::binary::Direction;
|
|
use crate::binary::State;
|
|
use crate::board_metadata::BoardMetadata;
|
|
use crate::board_metadata::CellLocation;
|
|
use crate::rules::Rules;
|
|
|
|
struct CellInfo {
|
|
neighbours: EnumMap<Direction, Option<CellLocation>>,
|
|
state: CellState,
|
|
}
|
|
|
|
impl CellInfo {
|
|
fn new(neighbours: EnumMap<Direction, Option<CellLocation>>) -> CellInfo {
|
|
CellInfo {
|
|
neighbours,
|
|
state: CellState::new(),
|
|
}
|
|
}
|
|
|
|
fn update_neighbour_state(&mut self, direction: Direction, new_state: State) -> () {
|
|
self.state.update_neighbour_state(direction, new_state)
|
|
}
|
|
|
|
fn update_state(&mut self, new_state: State) {
|
|
self.state.update_state(new_state)
|
|
}
|
|
}
|
|
|
|
pub struct Game {
|
|
cell_rules: Vec<State>,
|
|
board: Array2<CellInfo>,
|
|
rows: usize,
|
|
columns: usize,
|
|
}
|
|
|
|
impl Game {
|
|
// only updates the state of this cell for it and its neighbours;
|
|
// only state of this cell is used from new_cell
|
|
fn update_cell(&mut self, location: CellLocation, new_state: State) {
|
|
//println!("Updating cell {}:{}", location.row, location.column);
|
|
self.board[location].update_state(new_state);
|
|
|
|
for direction in Direction::iter() {
|
|
match self.board[location].neighbours[direction] {
|
|
Some(neighbour_location) => {
|
|
//println!("Updating neighbour cell {}:{}", neighbour_location.row, neighbour_location.column);
|
|
self.board[neighbour_location].update_neighbour_state(direction, new_state);
|
|
},
|
|
_ => {},
|
|
}
|
|
}
|
|
}
|
|
|
|
fn build_cell_rules<T: Rules>() -> Vec<State> {
|
|
let mut result = vec![State::None; 1 << 18];
|
|
|
|
for i in 0..1usize << 18 {
|
|
let original_state = CellState::from_number(i as u32);
|
|
let mut neighbour_counts = EnumMap::new();
|
|
for direction in Direction::iter() {
|
|
neighbour_counts[original_state.get_neighbour_state(direction)] += 1usize;
|
|
}
|
|
|
|
let new_state = T::get_next_state(original_state.get_state(), neighbour_counts);
|
|
result[i] = new_state;
|
|
//println!("Rule #{}: for state_counts [{}, {}, {}, {}] and old state {} new state is {}", i, state_counts[0], state_counts[1], state_counts[2], state_counts[3], current_state, new_state);
|
|
}
|
|
|
|
result
|
|
}
|
|
|
|
fn get_next_state(&self, cell_info: &CellInfo) -> State {
|
|
self.cell_rules[cell_info.state.get_number() as usize]
|
|
}
|
|
|
|
pub fn next_step(&mut self) -> usize {
|
|
let mut changes: Vec<_> = vec![];
|
|
for row in 0..self.rows {
|
|
for column in 0..self.columns {
|
|
let location = CellLocation::new(row, column);
|
|
let cell = &self.board[location];
|
|
let next_state = self.get_next_state(cell);
|
|
//println!("location: {}:{}, neighbours state {}, old state {}, next state {}", location.row, location.column, cell.neighbours_states, cell.state, next_state);
|
|
if next_state != cell.state.get_state() {
|
|
changes.push((location, next_state));
|
|
}
|
|
}
|
|
}
|
|
|
|
let changes_count = changes.len();
|
|
for (location, new_state) in changes {
|
|
self.update_cell(location, new_state);
|
|
}
|
|
|
|
changes_count
|
|
}
|
|
|
|
pub fn from_input<R: Rules>(input_data: &[String]) -> Self {
|
|
let rows = input_data.len();
|
|
let columns = input_data[0].len();
|
|
let board_metadata = BoardMetadata::new(rows, columns);
|
|
let mut states = board_metadata.create_board_default();
|
|
|
|
for row in 0..rows {
|
|
let chars = input_data[row].chars().collect::<Vec<_>>();
|
|
for column in 0..columns {
|
|
let ch = chars[column];
|
|
states[[row, column]] = match ch {
|
|
'.' => State::Floor,
|
|
'L' => State::SeatEmpty,
|
|
'#' => State::SeatOccupied,
|
|
_ => State::None,
|
|
}
|
|
}
|
|
}
|
|
|
|
let board = board_metadata.create_board_from_shape_fn(|cell_location| {
|
|
CellInfo::new(R::get_neighbours(cell_location, &board_metadata, &states))
|
|
});
|
|
|
|
let cell_rules = Self::build_cell_rules::<R>();
|
|
|
|
let mut game = Game {
|
|
rows,
|
|
columns,
|
|
board,
|
|
cell_rules,
|
|
};
|
|
|
|
for row in 0..rows {
|
|
for column in 0..columns {
|
|
let location = CellLocation::new(row, column);
|
|
game.update_cell(location, states[location]);
|
|
}
|
|
}
|
|
|
|
return game;
|
|
}
|
|
|
|
pub fn print_board(&self) {
|
|
for row in (&self.board).genrows() {
|
|
println!("{}", row.iter().map(|cell| {
|
|
match cell.state.get_state() {
|
|
State::Floor => '.',
|
|
State::SeatEmpty => 'L',
|
|
State::SeatOccupied => '#',
|
|
State::None => '0',
|
|
}
|
|
}).collect::<String>());
|
|
}
|
|
}
|
|
|
|
pub fn get_count_of_cells_for_state(&self, state: State) -> usize {
|
|
(&self.board).iter().filter(|&cell| cell.state.get_state() == state).count()
|
|
}
|
|
}
|
|
|