use core::fmt::{Display, Formatter};
use crate::{
tile::Tile,
tile_set::*,
player::*,
};
pub type Wall = [Tile; 136];
pub type PartialWall = [Option<Tile>; 136];
pub const fn make_dummy_wall() -> Wall { [Tile::MIN; 136] }
pub fn make_sorted_wall(num_reds: [u8; 3]) -> Wall {
let mut wall = [Tile::MIN; 136];
for encoding in 0u8..34u8 {
let tile = Tile::from_encoding(encoding).unwrap();
let suit = tile.suit();
let num = tile.num();
if num == 5 && suit <= 2 {
for i in 0..num_reds[suit as usize] {
wall[(encoding * 4 + i) as usize] = tile.to_red();
}
for i in num_reds[suit as usize]..4 {
wall[(encoding * 4 + i) as usize] = tile;
}
} else {
for i in 0..4 {
wall[(encoding * 4 + i) as usize] = tile;
}
}
}
wall
}
pub fn is_valid_wall(wall: Wall) -> bool {
TileSet34::from_iter(wall).into_iter().all(|n| n == 4)
}
pub const DEAL_INDEX: [[usize; 13]; 4] = [
[0x00, 0x01, 0x02, 0x03, 0x10, 0x11, 0x12, 0x13, 0x20, 0x21, 0x22, 0x23, 0x30],
[0x04, 0x05, 0x06, 0x07, 0x14, 0x15, 0x16, 0x17, 0x24, 0x25, 0x26, 0x27, 0x31],
[0x08, 0x09, 0x0a, 0x0b, 0x18, 0x19, 0x1a, 0x1b, 0x28, 0x29, 0x2a, 0x2b, 0x32],
[0x0c, 0x0d, 0x0e, 0x0f, 0x1c, 0x1d, 0x1e, 0x1f, 0x2c, 0x2d, 0x2e, 0x2f, 0x33],
];
pub const DORA_INDICATOR_INDEX: [usize; 5] = [130, 128, 126, 124, 122];
pub const URA_DORA_INDICATOR_INDEX: [usize; 5] = [131, 129, 127, 125, 123];
pub const KAN_DRAW_INDEX: [usize; 4] = [134, 135, 132, 133];
pub const MAX_NUM_DRAWS: u8 = 136 - 14;
pub fn deal(wall: &Wall, button: Player) -> [TileSet37; 4] {
let mut hists = [
TileSet37::default(),
TileSet37::default(),
TileSet37::default(),
TileSet37::default(),
];
for i in 0..4 {
for wall_index in DEAL_INDEX[i] {
let p = button.add(Player::new(i as u8));
hists[p.to_usize()][wall[wall_index].encoding() as usize] += 1;
}
}
hists
}
pub fn dora_indicator(wall: &Wall, i: usize) -> Tile {
wall[DORA_INDICATOR_INDEX[i]]
}
pub fn ura_dora_indicator(wall: &Wall, i: usize) -> Tile {
wall[URA_DORA_INDICATOR_INDEX[i]]
}
pub fn dora_indicators(wall: &Wall) -> [Tile; 5] {
DORA_INDICATOR_INDEX.map(|i| wall[i])
}
pub fn ura_dora_indicators(wall: &Wall) -> [Tile; 5] {
URA_DORA_INDICATOR_INDEX.map(|i| wall[i])
}
pub fn kan_draw(wall: &Wall, i: usize) -> Tile {
wall[KAN_DRAW_INDEX[i]]
}
pub fn get_missing_tiles_in_partial_wall(partial_wall: &PartialWall, num_reds: [u8; 3]) -> TileSet37 {
let mut missing = TileSet37::complete_set(num_reds);
for tile_or_hole in partial_wall {
if let &Some(tile) = tile_or_hole {
if missing[tile] == 0 {
panic!("More {} in the partial wall than expected.", tile)
}
missing[tile] -= 1;
}
}
missing
}
pub fn fill_missing_tiles_in_partial_wall(
partial_wall: &PartialWall, missing_tiles: impl IntoIterator<Item=Tile>) -> Wall {
let mut missing_iter = missing_tiles.into_iter();
partial_wall.map(|tile_or_hole|
tile_or_hole.or_else(|| missing_iter.next()).unwrap())
}
pub struct WallDisplay<'a>(&'a Wall);
pub trait WallDisplayMethod {
fn display(&self) -> WallDisplay;
}
impl WallDisplayMethod for Wall {
fn display(&self) -> WallDisplay { WallDisplay(self) }
}
impl<'a> Display for WallDisplay<'a> {
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
for (i, tile) in self.0.iter().enumerate() {
write!(f, "{} ", tile)?;
if i % 8 == 7 {
writeln!(f)?;
}
}
writeln!(f)
}
}
pub struct PartialWallDisplay<'a>(&'a PartialWall);
pub trait PartialWallDisplayMethod {
fn display(&self) -> PartialWallDisplay;
}
impl PartialWallDisplayMethod for PartialWall {
fn display(&self) -> PartialWallDisplay { PartialWallDisplay(self) }
}
impl<'a> Display for PartialWallDisplay<'a> {
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
for (i, maybe_tile) in self.0.iter().enumerate() {
if let Some(tile) = maybe_tile {
write!(f, "{} ", tile)?;
} else {
write!(f, "?? ")?;
}
if i % 8 == 7 {
writeln!(f)?;
}
}
Ok(())
}
}
#[cfg(test)]
mod tests {
extern crate std;
use super::*;
use crate::tile::tiles_from_str;
#[test]
fn sorted_wall_is_correct() {
let ans = concat!(
"111122223333444405556666777788889999m",
"111122223333444400556666777788889999p",
"111122223333444455556666777788889999s",
"1111222233334444555566667777z");
let wall = make_sorted_wall([1, 2, 0]);
itertools::assert_equal(wall, tiles_from_str(ans));
assert!(is_valid_wall(wall));
}
#[test]
fn sorted_wall_deals_correctly() {
let wall = make_sorted_wall([1, 1, 1]);
assert_eq!(deal(&wall, P1), [
TileSet37::new([
0, 0, 0, 4, 0, 0, 0, 4, 0,
0, 0, 4, 1, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0,
0, 0, 0,
]), TileSet37::new([
4, 0, 0, 0, 3, 0, 0, 0, 4,
0, 0, 0, 1, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0,
1, 0, 0,
]), TileSet37::new([
0, 4, 0, 0, 0, 4, 0, 0, 0,
4, 0, 0, 1, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0,
0, 0, 0,
]), TileSet37::new([
0, 0, 4, 0, 0, 0, 4, 0, 0,
0, 4, 0, 1, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0,
0, 0, 0,
]), ]);
}
}