summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/main.rs152
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 })
+}