Skip to content

Instantly share code, notes, and snippets.

@wjkoh
Last active August 2, 2025 10:45
Show Gist options
  • Select an option

  • Save wjkoh/b7f6e8eb15823227272fda132d275e2e to your computer and use it in GitHub Desktop.

Select an option

Save wjkoh/b7f6e8eb15823227272fda132d275e2e to your computer and use it in GitHub Desktop.
Go: Random Word Sampler from EFF Wordlist (Diceware)
package fred
import (
"crypto/rand"
_ "embed"
"encoding/csv"
"io"
"math/big"
)
//go:embed eff_large_wordlist.txt
var effLargeWordlist []byte
type wordSampler struct {
words []string
wordCount *big.Int
}
func NewSampler(words []string) *wordSampler {
return &wordSampler{
words: words,
wordCount: big.NewInt(int64(len(words))),
}
}
// NewSamplerFromEFFWordlist takes an EFF word list in a TSV format (a CSV with a tab delimiter).
// Check out https://www.eff.org/files/2016/07/18/eff_large_wordlist.txt
// or https://web.archive.org/web/20230708051203/https://www.eff.org/files/2016/07/18/eff_large_wordlist.txt
func NewSamplerFromEFFWordlist(r io.Reader) (*wordSampler, error) {
reader := csv.NewReader(r)
reader.Comma = '\t'
records, err := reader.ReadAll()
if err != nil {
return nil, err
}
words := make([]string, len(records))
for i, record := range records {
if len(record) != 2 {
return nil, csv.ErrFieldCount
}
words[i] = record[1]
}
return NewSampler(words), nil
}
func (g *wordSampler) SampleWords(n int) ([]string, error) {
samples := make([]string, n)
for i := range n {
index, err := rand.Int(rand.Reader, g.wordCount)
if err != nil {
return nil, err
}
samples[i] = g.words[index.Int64()]
}
return samples, nil
}
package main
import (
"bytes"
"slices"
"strings"
"testing"
)
func TestSampleWords(t *testing.T) {
g, err := NewSamplerFromEFFWordlist(bytes.NewReader(effLargeWordlist))
if err != nil {
t.Fatal(err)
}
words, err := g.SampleWords(3)
if err != nil {
t.Fatal(err)
}
slices.Sort(words)
words = slices.Compact(words)
if got, want := len(words), 3; got != want {
t.Errorf("len(words)=%v, want=%v", got, want)
}
for _, word := range words {
if got, want := strings.Contains(string(effLargeWordlist), word), true; got != want {
t.Errorf("Contains(word)=%v, want=%v", got, want)
}
}
}
@wjkoh
Copy link
Author

wjkoh commented Aug 2, 2025

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment