mod replacement_number;
use self::replacement_number::{
get_19m_replacement_number, get_shupai_replacement_number, get_zipai_replacement_number,
};
use std::env;
use std::fs::File;
use std::io::{self, BufWriter, Write};
use std::path::Path;
use std::process;
use xiangting::standard::core::{
NecessaryTilesMapValue, ReplacementNumberMapValue, UnnecessaryTilesMapValue,
};
use xiangting::standard::hash::{hash_19m, hash_shupai, hash_zipai};
use xiangting::standard::shupai_table::SHUPAI_SIZE;
use xiangting::standard::wanzi_19_table::WANZI_19_SIZE;
use xiangting::standard::zipai_table::ZIPAI_SIZE;
struct MapValue {
replacement_number: ReplacementNumberMapValue,
necessary_tiles: NecessaryTilesMapValue,
unnecessary_tiles: UnnecessaryTilesMapValue,
}
impl Default for MapValue {
fn default() -> Self {
MapValue {
replacement_number: 0u32,
necessary_tiles: [0u32; 3],
unnecessary_tiles: [0u32; 3],
}
}
}
type Map = Vec<MapValue>;
fn pack_values<const N: usize>(hand: &[u8; N]) -> MapValue {
debug_assert!([9, 7, 2].contains(&N));
const MAX_REPLACEMENT_NUMBER: u8 = 14;
let mut pack = MapValue::default();
for num_pair in 0..=1 {
for num_meld in 0..=4 {
let (replacement_number, necessary_tiles, unnecessary_tiles) = match N {
9 => {
let hand9 = hand.first_chunk::<9>().unwrap();
let mut initial_target_hand: [u8; 9] = [0u8; 9];
get_shupai_replacement_number(
hand9,
num_meld,
num_pair,
0,
0,
0,
&mut initial_target_hand,
MAX_REPLACEMENT_NUMBER,
0,
0,
)
}
7 => {
let hand7 = hand.first_chunk::<7>().unwrap();
let mut initial_target_hand: [u8; 7] = [0u8; 7];
get_zipai_replacement_number(
hand7,
num_meld,
num_pair,
0,
0,
0,
&mut initial_target_hand,
MAX_REPLACEMENT_NUMBER,
0,
0,
)
}
2 => {
let full_hand = [hand[0], 0, 0, 0, 0, 0, 0, 0, hand[1]];
let mut initial_target_hand: [u8; 9] = [0u8; 9];
get_19m_replacement_number(
&full_hand,
num_meld,
num_pair,
0,
0,
0,
&mut initial_target_hand,
MAX_REPLACEMENT_NUMBER,
0,
0,
)
}
_ => unreachable!(),
};
match (num_pair, num_meld) {
(0, 0) => (),
(0, 1) => pack.replacement_number |= replacement_number as u32,
(0, 2) => pack.replacement_number |= (replacement_number as u32) << 2,
(0, 3) => pack.replacement_number |= (replacement_number as u32) << 5,
(0, 4) => pack.replacement_number |= (replacement_number as u32) << 9,
(1, 0) => pack.replacement_number |= (replacement_number as u32) << 13,
(1, 1) => pack.replacement_number |= (replacement_number as u32) << 15,
(1, 2) => pack.replacement_number |= (replacement_number as u32) << 18,
(1, 3) => pack.replacement_number |= (replacement_number as u32) << 22,
(1, 4) => pack.replacement_number |= (replacement_number as u32) << 26,
_ => unreachable!(),
}
match (num_pair, num_meld) {
(0, 0) => (),
(0, 1) => pack.necessary_tiles[0] |= necessary_tiles as u32,
(0, 2) => pack.necessary_tiles[0] |= (necessary_tiles as u32) << 9,
(0, 3) => pack.necessary_tiles[0] |= (necessary_tiles as u32) << (9 * 2),
(0, 4) => pack.necessary_tiles[1] |= necessary_tiles as u32,
(1, 0) => pack.necessary_tiles[1] |= (necessary_tiles as u32) << 9,
(1, 1) => pack.necessary_tiles[1] |= (necessary_tiles as u32) << (9 * 2),
(1, 2) => pack.necessary_tiles[2] |= necessary_tiles as u32,
(1, 3) => pack.necessary_tiles[2] |= (necessary_tiles as u32) << 9,
(1, 4) => pack.necessary_tiles[2] |= (necessary_tiles as u32) << (9 * 2),
_ => unreachable!(),
}
match (num_pair, num_meld) {
(0, 0) => pack.unnecessary_tiles[0] |= unnecessary_tiles as u32,
(0, 1) => pack.unnecessary_tiles[0] |= (unnecessary_tiles as u32) << 9,
(0, 2) => pack.unnecessary_tiles[0] |= (unnecessary_tiles as u32) << (9 * 2),
(0, 3) => {
pack.unnecessary_tiles[0] |= (unnecessary_tiles as u32 & 0x01F0) << (9 * 3 - 4);
pack.unnecessary_tiles[1] |= unnecessary_tiles as u32 & 0x0F;
}
(0, 4) => pack.unnecessary_tiles[1] |= (unnecessary_tiles as u32) << 4,
(1, 0) => pack.unnecessary_tiles[1] |= (unnecessary_tiles as u32) << (4 + 9),
(1, 1) => pack.unnecessary_tiles[1] |= (unnecessary_tiles as u32) << (4 + 9 * 2),
(1, 2) => pack.unnecessary_tiles[2] |= unnecessary_tiles as u32,
(1, 3) => pack.unnecessary_tiles[2] |= (unnecessary_tiles as u32) << 9,
(1, 4) => pack.unnecessary_tiles[2] |= (unnecessary_tiles as u32) << (9 * 2),
_ => unreachable!(),
}
}
}
pack
}
fn create_entry<const N: usize>(hand: &[u8; N], map: &mut Map) {
debug_assert!([9, 7, 2].contains(&N));
let h = match N {
9 => hash_shupai(hand),
7 => hash_zipai(hand),
2 => {
let full_hand = [hand[0], 0, 0, 0, 0, 0, 0, 0, hand[1]];
hash_19m(&full_hand)
}
_ => unreachable!(),
};
map[h] = pack_values(hand);
}
fn build_map<const N: usize>(hand: &mut [u8; N], i: usize, n: usize, map: &mut Map) {
debug_assert!([9, 7, 2].contains(&N));
debug_assert!(i <= N);
debug_assert!(n <= 14);
if i == N {
create_entry(hand, map);
return;
}
for c in 0..=4 {
if n + c > 14 {
break;
}
hand[i] = c as u8;
build_map(hand, i + 1, n + c, map);
hand[i] = 0;
}
}
fn dump_map<const N: usize>(map: &Map, map_path: &Path) -> io::Result<()> {
debug_assert!([9, 7, 2].contains(&N));
let file = File::create(map_path)?;
let mut w = BufWriter::new(file);
writeln!(w, "// SPDX-FileCopyrightText: 2024 Apricot S.")?;
writeln!(w, "// SPDX-License-Identifier: MIT")?;
writeln!(
w,
"// This file is part of https://github.com/Apricot-S/xiangting"
)?;
writeln!(w)?;
writeln!(
w,
"use super::core::{{NecessaryTilesMapValue, ReplacementNumberMapValue, UnnecessaryTilesMapValue}};"
)?;
match N {
9 => writeln!(w, "use super::shupai_table::SHUPAI_SIZE;")?,
7 => writeln!(w, "use super::zipai_table::ZIPAI_SIZE;")?,
2 => writeln!(w, "use super::wanzi_19_table::WANZI_19_SIZE;")?,
_ => unreachable!(),
}
writeln!(w)?;
writeln!(w, "#[rustfmt::skip]")?;
match N {
9 => write!(
w,
"pub(super) static SHUPAI_REPLACEMENT_NUMBER_MAP: [ReplacementNumberMapValue; SHUPAI_SIZE] = ["
)?,
7 => write!(
w,
"pub(super) static ZIPAI_REPLACEMENT_NUMBER_MAP: [ReplacementNumberMapValue; ZIPAI_SIZE] = ["
)?,
2 => write!(
w,
"pub(super) static WANZI_19_REPLACEMENT_NUMBER_MAP: [ReplacementNumberMapValue; WANZI_19_SIZE] = ["
)?,
_ => unreachable!(),
}
for entry in map {
write!(w, "{},", entry.replacement_number)?;
}
writeln!(w, "];")?;
writeln!(w)?;
writeln!(w, "#[rustfmt::skip]")?;
match N {
9 => write!(
w,
"pub(super) static SHUPAI_NECESSARY_TILES_MAP: [NecessaryTilesMapValue; SHUPAI_SIZE] = ["
)?,
7 => write!(
w,
"pub(super) static ZIPAI_NECESSARY_TILES_MAP: [NecessaryTilesMapValue; ZIPAI_SIZE] = ["
)?,
2 => write!(
w,
"pub(super) static WANZI_19_NECESSARY_TILES_MAP: [NecessaryTilesMapValue; WANZI_19_SIZE] = ["
)?,
_ => unreachable!(),
}
for entry in map {
write!(w, "[")?;
write!(w, "{},", entry.necessary_tiles[0])?;
write!(w, "{},", entry.necessary_tiles[1])?;
write!(w, "{}", entry.necessary_tiles[2])?;
write!(w, "],")?;
}
writeln!(w, "];")?;
writeln!(w)?;
writeln!(w, "#[rustfmt::skip]")?;
match N {
9 => write!(
w,
"pub(super) static SHUPAI_UNNECESSARY_TILES_MAP: [UnnecessaryTilesMapValue; SHUPAI_SIZE] = ["
)?,
7 => write!(
w,
"pub(super) static ZIPAI_UNNECESSARY_TILES_MAP: [UnnecessaryTilesMapValue; ZIPAI_SIZE] = ["
)?,
2 => write!(
w,
"pub(super) static WANZI_19_UNNECESSARY_TILES_MAP: [UnnecessaryTilesMapValue; WANZI_19_SIZE] = ["
)?,
_ => unreachable!(),
}
for entry in map {
write!(w, "[")?;
write!(w, "{},", entry.unnecessary_tiles[0])?;
write!(w, "{},", entry.unnecessary_tiles[1])?;
write!(w, "{}", entry.unnecessary_tiles[2])?;
write!(w, "],")?;
}
writeln!(w, "];")?;
w.flush()?;
Ok(())
}
fn main() {
let start = std::time::Instant::now();
let args: Vec<String> = env::args().collect();
if args.len() != 4 {
eprintln!(
"Usage: {} <PATH TO SHUPAI MAP> <PATH TO ZIPAI MAP> <PATH TO WANZI 19 MAP>",
args[0]
);
process::exit(1);
}
let shupai_map_path = Path::new(&args[1]);
let zipai_map_path = Path::new(&args[2]);
let wanzi_19_map_path = Path::new(&args[3]);
{
let mut shupai_map = Map::new();
shupai_map.resize_with(SHUPAI_SIZE, Default::default);
let mut hand = [0u8; 9];
build_map(&mut hand, 0, 0, &mut shupai_map);
dump_map::<9>(&shupai_map, shupai_map_path).expect("Failed to dump shupai map");
}
{
let mut zipai_map = Map::new();
zipai_map.resize_with(ZIPAI_SIZE, Default::default);
let mut hand = [0u8; 7];
build_map(&mut hand, 0, 0, &mut zipai_map);
dump_map::<7>(&zipai_map, zipai_map_path).expect("Failed to dump zipai map");
}
{
let mut wanzi_19_map = Map::new();
wanzi_19_map.resize_with(WANZI_19_SIZE, Default::default);
let mut hand = [0u8; 2];
build_map(&mut hand, 0, 0, &mut wanzi_19_map);
dump_map::<2>(&wanzi_19_map, wanzi_19_map_path).expect("Failed to dump wanzi 19 map");
}
let elapsed_time = start.elapsed();
println!("elapsed time: {:?}", elapsed_time);
}