diff options
author | stale <redkugelblitzin@gmail.com> | 2022-07-12 23:34:58 -0300 |
---|---|---|
committer | stale <redkugelblitzin@gmail.com> | 2022-07-12 23:34:58 -0300 |
commit | 4e0bdd3f093f345dcdf49e46a2bd7e8bfaf4dfff (patch) | |
tree | c39b46bcc3370da4d6bdf20d7eed310b8f813aa5 | |
parent | 02098f0d579259545584e246f64ed5e41be9537b (diff) |
rework old, dumb code
-rw-r--r-- | src/conn.rs | 14 | ||||
-rw-r--r-- | src/livepos.rs | 4 | ||||
-rw-r--r-- | src/main.rs | 4 | ||||
-rw-r--r-- | src/minesweeper.rs | 191 |
4 files changed, 99 insertions, 114 deletions
diff --git a/src/conn.rs b/src/conn.rs index a0ba899..2b6ee80 100644 --- a/src/conn.rs +++ b/src/conn.rs @@ -77,9 +77,9 @@ pub async fn drive_conn(conn: (Conn, SplitStream<WebSocket>), rinfo: (RoomId, Ar }; let mut fields = cmd.split(" "); - let parse_pos = |mut fields: std::str::Split<&str>| -> Option<(usize, usize)> { - let x = fields.next().and_then(|xstr| xstr.parse::<usize>().ok()); - let y = fields.next().and_then(|ystr| ystr.parse::<usize>().ok()); + let parse_pos = |mut fields: std::str::Split<&str>| -> Option<(u32, u32)> { + let x = fields.next().and_then(|xstr| xstr.parse::<u32>().ok()); + let y = fields.next().and_then(|ystr| ystr.parse::<u32>().ok()); x.zip(y) }; if let Some(cmd_name) = fields.next() { @@ -100,7 +100,9 @@ pub async fn drive_conn(conn: (Conn, SplitStream<WebSocket>), rinfo: (RoomId, Ar "reveal" => { match parse_pos(fields) { Some(pos) => { - if let Err(e) = cmd_tx.send(MetaMove::Move(Move { t: MoveType::Reveal, pos }, conn.addr)) { + if let Err(e) = cmd_tx.send(MetaMove::Move( + Move {t: MoveType::Reveal, pos: pos.try_into().unwrap() }, conn.addr)) + { println!("{room_id} E: couldn't process {me}'s reveal command: {e}"); }; }, @@ -112,7 +114,9 @@ pub async fn drive_conn(conn: (Conn, SplitStream<WebSocket>), rinfo: (RoomId, Ar "flag" => { match parse_pos(fields) { Some(pos) => { - if let Err(e) = cmd_tx.send(MetaMove::Move(Move { t: MoveType::ToggleFlag, pos }, conn.addr)) { + if let Err(e) = cmd_tx.send(MetaMove::Move( + Move { t: MoveType::ToggleFlag, pos: pos.try_into().unwrap() }, conn.addr)) + { println!("{room_id} E: couldn't process {me}'s flag command: {e}"); }; }, diff --git a/src/livepos.rs b/src/livepos.rs index b19eda6..08f3246 100644 --- a/src/livepos.rs +++ b/src/livepos.rs @@ -7,7 +7,7 @@ use tokio::time::{self, Duration}; use warp::ws::Message; pub enum ReqData { - Pos((usize,usize)), + Pos((u32,u32)), StateDump, Quit, } @@ -69,7 +69,7 @@ pub async fn livepos(players: Arc<RwLock<PlayerMap>>, mut recv: tokio_mpsc::Unbo ); } -fn jsonenc_ids<'a, I: IntoIterator<Item=&'a usize>>(positions: &mut HashMap<usize, (usize,usize)>, ids: I) -> Result<String, serde_json::Error> { +fn jsonenc_ids<'a, I: IntoIterator<Item=&'a usize>>(positions: &mut HashMap<usize, (u32,u32)>, ids: I) -> Result<String, serde_json::Error> { let mut pairs = Vec::new(); for id in ids { pairs.push((id, positions[id])); diff --git a/src/main.rs b/src/main.rs index 7a85e63..c884dbc 100644 --- a/src/main.rs +++ b/src/main.rs @@ -228,10 +228,10 @@ async fn gameloop(moves: MoveStreamHandles, players: Arc<RwLock<PlayerMap>>, bco let done = |p: &Phase| { *p == Phase::Die || *p == Phase::Win }; match req { MetaMove::Move(m, o) => if !done(&game.phase) { - game = game.act(m); + game.act(m); desynced = true; if done(&game.phase) { - game.board = game.board.grade(); + game.board.grade(); final_player_name = players.read().await.get(&o).map(|p| p.name.clone()); } move_tx.send(MetaMove::StateSync).unwrap(); diff --git a/src/minesweeper.rs b/src/minesweeper.rs index ae4fe18..f4c5511 100644 --- a/src/minesweeper.rs +++ b/src/minesweeper.rs @@ -11,11 +11,6 @@ const CORRECT_BIT: u8 = 1 << 5; // grading for a rightly flagged mine // all the bits that aren't flags const TILE_NUMBITS: u8 = !(HIDDEN_BIT | FLAGGED_BIT | CORRECT_BIT); const MINED: u8 = HIDDEN_BIT | TILE_NUMBITS; -const NEIGH_OFFS: &[(isize,isize)] = &[ - (-1,-1),(0,-1),(1,-1), - (-1, 0), (1, 0), - (-1, 1),(0, 1),(1, 1), -]; #[derive(PartialEq)] pub enum Phase { SafeFirstMove, @@ -63,10 +58,9 @@ pub enum MoveType { #[derive(Debug)] pub struct Move { pub t: MoveType, - pub pos: (usize,usize), + pub pos: BoardPos, } -pub struct MoveResult(pub Board, pub bool); impl Game { pub fn new(conf: BoardConf) -> Self { let board = Board::new(conf); @@ -76,9 +70,9 @@ impl Game { board_conf: conf } } - pub fn act(mut self, m: Move) -> Self { - let lost_phase = | phase | { - match phase { + pub fn act(&mut self, m: Move) { + let lost_phase = | phase: &Phase | { + match *phase { Phase::SafeFirstMove => Phase::FirstMoveFail, Phase::Run => Phase::Die, _ => unreachable!(), @@ -87,16 +81,11 @@ impl Game { match m.t { MoveType::Reveal => { - let kaboom: bool; - self.board = { - let mr = self.board.reveal(m.pos); - kaboom = mr.1; - mr.0 - }; - if kaboom { self.phase = lost_phase(self.phase) } + let kaboom = self.board.reveal(m.pos); + 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, + MoveType::ToggleFlag => self.board.flag(m.pos), }; if self.phase == Phase::FirstMoveFail { @@ -105,7 +94,7 @@ impl Game { self.board.hidden_tiles += 1; self.board.move_mine_elsewhere(m.pos); self.phase = Phase::Run; - self = self.act(m); + self.act(m); } else { self.phase = Phase::Die; } @@ -116,7 +105,6 @@ impl Game { *tile = unhide(*tile); } } - self } } impl Board { @@ -126,7 +114,7 @@ impl Board { if w.get() < 3 || h.get() < 3 { conf.revealed_borders = false; } let mined_area = area - if conf.revealed_borders { 2*(w.get()-1) + 2*(h.get()-1) } else { 0 }; let mine_count = ((conf.mine_ratio.0 * mined_area) / conf.mine_ratio.1.get()).clamp(0, mined_area); - let b = Board { + let mut b = Board { data: [HIDDEN_BIT].repeat(area), width: w, height: h, @@ -135,108 +123,83 @@ impl Board { }; if conf.revealed_borders { let (w,h) = (w.get(),h.get()); - let mut b = b.spread_mines(mine_count, true); + b.spread_mines(mine_count, true); for x in 0..w { - b = b.reveal((x, 0)).0; - b = b.reveal((x, h-1)).0; + b.reveal((x, 0).try_into().unwrap()); + b.reveal((x, h-1).try_into().unwrap()); } for y in 1..h-1 { - b = b.reveal(( 0, y)).0; - b = b.reveal((w-1, y)).0; + b.reveal(( 0, y).try_into().unwrap()); + b.reveal((w-1, y).try_into().unwrap()); } - b - } else { b.spread_mines(mine_count, false) } + } else { b.spread_mines(mine_count, false); } + b } - pub fn spread_mines(mut self, mut count: usize, without_edges: bool) -> Self { + pub fn spread_mines(&mut self, mut count: usize, without_edges: bool) { let mut rng = thread_rng(); - let w = self.width.get(); - let h = self.height.get(); + let w = self.width.get() as u32; + let h = self.height.get() as u32; let (wr,hr) = if without_edges { ((1,w-1),(1,h-1)) } else { ((0,w),(0,h)) }; while count > 0 { - let randpos: (usize, usize) = (rng.sample(Uniform::new(wr.0, wr.1)), rng.sample(Uniform::new(hr.0, hr.1))); - let o = self.pos_to_off_unchecked(randpos); + let randpos = BoardPos(rng.sample(Uniform::new(wr.0, wr.1)), rng.sample(Uniform::new(hr.0, hr.1))); + let o = randpos.rel_offset_unchecked(&self); if self.data[o] == MINED { continue } else { self.data[o] = MINED; count -= 1; - let minepos = pos_u2i(randpos).unwrap(); - self.map_neighs(minepos, |neigh| { + self.map_neighs(randpos, |neigh| { if neigh != MINED { neigh + 1 } else { neigh } }); } } - self } - fn neighs<T>(&self, pos: (T,T)) -> Option<Vec<(usize,usize)>> - where T: TryInto<isize> - { - if let (Ok(ox),Ok(oy)) = (pos.0.try_into(),pos.1.try_into()) { - Some(NEIGH_OFFS - .iter() - .map(|(x,y)| (*x + ox, *y + oy)).filter_map(|p| self.bounded(p)) - .collect()) - } else { - None - } + fn neighs(&self, pos: BoardPos) -> Vec<BoardPos> { + const NEIGH_OFFS: &[(isize,isize)] = &[ + (-1,-1),(0,-1),(1,-1), + (-1, 0), (1, 0), + (-1, 1),(0, 1),(1, 1), + ]; + let ipos: (isize,isize) = pos.try_into().unwrap(); + NEIGH_OFFS + .iter() + .filter_map(|(x,y)| (*x + ipos.0, *y + ipos.1).try_into().ok()) + .filter(|pos: &BoardPos| pos.is_within(&self)) + .collect() } - fn map_neighs<T>(&mut self, pos: (T,T), mut f: impl FnMut(u8) -> u8) where T: TryInto<isize> { - if let Some(neighs) = self.neighs(pos) { - let npos = neighs.iter().filter_map(|pos| self.pos_to_off(*pos)).collect::<Vec<usize>>(); - npos.iter().for_each(|o| { - self.data[*o] = f(self.data[*o]); - }); - } + fn map_neighs<F: FnMut(u8) -> u8>(&mut self, pos: BoardPos, mut f: F) { + let neighs: Vec<usize> = self.neighs(pos).iter().filter_map(|pos| pos.rel_offset(&self)).collect(); + neighs.iter().for_each(|off| { self.data[*off] = f(self.data[*off]); }); } - pub fn pos_to_off(&self, pos: (usize,usize)) -> Option<usize> - { - self.bounded(pos).map(|x| self.pos_to_off_unchecked(x)) - } - pub fn pos_to_off_unchecked(&self, pos: (usize, usize)) -> usize { - pos.0 + pos.1 * self.width.get() - } - pub fn bounded<T>(&self, pos: (T,T)) -> Option<(usize, usize)> - where T: TryInto<usize> - { - if let (Ok(x),Ok(y)) = ( - pos.0.try_into(), - pos.1.try_into(), - ) { - (x < self.width.get() && y < self.height.get()).then(|| (x,y)) - } else { None } - } - pub fn flood_reveal(&mut self, pos: (usize,usize)) -> bool { + pub fn flood_reveal(&mut self, pos: BoardPos) -> bool { let mut queue = vec![pos]; while let Some(pos) = queue.pop() { - let off = self.pos_to_off_unchecked(pos); + let off = pos.rel_offset_unchecked(&self); let c = &mut self.data[off]; if *c & HIDDEN_BIT > 0 { *c = unhide(*c); self.hidden_tiles -= 1; if is_mine(*c) { return true; } if *c > 0 { continue; } - if let Some(mut adj) = self.neighs(pos) { - queue.append(&mut adj); - } + queue.append(&mut self.neighs(pos)); } } false } - pub fn reveal_numtile(&mut self, pos: (usize,usize)) -> bool { - if let Some(off) = self.pos_to_off(pos) { + pub fn reveal_numtile(&mut self, pos: BoardPos) -> bool { + if let Some(off) = pos.rel_offset(&self) { let count = self.data[off] as usize; if 1 <= count && count <= 8 { - if let Some(mut neighs) = self.neighs(pos) { - let total_neighs = neighs.len(); - neighs.retain(|x| self.data[self.pos_to_off_unchecked(*x)] & FLAGGED_BIT == 0); - if (total_neighs - neighs.len()) == count { - for pos in neighs.iter() { - if self.flood_reveal(*pos) { - return true; - } + let mut neighs = self.neighs(pos); + let total_neighs = neighs.len(); + neighs.retain(|pos| self.data[pos.rel_offset_unchecked(&self)] & FLAGGED_BIT == 0); + if (total_neighs - neighs.len()) == count { + for pos in neighs.iter() { + if self.flood_reveal(*pos) { + return true; } } } @@ -244,8 +207,8 @@ impl Board { } false } - pub fn reveal_in_place(&mut self, pos: (usize,usize)) -> bool { - if let Some(off) = self.pos_to_off(pos) { + pub fn reveal(&mut self, pos: BoardPos) -> bool { + if let Some(off) = pos.rel_offset(&self) { let v = self.data[off]; if 1 <= v && v <= 8 { self.reveal_numtile(pos) @@ -254,30 +217,26 @@ impl Board { } } else { false } } - pub fn reveal(mut self, pos: (usize,usize)) -> MoveResult { - let lost = self.reveal_in_place(pos); - MoveResult(self, lost) - } - pub fn grade(mut self) -> Board { + + pub fn grade(&mut self) { for i in &mut self.data { if *i == TILE_NUMBITS | FLAGGED_BIT | HIDDEN_BIT { *i |= CORRECT_BIT; } } - self } - pub fn flag(mut self, pos: (usize,usize)) -> MoveResult { - if let Some(off) = self.pos_to_off(pos) { + pub fn flag(&mut self, pos: BoardPos) { + if let Some(off) = pos.rel_offset(&self) { self.data[off] ^= FLAGGED_BIT; } - MoveResult(self, false) } pub fn render(&self) -> Vec<u8> { let mut ret = vec![]; for y in 0..self.height.get() { for x in 0..self.width.get() { - let c = &self.data[self.pos_to_off_unchecked((x,y))]; + let pos: BoardPos = (x,y).try_into().unwrap(); + let c = &self.data[pos.rel_offset_unchecked(&self)]; match *c { 0 => ret.push(b' '), _ if *c <= 8 => ret.push(b'0' + c), @@ -293,7 +252,7 @@ impl Board { ret } - pub fn move_mine_elsewhere(&mut self, pos: (usize, usize)) { + pub fn move_mine_elsewhere(&mut self, pos: BoardPos) { let mut surround_count = 0; self.map_neighs(pos, |val| { if (val & !FLAGGED_BIT) == MINED { @@ -302,7 +261,7 @@ impl Board { } else { val - 1 }}); - let off = self.pos_to_off(pos).unwrap(); + let off = pos.rel_offset_unchecked(&self); let vacant_pos = { let v = self.data.iter() .enumerate() @@ -310,9 +269,9 @@ impl Board { .map(|(p,_)| p) .next() .unwrap(); // there must be at least one - (v%self.width.get(), v/self.width.get()) + BoardPos((v%self.width.get()) as u32, (v/self.width.get()) as u32) }; - let voff = self.pos_to_off_unchecked(vacant_pos); + let voff = vacant_pos.rel_offset_unchecked(&self); debug_assert!(voff != off, "swapped mine to the same position in a FirstMoveFail/grace'd first move (???)"); { // swap 'em (keep these together, pls kthnx (bugs were had)) @@ -326,10 +285,32 @@ impl Board { } } -fn pos_u2i(pos: (usize, usize)) -> Option<(isize, isize)> { - if let (Ok(x),Ok(y)) = (pos.0.try_into(), pos.1.try_into()) - { Some((x,y)) } else { None } +#[derive(Debug, Clone, Copy)] +pub struct BoardPos(u32,u32); +impl BoardPos { + pub fn rel_offset(&self, b: &Board) -> Option<usize> { + self.is_within(b).then_some(self.rel_offset_unchecked(b)) + } + pub fn rel_offset_unchecked(&self, b: &Board) -> usize { + (self.0 + self.1 * b.width.get() as u32) as usize + } + pub fn is_within(&self, b: &Board) -> bool { + self.0 < b.width.get() as u32 && self.1 < b.height.get() as u32 + } +} +impl TryInto<(isize,isize)> for BoardPos { + type Error = <usize as TryInto<isize>>::Error; + fn try_into(self) -> Result<(isize,isize), Self::Error> { + Ok((self.0.try_into()?, self.1.try_into()?)) + } +} +impl<T: TryInto<u32>> TryFrom<(T,T)> for BoardPos { + type Error = <T as TryInto<u32>>::Error; + fn try_from(value: (T,T)) -> Result<Self, Self::Error> { + Ok(Self(value.0.try_into()?, value.1.try_into()?)) + } } + pub fn is_mine(v: u8) -> bool { (v & TILE_NUMBITS) == TILE_NUMBITS } |