You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
DateKeys/wordkey/dice.go

93 lines
3.1 KiB

package wordkey
import (
"fmt"
"strings"
)
// DiceListSize is the size of a list that dice draw from: five dice, each
// from 1 to 6, give 6^5 positions. Whoever does not trust the random numbers
// of a computer rolls them, and looks the words up in the list numbered for
// dice (DiceList).
const DiceListSize = 7776
// DiceNumber returns the dice of the word at position i of a list of
// DiceListSize words: five digits from 1 to 6, each one more than a digit of
// i in base 6, the first the most significant, so that 11111 is the first
// word and 66666 the last, as in the list of the EFF.
func DiceNumber(i int) (string, error) {
if i < 0 || i >= DiceListSize {
return "", fmt.Errorf("wordkey: no dice give position %d of a list of %d words", i, DiceListSize)
}
var b [5]byte
for k := len(b) - 1; k >= 0; k-- {
b[k] = '1' + byte(i%6)
i /= 6
}
return string(b[:]), nil
}
// DiceWord returns the word of list that the dice give, as DiceNumber
// numbers the words: five digits from 1 to 6. The list must have
// DiceListSize words.
func DiceWord(list []string, dice string) (string, error) {
if len(list) != DiceListSize {
return "", fmt.Errorf("wordkey: dice draw from a list of %d words, not %d", DiceListSize, len(list))
}
if len(dice) != 5 {
return "", fmt.Errorf("wordkey: %q is not five dice: five digits from 1 to 6", dice)
}
i := 0
for k := range len(dice) {
c := dice[k]
if c < '1' || c > '6' {
return "", fmt.Errorf("wordkey: %q is not five dice: five digits from 1 to 6", dice)
}
i = i*6 + int(c-'1')
}
return list[i], nil
}
// DiceWords returns the words that dice give, in their order: one number of
// five dice for each word, separated by white space, at least MinWords of
// them, and never the same word twice, which whoever rolls rolls again.
// Fair dice draw each word as Generate does, so the words are as strong.
func DiceWords(list []string, dice string) ([]string, error) {
if len(list) != DiceListSize {
return nil, fmt.Errorf("wordkey: dice draw from a list of %d words, not %d", DiceListSize, len(list))
}
numbers := strings.Fields(dice)
if len(numbers) < MinWords {
return nil, fmt.Errorf("wordkey: a key of words needs at least %d words, not %d", MinWords, len(numbers))
}
seen := make(map[string]bool, len(numbers))
words := make([]string, 0, len(numbers))
for _, n := range numbers {
w, err := DiceWord(list, n)
if err != nil {
return nil, err
}
if seen[w] {
return nil, fmt.Errorf("wordkey: the dice %s give %q a second time; roll them again", n, w)
}
seen[w] = true
words = append(words, w)
}
return words, nil
}
// DiceList returns list numbered for dice, to print it: a line for each
// word, its dice, a tab and the word, as the EFF publishes its list. For the
// English list it is the file of the EFF, byte for byte.
func DiceList(list []string) (string, error) {
if len(list) != DiceListSize {
return "", fmt.Errorf("wordkey: dice draw from a list of %d words, not %d", DiceListSize, len(list))
}
var b strings.Builder
for i, w := range list {
n, _ := DiceNumber(i)
b.WriteString(n + "\t" + w + "\n")
}
return b.String(), nil
}

Powered by TurnKey Linux.