Correct Horse Battery Staple

April 23, 2013

The only hard part of this exercise is creating a word list, as standard system dictionaries include strange and uncommon words that wouldn’t work well. I found a list of the 5000 most common English words at English Club, removed capitalized words and those containing digits and punctuation, eliminated words with less than five letters or more than ten letters, removed duplicates, and added the word staple (correct, horse and battery were already included in the list). Then it is a simple one-liner to generate an n-word passphrase in the style of xkcd:

(define (xkcd n) (take n (shuffle words)))

Here are some sample passphrases:

> (do ((i 0 (+ i 1))) ((= 25 i))
    (display (xkcd 4)) (newline))
(point coastal mainly hidden)
(trouble definitely eventually sport)
(farmer brake always trunk)
(gradually angry fifteen secular)
(blend wonder elephant commonly)
(pretend developer season boundary)
(license constantly legislator reality)
(supervisor recession remember unhappy)
(ridge university several symbolic)
(profound evidence tongue artificial)
(scenario performer artificial aware)
(officer succeed philosophy liberty)
(difficult mystery utility persist)
(thread anyone hockey already)
(silver recruit charm realize)
(scandal suspicion basket stiff)
(common border recall concerning)
(hunter economy steal infant)
(shame defense likewise articulate)
(discover expected mysterious attribute)
(status blessing jewelry railroad)
(publisher liver digital player)
(piano damage ladder legally)
(lightning seventh recording terrain)
(sunlight universe servant southwest)

Take, sort, randint and shuffle are defined in the Standard Prelude. You can see the wordlist and run the program at http://programmingpraxis.codepad.org/kHMq7z9M.

Pages: 1 2

11 Responses to “Correct Horse Battery Staple”

  1. Anon said

    perl -e ‘
    my @pass = ();
    my @arr = ();
    while (){
    chomp;
    #Pick words of 6+ chars
    push @arr, $_ if /.{5}.+/;
    }
    for (my $i = 0 ; $i < 5 ; $i++){
    # Passphrase is 5 random words
    push @pass, $arr[rand $#arr];
    }
    print ((join " ", @pass) . "\n");
    ' dictionary.txt

  2. Anon said

    That while in there should have a open and close angle brackets inside the parens, but the website seems to have stripped them off.

  3. Maurits said

    Heh… as it happens, I wrote one in response to the xkcd strip. Note that this is guaranteed to only use a given word once.

    my %words = read_words();
    my @chosen = ();

    for my $i (1 .. $num_words) {
    my @keys = keys %words;
    my $word = $keys[ rand(@keys) ];

    push @chosen, $word;

    delete $words{$word};
    }

    To ease compatibility with websites that have complexity requirements, you can upper-case the first letter and add a period on the end.

    I have successfully used this strategy with Google, Facebook, Reddit, Twitter, LinkedIn…

  4. […] today’s Programming Praxis exercise, our goal is to generate xkcd-style passphrases consisting of four […]

  5. My Haskell solution (see http://bonsaicode.wordpress.com/2013/04/23/programming-praxis-correct-horse-battery-staple/ for a version with comments):

    import Data.Char
    import System.Random.Shuffle
    
    valid :: String -> Bool
    valid s = length s > 4 && length s < 10 && all isLower s
    
    main :: IO ()
    main = putStrLn . unwords . take 4 =<<
           shuffleM . filter valid . lines =<<
           readFile "en_US.dic"
    
  6. Daniel Näslund said
    from random import choice as c
    import sys
    
    def isvalid(word):
        return word.islower() and word.isalpha() and 4 < len(word) < 10
    
    WORDS = [w for w in sys.stdin.read().split() if isvalid(w)]
    
    print '%s %s %s %s' % (c(WORDS), c(WORDS), c(WORDS), c(WORDS))
    
  7. Linus said

    Upgrading from 1200 baud to 9600 baud? I remember flamewars about the difference between baud and bps (I don’t even remember the maximum capacity of a telephone line – was it 2400baud? Each baud can carry many bits) – and I remember reading them at 300bps. Just about fast enough to read (if there were not too many ANSI-codes to change color)…

    We even soldered a 2400bps modem in school. Try doing that with a todays 953BGPA chip…

    And now I’m in a country where 128kbps is the top of the line for 100$/month. I’m feeling young again.

  8. Mike said

    Python version.

    import random
    
    def gen_pass_phrase(k=4):
    	with open("/12dicts/2of12.txt", "rt") as f:
    		words = [w for w in f.read().split() if len(w) < 9]
    		return ' '.join(random.sample(words, k))
    

    When testing this routine it generated this pass-phrase: fuzzy pompous fiancee sally

    I remember using a 300 baud acoustically coupled modem.

  9. Scala –

    	def randPw (k: Int): String = {
    			val lines = scala.io.Source.fromFile("/usr/share/dict/words").mkString.split("\n")
    			def pickRand (k: Int) : String = {
    				if (k <= 0) "" else lines(scala.util.Random.nextInt(lines.length).abs) + pickRand(k-1)
    			}
    			pickRand(k)
    	}
    	randPw(4)
    
  10. Reblogged this on David James Coding Blog and commented:
    Excited to attempt this challenge, maybe even generate my new password out of it!

  11. fisherro said

    Guile:

    
    (use-modules (ice-9 rdelim)     ; read-line
                 (ice-9 streams)
                 (srfi srfi-1)      ; lists
                 (srfi srfi-26))    ; cut/cute
    
    ;;; Generate a password of four random words.
    
    (let* ((words (stream->vector
                    (port->stream
                      (open-input-file "/usr/share/dict/words")
                      read-line)))
           (count (vector-length words)))
      (display
        (string-join
          (map
            (cut vector-ref words <>)
            (map
              (lambda (x)
                (random count))
              (iota 4)))
          " "))
      (newline))
    

    Using iota and map to make the list of four random numbers seemed silly, so I did it. ^_^

Leave a comment