cursive/examples/mines/game.rs
2020-01-06 15:51:38 -08:00

87 lines
2.2 KiB
Rust

use cursive::Vec2;
use rand::{thread_rng, Rng};
// use std::cmp::max;
#[derive(Clone, Copy)]
pub struct Options {
pub size: Vec2,
pub mines: usize,
}
#[derive(Clone, Copy)]
pub enum Cell {
Bomb,
Free(usize),
}
pub struct Board {
pub size: Vec2,
pub cells: Vec<Cell>,
}
impl Board {
pub fn new(options: Options) -> Self {
let n_cells = options.size.x * options.size.y;
if options.mines > n_cells {
// Something is wrong here...
// Use different options instead.
return Board::new(Options {
size: options.size,
mines: n_cells,
});
}
let mut board = Board {
size: options.size,
cells: vec![Cell::Free(0); n_cells],
};
for _ in 0..options.mines {
// Find a free cell to put a bomb
let i = loop {
let i = thread_rng().gen_range(0, n_cells);
if let Cell::Bomb = board.cells[i] {
continue;
}
break i;
};
// We know we'll go through since that's how we picked i...
board.cells[i] = Cell::Bomb;
// Increase count on adjacent cells
let pos = Vec2::new(i % options.size.x, i / options.size.x);
for p in board.neighbours(pos) {
if let Some(&mut Cell::Free(ref mut n)) = board.get_mut(p) {
*n += 1;
}
}
}
board
}
fn get_mut(&mut self, pos: Vec2) -> Option<&mut Cell> {
self.cell_id(pos).map(move |i| &mut self.cells[i])
}
pub fn cell_id(&self, pos: Vec2) -> Option<usize> {
if pos < self.size {
Some(pos.x + pos.y * self.size.x)
} else {
None
}
}
pub fn neighbours(&self, pos: Vec2) -> Vec<Vec2> {
let pos_min = pos.saturating_sub((1, 1));
let pos_max = (pos + (2, 2)).or_min(self.size);
(pos_min.x..pos_max.x)
.flat_map(|x| (pos_min.y..pos_max.y).map(move |y| Vec2::new(x, y)))
.filter(|&p| p != pos)
.collect()
}
}