Wordiff: Difference between revisions

From Rosetta Code
Content added Content deleted
(Original - at least I devised it with my partner on our own.)
m (fix some typos)
Line 17: Line 17:
Create a program to aid in the playing of the game by:
Create a program to aid in the playing of the game by:
* Asking for contestants names.
* Asking for contestants names.
* Chosing an initial random three or four letter word from the dictionary.
* Choosing an initial random three or four letter word from the dictionary.
* Asking each contestant in their turn for a wordiff word.
* Asking each contestant in their turn for a wordiff word.
* Checking the wordiff word is:
* Checking the wordiff word is:
Line 29: Line 29:
* An internal timer accumulates how long each user takes to respond in their turns.
* 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.
* Play is halted if the maximum playing time is exceeded on a players input.
:* That last player must have entered a wordiff or looses.
:* That last player must have entered a wordiff or loses.
:* If the game is timed-out, the loser is the person who took the longest `average` time to answer in their rounds.
:* If the game is timed-out, the loser is the person who took the longest `average` time to answer in their rounds.



Revision as of 18:03, 30 July 2021

Wordiff is a draft programming task. It is not yet considered ready to be promoted as a complete task, for reasons that should be found in its talk page.

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:

  1. a deletion of one letter;
  2. addition of one letter;
  3. 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.
  • Choosing 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 loses.
  • 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 ...