1use kdk_zeroize::prelude::*;
2use sha2::{Digest, Sha256};
3
4use crate::error::EntropyError;
5use crate::utils::check_length;
6use crate::DeckEntropy;
7
8pub const fn min_draws<const CARDS: u8, const N: usize>() -> Result<usize, EntropyError> {
12 match (CARDS, N) {
13 (40, 16) => Ok(28),
14 (48, 16) => Ok(26),
15 (52, 16) => Ok(25),
16 (58, 16) => Ok(24),
17 (58, 32) => Ok(55),
18 (78, 16) => Ok(22),
19 (78, 32) => Ok(45),
20 (108, 16) => Ok(22),
21 (108, 32) => Ok(41),
22 (112, 16) => Ok(22),
23 (112, 32) => Ok(42),
24 _ => Err(EntropyError::UnsupportedConfig(CARDS, N)),
25 }
26}
27
28pub const fn max_draws<const CARDS: u8, const N: usize>() -> Result<usize, EntropyError> {
30 match min_draws::<CARDS, N>() {
31 Ok(m) => {
32 let doubled = m * 2;
33 let cap = CARDS as usize;
34 Ok(if doubled > cap { cap } else { doubled })
35 }
36 Err(e) => Err(e),
37 }
38}
39
40pub fn deck_to_entropy<const CARDS: u8, const N: usize>(
68 cards: &[u8],
69) -> Result<DeckEntropy<CARDS, N>, EntropyError> {
70 let required = min_draws::<CARDS, N>()?;
71 let allowed = max_draws::<CARDS, N>()?;
72 check_length(cards.len(), required, allowed)?;
73
74 let mut present = [true; 256];
77 for (i, &card) in cards.iter().enumerate() {
78 if card >= CARDS {
79 return Err(EntropyError::RollOutOfRange(card, i));
80 }
81 if !present[card as usize] {
82 return Err(EntropyError::DuplicateCard(card, i));
83 }
84 present[card as usize] = false;
85 }
86
87 let mut hasher = Sha256::new();
88 for (i, &card) in cards.iter().enumerate() {
89 if i > 0 {
90 hasher.update(b"-");
91 }
92 if card >= 100 {
93 hasher.update([b'0' + card / 100]);
94 }
95 if card >= 10 {
96 hasher.update([b'0' + (card / 10) % 10]);
97 }
98 hasher.update([b'0' + card % 10]);
99 }
100 let digest = hasher.finalize();
101
102 let mut acc: DeckEntropy<CARDS, N> = DeckEntropy::new([0u8; N]);
103 acc.expose_secret_mut().copy_from_slice(&digest[..N]);
104 Ok(acc)
105}