Wordiff
Wordiff is an original game in which contestants take turns spelling new dictionary words that only differ from the last by a change in one letter.
The change can be either:
- a deletion of one letter;
- addition of one letter;
- or change in one letter.
Note:
- All words must be in the dictionary.
- No word in a game can be repeated.
- The first word must be three or four letters long.
- Task
Create a program to aid in the playing of the game by:
- Asking for contestants names.
- Chosing an initial random three or four letter word from the dictionary.
- Asking each contestant in their turn for a wordiff word.
- Checking the wordiff word is:
- in the dictionary,
- not a repetition of past words,
- and differs from the last appropriately.
- Optional stretch goal
Add timing.
- Allow players to set a maximum playing time for the game.
- An internal timer accumulates how long each user takes to respond in their turns.
- Play is halted if the maximum playing time is exceeded on a players input.
- That last player must have entered a wordiff or looses.
- If the game is timed-out, the loser is the person who took the longest `average` time to answer in their rounds.
Python
This is without timing, but ends by showing some wordiffs from the dictionary that could have worked on failure. <lang python># -*- coding: utf-8 -*-
from typing import List, Tuple, Dict, Set from itertools import cycle, islice from collections import Counter import re import random import urllib
dict_fname = 'unixdict.txt' dict_url1 = 'http://wiki.puzzlers.org/pub/wordlists/unixdict.txt' # ~25K words dict_url2 = 'https://raw.githubusercontent.com/dwyl/english-words/master/words.txt' # ~470K words
word_regexp = re.compile(r'^[a-z]{3,}$') # reduce dict words to those of three or more a-z characters.
def load_dictionary(fname: str=dict_fname) -> Set[str]:
"Return appropriate words from a dictionary file" with open(fname) as f: return {lcase for lcase in (word.strip().lower() for word in f) if word_regexp.match(lcase)}
def load_web_dictionary(url: str) -> Set[str]:
"Return appropriate words from a dictionary web page" words = urllib.request.urlopen(url).read().decode().strip().lower().split() return {word for word in words if word_regexp.match(word)}
def get_players() -> List[str]:
"Return inputted ordered list of contestant names." names = input('Space separated list of contestants: ') return [n.capitalize() for n in names.strip().split()]
def is_wordiff(wordiffs: List[str], word: str, dic: Set[str], comment=True) -> bool:
"Is word a valid wordiff from wordiffs[-1] ?" if word not in dic: if comment: print('That word is not in my dictionary') return False if word in wordiffs: if comment: print('That word was already used.') return False if len(word) < len(wordiffs[-1]): return is_wordiff_removal(word, wordiffs[-1], comment) elif len(word) > len(wordiffs[-1]): return is_wordiff_insertion(word, wordiffs[-1], comment) return is_wordiff_change(word, wordiffs[-1], comment)
def is_wordiff_removal(word: str, prev: str, comment=True) -> bool:
"Is word derived from prev by removing one letter?" ... ans = word in {prev[:i] + prev[i+1:] for i in range(len(prev))} if not ans: if comment: print('Word is not derived from previous by removal of one letter.') return ans
def is_wordiff_insertion(word: str, prev: str, comment=True) -> bool:
"Is word derived from prev by adding one letter?" diff = Counter(word) - Counter(prev) diffcount = sum(diff.values()) if diffcount != 1: if comment: print('More than one character insertion difference.') return False insert = list(diff.keys())[0] ans = word in {prev[:i] + insert + prev[i:] for i in range(len(prev) + 1)} if not ans: if comment: print('Word is not derived from previous by insertion of one letter.') return ans
def is_wordiff_change(word: str, prev: str, comment=True) -> bool:
"Is word derived from prev by changing exactly one letter?" ... diffcount = sum(w != p for w, p in zip(word, prev)) if diffcount != 1: if comment: print('More or less than exactly one character changed.') return False return True
def could_have_got(wordiffs: List[str], dic: Set[str]):
return (word for word in (dic - set(wordiffs)) if is_wordiff(wordiffs, word, dic, comment=False))
if __name__ == '__main__':
dic = load_web_dictionary(dict_url2) dic_3_4 = [word for word in dic if len(word) in {3, 4}] start = random.choice(dic_3_4) wordiffs = [start] players = get_players() for name in cycle(players): word = input(f"{name}: Input a wordiff from {wordiffs[-1]!r}: ").strip() if is_wordiff(wordiffs, word, dic): wordiffs.append(word) else: print(f'YOU HAVE LOST {name}!') print("Could have used:", ', '.join(islice(could_have_got(wordiffs, dic), 10)), '...') break</lang>
- Output:
Space separated list of contestants: Paddy Maggie Paddy: Input a wordiff from 'sett': sets Maggie: Input a wordiff from 'sets': bets Paddy: Input a wordiff from 'bets': buts Maggie: Input a wordiff from 'buts': bits Paddy: Input a wordiff from 'bits': bit Maggie: Input a wordiff from 'bit': bite Paddy: Input a wordiff from 'bite': biter Maggie: Input a wordiff from 'biter': bitter Paddy: Input a wordiff from 'bitter': sitter Maggie: Input a wordiff from 'sitter': titter Paddy: Input a wordiff from 'titter': tutter That word is not in my dictionary YOU HAVE LOST Paddy! Could have used: titfer, witter, tittery, totter, titler, kitter, twitter, tilter, gitter, jitter ...