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.
93 lines
3.1 KiB
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
|
|
}
|