325 lines
13 KiB
Rust
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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;
}
}
}