use core::{
num::{NonZeroUsize, NonZeroU8},
};
#[cfg(feature = "serialization")]
use core::{
fmt::{self, Formatter},
marker::PhantomData,
};
#[cfg(feature = "serialization")]
use serde::{
ser::{Serializer, SerializeStruct, SerializeTupleVariant},
de::{self, Deserializer, Visitor, SeqAccess, MapAccess, EnumAccess, VariantAccess},
Serialize, Deserialize,
};
use alloc::{
vec::Vec
};
use super::{
Field, FieldCoordinates,
};
pub struct Tile<Ct, Cf> {
pub state: TileState<Cf>,
pub payload: Ct
}
impl<Ct: Default, Cf> From<TileState<Cf>> for Tile<Ct, Cf> {
fn from(state: TileState<Cf>) -> Self {
Self { state, payload: Ct::default() }
}
}
impl<Ct: Default, Cf> Default for Tile<Ct, Cf> {
fn default() -> Self {
Self {
state: TileState::default(),
payload: Ct::default()
}
}
}
#[cfg(feature = "serialization")]
impl<Ct, Cf> Serialize for Tile<Ct, Cf>
where Ct: Serialize,
Cf: Serialize {
fn serialize<S: Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
let mut s = s.serialize_struct("Tile", 2)?;
s.serialize_field("state", &self.state)?;
s.serialize_field("payload", &self.payload)?;
s.end()
}
}
#[cfg(feature = "serialization")]
impl<'de, Ct, Cf> Deserialize<'de> for Tile<Ct, Cf>
where Ct: Deserialize<'de>,
Cf: Deserialize<'de> {
fn deserialize<D: Deserializer<'de>>(d: D) -> Result<Self, D::Error> {
const FIELDS: &[&str] = &["state", "payload"];
enum StructField { State, Payload }
impl<'de> Deserialize<'de> for StructField {
fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
struct StructFieldVisitor;
impl<'de> Visitor<'de> for StructFieldVisitor {
type Value = StructField;
fn expecting(&self, formatter: &mut Formatter) -> fmt::Result {
formatter.write_str("`state` or `payload`")
}
fn visit_str<E: de::Error>(self, value: &str) -> Result<StructField, E> {
match value {
"state" => Ok(StructField::State),
"payload" => Ok(StructField::Payload),
_ => Err(de::Error::unknown_field(value, FIELDS)),
}
}
}
deserializer.deserialize_identifier(StructFieldVisitor)
}
}
struct TileVisitor<Ct, Cf>(PhantomData<(Ct, Cf)>);
impl<'de, Ct, Cf> Visitor<'de> for TileVisitor<Ct, Cf>
where Ct: Deserialize<'de>,
Cf: Deserialize<'de> {
type Value = Tile<Ct, Cf>;
fn expecting(&self, formatter: &mut Formatter) -> fmt::Result {
formatter.write_str("struct Tile")
}
fn visit_seq<V: SeqAccess<'de>>(self, mut seq: V) -> Result<Self::Value, V::Error> {
let state = seq.next_element()?
.ok_or_else(|| de::Error::invalid_length(0, &self))?;
let payload = seq.next_element()?
.ok_or_else(|| de::Error::invalid_length(1, &self))?;
Ok(Tile {state, payload})
}
fn visit_map<V: MapAccess<'de>>(self, mut map: V) -> Result<Self::Value, V::Error> {
let mut state: Option<TileState<Cf>> = None;
let mut payload: Option<Ct> = None;
while let Some(key) = map.next_key()? {
match key {
StructField::State => {
if state.is_some() {
return Err(de::Error::duplicate_field("state"));
}
state = Some(map.next_value()?);
}
StructField::Payload => {
if payload.is_some() {
return Err(de::Error::duplicate_field("payload"));
}
payload = Some(map.next_value()?);
}
}
}
let state = state.ok_or_else(|| de::Error::missing_field("state"))?;
let payload = payload.ok_or_else(|| de::Error::missing_field("payload"))?;
Ok(Tile {state, payload})
}
}
d.deserialize_struct("Tile", FIELDS, TileVisitor(PhantomData))
}
}
#[derive(Copy, Clone, Debug)]
pub enum TileState<Cf> {
ClosedEmpty(Flag<Cf>),
OpenEmpty,
OpenNumber(NonZeroU8),
Mine(Flag<Cf>)
}
impl<Cf> TileState<Cf> {
#[inline]
pub fn is_closed(&self) -> bool {
match self {
Self::ClosedEmpty(_)
| Self::Mine(_) => true,
_ => false
}
}
#[inline]
pub fn is_open(&self) -> bool {
match self {
Self::OpenEmpty
| Self::OpenNumber(_) => true,
_ => false
}
}
#[inline]
pub fn is_mine(&self) -> bool {
match self {
Self::Mine(_) => true,
_ => false
}
}
#[inline(always)]
pub fn is_safe(&self) -> bool {
!self.is_mine()
}
#[inline]
pub fn is_required_to_open(&self) -> bool {
match self {
Self::ClosedEmpty(_) => true,
_ => false
}
}
#[inline]
pub fn flag_state(&self) -> Option<&Flag<Cf>> {
match self {
Self::ClosedEmpty(flag)
| Self::Mine(flag) => Some(flag),
_ => None
}
}
#[inline]
pub fn is_flagged(&self) -> bool {
if let Some(flag) = self.flag_state() {
if let Flag::Flagged = flag { true }
else { false }
} else { false }
}
#[inline]
pub fn peek_local(&self) -> Option<ClickOutcome> {
match self {
Self::ClosedEmpty(_) => None,
Self::OpenEmpty => Some(ClickOutcome::OpenClearing),
Self::OpenNumber(_) => Some(ClickOutcome::Chord),
Self::Mine(_) => Some(ClickOutcome::Explosion)
}
}
}
impl<Cf> Default for TileState<Cf> {
#[inline(always)]
fn default() -> Self {
Self::ClosedEmpty(Flag::default())
}
}
impl<Cf> PartialEq<TileState<Cf>> for TileState<Cf> {
fn eq(&self, other: &Self) -> bool {
match self {
Self::Mine(_) => {
if let Self::Mine(_) = other {
true
}
else {false}
},
_ => {
match other {
Self::Mine(_) => false,
_ => true
}
},
}
}
}
impl<Cf> Eq for TileState<Cf> {}
#[cfg(feature = "serialization")]
impl<Cf: Serialize> Serialize for TileState<Cf> {
fn serialize<S: Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
match self {
Self::ClosedEmpty(flag) => {
let mut s = s.serialize_tuple_variant("TileState", 0, "ClosedEmpty", 1)?;
s.serialize_field(flag)?;
s.end()
},
Self::OpenEmpty => {
s.serialize_unit_variant("TileState", 1, "OpenEmpty")
},
Self::OpenNumber(mines) => {
let mut s = s.serialize_tuple_variant("TileState", 2, "OpenNumber", 1)?;
s.serialize_field(mines)?;
s.end()
},
Self::Mine(flag) => {
let mut s = s.serialize_tuple_variant("TileState", 3, "Mine", 1)?;
s.serialize_field(flag)?;
s.end()
},
}
}
}
#[cfg(feature = "serialization")]
impl<'de, Cf: Deserialize<'de>> Deserialize<'de> for TileState<Cf> {
fn deserialize<D: Deserializer<'de>>(d: D) -> Result<Self, D::Error> {
const VARIANTS: &[&str] = &["ClosedEmpty", "OpenEmpty", "OpenNumber", "Mine"];
#[derive(Deserialize)]
#[repr(u8)]
enum Tag {
ClosedEmpty, OpenEmpty, OpenNumber, Mine
}
struct TsVisitor<Cf>(PhantomData<Cf>);
impl<'de, Cf: Deserialize<'de>> Visitor<'de> for TsVisitor<Cf> {
type Value = TileState<Cf>;
fn expecting(&self, f: &mut Formatter) -> fmt::Result {
write!(f,
"enum TileState<Cf>")
}
fn visit_enum<A: EnumAccess<'de>>(self, data: A) -> Result<Self::Value, A::Error> {
let (tag, variant_data) = data.variant::<Tag>()?;
match tag {
Tag::ClosedEmpty => {
let flag = variant_data.tuple_variant(1, FlagVisitor(PhantomData))?;
Ok(TileState::ClosedEmpty(flag))
},
Tag::OpenEmpty => {
variant_data.unit_variant()?;
Ok(TileState::OpenEmpty)
},
Tag::OpenNumber => {
let num = variant_data.tuple_variant(1, Nzu8Visitor)?;
Ok(TileState::OpenNumber(num))
},
Tag::Mine => {
let flag = variant_data.tuple_variant(1, FlagVisitor(PhantomData))?;
Ok(TileState::Mine(flag))
}
}
}
}
struct Nzu8Visitor;
impl<'de> Visitor<'de> for Nzu8Visitor {
type Value = NonZeroU8;
fn expecting(&self, f: &mut Formatter) -> fmt::Result {
write!(f,
"a non-zero u8")
}
fn visit_newtype_struct<D: Deserializer<'de>>(self, d: D) -> Result<Self::Value, D::Error> {
NonZeroU8::deserialize(d)
}
fn visit_seq<A: SeqAccess<'de>>(self, mut seq: A) -> Result<Self::Value, A::Error> {
let mut result = None;
while let Some(nzu8) = seq.next_element()? {
if result.is_some() {
return Err(serde::de::Error::duplicate_field("nearby mine count"));
}
result = Some(nzu8);
};
if let Some(nzu8) = result {
Ok(nzu8)
} else {
Err(serde::de::Error::missing_field("nearby mine count"))
}
}
}
struct FlagVisitor<Cf>(PhantomData<Cf>);
impl<'de, Cf: Deserialize<'de>> Visitor<'de> for FlagVisitor<Cf> {
type Value = Flag<Cf>;
fn expecting(&self, f: &mut Formatter) -> fmt::Result {
write!(f,
"enum Flag<Cf>")
}
fn visit_newtype_struct<D: Deserializer<'de>>(self, d: D) -> Result<Self::Value, D::Error> {
Flag::<Cf>::deserialize(d)
}
fn visit_seq<A: SeqAccess<'de>>(self, mut seq: A) -> Result<Self::Value, A::Error> {
let mut result = None;
while let Some(flag) = seq.next_element()? {
if result.is_some() {
return Err(serde::de::Error::duplicate_field("flag"));
}
result = Some(flag);
};
if let Some(flag) = result {
Ok(flag)
} else {
Err(serde::de::Error::missing_field("flag"))
}
}
}
d.deserialize_enum("TileState", VARIANTS, TsVisitor(PhantomData))
}
}
macro_rules! for_every_tile {
($field:expr, $anchor_location:expr, $f:expr, $include_shore:expr) => {
type StackFrame = (FieldCoordinates, [bool; 4]);
let mut stack = Vec::<StackFrame>::with_capacity(10);
let mut stack_top
= ($anchor_location, [true; 4]);
$f($field, stack_top.0);
loop {
let chosen_location
= if stack_top.1[0] {stack_top.1[0] = false; 0}
else if stack_top.1[1] {stack_top.1[1] = false; 1}
else if stack_top.1[2] {stack_top.1[2] = false; 2}
else if stack_top.1[3] {stack_top.1[3] = false; 3}
else if let Some(new_top) = stack.pop() {
stack_top = new_top;
continue;
} else {break};
let location_to_peek
= if chosen_location == 0 {[stack_top.0[0], stack_top.0[1] + 1]}
else if chosen_location == 1 {[stack_top.0[0], stack_top.0[1] - 1]}
else if chosen_location == 2 {[stack_top.0[0] - 1, stack_top.0[1]]}
else if chosen_location == 3 {[stack_top.0[0] + 1, stack_top.0[1]]}
else {unreachable!()};
if let Some(outcome) = $field.peek(location_to_peek) {
match outcome {
ClickOutcome::OpenClearing
| ClickOutcome::Nothing => {
stack.push(stack_top);
stack_top.0 = location_to_peek;
stack_top.1 = [true; 4];
$f($field, stack_top.0);
},
ClickOutcome::Chord
| ClickOutcome::Explosion => {}
ClickOutcome::OpenNumber(_) => {
if $include_shore {
stack.push(stack_top);
stack_top.0 = location_to_peek;
stack_top.1 = [true; 4];
$f($field, stack_top.0);
}
}
}
}
}
};
}
#[derive(Copy, Clone)]
pub struct Clearing<'f, Ct, Cf> {
field: &'f Field<Ct, Cf>,
anchor_location: FieldCoordinates
}
impl<'f, Ct, Cf> Clearing<'f, Ct, Cf> {
pub fn new(field: &'f Field<Ct, Cf>, anchor_location: FieldCoordinates) -> Option<Self> {
if field.get(anchor_location).is_some() {
if field.count_neighboring_mines(anchor_location) > 0 {
None
} else {
Some(Self {
field, anchor_location
})
}
} else {None}
}
#[inline(always)]
pub const fn field(self) -> &'f Field<Ct, Cf> { self.field }
#[inline(always)]
pub const fn anchor_location(self) -> FieldCoordinates { self.anchor_location }
#[cfg_attr(feature = "track_caller", track_caller)]
pub fn for_every_tile<F>(self, include_shore: bool, mut f: F)
where F: FnMut(&'f Field<Ct, Cf>, FieldCoordinates) {
for_every_tile!(self.field, self.anchor_location, f, include_shore);
}
#[cfg_attr(feature = "track_caller", track_caller)]
#[must_use = "fully traversing a clearing is an expensive operation involving memory allocation"]
pub fn size(self, include_shore: bool) -> NonZeroUsize {
let mut counter = 0_usize;
self.for_every_tile(include_shore, |_, _| counter += 1);
NonZeroUsize::new(counter)
.expect("unexpected zero clearing size (nonzero clearing size is a safety guarantee)")
}
#[cfg_attr(feature = "track_caller", track_caller)]
#[must_use = "fully traversing a clearing is an expensive operation involving memory allocation"]
pub fn includes(self, coordinates: FieldCoordinates, include_shore: bool) -> bool {
let mut includes = false;
self.for_every_tile(include_shore, |_, here| if here == coordinates {includes = true});
includes
}
}
pub struct ClearingMut<'f, Ct, Cf> {
field: &'f mut Field<Ct, Cf>,
anchor_location: FieldCoordinates
}
impl<'f, Ct, Cf> ClearingMut<'f, Ct, Cf> {
pub fn new(field: &'f mut Field<Ct, Cf>, anchor_location: FieldCoordinates) -> Option<Self> {
if field.get(anchor_location).is_some() {
if field.count_neighboring_mines(anchor_location) > 0 {
None
} else {
Some(Self {
field, anchor_location
})
}
} else {None}
}
#[inline(always)]
pub const fn field(self) -> &'f Field<Ct, Cf> { self.field }
#[inline(always)]
pub const fn anchor_location(self) -> FieldCoordinates { self.anchor_location }
#[cfg_attr(feature = "track_caller", track_caller)]
pub fn for_every_tile<F>(self, include_shore: bool, mut f: F)
where F: FnMut(&'f Field<Ct, Cf>, FieldCoordinates) {
for_every_tile!(self.field, self.anchor_location, f, include_shore);
}
#[cfg_attr(feature = "track_caller", track_caller)]
pub fn for_every_tile_mut<F>(self, include_shore: bool, mut f: F)
where F: FnMut(&mut Field<Ct, Cf>, FieldCoordinates) {
for_every_tile!(self.field, self.anchor_location, f, include_shore);
}
#[cfg_attr(feature = "track_caller", track_caller)]
#[must_use = "fully traversing a clearing is an expensive operation involving memory allocation"]
pub fn size(self, include_shore: bool) -> NonZeroUsize {
let mut counter = 0_usize;
self.for_every_tile(include_shore, |_: &Field<Ct, Cf>, _| counter += 1);
NonZeroUsize::new(counter)
.expect("unexpected zero clearing size (nonzero clearing size is a safety guarantee)")
}
#[cfg_attr(feature = "track_caller", track_caller)]
#[must_use = "fully traversing a clearing is an expensive operation involving memory allocation"]
pub fn includes(self, coordinates: FieldCoordinates, include_shore: bool) -> bool {
let mut includes = false;
self.for_every_tile(include_shore, |_, here| if here == coordinates {includes = true});
includes
}
pub fn open(self, include_shore: bool) -> (usize, NonZeroUsize) {
let [mut opened_size, mut total_size] = [0_usize; 2];
self.for_every_tile_mut(include_shore, |field, location| {
total_size += 1;
if let TileState::ClosedEmpty(_) = field[location].state {
field[location].state = TileState::OpenEmpty;
opened_size += 1;
}
});
(opened_size, NonZeroUsize::new(total_size)
.expect("unexpected zero clearing size (nonzero clearing size is a safety guarantee)")
)
}
}
impl<'f, Ct, Cf> From<ClearingMut<'f, Ct, Cf>> for Clearing<'f, Ct, Cf> {
fn from(op: ClearingMut<'f, Ct, Cf>) -> Self {
Self {field: op.field, anchor_location: op.anchor_location}
}
}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum Flag<Cf> {
NotFlagged,
Flagged,
Custom(Cf),
}
impl<Cf> Default for Flag<Cf> {
#[inline(always)]
fn default() -> Self {
Self::NotFlagged
}
}
impl<Cf> From<Cf> for Flag<Cf> {
#[inline(always)]
fn from(op: Cf) -> Self {
Self::Custom(op)
}
}
#[cfg(feature = "serialization")]
impl<Cf: Serialize> Serialize for Flag<Cf> {
fn serialize<S: Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
let (variant, variant_index, len) = match self {
Self::NotFlagged => ("NotFlagged", 0, 0),
Self::Flagged => ("Flagged", 1, 0),
Self::Custom(_) => ("Custom", 2, 1),
};
if let Self::Custom(cf) = self {
let mut s = s.serialize_tuple_variant("Flag", variant_index, variant, len)?;
s.serialize_field(cf)?;
s.end()
} else {
s.serialize_unit_variant("Flag", variant_index, variant)
}
}
}
#[cfg(feature = "serialization")]
impl<'de, Cf: Deserialize<'de>> Deserialize<'de> for Flag<Cf> {
fn deserialize<D: Deserializer<'de>>(d: D) -> Result<Self, D::Error> {
const VARIANTS: &[&str] = &["NotFlagged", "Flagged", "Custom"];
#[derive(Deserialize)]
#[repr(u8)]
enum Tag {
NotFlagged, Flagged, Custom
}
struct FlagVisitor<Cf>(PhantomData<Cf>);
impl<'de, Cf: Deserialize<'de>> Visitor<'de> for FlagVisitor<Cf> {
type Value = Flag<Cf>;
fn expecting(&self, f: &mut Formatter) -> fmt::Result {
write!(f,
"enum Flag<Cf>")
}
fn visit_enum<A: EnumAccess<'de>>(self, data: A) -> Result<Self::Value, A::Error> {
let (tag, variant_data) = data.variant::<Tag>()?;
match tag {
Tag::NotFlagged => {
variant_data.unit_variant()?;
Ok(Flag::NotFlagged)
},
Tag::Flagged => {
variant_data.unit_variant()?;
Ok(Flag::Flagged)
},
Tag::Custom => {
let cf = variant_data.tuple_variant(1, CfVisitor::<Cf>(PhantomData))?;
Ok(Flag::Custom(cf))
}
}
}
}
struct CfVisitor<Cf>(PhantomData<Cf>);
impl<'de, Cf: Deserialize<'de>> Visitor<'de> for CfVisitor<Cf> {
type Value = Cf;
fn expecting(&self, f: &mut Formatter) -> fmt::Result {
write!(f,
"a tuple enum variant with the custom flag as the only field")
}
fn visit_newtype_struct<D: Deserializer<'de>>(self, d: D) -> Result<Self::Value, D::Error> {
Cf::deserialize(d)
}
fn visit_seq<A: SeqAccess<'de>>(self, mut seq: A) -> Result<Self::Value, A::Error> {
let mut result = None;
while let Some(cf) = seq.next_element()? {
if result.is_some() {
return Err(serde::de::Error::duplicate_field("custom flag"));
}
result = Some(cf);
};
if let Some(cf) = result {
Ok(cf)
} else {
Err(serde::de::Error::missing_field("custom flag"))
}
}
}
d.deserialize_enum("Flag", VARIANTS, FlagVisitor::<Cf>(PhantomData))
}
}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
#[cfg_attr(feature = "serialization", derive(Serialize, Deserialize))]
pub enum ClickOutcome {
Nothing,
OpenClearing,
OpenNumber(NonZeroU8),
Chord,
Explosion
}
impl Default for ClickOutcome {
#[inline(always)]
fn default() -> Self {
Self::Nothing
}
}