Last active
August 29, 2015 14:03
-
-
Save hmarr/85be0a3c4ad10eeb17e0 to your computer and use it in GitHub Desktop.
Conway's Game of Life in Rust
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
use std::io::{print}; | |
use std::cmp::{min,max}; | |
use std::time::Duration; | |
#[deriving(Clone,Show,PartialEq)] | |
enum Cell { | |
Alive, | |
Dead, | |
} | |
struct Board { | |
cells: Vec<Cell>, | |
size: uint, | |
} | |
impl Board { | |
fn new(cells: Vec<Cell>) -> Board { | |
let size = (cells.len() as f64).sqrt() as uint; | |
Board { | |
cells: cells, | |
size: size, | |
} | |
} | |
fn from_str(layout: &str) -> Board { | |
let mut cells = Vec::new(); | |
for word in layout.words() { | |
match word { | |
"-" => cells.push(Dead), | |
"X" => cells.push(Alive), | |
_ => (), | |
} | |
} | |
Board::new(cells) | |
} | |
fn tick(&mut self) { | |
let new_cells = Vec::from_fn(self.size * self.size, |i| -> Cell { | |
let cell = self.cell(i); | |
match cell { | |
Alive => self.handle_live_cell(i), | |
Dead => self.handle_dead_cell(i), | |
} | |
}); | |
self.cells = new_cells; | |
} | |
fn cell(&self, i: uint) -> Cell { | |
self.cells[i] | |
} | |
fn handle_live_cell(&self, cell_index: uint) -> Cell { | |
match self.num_neighbours(cell_index) { | |
0...1 => Dead, | |
2...3 => Alive, | |
_ => Dead, | |
} | |
} | |
fn handle_dead_cell(&self, cell_index: uint) -> Cell { | |
match self.num_neighbours(cell_index) { | |
3 => Alive, | |
_ => Dead, | |
} | |
} | |
fn num_neighbours(&self, cell_index: uint) -> uint { | |
let cell_x: int = (cell_index % self.size) as int; | |
let cell_y: int = (cell_index / self.size) as int; | |
let mut neighbours = 0; | |
for y in range(max(cell_y - 1, 0), min(cell_y + 2, self.size as int)) { | |
for x in range(max(cell_x - 1, 0), min(cell_x + 2, self.size as int)) { | |
let neighbour_index: int = y * (self.size as int) + x; | |
if neighbour_index != cell_index as int { | |
neighbours += match self.cells[neighbour_index as uint] { | |
Alive => 1, | |
Dead => 0, | |
} | |
} | |
} | |
} | |
neighbours | |
} | |
fn render(&self) { | |
for y in range(0, self.size) { | |
for x in range(0, self.size) { | |
match self.cell(x + y * self.size) { | |
Alive => print("◉ "), | |
Dead => print("◯ "), | |
} | |
} | |
print("\n") | |
} | |
} | |
} | |
static BLINKER: &'static str = " | |
- - - - - | |
- - X - - | |
- - X - - | |
- - X - - | |
- - - - - | |
"; | |
static TOAD: &'static str = " | |
- - - - - - | |
- - - - - - | |
- - X X X - | |
- X X X - - | |
- - - - - - | |
- - - - - - | |
"; | |
static R_PENTAMINO: &'static str = " | |
- - - - - - - - - - | |
- - - - - - - - - - | |
- - - - - - - - - - | |
- - - - X X - - - - | |
- - - X X - - - - - | |
- - - - X - - - - - | |
- - - - - - - - - - | |
- - - - - - - - - - | |
- - - - - - - - - - | |
- - - - - - - - - - | |
"; | |
fn main() { | |
let args = std::os::args(); | |
let mut template_name = String::from_str("blinker"); | |
if args.len() > 1 { | |
template_name = args[1].clone(); | |
} | |
let template = match template_name.as_slice() { | |
"blinker" => BLINKER, | |
"toad" => TOAD, | |
"r-pentamino" => R_PENTAMINO, | |
_ => { | |
println!("invalid template '{}'", template_name); | |
std::os::set_exit_status(1); | |
return; | |
} | |
}; | |
let mut board = Board::from_str(template); | |
loop { | |
board.render(); | |
board.tick(); | |
std::io::timer::sleep(Duration::milliseconds(100)); | |
// Move cursor back up <board.size> lines | |
print!("{}[{}A", '\x1B', board.size); | |
} | |
} | |
#[test] | |
fn underpopulation_causes_death() { | |
let mut board = Board::new(vec![ | |
Dead, Alive, | |
Dead, Alive, | |
]); | |
board.tick(); | |
assert_eq!(board.cells, Vec::from_elem(4, Dead)); | |
} | |
#[test] | |
fn two_neighbours_lives_on() { | |
let mut board = Board::new(vec![ | |
Alive, Alive, | |
Dead, Alive, | |
]); | |
board.tick(); | |
assert_eq!(board.cell(1), Alive) | |
} | |
#[test] | |
fn three_neighbours_lives_on() { | |
let mut board = Board::new(vec![ | |
Dead, Dead, Alive, | |
Dead, Alive, Alive, | |
Dead, Dead, Alive, | |
]); | |
board.tick(); | |
assert_eq!(board.cell(5), Alive) | |
} | |
#[test] | |
fn overcrowding_causes_death() { | |
let mut board = Board::new(vec![ | |
Dead, Alive, Alive, | |
Dead, Alive, Alive, | |
Dead, Alive, Alive, | |
]); | |
board.tick(); | |
assert_eq!(board.cell(4), Dead) | |
assert_eq!(board.cell(5), Dead) | |
} | |
#[test] | |
fn two_neighbours_stays_dead() { | |
let mut board = Board::new(vec![ | |
Alive, Dead, | |
Dead, Alive, | |
]); | |
board.tick(); | |
assert_eq!(board.cell(1), Dead) | |
} | |
#[test] | |
fn three_neighbours_becomes_alive() { | |
let mut board = Board::new(vec![ | |
Dead, Dead, Alive, | |
Dead, Alive, Dead, | |
Dead, Dead, Alive, | |
]); | |
board.tick(); | |
assert_eq!(board.cell(5), Alive) | |
} | |
#[test] | |
fn four_neighbours_stays_dead() { | |
let mut board = Board::new(vec![ | |
Dead, Alive, Alive, | |
Dead, Alive, Dead, | |
Dead, Dead, Alive, | |
]); | |
board.tick(); | |
assert_eq!(board.cell(5), Dead) | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment