use super::common::{Entry, update_min};
use super::hash::{hash_19m, hash_shupai, hash_zipai};
use super::shupai_map::{SHUPAI_REPLACEMENT_NUMBER_MAP, SHUPAI_UNNECESSARY_TILES_MAP};
use super::unpack::{unpack_replacement_number, unpack_unnecessary_tiles};
use super::wanzi_19_map::{WANZI_19_REPLACEMENT_NUMBER_MAP, WANZI_19_UNNECESSARY_TILES_MAP};
use super::zipai_map::{ZIPAI_REPLACEMENT_NUMBER_MAP, ZIPAI_UNNECESSARY_TILES_MAP};
use crate::bingpai::{Bingpai, Bingpai3p};
use crate::tile::TileFlags;
fn update_dp(lhs: &mut Entry, rhs: &Entry) {
for i in (5..10).rev() {
let mut number = lhs.numbers[i];
let mut tiles = lhs.tiles[i] | rhs.tiles[0];
update_min(
&mut number,
&mut tiles,
rhs.numbers[i],
lhs.tiles[0] | rhs.tiles[i],
);
for j in 5..i {
update_min(
&mut number,
&mut tiles,
lhs.numbers[j] + rhs.numbers[i - j],
lhs.tiles[j] | rhs.tiles[i - j],
);
update_min(
&mut number,
&mut tiles,
lhs.numbers[i - j] + rhs.numbers[j],
lhs.tiles[i - j] | rhs.tiles[j],
);
}
lhs.numbers[i] = number;
lhs.tiles[i] = tiles;
}
for i in (0..5).rev() {
let mut number = lhs.numbers[i];
let mut tiles = lhs.tiles[i] | rhs.tiles[0];
update_min(
&mut number,
&mut tiles,
rhs.numbers[i],
lhs.tiles[0] | rhs.tiles[i],
);
for j in 1..i {
update_min(
&mut number,
&mut tiles,
lhs.numbers[j] + rhs.numbers[i - j],
lhs.tiles[j] | rhs.tiles[i - j],
);
}
lhs.numbers[i] = number;
lhs.tiles[i] = tiles;
}
}
fn update_dp_final(lhs: &mut Entry, rhs: &Entry) {
for i in (5..10).rev() {
let mut number = lhs.numbers[i];
let mut tiles = lhs.tiles[i] | rhs.tiles[0];
update_min(
&mut number,
&mut tiles,
rhs.numbers[i],
lhs.tiles[0] | rhs.tiles[i],
);
for j in 5..i {
update_min(
&mut number,
&mut tiles,
lhs.numbers[j] + rhs.numbers[i - j],
lhs.tiles[j] | rhs.tiles[i - j],
);
update_min(
&mut number,
&mut tiles,
lhs.numbers[i - j] + rhs.numbers[j],
lhs.tiles[i - j] | rhs.tiles[j],
);
}
lhs.numbers[i] = number;
lhs.tiles[i] = tiles;
}
}
pub(in super::super) fn calculate_unnecessary_tiles(bingpai: &Bingpai) -> (u8, TileFlags) {
let hash_m = hash_shupai(&bingpai.tile_counts()[0..9]);
let hash_p = hash_shupai(&bingpai.tile_counts()[9..18]);
let hash_s = hash_shupai(&bingpai.tile_counts()[18..27]);
let hash_z = hash_zipai(&bingpai.tile_counts()[27..34]);
let packed_rn_m = &SHUPAI_REPLACEMENT_NUMBER_MAP[hash_m];
let packed_rn_p = &SHUPAI_REPLACEMENT_NUMBER_MAP[hash_p];
let packed_rn_s = &SHUPAI_REPLACEMENT_NUMBER_MAP[hash_s];
let packed_rn_z = &ZIPAI_REPLACEMENT_NUMBER_MAP[hash_z];
let packed_ut_m = &SHUPAI_UNNECESSARY_TILES_MAP[hash_m];
let packed_ut_p = &SHUPAI_UNNECESSARY_TILES_MAP[hash_p];
let packed_ut_s = &SHUPAI_UNNECESSARY_TILES_MAP[hash_s];
let packed_ut_z = &ZIPAI_UNNECESSARY_TILES_MAP[hash_z];
let replacement_number_m = unpack_replacement_number(packed_rn_m);
let replacement_number_p = unpack_replacement_number(packed_rn_p);
let replacement_number_s = unpack_replacement_number(packed_rn_s);
let replacement_number_z = unpack_replacement_number(packed_rn_z);
let unnecessary_tiles_m = unpack_unnecessary_tiles(packed_ut_m);
let unnecessary_tiles_p = unpack_unnecessary_tiles(packed_ut_p);
let unnecessary_tiles_s = unpack_unnecessary_tiles(packed_ut_s);
let unnecessary_tiles_z = unpack_unnecessary_tiles(packed_ut_z);
let (mut entry0, entry1, entry2, entry3) = (
Entry {
numbers: replacement_number_m,
tiles: unnecessary_tiles_m.map(|t| t as TileFlags),
},
Entry {
numbers: replacement_number_p,
tiles: unnecessary_tiles_p.map(|t| (t as TileFlags) << 9),
},
Entry {
numbers: replacement_number_s,
tiles: unnecessary_tiles_s.map(|t| (t as TileFlags) << 18),
},
Entry {
numbers: replacement_number_z,
tiles: unnecessary_tiles_z.map(|t| (t as TileFlags) << 27),
},
);
update_dp(&mut entry0, &entry1);
update_dp(&mut entry0, &entry2);
update_dp_final(&mut entry0, &entry3);
let n = 5 + bingpai.num_required_bingpai_mianzi() as usize;
(entry0.numbers[n] as u8, entry0.tiles[n])
}
pub(in super::super) fn calculate_unnecessary_tiles_3p(bingpai: &Bingpai3p) -> (u8, TileFlags) {
let hash_m = hash_19m(&bingpai.tile_counts()[0..9]);
let hash_p = hash_shupai(&bingpai.tile_counts()[9..18]);
let hash_s = hash_shupai(&bingpai.tile_counts()[18..27]);
let hash_z = hash_zipai(&bingpai.tile_counts()[27..34]);
let packed_rn_m = &WANZI_19_REPLACEMENT_NUMBER_MAP[hash_m];
let packed_rn_p = &SHUPAI_REPLACEMENT_NUMBER_MAP[hash_p];
let packed_rn_s = &SHUPAI_REPLACEMENT_NUMBER_MAP[hash_s];
let packed_rn_z = &ZIPAI_REPLACEMENT_NUMBER_MAP[hash_z];
let packed_ut_m = &WANZI_19_UNNECESSARY_TILES_MAP[hash_m];
let packed_ut_p = &SHUPAI_UNNECESSARY_TILES_MAP[hash_p];
let packed_ut_s = &SHUPAI_UNNECESSARY_TILES_MAP[hash_s];
let packed_ut_z = &ZIPAI_UNNECESSARY_TILES_MAP[hash_z];
let replacement_number_m = unpack_replacement_number(packed_rn_m);
let replacement_number_p = unpack_replacement_number(packed_rn_p);
let replacement_number_s = unpack_replacement_number(packed_rn_s);
let replacement_number_z = unpack_replacement_number(packed_rn_z);
let unnecessary_tiles_m = unpack_unnecessary_tiles(packed_ut_m);
let unnecessary_tiles_p = unpack_unnecessary_tiles(packed_ut_p);
let unnecessary_tiles_s = unpack_unnecessary_tiles(packed_ut_s);
let unnecessary_tiles_z = unpack_unnecessary_tiles(packed_ut_z);
let (mut entry0, entry1, entry2, entry3) = (
Entry {
numbers: replacement_number_m,
tiles: unnecessary_tiles_m.map(|t| t as TileFlags),
},
Entry {
numbers: replacement_number_p,
tiles: unnecessary_tiles_p.map(|t| (t as TileFlags) << 9),
},
Entry {
numbers: replacement_number_s,
tiles: unnecessary_tiles_s.map(|t| (t as TileFlags) << 18),
},
Entry {
numbers: replacement_number_z,
tiles: unnecessary_tiles_z.map(|t| (t as TileFlags) << 27),
},
);
update_dp(&mut entry0, &entry1);
update_dp(&mut entry0, &entry2);
update_dp_final(&mut entry0, &entry3);
let n = 5 + bingpai.num_required_bingpai_mianzi() as usize;
(entry0.numbers[n] as u8, entry0.tiles[n])
}
#[cfg(test)]
mod tests {
use super::*;
use crate::test_utils::FromTileCode;
use crate::tile::{TileCounts, TileFlags};
#[test]
fn calculate_unnecessary_tiles_shisanyao_13() {
let tile_counts = TileCounts::from_code("19m19p19s1234567z");
let bingpai = Bingpai::new(&tile_counts).unwrap();
let (replacement_number, unnecessary_tiles) = calculate_unnecessary_tiles(&bingpai);
assert_eq!(replacement_number, 9);
assert_eq!(unnecessary_tiles, TileFlags::from_code("19m19p19s1234567z"));
}
#[test]
fn calculate_unnecessary_tiles_shisanyao_14() {
let tile_counts = TileCounts::from_code("119m19p19s1234567z");
let bingpai = Bingpai::new(&tile_counts).unwrap();
let (replacement_number, unnecessary_tiles) = calculate_unnecessary_tiles(&bingpai);
assert_eq!(replacement_number, 8);
assert_eq!(unnecessary_tiles, TileFlags::from_code("9m19p19s1234567z"));
}
#[test]
fn calculate_unnecessary_tiles_tenpai() {
let tile_counts = TileCounts::from_code("123m456p789s1122z");
let bingpai = Bingpai::new(&tile_counts).unwrap();
let (replacement_number, unnecessary_tiles) = calculate_unnecessary_tiles(&bingpai);
assert_eq!(replacement_number, 1);
assert_eq!(unnecessary_tiles, TileFlags::from_code(""));
}
#[test]
fn calculate_unnecessary_tiles_win() {
let tile_counts = TileCounts::from_code("123m456p789s11222z");
let bingpai = Bingpai::new(&tile_counts).unwrap();
let (replacement_number, unnecessary_tiles) = calculate_unnecessary_tiles(&bingpai);
assert_eq!(replacement_number, 0);
assert_eq!(unnecessary_tiles, TileFlags::from_code(""));
}
#[test]
fn calculate_unnecessary_tiles_with_meld_exclude() {
let tile_counts = TileCounts::from_code("123m456p789s2z");
let bingpai = Bingpai::new(&tile_counts).unwrap();
let (replacement_number, unnecessary_tiles) = calculate_unnecessary_tiles(&bingpai);
assert_eq!(replacement_number, 1);
assert_eq!(unnecessary_tiles, TileFlags::from_code(""));
}
#[test]
fn calculate_unnecessary_tiles_without_pair() {
let tile_counts = TileCounts::from_code("12389m456p12789s1z");
let bingpai = Bingpai::new(&tile_counts).unwrap();
let (replacement_number, unnecessary_tiles) = calculate_unnecessary_tiles(&bingpai);
assert_eq!(replacement_number, 2);
assert_eq!(unnecessary_tiles, TileFlags::from_code("89m12s1z"));
}
#[test]
fn calculate_unnecessary_tiles_too_many_meld_candidates() {
let tile_counts = TileCounts::from_code("12389m456p1289s11z");
let bingpai = Bingpai::new(&tile_counts).unwrap();
let (replacement_number, unnecessary_tiles) = calculate_unnecessary_tiles(&bingpai);
assert_eq!(replacement_number, 2);
assert_eq!(unnecessary_tiles, TileFlags::from_code("89m1289s"));
}
#[test]
fn calculate_unnecessary_tiles_not_enough_meld_candidates() {
let tile_counts = TileCounts::from_code("133345568m23677z");
let bingpai = Bingpai::new(&tile_counts).unwrap();
let (replacement_number, unnecessary_tiles) = calculate_unnecessary_tiles(&bingpai);
assert_eq!(replacement_number, 3);
assert_eq!(unnecessary_tiles, TileFlags::from_code("236z"));
}
#[test]
fn calculate_unnecessary_tiles_triplet_sequence() {
let tile_counts = TileCounts::from_code("222345p1234567z");
let bingpai = Bingpai::new(&tile_counts).unwrap();
let (replacement_number, unnecessary_tiles) = calculate_unnecessary_tiles(&bingpai);
assert_eq!(replacement_number, 5);
assert_eq!(unnecessary_tiles, TileFlags::from_code("1234567z"));
}
#[test]
fn calculate_unnecessary_tiles_sequence_isolated_sequence() {
let tile_counts = TileCounts::from_code("2344456p123456z");
let bingpai = Bingpai::new(&tile_counts).unwrap();
let (replacement_number, unnecessary_tiles) = calculate_unnecessary_tiles(&bingpai);
assert_eq!(replacement_number, 5);
assert_eq!(unnecessary_tiles, TileFlags::from_code("4p123456z"));
}
#[test]
fn calculate_unnecessary_tiles_pair_triplet_sequence() {
let tile_counts = TileCounts::from_code("11222345p12345z");
let bingpai = Bingpai::new(&tile_counts).unwrap();
let (replacement_number, unnecessary_tiles) = calculate_unnecessary_tiles(&bingpai);
assert_eq!(replacement_number, 4);
assert_eq!(unnecessary_tiles, TileFlags::from_code("12345z"));
}
#[test]
fn calculate_unnecessary_tiles_pair_sequence_sequence_pair() {
let tile_counts = TileCounts::from_code("2234556788p123z");
let bingpai = Bingpai::new(&tile_counts).unwrap();
let (replacement_number, unnecessary_tiles) = calculate_unnecessary_tiles(&bingpai);
assert_eq!(replacement_number, 3);
assert_eq!(unnecessary_tiles, TileFlags::from_code("123z"));
}
#[test]
fn calculate_unnecessary_tiles_prioritize_meld_candidates() {
let tile_counts = TileCounts::from_code("133345568s11567z");
let bingpai = Bingpai::new(&tile_counts).unwrap();
let (replacement_number, unnecessary_tiles) = calculate_unnecessary_tiles(&bingpai);
assert_eq!(replacement_number, 3);
assert_eq!(unnecessary_tiles, TileFlags::from_code("567z"));
}
#[test]
fn calculate_unnecessary_tiles_waiting_for_the_5th_tile_1() {
let tile_counts = TileCounts::from_code("1111m123p112233s");
let bingpai = Bingpai::new(&tile_counts).unwrap();
let (replacement_number, unnecessary_tiles) = calculate_unnecessary_tiles(&bingpai);
assert_eq!(replacement_number, 2);
assert_eq!(unnecessary_tiles, TileFlags::from_code("1m"));
}
#[test]
fn calculate_unnecessary_tiles_waiting_for_the_5th_tile_2() {
let tile_counts = TileCounts::from_code("1111234444m1111p");
let bingpai = Bingpai::new(&tile_counts).unwrap();
let (replacement_number, unnecessary_tiles) = calculate_unnecessary_tiles(&bingpai);
assert_eq!(replacement_number, 2);
assert_eq!(unnecessary_tiles, TileFlags::from_code("14m1p"));
}
#[test]
fn calculate_unnecessary_tiles_waiting_for_the_5th_tile_3() {
let tile_counts = TileCounts::from_code("11112222333444z");
let bingpai = Bingpai::new(&tile_counts).unwrap();
let (replacement_number, unnecessary_tiles) = calculate_unnecessary_tiles(&bingpai);
assert_eq!(replacement_number, 2);
assert_eq!(unnecessary_tiles, TileFlags::from_code("12z"));
}
#[test]
fn calculate_unnecessary_tiles_2_isolated_4_tiles_1() {
let tile_counts = TileCounts::from_code("1111247777m");
let bingpai = Bingpai::new(&tile_counts).unwrap();
let (replacement_number, unnecessary_tiles) = calculate_unnecessary_tiles(&bingpai);
assert_eq!(replacement_number, 2);
assert_eq!(unnecessary_tiles, TileFlags::from_code("7m"));
}
#[test]
fn calculate_unnecessary_tiles_2_isolated_4_tiles_2() {
let tile_counts = TileCounts::from_code("1111247777m1112z");
let bingpai = Bingpai::new(&tile_counts).unwrap();
let (replacement_number, unnecessary_tiles) = calculate_unnecessary_tiles(&bingpai);
assert_eq!(replacement_number, 2);
assert_eq!(unnecessary_tiles, TileFlags::from_code("147m2z"));
}
#[test]
fn calculate_unnecessary_tiles_2_isolated_4_tiles_3() {
let tile_counts = TileCounts::from_code("11114444m");
let bingpai = Bingpai::new(&tile_counts).unwrap();
let (replacement_number, unnecessary_tiles) = calculate_unnecessary_tiles(&bingpai);
assert_eq!(replacement_number, 2);
assert_eq!(unnecessary_tiles, TileFlags::from_code("14m"));
}
#[test]
fn calculate_unnecessary_tiles_2_isolated_4_tiles_4() {
let tile_counts = TileCounts::from_code("111124m1111z");
let bingpai = Bingpai::new(&tile_counts).unwrap();
let (replacement_number, unnecessary_tiles) = calculate_unnecessary_tiles(&bingpai);
assert_eq!(replacement_number, 2);
assert_eq!(unnecessary_tiles, TileFlags::from_code("1z"));
}
#[test]
fn calculate_unnecessary_tiles_2_isolated_4_tiles_5() {
let tile_counts = TileCounts::from_code("1111444478m");
let bingpai = Bingpai::new(&tile_counts).unwrap();
let (replacement_number, unnecessary_tiles) = calculate_unnecessary_tiles(&bingpai);
assert_eq!(replacement_number, 3);
assert_eq!(unnecessary_tiles, TileFlags::from_code("1478m"));
}
#[test]
fn calculate_unnecessary_tiles_3_isolated_4_tiles() {
let tile_counts = TileCounts::from_code("1111247777m1111z");
let bingpai = Bingpai::new(&tile_counts).unwrap();
let (replacement_number, unnecessary_tiles) = calculate_unnecessary_tiles(&bingpai);
assert_eq!(replacement_number, 2);
assert_eq!(unnecessary_tiles, TileFlags::from_code("7m1z"));
}
#[test]
fn calculate_unnecessary_tiles_4_honors_1() {
let tile_counts = TileCounts::from_code("1111z");
let bingpai = Bingpai::new(&tile_counts).unwrap();
let (replacement_number, unnecessary_tiles) = calculate_unnecessary_tiles(&bingpai);
assert_eq!(replacement_number, 2);
assert_eq!(unnecessary_tiles, TileFlags::from_code("1z"));
}
#[test]
fn calculate_unnecessary_tiles_4_honors_2() {
let tile_counts = TileCounts::from_code("123m1111z");
let bingpai = Bingpai::new(&tile_counts).unwrap();
let (replacement_number, unnecessary_tiles) = calculate_unnecessary_tiles(&bingpai);
assert_eq!(replacement_number, 2);
assert_eq!(unnecessary_tiles, TileFlags::from_code("1z"));
}
#[test]
fn calculate_unnecessary_tiles_4_honors_3() {
let tile_counts = TileCounts::from_code("11112222z");
let bingpai = Bingpai::new(&tile_counts).unwrap();
let (replacement_number, unnecessary_tiles) = calculate_unnecessary_tiles(&bingpai);
assert_eq!(replacement_number, 2);
assert_eq!(unnecessary_tiles, TileFlags::from_code("12z"));
}
#[test]
fn calculate_unnecessary_tiles_4_honors_4() {
let tile_counts = TileCounts::from_code("123m11p11112222z");
let bingpai = Bingpai::new(&tile_counts).unwrap();
let (replacement_number, unnecessary_tiles) = calculate_unnecessary_tiles(&bingpai);
assert_eq!(replacement_number, 3);
assert_eq!(unnecessary_tiles, TileFlags::from_code("12z"));
}
#[test]
fn calculate_unnecessary_tiles_different_3p_and_4p() {
let tile_counts = TileCounts::from_code("1111m111122233z");
let bingpai = Bingpai::new(&tile_counts).unwrap();
let (replacement_number, unnecessary_tiles) = calculate_unnecessary_tiles(&bingpai);
assert_eq!(replacement_number, 2);
assert_eq!(unnecessary_tiles, TileFlags::from_code("1z1z"));
}
#[test]
fn calculate_unnecessary_tiles_3p_different_3p_and_4p() {
let tile_counts = TileCounts::from_code("1111m111122233z");
let bingpai = Bingpai3p::new(&tile_counts).unwrap();
let (replacement_number, unnecessary_tiles) = calculate_unnecessary_tiles_3p(&bingpai);
assert_eq!(replacement_number, 3);
assert_eq!(unnecessary_tiles, TileFlags::from_code("1m1z"));
}
#[test]
fn calculate_unnecessary_tiles_3p_4_19m_1() {
let tile_counts = TileCounts::from_code("1111m");
let bingpai = Bingpai3p::new(&tile_counts).unwrap();
let (replacement_number, unnecessary_tiles) = calculate_unnecessary_tiles_3p(&bingpai);
assert_eq!(replacement_number, 2);
assert_eq!(unnecessary_tiles, TileFlags::from_code("1m"));
}
#[test]
fn calculate_unnecessary_tiles_3p_4_19m_2() {
let tile_counts = TileCounts::from_code("1111m123p");
let bingpai = Bingpai3p::new(&tile_counts).unwrap();
let (replacement_number, unnecessary_tiles) = calculate_unnecessary_tiles_3p(&bingpai);
assert_eq!(replacement_number, 2);
assert_eq!(unnecessary_tiles, TileFlags::from_code("1m"));
}
#[test]
fn calculate_unnecessary_tiles_3p_4_19m_3() {
let tile_counts = TileCounts::from_code("11119999m");
let bingpai = Bingpai3p::new(&tile_counts).unwrap();
let (replacement_number, unnecessary_tiles) = calculate_unnecessary_tiles_3p(&bingpai);
assert_eq!(replacement_number, 2);
assert_eq!(unnecessary_tiles, TileFlags::from_code("19m"));
}
}