diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/main.rs | 126 |
1 files changed, 72 insertions, 54 deletions
diff --git a/src/main.rs b/src/main.rs index 5970808..268fa58 100644 --- a/src/main.rs +++ b/src/main.rs @@ -27,12 +27,6 @@ mod minesweeper { pub board: Board, pub mine_count: usize, } - impl Display for Game { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - writeln!(f, "{}", self.board) - } - } - pub struct Board { pub data: Vec<u8>, pub width: usize, @@ -55,36 +49,28 @@ mod minesweeper { g } pub fn act(mut self, m: Move) -> Self { - let kaboom = match m.t { + let lost_phase = | phase | { + match phase { + Phase::SafeFirstMove => unimplemented!(), + Phase::Run => Phase::Die, + _ => unreachable!(), + } + }; + + match m.t { MoveType::Reveal => { - if self.phase == Phase::SafeFirstMove { - self.phase = Phase::Run; - } let kaboom: bool; self.board = { let mr = self.board.reveal(m.pos); kaboom = mr.1; mr.0 }; - kaboom - }, - MoveType::ToggleFlag => { - let kaboom: bool; - self.board = { - let mr = self.board.flag(m.pos); - kaboom = mr.1; - mr.0 - }; - kaboom + if kaboom { self.phase = lost_phase(self.phase) } + if self.phase == Phase::SafeFirstMove { self.phase = Phase::Run } }, + MoveType::ToggleFlag => self.board = self.board.flag(m.pos).0, }; - if kaboom { - self.phase = match self.phase { - Phase::SafeFirstMove => unimplemented!(), - Phase::Run => Phase::Die, - _ => unreachable!(), - } - } + if self.board.hidden_tiles == self.mine_count { self.phase = Phase::Win; } @@ -149,11 +135,7 @@ mod minesweeper { let nyr = usize::try_from(pos.1 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)); - } + self.flood_reveal((nx, ny)); Ok(0) }) }); @@ -180,39 +162,57 @@ mod minesweeper { self.data[off] ^= FLAGGED_BIT; MoveResult { 0: self, 1: false } } - } - impl Display for Board { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + pub fn render(&self, cursor: Option<(usize,usize)>) -> Vec<u8> { + const CYAN: &[u8] = &[27,b'[',b'3',b'6',b'm']; + const YELLOW: &[u8] = &[27,b'[',b'3',b'3',b'm']; + const GREEN: &[u8] = &[27,b'[',b'3',b'2',b'm']; + const RED: &[u8] = &[27,b'[',b'3',b'1',b'm']; + const FG: &[u8] = &[27,b'[',b'3',b'9',b'm']; + const ULINE: &[u8] = &[27,b'[',b'4',b'm']; + const REGULAR: &[u8] = &[27,b'[',b'0',b'm']; + + let mut ret = vec![27,b'[',b'2',b'J',27,b'[',b'0',b'm',b'\r']; + let mut cur_clr = FG; for y in 0..self.height { for x in 0..self.width { let c = &self.data[self.pos_to_off((x,y))]; + let is_cursor = if let Some(cursor) = cursor { cursor.0 == x && cursor.1 == y } else { false }; + if is_cursor { ret.extend_from_slice(ULINE); } 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")?, - _ if *c == MINE_VAL => write!(f, "O")?, - _ => write!(f, "?")?, + 0 => ret.push(b' '), + _ if *c <= 8 => { if cur_clr != FG { ret.extend_from_slice(FG); cur_clr = FG; } ret.push(b'0' + c); }, + _ if (*c & CORRECT_BIT) > 0 => { if cur_clr != GREEN { ret.extend_from_slice(GREEN); cur_clr = GREEN; } ret.push(b'F') }, + _ if (*c & FLAGGED_BIT) > 0 => { if cur_clr != YELLOW { ret.extend_from_slice(YELLOW); cur_clr = YELLOW; } ret.push(b'F'); }, + _ if (*c & HIDDEN_BIT) > 0 => { if cur_clr != CYAN { ret.extend_from_slice(CYAN); cur_clr = CYAN; } ret.push(b'-'); }, + _ if *c == MINE_VAL => { if cur_clr != RED { ret.extend_from_slice(RED); cur_clr = RED; } ret.push(b'O'); }, + _ => ret.push(b'?'), } + if is_cursor { ret.extend_from_slice(REGULAR); ret.extend_from_slice(cur_clr); } } - writeln!(f, "\r")?; + ret.push(b'\r'); + ret.push(b'\n'); } - Ok(()) + ret } } } use minesweeper::*; use std::convert::{ TryFrom, TryInto }; +use std::io::Write; fn main() -> Result<(), io::Error> { let board = Board::new(10,10); let mut game = Game::new(board, 10); - let mut cursor: (usize, usize) = (5,5); + let mut cursor: (usize, usize) = (0,0); + let stdout = io::stdout(); + let mut lstdout = stdout.lock(); while game.phase == Phase::Run || game.phase == Phase::SafeFirstMove { - println!("{}",game); + let screen = game.board.render(Some(cursor)); + lstdout.write_all(&screen)?; + lstdout.flush()?; + let mm = playerctrl( (cursor.0.try_into().unwrap(), cursor.1.try_into().unwrap()), (game.board.width.try_into().unwrap(), game.board.height.try_into().unwrap()))?; @@ -223,8 +223,8 @@ fn main() -> Result<(), io::Error> { MetaMove::Noop => (), } } - - print!("{}", game); + game.board = game.board.grade(); + lstdout.write_all(&game.board.render(None))?; Ok(()) } @@ -235,20 +235,38 @@ enum MetaMove { Quit, } fn playerctrl(mut cursor: (isize, isize), bounds: (isize, isize)) -> io::Result<MetaMove> { + const ERR_STR: &str = "bad/no input"; + let mut mov_dir = | dir: (isize,isize) | { + cursor.0 += dir.0; + cursor.1 += dir.1; + MetaMove::CursorMove(cursor_bounds(cursor, bounds)) + }; let stdin = io::stdin(); let lstdin = stdin.lock(); let mut input = lstdin.bytes(); - Ok(match input.next().expect("bad/no input byte")? { - b'w' => { cursor.1 -= 1; MetaMove::CursorMove(cursor_bounds(cursor, bounds)) }, - b's' => { cursor.1 += 1; MetaMove::CursorMove(cursor_bounds(cursor, bounds)) }, - b'a' => { cursor.0 -= 1; MetaMove::CursorMove(cursor_bounds(cursor, bounds)) }, - b'd' => { cursor.0 += 1; MetaMove::CursorMove(cursor_bounds(cursor, bounds)) }, - b'r' => MetaMove::Move(Move { t: MoveType::Reveal, pos: cursor_bounds(cursor, bounds) }), + Ok(match input.next().expect(ERR_STR)? { + b'w' => mov_dir((0,-1)), + b'a' => mov_dir((-1,0)), + b's' => mov_dir((0,1)), + b'd' => mov_dir((1,0)), + 13 => MetaMove::Move(Move { t: MoveType::Reveal, pos: cursor_bounds(cursor, bounds) }), b'f' => MetaMove::Move(Move { t: MoveType::ToggleFlag, pos: cursor_bounds(cursor, bounds) }), b'q' => MetaMove::Quit, + 27 => { + if input.next().expect(ERR_STR)? == b'[' { + match input.next().expect(ERR_STR)? { + b'A' => mov_dir((0,-1)), + b'B' => mov_dir((0,1)), + b'C' => mov_dir((1,0)), + b'D' => mov_dir((-1,0)), + _ => MetaMove::Noop, + } + } else { MetaMove::Noop } + } v => { println!("{:?}", v); MetaMove::Noop }, }) } + fn cursor_bounds(mut c: (isize, isize), b: (isize, isize)) -> (usize, usize) { if c.0 > b.0 - 1 { c.0 = 0; |