What do you think of using struct Board(Vec<Vec<Cell>>)
? It's nice that I can attach the Display
trait, and just print the board.
But on the flip-side, we have things that seem wrong, like:
fn next_step(b: Board) -> Board {
let Board(mut board) = b;
// ...
Board(board)
}
And I couldn't find a way to pass a Board
(or a reference to one) to nr_of_neighbors
. Moving the Vec
out in next_step
(or similarly when using a mutable reference there), prevented me from doing that. Or am I missing something?
Any additional feedback welcome as well!
- Note: it's a
Vec
instead of anarray
in case I add the feature that the player can choose the board size interactively. - I know the
as i64
isn't that great. Not sure though what's a nice, practical and concise way of dealing with that though.
Complete working code
Should run in any ANSI-compatible terminal.
use itertools::join;
use std::fmt;
use std::{thread, time};
#[repr(u8)]
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
enum Cell { Aliv, Dead }
impl fmt::Display for Cell {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
Cell::Aliv => write!(f, "x"),
Cell::Dead => write!(f, " "),
}
}
}
struct Board(Vec<Vec<Cell>>);
impl fmt::Display for Board {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let Board(vec) = self;
let str = join(vec.into_iter().map(|row| join(row, "|")), "\n");
write!(f, "{}", str)
}
}
fn main() {
let mut board = Board(vec![
vec![Cell::Aliv, Cell::Aliv, Cell::Aliv, Cell::Dead, Cell::Dead, Cell::Dead, Cell::Dead, Cell::Dead, Cell::Dead, Cell::Dead],
vec![Cell::Aliv, Cell::Dead, Cell::Aliv, Cell::Dead, Cell::Dead, Cell::Dead, Cell::Dead, Cell::Dead, Cell::Dead, Cell::Dead],
vec![Cell::Aliv, Cell::Aliv, Cell::Aliv, Cell::Dead, Cell::Dead, Cell::Dead, Cell::Dead, Cell::Dead, Cell::Dead, Cell::Dead],
vec![Cell::Dead, Cell::Aliv, Cell::Aliv, Cell::Dead, Cell::Dead, Cell::Dead, Cell::Dead, Cell::Dead, Cell::Dead, Cell::Dead],
vec![Cell::Dead, Cell::Dead, Cell::Dead, Cell::Dead, Cell::Dead, Cell::Dead, Cell::Dead, Cell::Dead, Cell::Dead, Cell::Dead],
vec![Cell::Dead, Cell::Dead, Cell::Dead, Cell::Dead, Cell::Dead, Cell::Dead, Cell::Dead, Cell::Dead, Cell::Dead, Cell::Dead],
vec![Cell::Dead, Cell::Dead, Cell::Dead, Cell::Dead, Cell::Dead, Cell::Dead, Cell::Dead, Cell::Dead, Cell::Dead, Cell::Dead],
vec![Cell::Dead, Cell::Dead, Cell::Dead, Cell::Dead, Cell::Dead, Cell::Dead, Cell::Dead, Cell::Dead, Cell::Dead, Cell::Dead],
vec![Cell::Dead, Cell::Dead, Cell::Dead, Cell::Dead, Cell::Dead, Cell::Dead, Cell::Dead, Cell::Dead, Cell::Dead, Cell::Dead],
vec![Cell::Dead, Cell::Dead, Cell::Dead, Cell::Dead, Cell::Dead, Cell::Dead, Cell::Dead, Cell::Dead, Cell::Dead, Cell::Dead],
]);
loop {
print!("{esc}[2J{esc}[1;1H", esc = 27 as char); // clear ANSI-compatible terminal
println!("{}", board);
board = next_step(board);
thread::sleep(time::Duration::from_secs(1));
}
}
fn next_step(b: Board) -> Board {
let Board(mut board) = b;
for x in 0..board.len() {
for y in 0..board[x].len() {
let n = nr_of_neighbors(&board, x, y);
let cell = board[x][y];
if cell == Cell::Aliv && (n == 2 || n == 3) {
// survives
} else if cell == Cell::Dead && n == 3 {
board[x][y] = Cell::Aliv;
} else {
board[x][y] = Cell::Dead;
}
}
}
Board(board)
}
fn nr_of_neighbors(board: &Vec<Vec<Cell>>, current_x: usize, current_y: usize) -> u32 {
let cur_x = current_x as i64;
let cur_y = current_y as i64;
let mut count: u32 = 0;
for x in cur_x-1..cur_x+2 {
for y in cur_y-1..cur_y+2 {
if x >=0 && y >= 0
&& x < board.len() as i64 && y < board[x as usize].len() as i64
&& !(x == cur_x && y == cur_y)
&& board[x as usize][y as usize] == Cell::Aliv {
count = count + 1;
}
}
}
count
}