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