xiangting/standard/
hash.rs

1// SPDX-FileCopyrightText: 2024 Apricot S.
2// SPDX-License-Identifier: MIT
3// This file is part of https://github.com/Apricot-S/xiangting
4
5use super::shupai_table::SHUPAI_TABLE;
6use super::wanzi_19_table::WANZI_19_TABLE;
7use super::zipai_table::ZIPAI_TABLE;
8
9pub fn hash_shupai(single_color_bingpai: &[u8]) -> usize {
10    let (hash, _) = single_color_bingpai
11        .iter()
12        .enumerate()
13        .fold((0, 0), |(h, n), (i, &c)| {
14            debug_assert!(i < 9);
15            debug_assert!(c <= 4);
16            debug_assert!(n + c <= 14);
17            let updated_n = n + c;
18            let updated_h = h + SHUPAI_TABLE[i][updated_n as usize][c as usize];
19            (updated_h, updated_n)
20        });
21    hash
22}
23
24pub fn hash_zipai(zipai_bingpai: &[u8]) -> usize {
25    let (hash, _) = zipai_bingpai
26        .iter()
27        .enumerate()
28        .fold((0, 0), |(h, n), (i, &c)| {
29            debug_assert!(i < 7);
30            debug_assert!(c <= 4);
31            debug_assert!(n + c <= 14);
32            let updated_n = n + c;
33            let updated_h = h + ZIPAI_TABLE[i][updated_n as usize][c as usize];
34            (updated_h, updated_n)
35        });
36    hash
37}
38
39pub fn hash_19m(wanzi_bingpai: &[u8]) -> usize {
40    let (hash, _) = wanzi_bingpai
41        .iter()
42        .step_by(8)
43        .enumerate()
44        .fold((0, 0), |(h, n), (i, &c)| {
45            debug_assert!(i < 2);
46            debug_assert!(c <= 4);
47            debug_assert!(n + c <= 8);
48            let updated_n = n + c;
49            let updated_h = h + WANZI_19_TABLE[i][updated_n as usize][c as usize];
50            (updated_h, updated_n)
51        });
52    hash
53}
54
55#[cfg(test)]
56mod tests {
57    use super::super::shupai_table::SHUPAI_SIZE;
58    use super::super::wanzi_19_table::WANZI_19_SIZE;
59    use super::super::zipai_table::ZIPAI_SIZE;
60    use super::*;
61
62    fn test_hash<const N: usize>(hand: &[u8; N], check: &mut [u8]) {
63        let h = match N {
64            9 => {
65                let h = hash_shupai(hand);
66                assert!(h < SHUPAI_SIZE, "Out of range.");
67                h
68            }
69            7 => {
70                let h = hash_zipai(hand);
71                assert!(h < ZIPAI_SIZE, "Out of range.");
72                h
73            }
74            2 => {
75                let full_hand = [hand[0], 0, 0, 0, 0, 0, 0, 0, hand[1]];
76                let h = hash_19m(&full_hand);
77                assert!(h < WANZI_19_SIZE, "Out of range.");
78                h
79            }
80            _ => unreachable!(),
81        };
82
83        assert!(check[h] == 0, "Collision.");
84        check[h] += 1;
85    }
86
87    fn build_hand<const N: usize>(i: usize, hand: &mut [u8; N], n: u8, check: &mut [u8]) {
88        assert!([9, 7, 2].contains(&N));
89        assert!(i <= N);
90        assert!(n <= 14);
91
92        if i == N {
93            test_hash(hand, check);
94            return;
95        }
96
97        assert!(hand[i] == 0);
98
99        for c in 0..=4 {
100            if n + c > 14 {
101                break;
102            }
103            hand[i] = c;
104            build_hand(i + 1, hand, n + c, check);
105            hand[i] = 0;
106        }
107    }
108
109    #[test]
110    fn test_hash_shupai() {
111        let mut hand = [0u8; 9];
112        let mut check = [0u8; SHUPAI_SIZE];
113        build_hand(0, &mut hand, 0, &mut check);
114        assert!(check.iter().all(|&c| c == 1), "A logic error.");
115    }
116
117    #[test]
118    fn test_hash_zipai() {
119        let mut hand = [0u8; 7];
120        let mut check = [0u8; ZIPAI_SIZE];
121        build_hand(0, &mut hand, 0, &mut check);
122        assert!(check.iter().all(|&c| c == 1), "A logic error.");
123    }
124
125    #[test]
126    fn test_hash_19m() {
127        let mut hand = [0u8; 2];
128        let mut check = [0u8; WANZI_19_SIZE];
129        build_hand(0, &mut hand, 0, &mut check);
130        assert!(check.iter().all(|&c| c == 1), "A logic error.");
131    }
132}