riichi_elements/
player.rs

1//! [`Player`] newtype (mod-4 arithmetic).
2
3use core::fmt::{Debug, Display, Formatter};
4use core::ops::{Add, Sub};
5use derive_more::{From, Into};
6
7/// Player index -- 0, 1, 2, 3 => player assigned east, south, west, north in the initial round.
8///
9/// This is forced to mod-4 arithmetic, and can represent both the absolute player index or
10/// the difference between player indices ("relative player").
11///
12/// Reason for reinventing the wheel instead of using `ux`, `bare_metal_modulo` etc.:
13/// This is trivial, and these don't support `serde`.
14///
15/// ## Optional `serde` support
16///
17/// Serializes as the player index number (0/1/2/3). Deserialization will check the range.
18///
19#[derive(Copy, Clone, Default, Eq, PartialEq, Hash, From, Into)]
20pub struct Player(u8);
21
22pub const P0: Player = Player(0);
23pub const P1: Player = Player(1);
24pub const P2: Player = Player(2);
25pub const P3: Player = Player(3);
26pub const ALL_PLAYERS: [Player; 4] = [P0, P1, P2, P3];
27
28impl Player {
29    pub const fn new(x: u8) -> Self { Player(x & 3) }
30    
31    pub const fn add(self, other: Player) -> Player {
32        Player(self.0.wrapping_add(other.0) & 3)
33    }
34
35    pub const fn add_u8(self, other: u8) -> Player {
36        Player(self.0.wrapping_add(other) & 3)
37    }
38
39    pub const fn sub(self, other: Player) -> Player {
40        Player(self.0.wrapping_sub(other.0) & 3)
41    }
42
43    pub const fn sub_u8(self, other: u8) -> Player {
44        Player(self.0.wrapping_sub(other) & 3)
45    }
46
47    pub const fn to_u8(self) -> u8 { self.0 }
48    pub const fn to_usize(self) -> usize { self.0 as usize }
49
50    /// Returns the player 1 turn after me, a.k.a. Successor, Shimocha (下家).
51    /// In a physical game, said player would sit to the right of me (CCW).
52    pub const fn succ(self) -> Self { self.add(P1) }
53
54    /// Returns the player 2 turns after me, a.k.a. Opposing, Toimen (対面).
55    /// In a physical game, said player would sit across the table from me.
56    pub const fn oppo(self) -> Self { self.add(P2) }
57
58    /// Returns the player 1 turn before me, a.k.a. Predecessor, Kamicha (上家).
59    /// In a physical game, said player would sit to the left of me (CW).
60    pub const fn pred(self) -> Self { self.add(P3) }
61}
62
63impl From<usize> for Player {
64    fn from(x: usize) -> Self { Self::new(x as u8) }
65}
66
67impl Into<usize> for Player {
68    fn into(self) -> usize { self.0 as usize }
69}
70
71impl Add for Player {
72    type Output = Player;
73    fn add(self, rhs: Self) -> Self::Output { self.add(rhs) }
74}
75
76impl Add<u8> for Player {
77    type Output = Player;
78    fn add(self, rhs: u8) -> Self::Output { self.add_u8(rhs) }
79}
80
81impl Sub for Player {
82    type Output = Player;
83    fn sub(self, rhs: Self) -> Self::Output { self.sub(rhs) }
84}
85
86impl Sub<u8> for Player {
87    type Output = Player;
88    fn sub(self, rhs: u8) -> Self::Output { self.sub_u8(rhs) }
89}
90
91impl Debug for Player {
92    fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
93        write!(f, "Player({})", self.0)
94    }
95}
96
97impl Display for Player {
98    fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
99        write!(f, "{}", self.0)
100    }
101}
102
103/// Shorthand for [`Player::new`].
104pub const fn player(i: u8) -> Player { Player::new(i) }
105
106/// Returns an array of all players, starting from the given player, in natural turn order.
107///
108/// Example:
109/// ```
110/// use riichi_elements::player::*;
111/// assert_eq!(all_players_from(P2), [P2, P3, P0, P1]);
112/// ```
113pub const fn all_players_from(player: Player) -> [Player; 4] {
114    [player.add(P0), player.add(P1), player.add(P2), player.add(P3)]
115}
116
117/// Returns an array of the 3 players after the given player, in natural turn order.
118///
119/// Example:
120/// ```
121/// use riichi_elements::player::*;
122/// assert_eq!(other_players_after(P2), [P3, P0, P1]);
123/// ```
124pub const fn other_players_after(player: Player) -> [Player; 3] {
125    [player.add(P1), player.add(P2), player.add(P3)]
126}
127
128#[cfg(feature = "serde")]
129mod player_serde {
130    use core::fmt::Formatter;
131    use serde::{*};
132    use serde::de::{Error, Visitor};
133    use super::*;
134
135    impl Serialize for Player {
136        fn serialize<S>(&self, s: S) -> Result<S::Ok, S::Error> where S: Serializer {
137            s.serialize_u8(self.0.into())
138        }
139    }
140
141    impl<'de> Deserialize<'de> for Player {
142        fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> where D: Deserializer<'de> {
143            struct PlayerVisitor;
144            impl<'a> Visitor<'a> for PlayerVisitor {
145                type Value = Player;
146
147                fn expecting(&self, f: &mut Formatter) -> core::fmt::Result {
148                    write!(f, "0..=3")
149                }
150
151                fn visit_u64<E>(self, v: u64) -> Result<Self::Value, E> where E: Error {
152                    if (0..=3).contains(&v) {
153                        Ok(Player(v as u8))
154                    } else {
155                        Err(E::custom("out of range"))
156                    }
157                }
158            }
159            deserializer.deserialize_u8(PlayerVisitor)
160        }
161    }
162}