RosettaCodeData/Task/Minesweeper-game/Rust/minesweeper-game.rust

317 lines
9.9 KiB
Plaintext

extern crate rand;
use std::io;
use std::io::Write;
fn main() {
use minesweeper::{MineSweeper, GameStatus};
let mut width = 6;
let mut height = 4;
let mut mine_perctg = 10;
let mut game = MineSweeper::new(width, height, mine_perctg);
loop {
let mut command = String::new();
println!(
"\n\
M I N E S W E E P E R\n\
\n\
Commands: \n\
line col - reveal line,col \n\
m line col - mark line,col \n\
q - quit\n\
n - new game\n\
n width height perc - new game size and mine percentage\n"
);
game.print();
print!("> ");
io::stdout().flush().unwrap();
while let Ok(_) = io::stdin().read_line(&mut command) {
let mut command_ok = false;
{
let values: Vec<&str> = command.trim().split(' ').collect();
if values.len() == 1 {
if values[0] == "q" {
println!("Goodbye");
return;
} else if values[0] == "n" {
println!("New game");
game = MineSweeper::new(width, height, mine_perctg);
command_ok = true;
}
} else if values.len() == 2 {
if let (Ok(x), Ok(y)) = (
values[0].parse::<usize>(),
values[1].parse::<usize>(),
)
{
game.play(x - 1, y - 1);
match game.game_status {
GameStatus::Won => println!("You won!"),
GameStatus::Lost => println!("You lost!"),
_ => (),
}
command_ok = true;
}
} else if values.len() == 3 {
if values[0] == "m" {
if let (Ok(x), Ok(y)) = (
values[1].parse::<usize>(),
values[2].parse::<usize>(),
)
{
game.mark(x - 1, y - 1);
command_ok = true;
}
}
} else if values.len() == 4 {
if values[0] == "n" {
if let (Ok(new_width), Ok(new_height), Ok(new_mines_perctg)) =
(
values[1].parse::<usize>(),
values[2].parse::<usize>(),
values[3].parse::<usize>(),
)
{
width = new_width;
height = new_height;
mine_perctg = new_mines_perctg;
game = MineSweeper::new(width, height, mine_perctg);
command_ok = true;
}
}
}
}
if command_ok {
game.print();
} else {
println!("Invalid command");
}
print!("> ");
io::stdout().flush().unwrap();
command.clear();
}
}
}
pub mod minesweeper {
pub struct MineSweeper {
cell: [[Cell; 100]; 100],
pub game_status: GameStatus,
mines: usize,
width: usize,
height: usize,
revealed_count: usize,
}
#[derive(Copy, Clone)]
struct Cell {
content: CellContent,
mark: Mark,
revealed: bool,
}
#[derive(Copy, Clone)]
enum CellContent {
Empty,
Mine,
MineNeighbour { count: u8 },
}
#[derive(Copy, Clone)]
enum Mark {
None,
Mine,
}
pub enum GameStatus {
InGame,
Won,
Lost,
}
extern crate rand;
use std::cmp::max;
use std::cmp::min;
use self::rand::Rng;
use self::CellContent::*;
use self::GameStatus::*;
impl MineSweeper {
pub fn new(width: usize, height: usize, percentage_of_mines: usize) -> MineSweeper {
let mut game = MineSweeper {
cell: [[Cell {
content: Empty,
mark: Mark::None,
revealed: false,
}; 100]; 100],
game_status: InGame,
mines: (width * height * percentage_of_mines) / 100,
width: width,
height: height,
revealed_count: 0,
};
game.put_mines();
game.calc_neighbours();
game
}
pub fn play(&mut self, x: usize, y: usize) {
match self.game_status {
InGame => {
if !self.cell[x][y].revealed {
match self.cell[x][y].content {
Mine => {
self.cell[x][y].revealed = true;
self.revealed_count += 1;
self.game_status = Lost;
}
Empty => {
self.flood_fill_reveal(x, y);
if self.revealed_count + self.mines == self.width * self.height {
self.game_status = Won;
}
}
MineNeighbour { .. } => {
self.cell[x][y].revealed = true;
self.revealed_count += 1;
if self.revealed_count + self.mines == self.width * self.height {
self.game_status = Won;
}
}
}
}
}
_ => println!("Game has ended"),
}
}
pub fn mark(&mut self, x: usize, y: usize) {
self.cell[x][y].mark = match self.cell[x][y].mark {
Mark::None => Mark::Mine,
Mark::Mine => Mark::None,
}
}
pub fn print(&self) {
print!("┌");
for _ in 0..self.width {
print!("─");
}
println!("┐");
for y in 0..self.height {
print!("│");
for x in 0..self.width {
self.cell[x][y].print();
}
println!("│");
}
print!("└");
for _ in 0..self.width {
print!("─");
}
println!("┘");
}
fn put_mines(&mut self) {
let mut rng = rand::thread_rng();
for _ in 0..self.mines {
while let (x, y, true) = (
rng.gen::<usize>() % self.width,
rng.gen::<usize>() % self.height,
true,
)
{
match self.cell[x][y].content {
Mine => continue,
_ => {
self.cell[x][y].content = Mine;
break;
}
}
}
}
}
fn calc_neighbours(&mut self) {
for x in 0..self.width {
for y in 0..self.height {
if !self.cell[x][y].is_bomb() {
let mut adjacent_bombs = 0;
for i in max(x as isize - 1, 0) as usize..min(x + 2, self.width) {
for j in max(y as isize - 1, 0) as usize..min(y + 2, self.height) {
adjacent_bombs += if self.cell[i][j].is_bomb() { 1 } else { 0 };
}
}
if adjacent_bombs == 0 {
self.cell[x][y].content = Empty;
} else {
self.cell[x][y].content = MineNeighbour { count: adjacent_bombs };
}
}
}
}
}
fn flood_fill_reveal(&mut self, x: usize, y: usize) {
let mut stack = Vec::<(usize, usize)>::new();
stack.push((x, y));
while let Some((i, j)) = stack.pop() {
if self.cell[i][j].revealed {
continue;
}
self.cell[i][j].revealed = true;
self.revealed_count += 1;
if let Empty = self.cell[i][j].content {
for m in max(i as isize - 1, 0) as usize..min(i + 2, self.width) {
for n in max(j as isize - 1, 0) as usize..min(j + 2, self.height) {
if !self.cell[m][n].is_bomb() && !self.cell[m][n].revealed {
stack.push((m, n));
}
}
}
}
}
}
}
impl Cell {
pub fn print(&self) {
print!(
"{}",
if self.revealed {
match self.content {
Empty => ' ',
Mine => '*',
MineNeighbour { count } => char::from(count + b'0'),
}
} else {
match self.mark {
Mark::Mine => '?',
Mark::None => '.',
}
}
);
}
pub fn is_bomb(&self) -> bool {
match self.content {
Mine => true,
_ => false,
}
}
}
}