summaryrefslogtreecommitdiff
path: root/src/main.rs
diff options
context:
space:
mode:
authorElizabeth Hunt <elizabeth.hunt@simponic.xyz>2024-05-27 14:59:25 -0700
committerElizabeth Hunt <elizabeth.hunt@simponic.xyz>2024-05-27 14:59:25 -0700
commit2f2159d6c81b7bb69ed16bbe1b7d70cf41ffe8fe (patch)
tree0e7f3735a2dd71d98c92cebe5de1c0762276a7b4 /src/main.rs
parenta2a468f43cb337238dba2332d1532598b0b1586c (diff)
downloadmineswp-rs-main.tar.gz
mineswp-rs-main.zip
finish base gameHEADmain
Diffstat (limited to 'src/main.rs')
-rw-r--r--src/main.rs126
1 files changed, 104 insertions, 22 deletions
diff --git a/src/main.rs b/src/main.rs
index b8705c7..d5d2ad1 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -15,6 +15,7 @@ impl fmt::Display for Cell {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if self.flagged {
write!(f, "F")?;
+ return Ok(());
}
if !self.revealed {
@@ -69,8 +70,12 @@ impl Grid {
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 height = self.0.len();
+ let width = self.0[0].len();
+
+ let row = rng.gen_range(0..height);
+ let col = rng.gen_range(0..width);
+
let cell: &mut Cell = &mut self.0[row][col];
if cell.mine {
continue;
@@ -82,6 +87,25 @@ impl Grid {
}
}
+ fn on_neighbors<F>(&mut self, x: usize, y: usize, mut f: F)
+ where
+ F: FnMut(&mut Cell, (usize, 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 {
+ continue;
+ }
+
+ let Some(cell) = self.0.get_mut(row).and_then(|row| row.get_mut(col)) else {
+ continue;
+ };
+
+ f(cell, (row, col));
+ }
+ }
+ }
+
pub fn new(rows: usize, cols: usize, mines: u8) -> Option<Self> {
if rows < 1 || cols < 1 {
return None;
@@ -112,7 +136,7 @@ enum GridCommand {
struct GameState {
grid: Grid,
- win: bool,
+ play: bool,
turn: u32,
}
@@ -168,18 +192,33 @@ fn parse_command(cmd: &str) -> Result<GridCommand, &'static str> {
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)),
+ "f" | "flag" => Ok(GridCommand::FLAG(coord)),
+ "r" | "reveal" => Ok(GridCommand::REVEAL(coord)),
_ => Err("Unknown command"),
}
}
-fn reveal_at(game_state: &mut GameState, coord: (usize, usize)) {
+fn reveal_at(game_state: &mut GameState, coord: (usize, usize)) -> Result<bool, &'static str> {
+ let Some(cell) = game_state
+ .grid
+ .0
+ .get_mut(coord.0)
+ .and_then(|row| row.get_mut(coord.1))
+ else {
+ return Err("invalid coordinates");
+ };
+
let mut seen = HashSet::new();
let mut stack = Vec::<(usize, usize)>::new();
stack.push(coord);
+ if cell.mine {
+ println!("that's a mine.");
+ return Ok(false);
+ }
+
while let Some((y, x)) = stack.pop() {
+ let coord = (y, x);
if seen.contains(&coord) {
continue;
}
@@ -188,45 +227,87 @@ fn reveal_at(game_state: &mut GameState, coord: (usize, usize)) {
let Some(cell) = game_state.grid.0.get_mut(y).and_then(|row| row.get_mut(x)) else {
continue;
};
+ cell.revealed = true;
+
if cell.neighbors > 0 || cell.mine {
continue;
}
- cell.revealed = true;
+ game_state
+ .grid
+ .on_neighbors(x, y, |_cell, coord| stack.push(coord));
+ }
- 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;
- }
+ return Ok(true);
+}
- stack.push((row, col));
- }
- }
- }
+fn flag_at(game_state: &mut GameState, coord: (usize, usize)) -> Result<bool, &'static str> {
+ let Some(cell) = game_state
+ .grid
+ .0
+ .get_mut(coord.0)
+ .and_then(|row| row.get_mut(coord.1))
+ else {
+ return Err("invalid coordinates");
+ };
+ cell.flagged = !cell.flagged;
+
+ let won = game_state.grid.0.iter().fold(true, |won, row| {
+ won && row
+ .iter()
+ .fold(won, |won, cell| won && cell.flagged == cell.mine)
+ });
+
+ return Ok(!won);
}
fn play_game(game_state: &mut GameState) {
- if game_state.win {
+ if !game_state.play {
return;
}
println!("{}\n", game_state);
print!("> ");
let _ = io::stdout().flush();
-
let mut buffer = String::new();
let stdin = io::stdin();
- stdin.read_line(&mut buffer);
+ match stdin.read_line(&mut buffer) {
+ Ok(_) => (),
+ Err(e) => {
+ eprintln!("error: {}", e);
+ return play_game(game_state);
+ }
+ }
let cmd = match parse_command(buffer.as_str()) {
Ok(command) => command,
Err(e) => {
- println!("error: {}", e);
+ eprintln!("error: {}", e);
return play_game(game_state);
}
};
+ match cmd {
+ GridCommand::REVEAL(coord) => {
+ game_state.play = match reveal_at(game_state, coord) {
+ Ok(continue_play) => continue_play,
+ Err(e) => {
+ eprintln!("error: {}", e);
+ return play_game(game_state);
+ }
+ }
+ }
+ GridCommand::FLAG(coord) => {
+ game_state.play = match flag_at(game_state, coord) {
+ Ok(continue_play) => continue_play,
+ Err(e) => {
+ eprintln!("error: {}", e);
+ return play_game(game_state);
+ }
+ }
+ }
+ }
+
game_state.turn += 1;
play_game(game_state);
}
@@ -236,11 +317,12 @@ fn main() {
eprintln!("failed to initialize grid");
process::exit(1);
};
-
let mut game_state = GameState {
grid,
- win: false,
+ play: true,
turn: 1,
};
play_game(&mut game_state);
+
+ main();
}