1use nanovec::NanoStackRadix;
2use rustc_hash::FxHashMap as HashMap;
3
4use crate::utils::*;
5use details::*;
6
7pub type WTable = HashMap<u32, WAlts>;
20pub type WTableStatic = phf::Map<u32, u64>;
21
22pub type WAlts = NanoStackRadix<u64, 55>;
23
24pub const W_TABLE_NUM_KEYS: usize = 66913;
27
28pub fn make_w_table(c_table: &super::c_table::CTable) -> WTable {
29 let mut w_table = WTable::with_capacity_and_hasher(
30 W_TABLE_NUM_KEYS, Default::default());
31 for key in c_table.keys() {
32 make_waiting_for_c_entry(&mut w_table, *key);
33 }
34 w_table
35}
36
37fn make_waiting_for_c_entry(w_table: &mut WTable, key: u32) {
38 let num_tiles = key_sum(key);
39 let num_complete_groups = num_tiles / 3;
40 let has_pair = (num_tiles % 3) == 2;
41
42 let mut push = |new_key, pos: i8, waiting_kind: WaitingKind, _has_pair: bool| {
43 w_table.entry(new_key).or_default().push(pack_alt(waiting_kind, pos) as u64)
44 };
45
46 if !has_pair {
47 for pos in 0..=8 {
48 if let Some(new_key) = check_pattern(key, 0o1, pos, 0) {
49 push(new_key, pos, WaitingKind::Tanki, true);
50 }
51 }
52 }
53 if num_complete_groups <= 3 {
54 for pos in 0..=8 {
57 if let Some(new_key) = check_pattern(key, 0o2, pos, 0) {
58 push(new_key, pos, WaitingKind::Shanpon, has_pair);
59 }
60 }
61 for pos in 0..=6 {
62 if let Some(new_key) = check_pattern(key, 0o101, pos, 1) {
63 push(new_key, pos, WaitingKind::Kanchan, has_pair);
64 }
65 }
66 for pos in 0..=7 {
67 let key_low = check_pattern(key, 0o11, pos, -1);
68 let key_high = check_pattern(key, 0o11, pos, 2);
69 if key_low.is_some() && key_high.is_some() {
70 push(key_low.unwrap(), pos, WaitingKind::RyanmenBoth, has_pair);
71 } else if let Some(key) = key_low {
72 push(key, pos, WaitingKind::RyanmenLow, has_pair);
73 } else if let Some(key) = key_high {
74 push(key, pos, WaitingKind::RyanmenHigh, has_pair);
75 }
76 }
77 }
78}
79
80#[derive(Copy, Clone, Debug, Eq, PartialEq)]
81pub struct WaitingPattern {
82 pub complete_key: u32,
83 pub waiting_kind: WaitingKind,
84 pub pattern_pos: u8,
85}
86
87#[derive(
88 Copy, Clone, Debug, Default, Eq, PartialEq, Ord, PartialOrd,
89 strum::FromRepr, strum::AsRefStr, strum::EnumString,
90)]
91#[repr(u8)]
92pub enum WaitingKind {
93 #[default]
94 Tanki = 0, Shanpon, Kanchan, RyanmenHigh, RyanmenLow, RyanmenBoth, }
101
102impl WaitingKind {
103 pub const fn is_shuntsu(self) -> bool {
104 use WaitingKind::*;
105 matches!(self, Kanchan | RyanmenHigh | RyanmenLow | RyanmenBoth)
106 }
107
108 pub const fn pattern(self) -> u32 {
109 use WaitingKind::*;
110 match self {
111 Tanki => 0o1,
112 Shanpon => 0o2,
113 Kanchan => 0o101,
114 RyanmenHigh | RyanmenLow | RyanmenBoth => 0o11,
115 }
116 }
117
118 pub const fn pattern_at(self, pos: u8) -> u32 {
119 self.pattern() << ((pos as u32) * 3)
120 }
121}
122
123pub fn w_entry_iter(key: u32, packed_alts: u64) -> impl Iterator<Item = WaitingPattern> {
124 w_entry_iter_alts(key, WAlts::from_packed(packed_alts))
125}
126
127pub fn w_entry_iter_alts(key: u32, alts: WAlts) -> impl Iterator<Item = WaitingPattern> {
128 alts
129 .map(|packed| unpack_alt(packed as u8))
130 .map(move |(waiting_kind, pos)| {
131 let complete_key = key - waiting_kind.pattern_at(pos);
132 WaitingPattern{
133 complete_key,
134 waiting_kind,
135 pattern_pos: pos,
136 }
137 })
138}
139
140pub(crate) mod details {
141 use crate::utils::*;
142 use super::*;
143
144 pub fn check_pattern(key: u32, pattern: u8, pos: i8, waiting_offset: i8) -> Option<u32> {
148 let new_key = key + ((pattern as u32) << ((pos as u32) * 3));
149 let waiting_pos = pos + waiting_offset;
150 if (0..=8).contains(&waiting_pos) &&
151 !key_is_overflow(new_key) &&
152 ((new_key >> ((waiting_pos as u32) * 3)) & 0o7) < 4 {
153 Some(new_key)
154 } else {
155 None
156 }
157 }
158
159 pub fn pack_alt(waiting_kind: WaitingKind, pos: i8) -> u8 {
166 9 * (waiting_kind as u8) + (pos as u8) + 1
167 }
168
169 pub fn unpack_alt(packed: u8) -> (WaitingKind, u8) {
173 let x = packed - 1;
174 (WaitingKind::from_repr(x / 9).unwrap(), x % 9)
175 }
176}