stuff
This commit is contained in:
parent
40ae10437b
commit
a2a468f43c
220
src/main.rs
220
src/main.rs
@ -1,5 +1,7 @@
|
||||
use rand::Rng;
|
||||
use std::fmt;
|
||||
use std::collections::HashSet;
|
||||
use std::io::Write;
|
||||
use std::{fmt, io, process};
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
struct Cell {
|
||||
@ -40,65 +42,205 @@ struct Grid(Vec<Vec<Cell>>);
|
||||
|
||||
impl fmt::Display for Grid {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
let mut y = 'a' as u8;
|
||||
for row in self.0.iter() {
|
||||
write!(f, "{:2} ", y as char)?;
|
||||
y += 1;
|
||||
|
||||
for cell in row.iter() {
|
||||
write!(f, " {} ", cell)?;
|
||||
}
|
||||
write!(f, "\n")?;
|
||||
}
|
||||
|
||||
write!(f, "{:2} ", "")?;
|
||||
let mut col = 0;
|
||||
for _ in self.0[0].iter() {
|
||||
write!(f, "{:2} ", col)?;
|
||||
col += 1;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn mark_neighbors(grid: &mut Grid, x: usize, y: usize) {
|
||||
for row in y.saturating_sub(1)..=y.saturating_add(1) {
|
||||
for col in x.saturating_sub(1)..=x.saturating_add(1) {
|
||||
if x == col && y == row {
|
||||
impl Grid {
|
||||
fn plant_mines_and_set_neighbors(&mut self, mines: u8) {
|
||||
let mut rng = rand::thread_rng();
|
||||
|
||||
let mut mines_left = mines;
|
||||
while mines_left > 0 {
|
||||
let row = rng.gen_range(0..self.0.len());
|
||||
let col = rng.gen_range(0..self.0[0].len());
|
||||
let cell: &mut Cell = &mut self.0[row][col];
|
||||
if cell.mine {
|
||||
continue;
|
||||
}
|
||||
|
||||
let Some(cell) = grid.0.get_mut(row).and_then(|row| row.get_mut(col)) else {
|
||||
continue;
|
||||
};
|
||||
mines_left -= 1;
|
||||
cell.mine = true;
|
||||
self.on_neighbors(col, row, |cell, _| cell.neighbors += 1);
|
||||
}
|
||||
}
|
||||
|
||||
cell.neighbors += 1;
|
||||
pub fn new(rows: usize, cols: usize, mines: u8) -> Option<Self> {
|
||||
if rows < 1 || cols < 1 {
|
||||
return None;
|
||||
}
|
||||
|
||||
let mut grid: Grid = Grid(vec![
|
||||
vec![
|
||||
Cell {
|
||||
neighbors: 0,
|
||||
flagged: false,
|
||||
mine: false,
|
||||
revealed: false,
|
||||
};
|
||||
rows
|
||||
];
|
||||
cols
|
||||
]);
|
||||
grid.plant_mines_and_set_neighbors(mines);
|
||||
|
||||
return Some(grid);
|
||||
}
|
||||
}
|
||||
|
||||
enum GridCommand {
|
||||
REVEAL((usize, usize)),
|
||||
FLAG((usize, usize)),
|
||||
}
|
||||
|
||||
struct GameState {
|
||||
grid: Grid,
|
||||
win: bool,
|
||||
turn: u32,
|
||||
}
|
||||
|
||||
impl fmt::Display for GameState {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "turn({})\n", self.turn)?;
|
||||
write!(f, "{}", self.grid)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_coord(coord: &str) -> Result<(usize, usize), &'static str> {
|
||||
let mut parts = coord.chars();
|
||||
if coord.len() < 2 {
|
||||
return Err("expected at least two characters when parsing coordinate");
|
||||
}
|
||||
|
||||
let mut y: usize = 0;
|
||||
let mut x: usize = 0;
|
||||
loop {
|
||||
let Some(next) = parts.next() else {
|
||||
break;
|
||||
};
|
||||
if next >= 'a' && next <= 'z' {
|
||||
y = ((next as u8) - 'a' as u8) as usize;
|
||||
}
|
||||
if next >= '0' && next <= '9' {
|
||||
x = ((next as u8) - '0' as u8) as usize;
|
||||
}
|
||||
}
|
||||
|
||||
return Ok((y, x));
|
||||
}
|
||||
|
||||
fn parse_command(cmd: &str) -> Result<GridCommand, &'static str> {
|
||||
let mut parts = cmd.split_whitespace();
|
||||
|
||||
if parts.clone().count() == 1 {
|
||||
if let Some(coord_str) = parts.next() {
|
||||
match parse_coord(coord_str) {
|
||||
Ok(coord) => return Ok(GridCommand::REVEAL(coord)),
|
||||
Err(e) => return Err(e),
|
||||
}
|
||||
} else {
|
||||
return Err("Can't get coordinate part of command");
|
||||
}
|
||||
}
|
||||
|
||||
let command_part = parts.next().ok_or("Can't get command part of input")?;
|
||||
let coord_str = parts.next().ok_or("Can't get coordinate part of command")?;
|
||||
|
||||
let coord = parse_coord(coord_str).or(Err("Invalid coordinates"))?;
|
||||
|
||||
match command_part.to_lowercase().as_str() {
|
||||
"flag" => Ok(GridCommand::FLAG(coord)),
|
||||
"reveal" => Ok(GridCommand::REVEAL(coord)),
|
||||
_ => Err("Unknown command"),
|
||||
}
|
||||
}
|
||||
|
||||
fn reveal_at(game_state: &mut GameState, coord: (usize, usize)) {
|
||||
let mut seen = HashSet::new();
|
||||
let mut stack = Vec::<(usize, usize)>::new();
|
||||
stack.push(coord);
|
||||
|
||||
while let Some((y, x)) = stack.pop() {
|
||||
if seen.contains(&coord) {
|
||||
continue;
|
||||
}
|
||||
seen.insert(coord);
|
||||
|
||||
let Some(cell) = game_state.grid.0.get_mut(y).and_then(|row| row.get_mut(x)) else {
|
||||
continue;
|
||||
};
|
||||
if cell.neighbors > 0 || cell.mine {
|
||||
continue;
|
||||
}
|
||||
|
||||
cell.revealed = true;
|
||||
|
||||
for row in y.saturating_sub(1)..=y.saturating_add(1) {
|
||||
for col in x.saturating_sub(1)..=x.saturating_add(1) {
|
||||
if x == col && y == row {
|
||||
continue;
|
||||
}
|
||||
|
||||
stack.push((row, col));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn plant_mines(grid: &mut Grid, mines: u8) {
|
||||
let mut rng = rand::thread_rng();
|
||||
|
||||
let mut mines_left = mines;
|
||||
while mines_left > 0 {
|
||||
let row = rng.gen_range(0..grid.0.len());
|
||||
let col = rng.gen_range(0..grid.0[0].len());
|
||||
let cell: &mut Cell = &mut grid.0[row][col];
|
||||
|
||||
if !cell.mine {
|
||||
mines_left -= 1;
|
||||
cell.mine = true;
|
||||
|
||||
mark_neighbors(grid, col, row);
|
||||
}
|
||||
fn play_game(game_state: &mut GameState) {
|
||||
if game_state.win {
|
||||
return;
|
||||
}
|
||||
|
||||
println!("{}\n", game_state);
|
||||
print!("> ");
|
||||
let _ = io::stdout().flush();
|
||||
|
||||
let mut buffer = String::new();
|
||||
let stdin = io::stdin();
|
||||
stdin.read_line(&mut buffer);
|
||||
|
||||
let cmd = match parse_command(buffer.as_str()) {
|
||||
Ok(command) => command,
|
||||
Err(e) => {
|
||||
println!("error: {}", e);
|
||||
return play_game(game_state);
|
||||
}
|
||||
};
|
||||
|
||||
game_state.turn += 1;
|
||||
play_game(game_state);
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let mut grid = Grid(vec![
|
||||
vec![
|
||||
Cell {
|
||||
neighbors: 0,
|
||||
flagged: false,
|
||||
mine: false,
|
||||
revealed: false,
|
||||
};
|
||||
9
|
||||
];
|
||||
9
|
||||
]);
|
||||
let Some(grid) = Grid::new(9, 9, 10) else {
|
||||
eprintln!("failed to initialize grid");
|
||||
process::exit(1);
|
||||
};
|
||||
|
||||
plant_mines(&mut grid, 10);
|
||||
|
||||
println!("{}", grid);
|
||||
let mut game_state = GameState {
|
||||
grid,
|
||||
win: false,
|
||||
turn: 1,
|
||||
};
|
||||
play_game(&mut game_state);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user