use core::fmt::{Display, Formatter};
use core::ops::{Index, IndexMut};
use super::TileSet37;
use derive_more::{
Constructor, From, Into, IntoIterator, Index, IndexMut,
};
use crate::tile::Tile;
#[derive(Clone, Debug, Eq, PartialEq, Constructor, From, Into, IntoIterator, Index, IndexMut)]
pub struct TileSet34(pub [u8; 34]);
impl Index<Tile> for TileSet34 {
type Output = u8;
fn index(&self, tile: Tile) -> &Self::Output {
&self.0[tile.normal_encoding() as usize] }
}
impl IndexMut<Tile> for TileSet34 {
fn index_mut(&mut self, tile: Tile) -> &mut Self::Output {
&mut self.0[tile.normal_encoding() as usize] }
}
impl Default for TileSet34 {
fn default() -> Self { TileSet34([0u8; 34]) }
}
impl Display for TileSet34 {
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
for xs in self.0.chunks(9) {
for x in xs {
write!(f, "{}", x)?;
}
write!(f, ",")?;
}
Ok(())
}
}
impl From<&TileSet37> for TileSet34 {
fn from(original: &TileSet37) -> Self {
let mut result: [u8; 34] = (original[..34]).try_into().unwrap();
result[4] += original[34];
result[13] += original[35];
result[22] += original[36];
Self(result)
}
}
impl FromIterator<Tile> for TileSet34 {
fn from_iter<T: IntoIterator<Item=Tile>>(tiles: T) -> Self {
let mut ts = Self::default();
for tile in tiles {
ts[tile.to_normal()] += 1;
}
ts
}
}
impl TileSet34 {
pub const fn empty_set() -> Self { TileSet34([0; 34]) }
pub const fn complete_set() -> Self { TileSet34([4; 34]) }
pub fn from_packed(packed: [u32; 4]) -> Self {
let mut ts34 = Self::default();
let mut i = 0;
for s in 0..3 {
let mut m = packed[s];
for _ in 0..9 {
ts34[i] = (m & 0o7) as u8;
i += 1;
m >>= 3;
}
}
let mut m = packed[3];
for _ in 0..7 {
ts34[i] = (m & 0o7) as u8;
i += 1;
m >>= 3;
}
ts34
}
pub fn packed_34(&self) -> [u32; 4] {
let mut packed = [0u32; 4];
let h = &self.0;
for i in (0..34).rev() {
let s = i / 9;
packed[s] = (packed[s] << 3) | (h[i] as u32);
}
packed
}
pub fn iter_tiles(&self) -> impl Iterator<Item=Tile> {
self.0.into_iter().enumerate().flat_map(|(encoding, count)|
itertools::repeat_n(
Tile::from_encoding(encoding as u8).unwrap(),
count as usize))
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::tile::*;
#[test]
fn ts34_treats_red_as_normal() {
let mut h = TileSet34::default();
h[t!("5m")] = 1;
h[t!("0p")] = 2;
h[t!("6s")] = 3;
assert_eq!(h, [
0, 0, 0, 0, 1, 0, 0, 0, 0,
0, 0, 0, 0, 2, 0, 0, 0, 0,
0, 0, 0, 0, 0, 3, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0,
].into());
}
#[test]
fn ts34_packs_correctly() {
let h = TileSet34::from_iter(tiles_from_str("147m258p369s77z"));
assert_eq!(h.packed_34(), [
0o001001001,
0o010010010,
0o100100100,
0o2000000,
]);
assert_eq!(TileSet34::from_packed(h.packed_34()), h);
}
}