325 lines
13 KiB
Rust
325 lines
13 KiB
Rust
use std::io::{stdin,stdout,Write};
|
||
use rand::Rng;
|
||
use colored::*;
|
||
use console::Term;
|
||
use std::time::Instant;
|
||
|
||
fn output_board(board: &Vec<Vec<i64>>, state: &Vec<Vec<char>>, x_hovered: i64, y_hovered: i64) {
|
||
let mut output: String = "".to_string();
|
||
|
||
for y in 0..board.len() {
|
||
for x in 0..board[y].len() {
|
||
let background_color: u8;
|
||
|
||
if state[y][x] != 'u' {
|
||
background_color = 128;
|
||
}
|
||
else {
|
||
background_color = 196;
|
||
}
|
||
|
||
if x == x_hovered as usize && y == y_hovered as usize {
|
||
output.push_str(&"X".truecolor(0,0,0).on_truecolor(background_color,background_color,background_color).to_string());
|
||
}
|
||
else if state[y][x] == 'u' {
|
||
if board[y][x] == -1 {
|
||
output.push_str(&"▲".truecolor(128,64,0).on_truecolor(background_color,background_color,background_color).to_string());
|
||
}
|
||
else if board[y][x] == 0 {
|
||
output.push_str(&" ".on_truecolor(background_color,background_color,background_color).to_string());
|
||
}
|
||
else if board[y][x] == 1 {
|
||
output.push_str(&"1".truecolor(1,0,254).on_truecolor(background_color,background_color,background_color).to_string());
|
||
}
|
||
else if board[y][x] == 2 {
|
||
output.push_str(&"2".truecolor(1,127,1).on_truecolor(background_color,background_color,background_color).to_string());
|
||
}
|
||
else if board[y][x] == 3 {
|
||
output.push_str(&"3".truecolor(254,0,0).on_truecolor(background_color,background_color,background_color).to_string());
|
||
}
|
||
else if board[y][x] == 4 {
|
||
output.push_str(&"4".truecolor(1,0,128).on_truecolor(background_color,background_color,background_color).to_string());
|
||
}
|
||
else if board[y][x] == 5 {
|
||
output.push_str(&"5".truecolor(129,1,2).on_truecolor(background_color,background_color,background_color).to_string());
|
||
}
|
||
else if board[y][x] == 6 {
|
||
output.push_str(&"6".truecolor(0,128,129).on_truecolor(background_color,background_color,background_color).to_string());
|
||
}
|
||
else if board[y][x] == 7 {
|
||
output.push_str(&"7".truecolor(0,0,0).on_truecolor(background_color,background_color,background_color).to_string());
|
||
}
|
||
else if board[y][x] == 8 {
|
||
output.push_str(&"8".truecolor(128,128,128).on_truecolor(background_color,background_color,background_color).to_string());
|
||
}
|
||
}
|
||
else if state[y][x] == 'm' {
|
||
output.push_str(&"⚑".truecolor(228,16,32).on_truecolor(background_color,background_color,background_color).to_string());
|
||
}
|
||
else if state[y][x] == '?' {
|
||
output.push_str(&"?".truecolor(0,0,0).on_truecolor(background_color,background_color,background_color).to_string());
|
||
}
|
||
else {
|
||
output.push_str(&" ".on_truecolor(background_color,background_color,background_color).to_string());
|
||
}
|
||
}
|
||
output.push_str("\n");
|
||
}
|
||
print!("{}[2J", 27 as char);
|
||
println!("{}",output);
|
||
}
|
||
|
||
fn input() -> String{
|
||
let mut s=String::new();
|
||
let _=stdout().flush();
|
||
stdin().read_line(&mut s).expect("Did not enter a correct string");
|
||
if let Some('\n')=s.chars().next_back() {
|
||
s.pop();
|
||
}
|
||
if let Some('\r')=s.chars().next_back() {
|
||
s.pop();
|
||
}
|
||
return s;
|
||
}
|
||
|
||
fn place_nails(board: &mut Vec<Vec<i64>>, number_of_nails: i64, width: i64, height: i64) {
|
||
for _nail in 0..number_of_nails {
|
||
// Generate coordinates for nails, if there is already a nail there, then generate new
|
||
// coordinates, should add case for if there are more mines than locations, but why would
|
||
// someone do that?
|
||
let mut x_coord = rand::thread_rng().gen_range(0..width);
|
||
let mut y_coord = rand::thread_rng().gen_range(0..height);
|
||
while board[y_coord as usize][x_coord as usize] == -1 {
|
||
x_coord = rand::thread_rng().gen_range(0..width);
|
||
y_coord = rand::thread_rng().gen_range(0..height);
|
||
}
|
||
|
||
board[y_coord as usize][x_coord as usize] = -1;
|
||
}
|
||
}
|
||
|
||
fn determine_nails_in_range(board: &mut Vec<Vec<i64>>) {
|
||
for x in 0..board.len() {
|
||
for y in 0..board[x].len() {
|
||
if board[x][y] != -1 {
|
||
let mut num_of_nails = 0;
|
||
|
||
for i in -1..2 {
|
||
for j in -1..2 {
|
||
// Check if index is in range (>= 0 and < length of vector)
|
||
if (x as i64 + i >= 0 && (x as i64 + i) < board.len() as i64) && (y as i64 + j >= 0 && (y as i64 + j) < board[x].len() as i64) {
|
||
if board[(x as i64 + i) as usize][(y as i64 + j) as usize] == -1 {
|
||
num_of_nails += 1; // Increment the nail count
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
board[x][y] = num_of_nails;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
fn find_connected_coordinates(grid: &Vec<Vec<i64>>, start_row: usize, start_col: usize) -> Vec<(usize, usize)> {
|
||
let rows = grid.len();
|
||
let cols = grid[0].len();
|
||
let target_value = grid[start_row][start_col];
|
||
|
||
// Directions for 8 possible moves (vertical, horizontal, and diagonal)
|
||
let directions: [(i64, i64); 8] = [
|
||
(-1, 0), // Up
|
||
(1, 0), // Down
|
||
(0, -1), // Left
|
||
(0, 1), // Right
|
||
(-1, -1),// Top-left diagonal
|
||
(-1, 1), // Top-right diagonal
|
||
(1, -1), // Bottom-left diagonal
|
||
(1, 1), // Bottom-right diagonal
|
||
];
|
||
|
||
// Visited cells
|
||
let mut visited = vec![vec![false; cols]; rows];
|
||
|
||
// Vector to store the reachable coordinates
|
||
let mut result = Vec::new();
|
||
|
||
// DFS function
|
||
fn dfs(
|
||
grid: &Vec<Vec<i64>>,
|
||
row: usize,
|
||
col: usize,
|
||
target_value: i64,
|
||
visited: &mut Vec<Vec<bool>>,
|
||
result: &mut Vec<(usize, usize)>,
|
||
directions: &[(i64, i64)],
|
||
) {
|
||
// Check if the position is out of bounds or already visited or doesn't match the target value
|
||
if row >= grid.len() || col >= grid[0].len() || visited[row][col] || grid[row][col] != target_value {
|
||
return;
|
||
}
|
||
|
||
// Mark the cell as visited and add the coordinate to the result
|
||
visited[row][col] = true;
|
||
result.push((row, col));
|
||
|
||
// Flag to check if we're at the boundary of the connected region
|
||
let mut is_boundary = false;
|
||
|
||
for &direction in directions.iter() {
|
||
let (dx, dy) = direction;
|
||
let new_row = row as i64 + dx;
|
||
let new_col = col as i64 + dy;
|
||
|
||
if new_row >= 0 && new_row < grid.len() as i64 && new_col >= 0 && new_col < grid[0].len() as i64 {
|
||
let new_row = new_row as usize;
|
||
let new_col = new_col as usize;
|
||
|
||
if grid[new_row][new_col] != target_value {
|
||
is_boundary = true;
|
||
}
|
||
|
||
dfs(grid, new_row, new_col, target_value, visited, result, directions);
|
||
}
|
||
}
|
||
|
||
if is_boundary {
|
||
for &direction in directions.iter() {
|
||
let (dx, dy) = direction;
|
||
let new_row = row as i64 + dx;
|
||
let new_col = col as i64 + dy;
|
||
|
||
if new_row >= 0 && new_row < grid.len() as i64 && new_col >= 0 && new_col < grid[0].len() as i64 {
|
||
let new_row = new_row as usize;
|
||
let new_col = new_col as usize;
|
||
|
||
// Add cells with a different value to the result
|
||
if grid[new_row][new_col] != target_value && !visited[new_row][new_col] {
|
||
result.push((new_row, new_col));
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
dfs(grid, start_row, start_col, target_value, &mut visited, &mut result, &directions);
|
||
|
||
result
|
||
}
|
||
|
||
fn uncover(board: &Vec<Vec<i64>>, state: &mut Vec<Vec<char>>, x_hovered: usize, y_hovered: usize, alive: &mut bool) {
|
||
state[y_hovered][x_hovered] = 'u';
|
||
if board[y_hovered][x_hovered] == -1 {
|
||
*alive = false;
|
||
}
|
||
|
||
if board[y_hovered][x_hovered] == 0 {
|
||
let connected = find_connected_coordinates(&board, y_hovered, x_hovered);
|
||
for (y,x) in connected.iter() {
|
||
state[*y][*x] = 'u';
|
||
}
|
||
}
|
||
}
|
||
|
||
fn detect_win(state: &Vec<Vec<char>>, number_of_nails: i64, start: Instant) {
|
||
let mut number_of_covered_cells: i64 = 0;
|
||
for y in 0..state.len() {
|
||
for x in 0..state[y].len() {
|
||
if state[y][x] != 'u' {
|
||
number_of_covered_cells = number_of_covered_cells + 1;
|
||
}
|
||
}
|
||
}
|
||
if number_of_covered_cells == number_of_nails {
|
||
println!("Congratulations, you won!!!");
|
||
let end = std::time::Instant::now();
|
||
println!("In {:?}", end-start);
|
||
std::process::exit(0);
|
||
}
|
||
}
|
||
|
||
fn main() {
|
||
let stdout = Term::buffered_stdout();
|
||
|
||
let mut board: Vec<Vec<i64>> = Vec::new();
|
||
let mut state: Vec<Vec<char>> = Vec::new(); // u = uncovered, c = covered, m = marked
|
||
|
||
let height: i64 = 8;
|
||
let width: i64 = 8;
|
||
let number_of_nails: i64 = 10;
|
||
//let percentage_of_cells_are_nails: i64 = 15;
|
||
//let number_of_nails: i64 = ((percentage_of_cells_are_nails / 100) as i64) * ((width * height) as i64) as i64;
|
||
|
||
let mut x_hovered: i64 = 0;
|
||
let mut y_hovered: i64 = 0;
|
||
|
||
let mut alive: bool = true;
|
||
|
||
for y in 0..height {
|
||
board.push(Vec::new());
|
||
state.push(Vec::new());
|
||
for x in 0..width {
|
||
board[y as usize].push(0);
|
||
state[y as usize].push('c');
|
||
//board[x as usize][y as usize] = -1;
|
||
}
|
||
}
|
||
let start = std::time::Instant::now();
|
||
|
||
place_nails(&mut board, number_of_nails, width, height);
|
||
determine_nails_in_range(&mut board);
|
||
output_board(&board, &state, x_hovered, y_hovered);
|
||
|
||
'gameloop: loop {
|
||
if let Ok(character) = stdout.read_char() {
|
||
match character {
|
||
'w' | 'k' => if y_hovered > 0 { y_hovered -= 1 },
|
||
'a' | 'h' => if x_hovered > 0 { x_hovered -= 1},
|
||
's' | 'j' => if y_hovered < height-1 { y_hovered += 1 },
|
||
'd' | 'l' => if x_hovered < width-1 {x_hovered += 1},
|
||
'u' => uncover(&board, &mut state, x_hovered as usize, y_hovered as usize, &mut alive),
|
||
'm' => {
|
||
if state[y_hovered as usize][x_hovered as usize] == 'm' {
|
||
state[y_hovered as usize][x_hovered as usize] = 'c'
|
||
}
|
||
else {
|
||
state[y_hovered as usize][x_hovered as usize] = 'm'
|
||
}
|
||
},
|
||
'?' => {
|
||
if state[y_hovered as usize][x_hovered as usize] == '?' {
|
||
state[y_hovered as usize][x_hovered as usize] = 'c'
|
||
}
|
||
else {
|
||
state[y_hovered as usize][x_hovered as usize] = '?'
|
||
}
|
||
},
|
||
'q' => break 'gameloop,
|
||
_ => (),
|
||
}
|
||
}
|
||
if alive {
|
||
output_board(&board, &state, x_hovered, y_hovered);
|
||
detect_win(&state, number_of_nails, start);
|
||
}
|
||
else if alive == false {
|
||
println!("GAME OVER!\nYou got Tetanus!");
|
||
println!("⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣀⡀⠀⠀⠀⠀⠀⠀⠀");
|
||
println!(" ⢀⣾⣿O⣷⠀⠀⠀⠀⠀⠀");
|
||
println!(" ⠀⠸⣿⣿⣿C⠀⠀⠀- HOLY SHIT⠀⠀⠀");
|
||
println!(" ⣀⣤⡈⠛⢉⠀⠀⠀⠀⠀⠀⠀");
|
||
println!(" ⢀⣴⣿⣿⣿⣿⣿⣿⠃⠀⠀⠀⠀⠀⠀");
|
||
println!(" ⣴⣿⣿⢿⣿⣿⣿⣿⣿⠀⡄⠀⠀⠀⠀⠀");
|
||
println!(" ⠀⠀⠀⠀⢰⣿⡏⠀⢸⣿⣿⣿⣿⡇⢸⣷⣤⣀⠀⠀⠀");
|
||
println!(" ⠀⠀⠀⠀⣼⣿⠁⠀⢸⣿⣿⣿⣿⠁⠀⠙⠻⢿⣿⣶⠀");
|
||
println!(" ⠀⠀⠀⠀⠛⠋⠀⠀⠸⣿⣿⣿⡏⠀⠀⠀⠀⠀⠈⠉⠀");
|
||
println!(" ⠀⠀⠀⠀⠀⠀⠀⠀⣄⠙⣿⣿⣷⡄⠀⠀⠀⠀⠀⠀⠀");
|
||
println!(" ⠀⠀⠀⠀⠀⠀⠀⢸⣿⣦⠈⢿⣿⣿⣦⠀⠀⠀⠀⠀⠀");
|
||
println!(" ⠀⠀⠀⠀⠀⠀⠀⣼⣿⡟⠀⠀⠻⣿⣿⣧⠀⠀⠀⠀⠀");
|
||
println!(" ⠀⠀⠀⠀⠀⣠⣾⣿⠟⠁⠀⠀⠀⠘⢿⣿⣧/⠀⠀⠀");
|
||
println!(" ⠀⠀⠀⠀⢾⣿⠟⠁⠀⠀⠀⠀⠀⠀⠈⢻⣿⠇⠀⠀⠀");
|
||
break 'gameloop;
|
||
}
|
||
}
|
||
}
|