212 lines
7.2 KiB
Rust
212 lines
7.2 KiB
Rust
/*
|
|
Author: @kirbylife
|
|
license: GPLv2
|
|
*/
|
|
|
|
|
|
extern crate ncurses;
|
|
extern crate rand;
|
|
|
|
use ncurses::*;
|
|
use rand::Rng;
|
|
use std::collections::LinkedList;
|
|
|
|
const ROWS: usize = 15;
|
|
const COLUMNS: usize = 10;
|
|
const MINES: i32 = ((ROWS * COLUMNS) as i32 * 20 / 100);
|
|
|
|
fn get_arround(pivot: [usize; 2]) -> LinkedList<[usize; 2]> {
|
|
// Returns a list with the coordinates around the pivot ignoring the ones that come off the board
|
|
let mut list: LinkedList<[usize; 2]> = LinkedList::new();
|
|
for r in (pivot[0] as i32 - 1)..(pivot[0] as i32 + 2) {
|
|
for c in (pivot[1] as i32- 1)..(pivot[1] as i32 + 2) {
|
|
if r >= 0 && r < ROWS as i32 && c >= 0 && c < COLUMNS as i32 {
|
|
list.push_front([r as usize,c as usize]);
|
|
}
|
|
}
|
|
}
|
|
list
|
|
}
|
|
|
|
fn gen_game(mut counter: i32) -> [[i8; COLUMNS]; ROWS] {
|
|
// returns a matrix with a board already with the mines placed at random
|
|
// -1: box with mine
|
|
// 0-8: box without mine
|
|
let mut rng = rand::thread_rng();
|
|
let mut game = [[0 as i8; COLUMNS]; ROWS];
|
|
while counter != 0 {
|
|
let rnd_row = rng.gen_range(0, ROWS);
|
|
let rnd_column = rng.gen_range(0, COLUMNS);
|
|
if game[rnd_row][rnd_column] == 0 {
|
|
game[rnd_row][rnd_column] = -1;
|
|
counter -= 1;
|
|
}
|
|
}
|
|
for (i, row) in game.clone().iter().enumerate() {
|
|
for (j, item) in row.iter().enumerate() {
|
|
if item != &-1 {
|
|
let mut counter: i8 = 0;
|
|
for x in get_arround([i, j]).iter() {
|
|
if game[x[0] as usize][x[1] as usize] == -1 {
|
|
counter += 1;
|
|
}
|
|
}
|
|
game[i][j] = counter;
|
|
}
|
|
}
|
|
}
|
|
game
|
|
}
|
|
|
|
fn reveal(pivot: [usize; 2], mut board: [[char; COLUMNS]; ROWS], game: [[i8; COLUMNS]; ROWS]) -> [[char; COLUMNS]; ROWS] {
|
|
// recursively, reveals the zones of the board without mines
|
|
let [row, column] = pivot;
|
|
if game[row][column] != 0 && game[row][column] != -1 {
|
|
board[row][column] = format!("{}", game[row][column]).chars().next().unwrap();
|
|
}
|
|
if game[row][column] == 0 && board[row][column] == '-' {
|
|
board[row][column] = format!("{}", game[row][column]).chars().next().unwrap();
|
|
for tmp_pivot in get_arround(pivot) {
|
|
board = reveal(tmp_pivot, board, game);
|
|
}
|
|
}
|
|
board
|
|
}
|
|
|
|
fn check_revealed(board: [[char; COLUMNS]; ROWS], game: [[i8; COLUMNS]; ROWS]) -> i32 {
|
|
// Return the number of boxes already revealed
|
|
let mut count: i32 = 0;
|
|
for (i, row) in board.iter().enumerate() {
|
|
for (j, item) in row.iter().enumerate() {
|
|
if game[i][j] != -1 && item != &'#' && item != &'-' {
|
|
count += 1;
|
|
}
|
|
}
|
|
}
|
|
count
|
|
}
|
|
|
|
fn main() {
|
|
let mut running = true;
|
|
let mut board = [['-'; COLUMNS]; ROWS];
|
|
let game = gen_game(MINES);
|
|
let mut pivot = [0 as usize; 2];
|
|
initscr();
|
|
raw();
|
|
|
|
keypad(stdscr(), true);
|
|
noecho();
|
|
|
|
while running == true {
|
|
clear();
|
|
let revealed = check_revealed(board, game);
|
|
if revealed == ((COLUMNS * ROWS) as i32 - MINES) {
|
|
print!("You win");
|
|
break;
|
|
}
|
|
for (i, row) in board.iter().enumerate() {
|
|
for (j, item) in row.iter().enumerate() {
|
|
if pivot[0] == i && pivot[1] == j {
|
|
addstr(format!("<{}>", item).as_ref());
|
|
}
|
|
else {
|
|
addstr(format!(" {} ", item).as_ref());
|
|
}
|
|
}
|
|
match i {
|
|
0 => addstr("\t To move use the arrow keys"),
|
|
1 => addstr("\t To flag a box, press 'f'"),
|
|
2 => addstr("\t To reveal a box, press 'Space'"),
|
|
3 => addstr("\t To exit, press 'q'"),
|
|
_ => addstr(""),
|
|
};
|
|
addstr("\n");
|
|
}
|
|
addstr(format!("{} - {}\n", pivot[0], pivot[1]).as_ref());
|
|
let ch = getch();
|
|
if ch == KEY_UP {
|
|
// Go up
|
|
if pivot[0] > 0 {
|
|
pivot[0] -= 1;
|
|
}
|
|
}
|
|
else if ch == KEY_DOWN {
|
|
// Go down
|
|
if pivot[0] < ROWS-1 {
|
|
pivot[0] += 1;
|
|
}
|
|
}
|
|
else if ch == KEY_LEFT {
|
|
// Go left
|
|
if pivot[1] > 0 {
|
|
pivot[1] -= 1;
|
|
}
|
|
}
|
|
else if ch == KEY_RIGHT {
|
|
// Go Right
|
|
if pivot[1] < COLUMNS-1 {
|
|
pivot[1] += 1;
|
|
}
|
|
}
|
|
else if ch == 'f' as i32 || ch == 'F' as i32 {
|
|
// Flag a box with possibly a mine
|
|
if board[pivot[0]][pivot[1]] == '#' {
|
|
board[pivot[0]][pivot[1]] = '-';
|
|
}
|
|
else if board[pivot[0]][pivot[1]] == '-' {
|
|
board[pivot[0]][pivot[1]] = '#';
|
|
}
|
|
}
|
|
else if ch == ' ' as i32 {
|
|
// Reveal a box
|
|
if board[pivot[0]][pivot[1]] == '-' && game[pivot[0]][pivot[1]] == 0 {
|
|
// If a box with a 0 is revealed, the entire area around it is revealed
|
|
board = reveal(pivot, board, game);
|
|
}
|
|
else if board[pivot[0]][pivot[1]] == '-' && game[pivot[0]][pivot[1]] != -1 {
|
|
// If a box with a value other than 0 and -1 is revealed, only that box is revealed
|
|
board[pivot[0]][pivot[1]] = format!("{}", game[pivot[0]][pivot[1]]).chars().next().unwrap();
|
|
}
|
|
else if board[pivot[0]][pivot[1]] != '#' && game[pivot[0]][pivot[1]] != 0 && game[pivot[0]][pivot[1]] != -1 {
|
|
// If space is pressed in a box already revealed, its neighboring cells are revealed
|
|
let mut count = 0;
|
|
for [r, c] in get_arround(pivot) {
|
|
if board[r][c] == '#' {
|
|
count += 1;
|
|
}
|
|
}
|
|
if count == game[pivot[0]][pivot[1]] {
|
|
// It's only relieved if the number of boxes around it has already been flagged
|
|
for [r, c] in get_arround(pivot) {
|
|
if board[r][c] != '#' && game[r][c] == -1 {
|
|
// If a flag was in an incorrect box, the game is over
|
|
print!("Game Over");
|
|
running = false;
|
|
break;
|
|
}
|
|
else if board[r][c] != '#' && game[r][c] != 0 {
|
|
board[r][c] = format!("{}", game[r][c]).chars().next().unwrap();
|
|
}
|
|
else if game[r][c] == 0 {
|
|
// If a box around it had a 0, that whole area is revealed
|
|
board = reveal([r, c], board, game);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else if board[pivot[0]][pivot[1]] == '-' && game[pivot[0]][pivot[1]] == -1 {
|
|
// If a box with a mine is revealed, the game is over
|
|
print!("Game over");
|
|
break;
|
|
}
|
|
}
|
|
|
|
else if ch == 'q' as i32 || ch == 'Q' as i32 {
|
|
// if you press q, the game ends
|
|
break;
|
|
}
|
|
refresh();
|
|
}
|
|
endwin();
|
|
}
|