use std::fmt::{Display, Formatter};
use bitflags::bitflags;
pub use view::View;
mod view;
#[derive(Copy, Clone, Debug, Default, Eq, PartialEq)]
#[repr(transparent)]
pub struct Field(u8);
bitflags! {
impl Field: u8 {
const FLAGS = 0b1111_0000;
const ADJACENT_MINES = 0b0000_1111;
const MINED = 0b0001_0000;
const VISITED = 0b0010_0000;
const FLAGGED = 0b0100_0000;
const IS_DUD = 0b1000_0000;
}
}
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub enum VisitResult {
Cleared,
AlreadyVisited,
Flagged,
SteppedOnMine,
SteppedOnDud,
}
impl Field {
#[must_use]
pub const fn has_mine(self) -> bool {
self.contains(Self::MINED)
}
#[must_use]
pub const fn has_been_visited(self) -> bool {
self.contains(Self::VISITED)
}
#[must_use]
pub const fn is_flagged(self) -> bool {
self.contains(Self::FLAGGED)
}
#[must_use]
pub const fn is_dud(self) -> bool {
self.contains(Self::IS_DUD)
}
#[must_use]
pub const fn adjacent_mines(self) -> u8 {
self.intersection(Self::ADJACENT_MINES).0
}
pub fn set_mine(&mut self) {
self.insert(Self::MINED);
}
pub fn set_dud(&mut self) {
self.insert(Self::IS_DUD);
}
pub const fn set_adjacent_mines(&mut self, adjacent_mines: u8) {
*self = self
.intersection(Self::FLAGS)
.union(Self::ADJACENT_MINES.intersection(Self(adjacent_mines)));
}
pub fn visit(&mut self) -> VisitResult {
if self.is_flagged() {
return VisitResult::Flagged;
}
if self.has_been_visited() {
return VisitResult::AlreadyVisited;
}
self.insert(Self::VISITED);
if !self.has_mine() {
return VisitResult::Cleared;
}
if self.is_dud() {
return VisitResult::SteppedOnDud;
}
VisitResult::SteppedOnMine
}
pub fn toggle_flag(&mut self) {
if !self.has_been_visited() {
self.toggle(Self::FLAGGED);
}
}
#[must_use]
pub const fn view(self, game_over: bool) -> View {
match (
game_over,
self.has_been_visited(),
self.is_flagged(),
self.has_mine(),
self.is_dud(),
) {
(false, false, true, _, _) | (true, false, true, true, _) => View::Flag,
(_, true, _, true, true) => View::SteppedOnDud,
(_, true, _, true, false) => View::SteppedOnMine,
(false, true, false, false, _) | (true, _, _, false, _) => View::Clear {
adjacent_mines: self.adjacent_mines(),
},
(true, false, false, true, _) => View::Mine,
_ => View::Covered,
}
}
}
impl Display for Field {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
self.view(f.alternate()).fmt(f)
}
}