shogi_core/
game_resolution.rs

1/// How a game is resolved.
2///
3/// [`GameResolution`] and <code>[Option]<[GameResolution]></code> are both 1-byte data types.
4/// Because they are cheap to copy, they implement [`Copy`].
5#[repr(u8)]
6#[derive(Eq, PartialEq, Clone, Copy, Debug)]
7pub enum GameResolution {
8    /// White's king was mated or white resigned.
9    ///
10    /// Discriminant = 1.
11    BlackWins = 1,
12    /// Black's king was mated or black resigned.
13    ///
14    /// Discriminant = 2.
15    WhiteWins = 2,
16    /// This can happen if e.g. `持将棋` (*jishōgi*) happens.
17    ///
18    /// Discriminant = 3.
19    Draw = 3,
20    /// This can happen if e.g. `千日手` (*sennichite*, repetition) happens.
21    ///
22    /// Discriminant = 4.
23    Rematch = 4,
24    /// The game was aborted.
25    ///
26    /// Discriminant = 5.
27    Aborted = 5,
28}
29
30impl GameResolution {
31    /// Converts a [`u8`] to [`GameResolution`] without checking.
32    ///
33    /// # Safety
34    /// `repr` must be a valid representation of [`GameResolution`].
35    /// This condition is equivalent to `1 <= repr && repr <= 5`.
36    #[export_name = "GameResolution_from_u8_unchecked"]
37    pub unsafe extern "C" fn from_u8_unchecked(repr: u8) -> Self {
38        core::mem::transmute(repr)
39    }
40}
41
42impl_ord_for_fieldless_enum!(GameResolution);
43impl_hash_for_fieldless_enum!(GameResolution);
44
45/// <code>[Option]<[GameResolution]></code> with defined representation.
46///
47/// The representation is:
48/// [`None`] => `0`, <code>[Some]\(x\)</code> => `x`.
49/// Therefore, valid representations of this type are precisely `0..=5`.
50///
51/// This type is provided for C interoperability.
52/// cbindgen cannot deduce that <code>[Option]<[GameResolution]></code> can be represented by `uint8_t` in C, so we need to define the bridge type.
53/// Users of this type should convert to/from <code>[Option]<[GameResolution]></code>.
54///
55/// See: <https://github.com/eqrion/cbindgen/issues/326>.
56#[repr(transparent)]
57#[derive(Eq, PartialEq, Clone, Copy, Debug, Default)]
58pub struct OptionGameResolution(u8);
59
60impl From<Option<GameResolution>> for OptionGameResolution {
61    #[inline(always)]
62    fn from(arg: Option<GameResolution>) -> Self {
63        Self(match arg {
64            Some(result) => result as u8,
65            None => 0,
66        })
67    }
68}
69
70impl From<OptionGameResolution> for Option<GameResolution> {
71    #[inline(always)]
72    fn from(arg: OptionGameResolution) -> Self {
73        if arg.0 == 0 {
74            None
75        } else {
76            // Safety: arg is a valid OptionGameResolution, which means 0 <= arg.0 && arg.0 <= 5.
77            // arg.0 == 0 is ruled out.
78            Some(unsafe { GameResolution::from_u8_unchecked(arg.0) })
79        }
80    }
81}
82
83impl_ord_for_single_field!(OptionGameResolution);
84impl_hash_for_single_field!(OptionGameResolution);
85
86#[cfg(test)]
87mod tests {
88    use super::*;
89
90    #[test]
91    fn game_resolution_is_one_byte() {
92        assert_eq!(core::mem::size_of::<GameResolution>(), 1);
93    }
94
95    #[test]
96    fn option_game_resolution_default_is_compatible() {
97        // Option<T>'s default value is [`None`].
98        assert_eq!(OptionGameResolution::default(), None.into());
99    }
100}