diff options
author | stale <redkugelblitzin@gmail.com> | 2022-04-20 05:57:12 -0300 |
---|---|---|
committer | stale <redkugelblitzin@gmail.com> | 2022-04-20 05:57:12 -0300 |
commit | 892bc0d4bfaeb92ef115ca652a883ea0011563f6 (patch) | |
tree | 3488d2e0955f16dddefb28a72ff4a67d31ad96f8 /src/main.rs |
minesweeper gameloop wip
Diffstat (limited to 'src/main.rs')
-rw-r--r-- | src/main.rs | 152 |
1 files changed, 152 insertions, 0 deletions
diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..a38759d --- /dev/null +++ b/src/main.rs @@ -0,0 +1,152 @@ +use std::{ io, io::Read }; + +mod minesweeper { + use std::convert::TryFrom; + use std::fmt::Display; + use rand::{ thread_rng, Rng, distributions::Uniform }; + + const HIDDEN_BIT: u8 = 1 << 7; + const FLAGGED_BIT: u8 = 1 << 6; + const CORRECT_BIT: u8 = 1 << 5; // grading for a rightly flagged mine + const MINE_VAL: u8 = !(HIDDEN_BIT | FLAGGED_BIT | CORRECT_BIT); + const NEIGH_OFFS: &[(i8,i8)] = &[ + (-1,-1),(0,-1),(1,-1), + (-1, 0), (1, 0), + (-1, 1),(0, 1),(1, 1), + ]; + pub struct Board { + pub data: Vec<u8>, + pub width: usize, + pub height: usize, + } + + pub struct Move(pub Board, pub bool); + + impl Board { + pub fn new(w: usize, h: usize, mine_count: usize) -> Board { + let mut b = Board { + data: [HIDDEN_BIT].repeat(w*h), + width: w, + height: h, + }; + + let mut rng = thread_rng(); + let mut c = mine_count; + while c > 0 { + let x: usize = rng.sample(Uniform::new(0,w-1)); + let y: usize = rng.sample(Uniform::new(0,h-1)); + let o = b.pos_to_off(x,y); + if b.data[o] == MINE_VAL | HIDDEN_BIT { continue } + else { + b.data[o] |= MINE_VAL; + c -= 1; + for (ox,oy) in NEIGH_OFFS { + let nxr = usize::try_from(x as i8 + ox); + let nyr = usize::try_from(y as i8 + oy); + let _ = nxr.and_then(|nx: usize| { nyr.and_then(|ny: usize| { + if nx > w - 1 || ny > h - 1 { return usize::try_from(-1) }; + let off = b.pos_to_off(nx,ny); + let c = &mut b.data[off]; + if *c != HIDDEN_BIT | MINE_VAL { + *c += 1; + } + Ok(0) + }) + }); + } + } + } + b + } + + pub fn pos_to_off(&self, x: usize, y: usize) -> usize { + x + y * self.width + } + pub fn flood_reveal(&mut self, x: usize, y: usize) { + if x > self.width || y > self.height { return; } + let off = self.pos_to_off(x,y); + let c = &mut self.data[off]; + if *c & HIDDEN_BIT > 0 { + *c &= !HIDDEN_BIT; + if *c > 0 { return } + drop(c); + for (ox,oy) in NEIGH_OFFS { + let nxr = usize::try_from(x as i8 + ox); + let nyr = usize::try_from(y as i8 + oy); + let _ = nxr.and_then(|nx: usize| { nyr.and_then(|ny: usize| { + if nx > self.width - 1 || ny > self.height - 1 { return usize::try_from(-1) }; + let off = self.pos_to_off(nx,ny); + let c = &mut self.data[off]; + if *c == HIDDEN_BIT { + self.flood_reveal(nx, ny); + } + Ok(0) + }) + }); + } + } + } + pub fn reveal(mut self, x: usize, y: usize) -> Move { + if x > self.width - 1 || y > self.height - 1 { panic!("OOB reveal"); } + let off = self.pos_to_off(x,y); + self.flood_reveal(x,y); + let c = self.data[off]; + Move { 0: self, 1: (c & !(FLAGGED_BIT | CORRECT_BIT)) == MINE_VAL } // Kaboom + } + } + + impl Display for Board { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + for y in 0..self.height { + for x in 0..self.width { + let c = &self.data[self.pos_to_off(x,y)]; + match *c { + 0 => write!(f, " ")?, + _ if *c <= 8 => write!(f, "{}", c.to_string())?, + _ if (*c & FLAGGED_BIT) > 0 => write!(f, "F")?, + _ if (*c & HIDDEN_BIT) > 0 => write!(f, "-")?, + _ if (*c & CORRECT_BIT) > 0 => write!(f, "C")?, + _ => write!(f, "?")?, + } + } + writeln!(f, "\r")?; + } + Ok(()) + } + } +} + +fn main() -> Result<(), io::Error> { + let mut board = minesweeper::Board::new(10,10,100/10); + let mut cursor: (usize, usize) = (5,5); + let mut gstate = 1; + while gstate != 0 { + println!("{}", board); + let done: bool; + let m = playerctrl(board, &mut cursor)?; + (board, done) = (m.0, m.1); + if done { gstate = 0 } + } + Ok(()) +} + +fn playerctrl(mut board: minesweeper::Board, cursor: &mut (usize, usize)) -> io::Result<minesweeper::Move> { + let stdin = io::stdin(); + let lstdin = stdin.lock(); + let mut input = lstdin.bytes(); + let mut kaboom = false; + match input.next().expect("bad/no input byte")? { + b'w' => cursor.1 -= 1, + b's' => cursor.1 += 1, + b'a' => cursor.0 -= 1, + b'd' => cursor.0 += 1, + b'r' => { + let m = board.reveal(cursor.0, cursor.1); + (board, kaboom) = (m.0, m.1); + }, + b'f' => unimplemented!(), + b'q' => kaboom = true, + v => println!("{:?}", v), + }; + Ok(minesweeper::Move { 0: board, 1: kaboom }) +} |