Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Code golf challenge: XKCD 936 passwords

113 views
Skip to first unread message

Chris Angelico

unread,
Oct 8, 2013, 2:17:21 AM10/8/13
to pytho...@python.org
Who's up for some fun? Implement an XKCD-936-compliant password
generator in Python 3, in less code than this:

print(*__import__("random").sample(open("/usr/share/dict/words").read().split("\n"),4))

Second challenge: Use it for generating all your passwords :)

[1] https://en.wikipedia.org/wiki/Code_golf
[2] http://xkcd.com/936/

ChrisA

spruce...@gmail.com

unread,
Oct 8, 2013, 2:45:39 AM10/8/13
to
Well, here's a start:

import random as r
print(*r.sample(open("/usr/share/dict/words").readlines(),4))

Shaves off 6 characters.


spruce...@gmail.com

unread,
Oct 8, 2013, 2:48:01 AM10/8/13
to
And if we were actually trying then that filename should just be "/w". Would get rid of another 19 chars.

Chris Angelico

unread,
Oct 8, 2013, 2:52:03 AM10/8/13
to pytho...@python.org
On Tue, Oct 8, 2013 at 5:48 PM, <spruce...@gmail.com> wrote:
> And if we were actually trying then that filename should just be "/w". Would get rid of another 19 chars.

I'm working this on the assumption that the dictionary file already
exists (that's where it is on my Debian Linux systems, for instance)
and shouldn't be moved :)

ChrisA

Steve Simmons

unread,
Oct 8, 2013, 3:02:25 AM10/8/13
to pytho...@python.org
Chris Angelico <ros...@gmail.com> wrote:
On Tue, Oct 8, 2013 at 5:48 PM,  <spruce...@gmail.com> wrote:
And if we were actually trying then that filename should just be "/w". Would get rid of another 19 chars.

I'm working this on the assumption that the dictionary file already
exists (that's where it is on my Debian Linux systems, for instance)
and shouldn't be moved :)

ChrisA

Typical MUD Master - making up rules as you go along :-)

Sent from a Galaxy far far away

Chris Angelico

unread,
Oct 8, 2013, 3:13:39 AM10/8/13
to pytho...@python.org
On Tue, Oct 8, 2013 at 6:02 PM, Steve Simmons <square...@gmail.com> wrote:
> Typical MUD Master - making up rules as you go along :-)

Totally. Under the auspices of Rule Zero:
http://tvtropes.org/pmwiki/pmwiki.php/Main/RuleOfFun

:)

ChrisA

Mark Lawrence

unread,
Oct 8, 2013, 3:48:17 AM10/8/13
to pytho...@python.org
Very impressive, you've saved a total of 25 characters on one line and
added too many lines to count to your emails, which I've snipped.
Please read and digest this
https://wiki.python.org/moin/GoogleGroupsPython, thanks in anticipation.

--
Roses are red,
Violets are blue,
Most poems rhyme,
But this one doesn't.

Mark Lawrence

Roy Smith

unread,
Oct 8, 2013, 8:33:48 AM10/8/13
to
In article <mailman.830.13812151...@python.org>,
In the old days, it used to be /usr/dict/words. Port Python to v6, and
save another 6 characters :-)

Denis McMahon

unread,
Oct 8, 2013, 11:36:50 AM10/8/13
to
On Tue, 08 Oct 2013 08:33:48 -0400, Roy Smith wrote:

> In article <mailman.830.13812151...@python.org>,
> Chris Angelico <ros...@gmail.com> wrote:
>
>> On Tue, Oct 8, 2013 at 5:48 PM, <spruce...@gmail.com> wrote:
>> > And if we were actually trying then that filename should just be
>> > "/w".
>> > Would get rid of another 19 chars.
>>
>> I'm working this on the assumption that the dictionary file already
>> exists (that's where it is on my Debian Linux systems, for instance)
>> and shouldn't be moved :)

> In the old days, it used to be /usr/dict/words. Port Python to v6, and
> save another 6 characters :-)

Doesn't matter where it is, a link to it exists at "/w" now ;)

--
Denis McMahon, denismf...@gmail.com

rand...@fastmail.us

unread,
Oct 8, 2013, 11:47:56 AM10/8/13
to pytho...@python.org
On Tue, Oct 8, 2013, at 2:45, spruce...@gmail.com wrote:
> On Monday, October 7, 2013 8:17:21 PM UTC-10, Chris Angelico wrote:
> > print(*__import__("random").sample(open("/usr/share/dict/words").read().split("\n"),4)) # 87
>
> import random as r
> print(*r.sample(open("/usr/share/dict/words").readlines(),4)) # 80

How about this? My version is also portable to systems with different
file locations, and localizable to different language dictionaries (Some
assembly required).

import sys,random
print(*map(str.strip,random.sample(list(sys.stdin),4))) # 73

Importing random as r doesn't actually save anything, since " as r" is
the same five characters you saved from the one use of it.

Tim Chase

unread,
Oct 8, 2013, 12:07:32 PM10/8/13
to Denis McMahon, pytho...@python.org
You prodigal...wasting a "/". I just symlinked it from my current
working directory so it exists at "w". ;-)

-tkc


Tim Chase

unread,
Oct 8, 2013, 12:08:20 PM10/8/13
to pytho...@python.org
On 2013-10-08 17:17, Chris Angelico wrote:
> Who's up for some fun? Implement an XKCD-936-compliant password
> generator in Python 3, in less code than this:
>
> print(*__import__("random").sample(open("/usr/share/dict/words").read().split("\n"),4))
>
> Second challenge: Use it for generating all your passwords :)

UnicodeDecodeError: 'ascii' codec can't decode byte 0xc3 in position
9104: ordinal not in range(128)

-tkc


Tobiah

unread,
Oct 8, 2013, 1:38:33 PM10/8/13
to
Yeah, but that's a lot of pixels! Link it to "'" in the current directory.

Tobiah

unread,
Oct 8, 2013, 1:58:18 PM10/8/13
to
So how about finding the last word that starts with
each lower case letter of the alphabet in turn:

azures
bywords
czars
...

Tobiah

spruce...@gmail.com

unread,
Oct 8, 2013, 4:27:45 PM10/8/13
to
On Tuesday, October 8, 2013 5:47:56 AM UTC-10, rand...@fastmail.us wrote:
> Importing random as r doesn't actually save anything, since " as r" is
> the same five characters you saved from the one use of it.

I realize, it just looks nicer than the original __import__, and since it doesn't add any characters...
The optimization was using readlines.

Chris Angelico

unread,
Oct 8, 2013, 4:35:24 PM10/8/13
to pytho...@python.org
Are you aware that readlines keeps the \n at the end of each line,
though? Looks a lot less clean in its output that way. That's why I
used .split() in the first place.

ChrisA

Rob Day

unread,
Oct 8, 2013, 6:27:51 PM10/8/13
to pytho...@python.org
On 08/10/13 07:17, Chris Angelico wrote:
> Who's up for some fun? Implement an XKCD-936-compliant password
> generator in Python 3, in less code than this:
>
> print(*__import__("random").sample(open("/usr/share/dict/words").read().split("\n"),4))
>
>
print("imploring epsilon decamp graveyard's")
# Chosen by fair random sampling, guaranteed to be random

Comments don't count as code, right? ;)

Roy Smith

unread,
Oct 8, 2013, 9:38:12 PM10/8/13
to
On Monday, October 7, 2013 8:17:21 PM UTC-10, Chris Angelico wrote:

> > print(*__import__("random").sample(open("/usr/share/dict/words").read().spli
> > t("\n"),4))


In article <68365e43-498f-4ad2...@googlegroups.com>,
spruce...@gmail.com wrote:

> import random as r
> print(*r.sample(open("/usr/share/dict/words").readlines(),4))
>
> Shaves off 6 characters.


If you're willing to accept a sub-optimal random number generator:

# Python-2, sorry
import os
print list(set(open('/usr/share/dict/words')))[os.getpid():][:4]

I'll also point out that on my OSX box, I could have used
/usr/share/dict/web2 and saved one more character :-)

Chris Angelico

unread,
Oct 8, 2013, 10:10:02 PM10/8/13
to pytho...@python.org
On Wed, Oct 9, 2013 at 12:38 PM, Roy Smith <r...@panix.com> wrote:
> If you're willing to accept a sub-optimal random number generator:
>
> # Python-2, sorry
> import os
> print list(set(open('/usr/share/dict/words')))[os.getpid():][:4]

So that steps by your pid? That's going to drastically reduce the
entropy in the result, which is kinda the point of XKCD 936. I'll call
that one dubious... though it's still better than Rob's entry. (But
thanks Rob, I do like that one :) )

ChrisA

Roy Smith

unread,
Oct 8, 2013, 10:52:36 PM10/8/13
to
In article <mailman.885.13812846...@python.org>,
Chris Angelico <ros...@gmail.com> wrote:

> On Wed, Oct 9, 2013 at 12:38 PM, Roy Smith <r...@panix.com> wrote:
> > If you're willing to accept a sub-optimal random number generator:
> >
> > # Python-2, sorry
> > import os
> > print list(set(open('/usr/share/dict/words')))[os.getpid():][:4]
>
> So that steps by your pid? That's going to drastically reduce the
> entropy in the result,

Well, I did say it was a sub-optimal random number generator :-)

I've been experimenting with os.urandom() as an entropy source, but
can't get the (source code) character count down far enough.

Steven D'Aprano

unread,
Oct 9, 2013, 1:24:12 AM10/9/13
to
On Tue, 08 Oct 2013 23:27:51 +0100, Rob Day wrote:

> print("imploring epsilon decamp graveyard's") # Chosen by fair random
> sampling, guaranteed to be random
>
> Comments don't count as code, right? ;)

"cat cat cat cat..."

That's the trouble with random, you can never quite tell.



--
Steven

Nick Cash

unread,
Oct 9, 2013, 12:07:58 PM10/9/13
to Chris Angelico, pytho...@python.org
>> # Python-2, sorry
>> import os
>> print list(set(open('/usr/share/dict/words')))[os.getpid():][:4]

> So that steps by your pid?

Not really. It seems to rely on list(set(...)) kinda randomizing order... which is definitely not safe without hash randomization.

But this brings up an interesting concept... if we can assume Python 2.7 with the -R flag, or Python 3.3+, I think we do get true pseudo-randomization out of list(set(...))... which means we can trim some!

print(list(set(open('/usr/share/dict/words')))[:4])

No module imports needed! Although it's not as pretty as the original post, but neither was Roy's.

-Nick Cash

rand...@fastmail.us

unread,
Oct 9, 2013, 4:22:31 PM10/9/13
to Rob Day, pytho...@python.org
On Tue, Oct 8, 2013, at 18:27, Rob Day wrote:
> On 08/10/13 07:17, Chris Angelico wrote:
> > Who's up for some fun? Implement an XKCD-936-compliant password
> > generator in Python 3, in less code than this:
> >
> > print(*__import__("random").sample(open("/usr/share/dict/words").read().split("\n"),4))
> >
> >
> print("imploring epsilon decamp graveyard's")
> # Chosen by fair random sampling, guaranteed to be random

Of course, the choice of dictionary makes all the difference in the
world.

print('kayo wide sitz keen')
# Chosen by fair random sampling among SOWPODS words of four or fewer
letters.
# This is a set of 6870 words, XKCD Std. #936 requires a set of at least
2048.
# This password's 50.98 bits of entropy exceed the standard's
recommendations by 6.98.

This can be further improved to the standard's recommended entropy level
(but not strictly the same method) by selecting only the first one from
this set, and selecting the remaining four words from the set of 1416
words of 3 letters or less, for 44.14 bits of entropy:
print('axis ar cha tam')

Or, keeping with the spirit of the standard, mix the set of 1416 words 3
letters or less with 632 randomly selected words from the four-letter
set:
print('pal alp govs deb')

Roy Smith

unread,
Oct 9, 2013, 8:29:00 PM10/9/13
to
In article <mailman.901.13813348...@python.org>,
Nick Cash <nick...@npcinternational.com> wrote:

> >> # Python-2, sorry
> >> import os
> >> print list(set(open('/usr/share/dict/words')))[os.getpid():][:4]
>
> > So that steps by your pid?
>
> Not really. It seems to rely on list(set(...)) kinda randomizing order...
> which is definitely not safe without hash randomization.

Exactly. I *did* preface this with:

>>> If you're willing to accept a sub-optimal random number generator:

[Nick, again]
> But this brings up an interesting concept... if we can assume Python 2.7 with
> the -R flag, or Python 3.3+, I think we do get true pseudo-randomization out
> of list(set(...))... which means we can trim some!
>
> print(list(set(open('/usr/share/dict/words')))[:4])

Excellent! I didn't know about -R before this, so not only has this
been fun, it's been educational too!
0 new messages