Skip to content

Instantly share code, notes, and snippets.

@theepicsnail
Last active August 24, 2023 09:38
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save theepicsnail/0ac1236bb1ae285ee04f96d26432a0aa to your computer and use it in GitHub Desktop.
Save theepicsnail/0ac1236bb1ae285ee04f96d26432a0aa to your computer and use it in GitHub Desktop.
Tool for converting a playing card or tarot card deck into an entropy string.
/* This program will convert a shuffled deck into a binary entropy string. */
/*
Big-int MAC/MAD function.
https://en.wikipedia.org/wiki/Multiply%E2%80%93accumulate_operation
We're storing unsigned monotonic numbers in base 256 using a Vec<u8>.
We never multiply or add more than a single u8 at a time, so this lets us do linear-time MAC operations.
This will resize the bigint as necessary.
Because this bigint is unsigned, we do not try to support negatives.
Because this is monotonic we do not try to support subtraction or division.
*/
fn big_multiply_add(base256_bigint: &mut Vec<u8>, multiplier: u8, add: u8) {
let mut carry: u8 = add;
base256_bigint.iter_mut().for_each(|digit| {
let product = u16::from(*digit) * u16::from(multiplier) + u16::from(carry);
*digit = product as u8;
carry = (product >> 8) as u8;
});
if carry > 0 {
base256_bigint.push(carry);
}
}
enum Mode {
Playing128, // Provide 25 cards from a playing card deck for a 12 word phrase 132 bits.
Tarot128, // Provide 22 cards from a tarot card deck for a 12 word phrase 133 bits.
Tarot256, // Provide 45 cards from a tarot card deck for a 24 word phrase 259 bits.
}
struct Settings {
inputs_required: usize,
deck: &'static [&'static str],
bits: usize,
}
fn get_settings(mode: Mode) -> Settings {
match mode {
Mode::Playing128 => Settings {
inputs_required: 25,
deck: &PLAYING_CARDS,
bits: 128,
},
Mode::Tarot128 => Settings {
inputs_required: 22,
deck: &TAROT_CARDS,
bits: 128,
},
Mode::Tarot256 => Settings {
inputs_required: 45,
deck: &TAROT_CARDS,
bits: 256,
},
}
}
fn main() {
let args: Vec<String> = std::env::args().collect();
if args.len() != 3 {
println!(
r#"
Usage: {0} [P128|T128|T256] "Card1 Card2 ..."
P128 - Generate 128 bits from 25 playing cards.
T128 - Generate 128 bits from 22 tarot cards.
T256 - Generate 256 bits from 45 tarot cards.
Example usage:
{0} P128 "QC TS AD 3H 4C 7D 6D KC 7C 5D TC QD AC 8S 9H 6C 2S 7H 9S TH 5S 4H JC QS 2D"
Generates the output:
11111110001000010110010001000110110101110100110100011000000100011011001001011001100000001111101000010100111100010110000101000101
"#,
args[0]
);
return;
}
let mode = match args[1].as_str() {
"P128" => Mode::Playing128,
"T128" => Mode::Tarot128,
"T256" => Mode::Tarot256,
_ => {
println!("Invalid mode.");
return;
}
};
let settings = get_settings(mode);
let cards: Vec<&str> = args[2].split_whitespace().collect();
if cards.len() != settings.inputs_required {
print!(
"Expected {} inputs. Got {}.",
settings.inputs_required,
cards.len()
);
return;
}
let mut bigint: Vec<u8> = vec![];
let mut deck: Vec<&str> = settings.deck.to_vec();
for &card in &cards {
if let Some(index) = deck.iter().position(|&v| v == card) {
big_multiply_add(&mut bigint, deck.len() as u8, index as u8);
deck.remove(index);
} else {
println!("Invalid or already used card '{}'", card);
return;
}
}
let binary = bigint
.iter()
.rev()
.map(|byte| format!("{:08b}", byte))
.collect::<Vec<String>>()
.concat();
if binary.len() >= settings.bits {
println!("{}", &binary[binary.len() - settings.bits..]);
} else {
let padding = "0".repeat(settings.bits - binary.len());
println!("{}{}", padding, binary);
}
}
/*
Standard deck of playing cards
Labeled as:
"{Face}{Suit}" (e.g. "3C" for 3 of Clubs)
Where Faces are (C)lubs (D)iamonds (H)earts (S)pades
and suits are (A)ce (2) (3) (4) (5) (6) (7) (8) (9) (T)en (J)ack (Q)ueen (K)ing.
*/
const PLAYING_CARDS: [&str; 52] = [
"AC", "2C", "3C", "4C", "5C", "6C", "7C", "8C", "9C", "TC", "JC", "QC", "KC", // Clubs
"AD", "2D", "3D", "4D", "5D", "6D", "7D", "8D", "9D", "TD", "JD", "QD", "KD", // Diamonds
"AH", "2H", "3H", "4H", "5H", "6H", "7H", "8H", "9H", "TH", "JH", "QH", "KH", // Hearts
"AS", "2S", "3S", "4S", "5S", "6S", "7S", "8S", "9S", "TS", "JS", "QS", "KS", // Spades
];
/*
Standard tarot card deck
Labeled as:
"00" - "21" for the Major Arcana (which are numbered) Major Arcana (which are numbered)
"{Face}{Suit}" (e.g. "3C" for 3 of Cups)
Where Faces are (W)ands, (C)ups, (S)words, (P)entacles
and Suits are (A)ce (2) (3) (4) (5) (6) (7) (8) (9) (T)en (P)age k(N)ight (Q)ueen (K)ing.
Note: King is 'K' and Knight is 'N' to keep them distinct. This was chosen as it's standard chess notation for them.
*/
const TAROT_CARDS: [&str; 78] = [
"00", // The Fool (0)
"01", // The Magician (1)
"02", // The High Priestess (2)
"03", // The Empress (3)
"04", // The Emperor (4)
"05", // The Hierophant (5)
"06", // The Lovers (6)
"07", // The Chariot (7)
"08", // Strength (8)
"09", // The Hermit (9)
"10", // Wheel of Fortune (10)
"11", // Justice (11)
"12", // The Hanged Man (12)
"13", // Death (13)
"14", // Temperance (14)
"15", // The Devil (15)
"16", // The Tower (16)
"17", // The Star (17)
"18", // The Moon (18)
"19", // The Sun (19)
"20", // Judgment (20)
"21", // The World (21)
"AW", // Ace of Wands
"2W", // Two of Wands
"3W", // Three of Wands
"4W", // Four of Wands
"5W", // Five of Wands
"6W", // Six of Wands
"7W", // Seven of Wands
"8W", // Eight of Wands
"9W", // Nine of Wands
"TW", // Ten of Wands
"PW", // Page of Wands
"NW", // Knight of Wands
"QW", // Queen of Wands
"KW", // King of Wands
"AC", // Ace of Cups
"2C", // Two of Cups
"3C", // Three of Cups
"4C", // Four of Cups
"5C", // Five of Cups
"6C", // Six of Cups
"7C", // Seven of Cups
"8C", // Eight of Cups
"9C", // Nine of Cups
"TC", // Ten of Cups
"PC", // Page of Cups
"NC", // Knight of Cups
"QC", // Queen of Cups
"KC", // King of Cups
"AS", // Ace of Swords
"2S", // Two of Swords
"3S", // Three of Swords
"4S", // Four of Swords
"5S", // Five of Swords
"6S", // Six of Swords
"7S", // Seven of Swords
"8S", // Eight of Swords
"9S", // Nine of Swords
"TS", // Ten of Swords
"PS", // Page of Swords
"NS", // Knight of Swords
"QS", // Queen of Swords
"KS", // King of Swords
"AP", // Ace of Pentacles
"2P", // Two of Pentacles
"3P", // Three of Pentacles
"4P", // Four of Pentacles
"5P", // Five of Pentacles
"6P", // Six of Pentacles
"7P", // Seven of Pentacles
"8P", // Eight of Pentacles
"9P", // Nine of Pentacles
"TP", // Ten of Pentacles
"PP", // Page of Pentacles
"NP", // Knight of Pentacles
"QP", // Queen of Pentacles
"KP", // King of Pentacles
];
/*
When I bought a deck of tarot cards, they came in this order:
10 2W 00 13 5W KC QS NP
11 3W 01 14 6W AC AS AP
12 4W 02 15 7W 2C 2S 2P
PC TC 03 16 8W 3C 3S 3P
NC PS 04 17 9W 4C 4S 4P
QC NS 05 18 TW 5C 5S 5P
TS TP 06 19 PW 6C 6S 6P
KS KP 07 20 NW 7C 7S 7P
PP QP 08 21 QW 8C 8S 8P
09 AW KW 9C 9S 9P
Neat.
*/
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment