riichi_elements/tile_set/
tile_set_37.rs

1use core::fmt::{Display, Formatter};
2use core::ops::{Index, IndexMut};
3
4use derive_more::{
5    Constructor, From, Into, IntoIterator, Index, IndexMut,
6};
7
8use crate::tile::Tile;
9
10/// Histogram for all 37 kinds of tiles (including reds).
11/// Can be directly indexed with [`Tile`].
12#[derive(Clone, Debug, Eq, PartialEq, Constructor, From, Into, IntoIterator, Index, IndexMut)]
13pub struct TileSet37(pub [u8; 37]);
14
15impl Index<Tile> for TileSet37 {
16    type Output = u8;
17    fn index(&self, tile: Tile) -> &Self::Output {
18        &self.0[tile.encoding() as usize]
19    }
20}
21
22impl IndexMut<Tile> for TileSet37 {
23    fn index_mut(&mut self, tile: Tile) -> &mut Self::Output {
24        &mut self.0[tile.encoding() as usize]
25    }
26}
27
28impl Default for TileSet37 {
29    fn default() -> Self { TileSet37([0; 37]) }
30}
31
32impl Display for TileSet37 {
33    fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
34        for xs in [
35            &self.0[0..9],
36            &self.0[9..18],
37            &self.0[18..27],
38            &self.0[27..34],
39            &self.0[34..37],
40        ] {
41            for x in xs {
42                write!(f, "{}", x)?;
43            }
44            write!(f, ",")?;
45        }
46        Ok(())
47    }
48}
49
50impl FromIterator<Tile> for TileSet37 {
51    fn from_iter<T: IntoIterator<Item=Tile>>(tiles: T) -> Self {
52        let mut ts = Self::default();
53        for tile in tiles {
54            ts[tile] += 1;
55        }
56        ts
57    }
58}
59
60impl TileSet37 {
61    /// An empty tile set.
62    pub const fn empty_set() -> Self { TileSet37([0; 37]) }
63
64    /// The complete set of tiles in a game, given the number of red 5's in play.
65    /// Each red 5 replaces its corresponding normal 5; the total number of tiles remains 136
66    /// (34 x 4).
67    pub const fn complete_set(num_reds: [u8; 3]) -> Self {
68        let mut a = [4; 37];
69        a[34] = num_reds[0];
70        a[35] = num_reds[1];
71        a[36] = num_reds[2];
72        a[4] = 4 - num_reds[0];
73        a[13] = 4 - num_reds[1];
74        a[22] = 4 - num_reds[2];
75        TileSet37(a)
76    }
77
78    /// Same as [`super::TileSet34::packed_34`], but collapsing red 5's into normal 5's.
79    pub fn packed_34(&self) -> [u32; 4] {
80        let mut packed = [0u32; 4];
81        let h = &self.0;
82        for i in (0..34).rev() {
83            let s = i / 9;
84            packed[s] = (packed[s] << 3) | (h[i] as u32);
85        }
86        packed[0] += (h[34] as u32) << (3 * 4);
87        packed[1] += (h[35] as u32) << (3 * 4);
88        packed[2] += (h[36] as u32) << (3 * 4);
89        packed
90    }
91
92    /// Iterates through all tiles in this tile set, in encoding order (i.e. reds come after honors).
93    pub fn iter_tiles(&self) -> impl Iterator<Item=Tile> {
94        self.0.into_iter().enumerate().flat_map(|(encoding, count)|
95            itertools::repeat_n(
96                Tile::from_encoding(encoding as u8).unwrap(),
97                count as usize))
98    }
99}
100
101#[cfg(test)]
102mod tests {
103    use super::*;
104    use crate::tile::*;
105
106    #[test]
107    fn tile_set_is_indexable_with_tile() {
108        let mut h = TileSet37::from_iter(tiles_from_str("1112345678999m"));
109        h[t!("9m")] -= 2;
110        h[t!("7z")] += 2;
111        assert_eq!(h, [
112            3, 1, 1, 1, 1, 1, 1, 1, 1,
113            0, 0, 0, 0, 0, 0, 0, 0, 0,
114            0, 0, 0, 0, 0, 0, 0, 0, 0,
115            0, 0, 0, 0, 0, 0, 2,
116            0, 0, 0u8,
117        ].into());
118    }
119}