Hangman
Guess a secret word from a list of over a hundred, a letter at a time or the whole word at once, before six wrong guesses finish the drawing. A hint counts, among the words that still fit what you know, how many hold each letter you have not tried. Words come from a seed, none twice until the list is used up.
Every screen below was recorded under CPython. When this page was built, the EML interpreter replayed each session from the same input and printed the same bytes.
About
Guess the secret word before the drawing is finished. Guess one letter at a time, or the whole word at once; each wrong guess adds a part to the drawing - head, body, two arms, two legs - and the sixth one hangs you. The words come from a list of 105, picked from a seed, none twice until the whole list has been played.
main.eml- the menu, one word from the first guess to the last, the drawing and the scorewords.eml- the word list, which words still fit what you know, and the letter counts for a hintrng.eml- random numbers written in EML, the generator of P008
How each part works:
- A guessed letter shows in every place it stands in the word. Upper case is read as lower case, and a letter you have tried already costs nothing.
- A whole word of the wrong length costs nothing, only a reminder of the length. A wrong word of the right length counts as a wrong guess.
- A hint looks at the words in the list that still fit what you know: the right length, the shown letters in their places, none of the missed letters, no shown letter hiding behind a blank - the game would have shown it there too - and none of the whole words you guessed wrong. For each letter you have not tried, it counts in how many of those words the letter stands, once a word, and names the three highest, a tie going to the letter first in the alphabet. The count is the corpus case
char-frequency-table's tally: check whether the letter is already in the table, then add one or start at one. A hint costs nothing.
What is checked: menu choices 1 to 4; a guess of letters only, a to z, after spaces at the ends are dropped; a seed from 0 to 999999999. An empty guess gives the word up, and an empty seed cancels.
Sessions: sessions/basic.in plays three words from the default seed 2026. - spoon: one miss, then a hint, where 2 words still fit and s stands in both. Then s and o, and the whole word. - trumpet: four hints along the way, from 10 words that fit down to one, and the word typed in capitals. - pebble: six misses in a row, which show the drawing grow, and the word is lost. Then the score: 2 won, 1 lost.
sessions/bad-input.in gives: - menu choices 0 and x; - seeds abc, -1 and 1234567890, and an empty seed that cancels; - seed 7, then the guesses 1, "a b" and ab!; - E in capitals, and then e again; - zebra, which has the wrong length, and bottle, which has the right length but is wrong. After that the hint counts 14 words, with bottle left out; - an empty guess, which gives the word up.
Built on the verified corpus case char-frequency-table (counting each character in a dict, in the order the characters first appear).
Recorded sessions
What the screen shows while someone uses the program. Each typed line appears after its prompt, the way a terminal shows it.
bad-input
interpreter: byte-equal== Hangman ==
Guess the word a letter at a time; six wrong guesses and you are hanged.
1) new word 2) seed 3) score 4) quit
choice> 0
Pick a number from 1 to 4.
1) new word 2) seed 3) score 4) quit
choice> x
Pick a number from 1 to 4.
1) new word 2) seed 3) score 4) quit
choice> 2
seed (0 to 999999999)> abc
Type a number from 0 to 999999999.
seed (0 to 999999999)> -1
Type a number from 0 to 999999999.
seed (0 to 999999999)> 1234567890
Type a number from 0 to 999999999.
seed (0 to 999999999)>
Cancelled.
1) new word 2) seed 3) score 4) quit
choice> 2
seed (0 to 999999999)> 7
Words now come from seed 7.
1) new word 2) seed 3) score 4) quit
choice> 1
A word of 6 letters.
+---+
| |
|
|
|
|
=========
Word: _ _ _ _ _ _ missed: none wrong guesses left: 6
letter, the whole word, ? for a hint, or nothing to give up> 1
Type a letter from a to z, the whole word, or ?.
+---+
| |
|
|
|
|
=========
Word: _ _ _ _ _ _ missed: none wrong guesses left: 6
letter, the whole word, ? for a hint, or nothing to give up> a b
Type a letter from a to z, the whole word, or ?.
+---+
| |
|
|
|
|
=========
Word: _ _ _ _ _ _ missed: none wrong guesses left: 6
letter, the whole word, ? for a hint, or nothing to give up> ab!
Type a letter from a to z, the whole word, or ?.
+---+
| |
|
|
|
|
=========
Word: _ _ _ _ _ _ missed: none wrong guesses left: 6
letter, the whole word, ? for a hint, or nothing to give up> E
Yes - e is in it 1 time.
+---+
| |
|
|
|
|
=========
Word: _ _ _ _ _ e missed: none wrong guesses left: 6
letter, the whole word, ? for a hint, or nothing to give up> e
You have tried e already.
+---+
| |
|
|
|
|
=========
Word: _ _ _ _ _ e missed: none wrong guesses left: 6
letter, the whole word, ? for a hint, or nothing to give up> zebra
The word has 6 letters - that guess costs nothing.
+---+
| |
|
|
|
|
=========
Word: _ _ _ _ _ e missed: none wrong guesses left: 6
letter, the whole word, ? for a hint, or nothing to give up> bottle
Not bottle.
+---+
| |
O |
|
|
|
=========
Word: _ _ _ _ _ e missed: none wrong guesses left: 5
letter, the whole word, ? for a hint, or nothing to give up> ?
14 words in the list still fit. The letters in the most of them: l (11), a (7), d (5).
+---+
| |
O |
|
|
|
=========
Word: _ _ _ _ _ e missed: none wrong guesses left: 5
letter, the whole word, ? for a hint, or nothing to give up>
You give up - the word was fiddle.
1) new word 2) seed 3) score 4) quit
choice> 3
Won 0, lost 1.
1) new word 2) seed 3) score 4) quit
choice> 4
Bye.
What was typed (21 lines)
0
x
2
abc
-1
1234567890
2
7
1
1
a b
ab!
E
e
zebra
bottle
?
3
4
basic
interpreter: byte-equal== Hangman ==
Guess the word a letter at a time; six wrong guesses and you are hanged.
1) new word 2) seed 3) score 4) quit
choice> 1
A word of 5 letters.
+---+
| |
|
|
|
|
=========
Word: _ _ _ _ _ missed: none wrong guesses left: 6
letter, the whole word, ? for a hint, or nothing to give up> e
No e.
+---+
| |
O |
|
|
|
=========
Word: _ _ _ _ _ missed: e wrong guesses left: 5
letter, the whole word, ? for a hint, or nothing to give up> ?
2 words in the list still fit. The letters in the most of them: s (2), a (1), g (1).
+---+
| |
O |
|
|
|
=========
Word: _ _ _ _ _ missed: e wrong guesses left: 5
letter, the whole word, ? for a hint, or nothing to give up> s
Yes - s is in it 1 time.
+---+
| |
O |
|
|
|
=========
Word: s _ _ _ _ missed: e wrong guesses left: 5
letter, the whole word, ? for a hint, or nothing to give up> o
Yes - o is in it 2 times.
+---+
| |
O |
|
|
|
=========
Word: s _ o o _ missed: e wrong guesses left: 5
letter, the whole word, ? for a hint, or nothing to give up> spoon
You got it: spoon, with 1 wrong guess.
1) new word 2) seed 3) score 4) quit
choice> 1
A word of 7 letters.
+---+
| |
|
|
|
|
=========
Word: _ _ _ _ _ _ _ missed: none wrong guesses left: 6
letter, the whole word, ? for a hint, or nothing to give up> ?
10 words in the list still fit. The letters in the most of them: e (8), a (6), i (5).
+---+
| |
|
|
|
|
=========
Word: _ _ _ _ _ _ _ missed: none wrong guesses left: 6
letter, the whole word, ? for a hint, or nothing to give up> e
Yes - e is in it 1 time.
+---+
| |
|
|
|
|
=========
Word: _ _ _ _ _ e _ missed: none wrong guesses left: 6
letter, the whole word, ? for a hint, or nothing to give up> ?
4 words in the list still fit. The letters in the most of them: a (2), c (2), i (2).
+---+
| |
|
|
|
|
=========
Word: _ _ _ _ _ e _ missed: none wrong guesses left: 6
letter, the whole word, ? for a hint, or nothing to give up> a
No a.
+---+
| |
O |
|
|
|
=========
Word: _ _ _ _ _ e _ missed: a wrong guesses left: 5
letter, the whole word, ? for a hint, or nothing to give up> ?
2 words in the list still fit. The letters in the most of them: m (2), c (1), h (1).
+---+
| |
O |
|
|
|
=========
Word: _ _ _ _ _ e _ missed: a wrong guesses left: 5
letter, the whole word, ? for a hint, or nothing to give up> m
Yes - m is in it 1 time.
+---+
| |
O |
|
|
|
=========
Word: _ _ _ m _ e _ missed: a wrong guesses left: 5
letter, the whole word, ? for a hint, or nothing to give up> c
No c.
+---+
| |
O |
| |
|
|
=========
Word: _ _ _ m _ e _ missed: a, c wrong guesses left: 4
letter, the whole word, ? for a hint, or nothing to give up> ?
Only one word in the list still fits.
+---+
| |
O |
| |
|
|
=========
Word: _ _ _ m _ e _ missed: a, c wrong guesses left: 4
letter, the whole word, ? for a hint, or nothing to give up> TRUMPET
You got it: trumpet, with 2 wrong guesses.
1) new word 2) seed 3) score 4) quit
choice> 1
A word of 6 letters.
+---+
| |
|
|
|
|
=========
Word: _ _ _ _ _ _ missed: none wrong guesses left: 6
letter, the whole word, ? for a hint, or nothing to give up> a
No a.
+---+
| |
O |
|
|
|
=========
Word: _ _ _ _ _ _ missed: a wrong guesses left: 5
letter, the whole word, ? for a hint, or nothing to give up> i
No i.
+---+
| |
O |
| |
|
|
=========
Word: _ _ _ _ _ _ missed: a, i wrong guesses left: 4
letter, the whole word, ? for a hint, or nothing to give up> o
No o.
+---+
| |
O |
/| |
|
|
=========
Word: _ _ _ _ _ _ missed: a, i, o wrong guesses left: 3
letter, the whole word, ? for a hint, or nothing to give up> u
No u.
+---+
| |
O |
/|\ |
|
|
=========
Word: _ _ _ _ _ _ missed: a, i, o, u wrong guesses left: 2
letter, the whole word, ? for a hint, or nothing to give up> y
No y.
+---+
| |
O |
/|\ |
/ |
|
=========
Word: _ _ _ _ _ _ missed: a, i, o, u, y wrong guesses left: 1
letter, the whole word, ? for a hint, or nothing to give up> s
No s.
+---+
| |
O |
/|\ |
/ \ |
|
=========
Hanged - the word was pebble.
1) new word 2) seed 3) score 4) quit
choice> 3
Won 2, lost 1.
1) new word 2) seed 3) score 4) quit
choice> 4
Bye.
What was typed (25 lines)
1
e
?
s
o
spoon
1
?
e
?
a
?
m
c
?
TRUMPET
1
a
i
o
u
y
s
3
4
Modules
The program as written, entry module first. Each module transpiles to its own Python file, which is what eml project run executes.
main.eml(entry)
eml# P051 hangman: guess the secret word a letter at a time before the drawing
# is finished - six wrong guesses hang you. A hint counts, among the words
# that still fit, how many hold each letter you have not tried.
import words
import rng
def trim(s):
0 => i
len(s) => j
while i < j and s[i] == " ":
i + 1 => i
while j > i and s[j - 1] == " ":
j - 1 => j
return s[i:j]
def lowered(s):
# Capital letters to small ones; anything else unchanged.
"ABCDEFGHIJKLMNOPQRSTUVWXYZ" => big
"abcdefghijklmnopqrstuvwxyz" => small
"" => out
for ch in s:
ch => c
for k in [0:25]:
if ch == big[k]:
small[k] => c
out + c => out
return out
def letters_only(s):
if s == "":
return False
for ch in s:
if not (ch >= "a" and ch <= "z"):
return False
return True
def has(s, ch):
for x in s:
if x == ch:
return True
return False
def counted(n, one, many):
if n == 1:
return "1 " + one
return str(n) + " " + many
def gallows(wrong):
# The drawing after `wrong` wrong guesses: head, body, two arms, two legs.
" " => head
" " => body
" " => legs
if wrong >= 1:
"O" => head
if wrong == 2:
" | " => body
elif wrong == 3:
"/| " => body
elif wrong >= 4:
"/|\\" => body
if wrong == 5:
"/ " => legs
elif wrong >= 6:
"/ \\" => legs
return [" +---+", " | |", " " + head + " |", " " + body + " |", " " + legs + " |", " |", "========="]
def pattern_of(secret, guessed):
"" => p
for ch in secret:
if has(guessed, ch):
p + ch => p
else:
p + "_" => p
return p
def spaced(p):
"" => out
for ch in p:
if out != "":
out + " " => out
out + ch => out
return out
def listed(s):
"" => out
for ch in s:
if out != "":
out + ", " => out
out + ch => out
if out == "":
return "none"
return out
def pick(game):
# A word not played since the list was last used up.
words.word_list() => ws
if len(game[3]) == len(ws):
[] => game[3]
while True:
ws[game[0].below(len(ws))] => w
False => played
for p in game[3]:
if p == w:
True => played
if not played:
game[3] + [w] => game[3]
return w
def hint(pattern, misses, guessed, ruled_out):
words.candidates(pattern, misses, ruled_out) => fit
if len(fit) == 1:
"Only one word in the list still fits." ^0
return
words.best_letters(words.letter_table(fit, guessed), 3) => best
"" => s
for b in best:
if s != "":
s + ", " => s
s + b[0] + " (" + str(b[1]) + ")" => s
(str(len(fit)) + " words in the list still fit. The letters in the most of them: " + s + ".") ^0
def play(game):
pick(game) => secret
("A word of " + str(len(secret)) + " letters.") ^0
"" => guessed
"" => misses
[] => ruled_out
0 => wrong
while True:
pattern_of(secret, guessed) => pattern
for line in gallows(wrong):
line ^0
("Word: " + spaced(pattern) + " missed: " + listed(misses) + " wrong guesses left: " + str(6 - wrong)) ^0
lowered(trim(input("letter, the whole word, ? for a hint, or nothing to give up> "))) => answer
if answer == "":
("You give up - the word was " + secret + ".") ^0
return False
if answer == "?":
hint(pattern, misses, guessed, ruled_out)
elif not letters_only(answer):
"Type a letter from a to z, the whole word, or ?." ^0
elif len(answer) == 1:
if has(guessed, answer):
("You have tried " + answer + " already.") ^0
else:
guessed + answer => guessed
0 => n
for ch in secret:
if ch == answer:
n + 1 => n
if n > 0:
("Yes - " + answer + " is in it " + counted(n, "time", "times") + ".") ^0
else:
misses + answer => misses
wrong + 1 => wrong
("No " + answer + ".") ^0
elif len(answer) != len(secret):
("The word has " + str(len(secret)) + " letters - that guess costs nothing.") ^0
elif answer == secret:
secret => guessed
else:
ruled_out + [answer] => ruled_out
wrong + 1 => wrong
("Not " + answer + ".") ^0
if pattern_of(secret, guessed) == secret:
("You got it: " + secret + ", with " + counted(wrong, "wrong guess", "wrong guesses") + ".") ^0
return True
if wrong == 6:
for line in gallows(wrong):
line ^0
("Hanged - the word was " + secret + ".") ^0
return False
def ask_seed():
while True:
trim(input("seed (0 to 999999999)> ")) => answer
if answer == "":
return -1
True => ok
if len(answer) > 9:
False => ok
for ch in answer:
if not (ch >= "0" and ch <= "9"):
False => ok
if ok:
return int(answer)
"Type a number from 0 to 999999999." ^0
"== Hangman ==" ^0
"Guess the word a letter at a time; six wrong guesses and you are hanged." ^0
# [the generator, words won, words lost, words played since the list was used up]
[rng.Rng(2026), 0, 0, []] => game
True => running
while running:
"" ^0
"1) new word 2) seed 3) score 4) quit" ^0
trim(input("choice> ")) => choice
if choice == "1":
if play(game):
game[1] + 1 => game[1]
else:
game[2] + 1 => game[2]
elif choice == "2":
ask_seed() => seed
if seed == -1:
"Cancelled." ^0
else:
rng.Rng(seed) => game[0]
[] => game[3]
("Words now come from seed " + str(seed) + ".") ^0
elif choice == "3":
("Won " + str(game[1]) + ", lost " + str(game[2]) + ".") ^0
elif choice == "4":
False => running
else:
"Pick a number from 1 to 4." ^0
"Bye." ^0
Python projection (main.py)
import words
import rng
def trim(s):
i = 0
j = len(s)
while i < j and s[i] == " ":
i = i + 1
while j > i and s[j - 1] == " ":
j = j - 1
return s[i:j]
def lowered(s):
big = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
small = "abcdefghijklmnopqrstuvwxyz"
out = ""
for ch in s:
c = ch
for k in range(0, 26):
if ch == big[k]:
c = small[k]
out = out + c
return out
def letters_only(s):
if s == "":
return False
for ch in s:
if not (ch >= "a" and ch <= "z"):
return False
return True
def has(s, ch):
for x in s:
if x == ch:
return True
return False
def counted(n, one, many):
if n == 1:
return "1 " + one
return str(n) + " " + many
def gallows(wrong):
head = " "
body = " "
legs = " "
if wrong >= 1:
head = "O"
if wrong == 2:
body = " | "
elif wrong == 3:
body = "/| "
elif wrong >= 4:
body = "/|\\"
if wrong == 5:
legs = "/ "
elif wrong >= 6:
legs = "/ \\"
return [" +---+", " | |", " " + head + " |", " " + body + " |", " " + legs + " |", " |", "========="]
def pattern_of(secret, guessed):
p = ""
for ch in secret:
if has(guessed, ch):
p = p + ch
else:
p = p + "_"
return p
def spaced(p):
out = ""
for ch in p:
if out != "":
out = out + " "
out = out + ch
return out
def listed(s):
out = ""
for ch in s:
if out != "":
out = out + ", "
out = out + ch
if out == "":
return "none"
return out
def pick(game):
ws = words.word_list()
if len(game[3]) == len(ws):
game[3] = []
while True:
w = ws[game[0].below(len(ws))]
played = False
for p in game[3]:
if p == w:
played = True
if not played:
game[3] = game[3] + [w]
return w
def hint(pattern, misses, guessed, ruled_out):
fit = words.candidates(pattern, misses, ruled_out)
if len(fit) == 1:
print("Only one word in the list still fits.")
return
best = words.best_letters(words.letter_table(fit, guessed), 3)
s = ""
for b in best:
if s != "":
s = s + ", "
s = s + b[0] + " (" + str(b[1]) + ")"
print(str(len(fit)) + " words in the list still fit. The letters in the most of them: " + s + ".")
def play(game):
secret = pick(game)
print("A word of " + str(len(secret)) + " letters.")
guessed = ""
misses = ""
ruled_out = []
wrong = 0
while True:
pattern = pattern_of(secret, guessed)
for line in gallows(wrong):
print(line)
print("Word: " + spaced(pattern) + " missed: " + listed(misses) + " wrong guesses left: " + str(6 - wrong))
answer = lowered(trim(input("letter, the whole word, ? for a hint, or nothing to give up> ")))
if answer == "":
print("You give up - the word was " + secret + ".")
return False
if answer == "?":
hint(pattern, misses, guessed, ruled_out)
elif not letters_only(answer):
print("Type a letter from a to z, the whole word, or ?.")
elif len(answer) == 1:
if has(guessed, answer):
print("You have tried " + answer + " already.")
else:
guessed = guessed + answer
n = 0
for ch in secret:
if ch == answer:
n = n + 1
if n > 0:
print("Yes - " + answer + " is in it " + counted(n, "time", "times") + ".")
else:
misses = misses + answer
wrong = wrong + 1
print("No " + answer + ".")
elif len(answer) != len(secret):
print("The word has " + str(len(secret)) + " letters - that guess costs nothing.")
elif answer == secret:
guessed = secret
else:
ruled_out = ruled_out + [answer]
wrong = wrong + 1
print("Not " + answer + ".")
if pattern_of(secret, guessed) == secret:
print("You got it: " + secret + ", with " + counted(wrong, "wrong guess", "wrong guesses") + ".")
return True
if wrong == 6:
for line in gallows(wrong):
print(line)
print("Hanged - the word was " + secret + ".")
return False
def ask_seed():
while True:
answer = trim(input("seed (0 to 999999999)> "))
if answer == "":
return -1
ok = True
if len(answer) > 9:
ok = False
for ch in answer:
if not (ch >= "0" and ch <= "9"):
ok = False
if ok:
return int(answer)
print("Type a number from 0 to 999999999.")
print("== Hangman ==")
print("Guess the word a letter at a time; six wrong guesses and you are hanged.")
game = [rng.Rng(2026), 0, 0, []]
running = True
while running:
print("")
print("1) new word 2) seed 3) score 4) quit")
choice = trim(input("choice> "))
if choice == "1":
if play(game):
game[1] = game[1] + 1
else:
game[2] = game[2] + 1
elif choice == "2":
seed = ask_seed()
if seed == -1:
print("Cancelled.")
else:
game[0] = rng.Rng(seed)
game[3] = []
print("Words now come from seed " + str(seed) + ".")
elif choice == "3":
print("Won " + str(game[1]) + ", lost " + str(game[2]) + ".")
elif choice == "4":
running = False
else:
print("Pick a number from 1 to 4.")
print("Bye.")
words.eml
eml# P051 hangman - the word list, and what the letters of the words that still
# fit say about the next guess.
def word_list():
return ["anchor", "apple", "badger", "basket", "beacon", "bicycle", "blanket", "bottle", "bridge", "bucket",
"butter", "cactus", "camera", "candle", "canyon", "carpet", "castle", "cherry", "chimney", "circus",
"cobweb", "comet", "compass", "cookie", "cotton", "crayon", "dolphin", "donkey", "dragon", "drawer",
"eagle", "engine", "falcon", "feather", "fiddle", "forest", "fossil", "garden", "giraffe", "glacier",
"goblet", "guitar", "hammer", "harbor", "helmet", "hockey", "island", "jacket", "jigsaw", "jungle",
"kettle", "kitten", "ladder", "lantern", "lemon", "lizard", "magnet", "marble", "meadow", "mitten",
"monkey", "muffin", "needle", "nugget", "oyster", "paddle", "parrot", "pebble", "pepper", "pickle",
"pillow", "pirate", "planet", "pocket", "puzzle", "rabbit", "ribbon", "rocket", "saddle", "salmon",
"sandal", "shadow", "silver", "socket", "spider", "spoon", "squirrel", "stable", "string", "sugar",
"teapot", "ticket", "tomato", "trumpet", "tunnel", "turtle", "umbrella", "valley", "velvet", "violin",
"walnut", "window", "wizard", "yogurt", "zipper"]
def fits(word, pattern, misses):
# Could word be the secret? pattern has a letter where one is known and
# "_" elsewhere; misses are letters known not to be in it. A known
# letter also cannot hide behind a "_": the game shows every place a
# guessed letter stands.
if len(word) != len(pattern):
return False
for i in [0:len(word) - 1]:
word[i] => ch
if pattern[i] != "_":
if ch != pattern[i]:
return False
else:
for m in misses:
if ch == m:
return False
for p in pattern:
if ch == p:
return False
return True
def candidates(pattern, misses, ruled_out):
# The words that fit, leaving out whole words already guessed wrong.
[] => out
for w in word_list():
if fits(w, pattern, misses):
False => wrong
for r in ruled_out:
if r == w:
True => wrong
if not wrong:
out + [w] => out
return out
def letter_table(words, guessed):
# For every letter not guessed yet, in how many of the words it stands -
# counted once a word, kept in the order letters first appear (the
# check-then-insert tally of the corpus case char-frequency-table).
{} => counts
for w in words:
"" => seen
for ch in w:
0 => known
for g in guessed + seen:
if g == ch:
1 => known
if known == 0:
seen + ch => seen
if ch in counts:
counts[ch] + 1 => counts[ch]
else:
1 => counts[ch]
return counts
def best_letters(counts, k):
# The k letters in the most words, most first; a tie goes to the letter
# that comes first in the alphabet.
[] => out
"" => taken
while len(out) < k and len(out) < len(counts):
"" => pick
0 => most
for ch in counts:
0 => used
for t in taken:
if t == ch:
1 => used
if used == 0:
if counts[ch] > most or (counts[ch] == most and ch < pick):
ch => pick
counts[ch] => most
taken + pick => taken
out + [[pick, most]] => out
return out
Python projection (words.py)
def word_list():
return ["anchor", "apple", "badger", "basket", "beacon", "bicycle", "blanket", "bottle", "bridge", "bucket", "butter", "cactus", "camera", "candle", "canyon", "carpet", "castle", "cherry", "chimney", "circus", "cobweb", "comet", "compass", "cookie", "cotton", "crayon", "dolphin", "donkey", "dragon", "drawer", "eagle", "engine", "falcon", "feather", "fiddle", "forest", "fossil", "garden", "giraffe", "glacier", "goblet", "guitar", "hammer", "harbor", "helmet", "hockey", "island", "jacket", "jigsaw", "jungle", "kettle", "kitten", "ladder", "lantern", "lemon", "lizard", "magnet", "marble", "meadow", "mitten", "monkey", "muffin", "needle", "nugget", "oyster", "paddle", "parrot", "pebble", "pepper", "pickle", "pillow", "pirate", "planet", "pocket", "puzzle", "rabbit", "ribbon", "rocket", "saddle", "salmon", "sandal", "shadow", "silver", "socket", "spider", "spoon", "squirrel", "stable", "string", "sugar", "teapot", "ticket", "tomato", "trumpet", "tunnel", "turtle", "umbrella", "valley", "velvet", "violin", "walnut", "window", "wizard", "yogurt", "zipper"]
def fits(word, pattern, misses):
if len(word) != len(pattern):
return False
for i in range(0, len(word)):
ch = word[i]
if pattern[i] != "_":
if ch != pattern[i]:
return False
else:
for m in misses:
if ch == m:
return False
for p in pattern:
if ch == p:
return False
return True
def candidates(pattern, misses, ruled_out):
out = []
for w in word_list():
if fits(w, pattern, misses):
wrong = False
for r in ruled_out:
if r == w:
wrong = True
if not wrong:
out = out + [w]
return out
def letter_table(words, guessed):
counts = {}
for w in words:
seen = ""
for ch in w:
known = 0
for g in guessed + seen:
if g == ch:
known = 1
if known == 0:
seen = seen + ch
if ch in counts:
counts[ch] = counts[ch] + 1
else:
counts[ch] = 1
return counts
def best_letters(counts, k):
out = []
taken = ""
while len(out) < k and len(out) < len(counts):
pick = ""
most = 0
for ch in counts:
used = 0
for t in taken:
if t == ch:
used = 1
if used == 0:
if counts[ch] > most or counts[ch] == most and ch < pick:
pick = ch
most = counts[ch]
taken = taken + pick
out = out + [[pick, most]]
return out
rng.eml
eml# P051 hangman - random numbers written in EML: the linear congruential
# generator of P008 (number guessing), with the constants of the C
# standard's example rand(). Python's random module is not used, so a seed
# gives the same words on every machine, and the interpreter can check a
# whole session byte for byte.
class Rng:
def __init__(self, seed):
seed % 2147483648 => self.state
def step(self):
(1103515245 * self.state + 12345) % 2147483648 => self.state
return self.state
def below(self, n):
# A number from 0 to n - 1, taken from the high bits of the state: the
# low bits of this generator repeat with short periods. Dividing by
# 65536 is exact in a float for a state below 2^31.
return int(self.step() / 65536) % n
Python projection (rng.py)
class Rng:
def __init__(self, seed):
self.state = seed % 2147483648
def step(self):
self.state = (1103515245 * self.state + 12345) % 2147483648
return self.state
def below(self, n):
return int(self.step() / 65536) % n