I'm working on modernizing Rosetta Code's infrastructure. Starting with communications. Please accept this time-limited open invite to RC's Slack.. --Michael Mol (talk) 20:59, 30 May 2020 (UTC)

# Wordiff

Wordiff
You are encouraged to solve this task according to the task description, using any language you may know.

Wordiff is an original game in which contestants take turns spelling new dictionary words of three or more characters that only differ from the last by a change in one letter.

The change can be either:

1. a deletion 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.

Create a program to aid in the playing of the game by:

• 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 goals
• 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.

## Julia

`isoneless(nw, ow) = any(i -> nw == ow[begin:i-1] * ow[i+1:end], eachindex(ow))isonemore(nw, ow) = isoneless(ow, nw)isonechanged(x, y) = length(x) == length(y) && count(i -> x[i] != y[i], eachindex(y)) == 1onefrom(nw, ow) = isoneless(nw, ow) || isonemore(nw, ow) || isonechanged(nw, ow) function askprompt(prompt)    ans = ""    while isempty(ans)        print(prompt)        ans = strip(readline())    end    return ansend function wordiff(dictfile = "unixdict.txt")    wordlist = [w for w in split(read(dictfile, String), r"\s+") if !occursin(r"\W", w) && length(w) > 2]    starters = [w for w in wordlist if 3 <= length(w) <= 4]     timelimit = something(tryparse(Float64, askprompt("Time limit (min) or 0 for none: ")), 0.0)     players = split(askprompt("Enter players' names. Separate by commas: "), r"\s*,\s*")    times, word = Dict(player => Float32[] for player in players), rand(starters)    used, totalsecs, timestart = [word], timelimit * 60, time()    while length(players) > 1        player = popfirst!(players)        playertimestart = time()        newword = askprompt("\$player, your move. The current word is \$word.  Your worddiff? ")        if timestart + totalsecs > time()            if onefrom(newword, word) && !(newword in used) && lowercase(newword) in wordlist                println("Correct.")                push!(players, player)                word = newword                push!(used, newword)                push!(times[player], time() - playertimestart)            else                println("Wordiff choice incorrect. Player \$player exits game.")            end        else  # out of time            println("Sorry, time was up. Timing ranks for remaining players:")            avtimes = Dict(p => isempty(times[p]) ? NaN : sum(times[p]) / length(times[p])                for p in players)            sort!(players, lt = (x, y) -> avtimes[x] < avtimes[y])            foreach(p -> println("    \$p:", lpad(avtimes[p], 10), " seconds average"), players)            break        end        sleep(rand() * 3)    end    length(players) < 2 && println("Player \$(first(players)) is the only one left, and wins the game.")end wordiff() `
Output:
```Time limit (min) or 0 for none: 0.7
Enter players' names. Separate by commas: Ann, Betty, Sam, Ron, Kim
Correct.
Correct.
Correct.
Correct.
Wordiff choice incorrect. Player Kim exits game.
Correct.
Sorry, time was up. Timing ranks for remaining players:
Ron:     4.134 seconds average
Ann:     6.265 seconds average
Sam: 6.9189997 seconds average
```

## Nim

Translation of: Python
`import httpclient, sequtils, sets, strutils, sugarfrom unicode import capitalize const  DictFname = "unixdict.txt"  DictUrl1 = "http://wiki.puzzlers.org/pub/wordlists/unixdict.txt"    # ~25K words  DictUrl2 = "https://raw.githubusercontent.com/dwyl/english-words/master/words.txt"  # ~470K words  type Dictionary = HashSet[string]  proc loadDictionary(fname = DictFname): Dictionary =  ## Return appropriate words from a dictionary file.  for word in fname.lines():    if word.len >= 3 and word.allCharsInSet(Letters): result.incl word.toLowerAscii  proc loadWebDictionary(url: string): Dictionary =  ## Return appropriate words from a dictionary web page.  let client = newHttpClient()  for word in client.getContent(url).splitLines():    if word.len >= 3 and word.allCharsInSet(Letters): result.incl word.toLowerAscii  proc getPlayers(): seq[string] =  ## Return inputted ordered list of contestant names.  try:    stdout.write "Space separated list of contestants: "    stdout.flushFile()    result = stdin.readLine().splitWhitespace().map(capitalize)    if result.len == 0:      quit "Empty list of names. Quitting.", QuitFailure  except EOFError:    echo()    quit "Encountered end of file. Quitting.", QuitFailure  proc isWordiffRemoval(word, prev: string; comment = true): bool =  ## Is "word" derived from "prev" by removing one letter?  for i in 0..prev.high:    if word == prev[0..<i] & prev[i+1..^1]: return true  if comment: echo "Word is not derived from previous by removal of one letter."  result = false  proc isWordiffInsertion(word, prev: string; comment = true): bool =  ## Is "word" derived from "prev" by adding one letter?  for i in 0..word.high:    if prev == word[0..<i] & word[i+1..^1]: return true  if comment: echo "Word is not derived from previous by insertion of one letter."  return false  proc isWordiffChange(word, prev: string; comment = true): bool =  ## Is "word" derived from "prev" by changing exactly one letter?  var diffcount = 0  for i in 0..word.high:    diffcount += ord(word[i] != prev[i])  if diffcount != 1:    if comment:      echo "More or less than exactly one character changed."    return false  result = true  proc isWordiff(word: string; wordiffs: seq[string]; dict: Dictionary; comment = true): bool =  ## Is "word" a valid wordiff from "wordiffs[^1]"?  if word notin dict:    if comment:      echo "That word is not in my dictionary."      return false  if word in wordiffs:    if comment:      echo "That word was already used."      return false  result = if word.len < wordiffs[^1].len: word.isWordiffRemoval(wordiffs[^1], comment)           elif word.len > wordiffs[^1].len: word.isWordiffInsertion(wordiffs[^1], comment)           else: word.isWordiffChange(wordiffs[^1], comment)  proc couldHaveGot(wordiffs: seq[string]; dict: Dictionary): seq[string] =  for word in dict - wordiffs.toHashSet:    if word.isWordiff(wordiffs, dict, comment = false):      result.add word  when isMainModule:  import random   randomize()  let dict = loadDictionary(DictFname)  let dict34 = collect(newSeq):                 for word in dict:                   if word.len in [3, 4]: word  let start = sample(dict34)  var wordiffs = @[start]  let players = getPlayers()  var iplayer = 0  var word: string  while true:    let name = players[iplayer]    while true:      stdout.write "\$1, input a wordiff from “\$2”: ".format(name, wordiffs[^1])      stdout.flushFile()      try:        word = stdin.readLine().strip()        if word.len > 0: break      except EOFError:        quit "Encountered end of file. Quitting.", QuitFailure    if word.isWordiff(wordiffs, dict):      wordiffs.add word    else:      echo "You have lost, \$#.".format(name)      let possibleWords = couldHaveGot(wordiffs, dict)      if possibleWords.len > 0:        echo "You could have used: ", possibleWords[0..min(possibleWords.high, 20)].join(" ")      break    iplayer = (iplayer + 1) mod players.len`
Output:
```Space separated list of contestants: Paddy Maggie
Paddy, input a wordiff from “beta”: bet
Maggie, input a wordiff from “bet”: bee
Paddy, input a wordiff from “bee”: tee
Maggie, input a wordiff from “tee”: teen
Paddy, input a wordiff from “teen”: teeny
That word is not in my dictionary.
You could have used: then steen tern keen teem teet been seen ten```

## Phix

Doubtless this could be improved in umpteen ways. Not quite yet working under pwa/p2js, but not too far off.

```-- demo\rosetta\Wordiff.exw
include pGUI.e
Ihandle dlg, playerset, playtime, current, remain, turn, input,
help, quit, hframe, history, timer
atom t0, t1, t=0

constant title = "Wordiff game",
help_text = """
Allows single or multi-player modes.
Enter eg "Pete" to play every round yourself,
"Computer" for the computer to play itself,
"Pete,Computer" (or vice versa) to play against the computer,
"Pete,Sue" for a standard two-payer game, or
"Pete,Computer,Sue,Computer" for auto-plays between each human.
Words must be 3 letters or more, and present in the dictionary,
and not already used. You must key return (not tab) to finish
entering your move. The winner is the fastest average time, if
the timer is running (/non-zero), otherwise play continues
until player elimination leaves one (or less) remaining.
NB: Pressing tab or clicking on help will restart or otherwise
mess up the gameplay.
"""
function help_cb(Ihandln /*ih*/)
IupMessage(title,help_text)
IupSetFocus(dlg)
return IUP_DEFAULT
end function

function over2(string word) return length(word)>2 end function
function less5(string word) return length(word)<5 end function

sequence words = filter(unix_dict(),over2),
valid = {},
used = {}

string word
integer lw

sequence players, eliminated, times, averages
integer player

function levenshtein1(string w)
bool res = false
integer l = length(w)
if not find(w,used) and abs(l-lw)<=1 then
sequence costs = tagset(l+1,0)
for i=1 to lw do
costs = i
integer newcost = i-1, pj = i
for j=1 to l do
integer cj = costs[j+1],
ne = word[i]!=w[j],
nc = newcost+ne
pj = min({pj+1, cj+1, nc})
costs[j+1] = pj
newcost = cj
end for
end for
res = costs[\$-1]==1
end if
return res
end function

procedure game_over()
IupSetAttribute(history,"APPENDITEM","GAME OVER:")
if length(valid) then
string valids = "You could have had "&join(valid,", ")
IupSetAttribute(history,"APPENDITEM",valids)
end if
string winner = "nobody"
atom best = -1
for i=1 to length(players) do
string player = players[i], msg
if eliminated[i] then
msg = ": eliminated"
else
atom average = averages[i]
if average=-1 then
msg = ": no times"
else
msg = sprintf(": %.3f",average)
if best=-1 or average<best then
winner = player
best = average
end if
end if
end if
IupSetAttribute(history,"APPENDITEM",player&msg)
end for
IupSetAttribute(history,"APPENDITEM","And the winner is: "&winner)
IupSetInt(history,"TOPITEM",IupGetInt(history,"COUNT"))
IupSetInt(timer,"RUN",false)
end procedure

procedure score(string move)
times[player] = append(times[player],time()-t1)
averages[player] = sum(times[player])/length(times[player])
used = append(used,move)
word = move
lw = length(word)
valid = filter(words,levenshtein1)
IupSetStrAttribute(current,"TITLE","Current word: "&word)
end procedure

while true do
player = mod(player,length(players))+1
if not eliminated[player] then exit end if
end while
IupSetStrAttribute(turn,"TITLE",players[player]&"'s turn:")
IupRefreshChildren(turn)
IupSetStrAttribute(input,"VALUE",word)
t1 = time()
end procedure

procedure autoplay()
while true do
if length(valid)=0 then
IupSetAttribute(history,"APPENDITEM","no more moves possible")
game_over()
exit
end if
if proper(players[player])!="Computer" then exit end if
string move = valid[rand(length(valid))]
IupSetStrAttribute(history,"APPENDITEM","%s's move: %s\n",
{players[player],move})
IupSetInt(history,"TOPITEM",IupGetInt(history,"COUNT"))
score(move)
end while
end procedure

procedure new_game(bool bStart=true)
bool bActive = length(players)!=0
IupSetInt(turn,"ACTIVE",bActive)
IupSetInt(input,"ACTIVE",bActive)
if bActive and bStart then
sequence w34 = filter(words,less5)
while true do
integer r = rand(length(w34))
word = w34[r]
lw = length(word)
used = {word}
valid = filter(words,levenshtein1)
if length(valid)!=0 then exit end if
w34[r..r] = {}
end while
IupSetStrAttribute(current,"TITLE","Current word: "&word)
IupSetStrAttribute(turn,"TITLE",players[player]&"'s turn:")
IupRefreshChildren(turn)
IupSetStrAttribute(input,"VALUE",word)
IupSetAttribute(history,"REMOVEITEM","ALL")
IupSetAttribute(history,"APPENDITEM","Initial word: "&word)
IupSetInt(history,"TOPITEM",IupGetInt(history,"COUNT"))
integer l = length(players)
eliminated = repeat(false,l)
times = repeat({},l)
averages = repeat(-1,l)
t0 = time()
t1 = time()
IupSetInt(timer,"RUN",t!=0)
autoplay()
end if
end procedure

function players_cb(Ihandln /*playerset*/)
players = split(IupGetAttribute(playerset,"VALUE"),",")
player = 1
new_game(false)
return IUP_DEFAULT
end function

function playtime_cb(Ihandle /*playtime*/)
t = IupGetInt(playtime, "VALUE")
if t then
IupSetInt(remain,"VISIBLE",true)
IupSetStrAttribute(remain,"TITLE","Remaining: %.1fs",{t})
else
IupSetInt(remain,"VISIBLE",false)
end if
IupRefreshChildren(remain)
return IUP_DEFAULT
end function

function focus_cb(Ihandle /*input*/)
new_game(true)
return IUP_DEFAULT
end function

procedure verify_move()
string move = IupGetAttribute(input,"VALUE"),
okstr = "ok"
bool ok = not find(move,used)
if not ok then
else
ok = find(move,words)
if not ok then
okstr = "not in dictionary"
else
ok = find(move,valid)
if not ok then
okstr = "more than one change"
else
used = append(used,move)
end if
end if
end if
if not ok then
okstr &= ", player eliminated"
end if
IupSetStrAttribute(history,"APPENDITEM","%s's move: %s %s\n",
{players[player],move,okstr})
IupSetInt(history,"TOPITEM",IupGetInt(history,"COUNT"))
if not ok then
eliminated[player] = true
if length(players)-sum(eliminated)<=1 then
game_over()
return
end if
else
score(move)
end if
autoplay()
end procedure

function timer_cb(Ihandle /*timer*/)
atom e = time()-t0
if e>=t then
IupSetStrAttribute(remain,"TITLE","Remaining: 0s")
IupSetInt(turn,"ACTIVE",false)
IupSetInt(input,"ACTIVE",false)
game_over()
else
IupSetStrAttribute(remain,"TITLE","Remaining: %.1fs",{t-e})
end if
return IUP_DEFAULT
end function

function quit_cb(Ihandle /*ih*/)
return IUP_CLOSE
end function

function key_cb(Ihandle /*dlg*/, atom c)
if c=K_ESC then return IUP_CLOSE
elsif c=K_CR then
Ihandln focus = IupGetFocus()
if focus=playerset then
IupSetFocus(playtime)
elsif focus=playtime then
IupSetFocus(input)
new_game()
elsif focus=input then
verify_move()
end if
elsif c=K_F1 then return help_cb(NULL)
elsif c=K_cC then
integer n = IupGetInt(history,"COUNT")
sequence hist = repeat(0,n)
for i=1 to n do
hist[i] = IupGetAttributeId(history,"",i)
end for
hist = join(hist,"\n")
Ihandln clip = IupClipboard()
IupSetAttribute(clip,"TEXT",hist)
clip = IupDestroy(clip)
end if
return IUP_CONTINUE
end function

IupOpen()
playerset = IupText(`EXPAND=HORIZONTAL`)
playtime = IupText(`SPIN=Yes, SPINMIN=0, RASTERSIZE=48x`)
IupSetCallback({playerset,playtime},"KILLFOCUS_CB",Icallback("players_cb"))
IupSetCallback(playtime,"VALUECHANGED_CB",Icallback("playtime_cb"))
turn = IupLabel("turn","ACTIVE=NO")
input = IupText("EXPAND=HORIZONTAL, ACTIVE=NO")
IupSetCallback(input,"GETFOCUS_CB",Icallback("focus_cb"))
current = IupLabel("Current word:","EXPAND=HORIZONTAL")
remain = IupLabel("Remaining time:0s","VISIBLE=NO")
history = IupList("VISIBLELINES=10, EXPAND=YES, CANFOCUS=NO")
quit = IupButton("Close", Icallback("quit_cb"))
timer = IupTimer(Icallback("timer_cb"),100,false)
sequence buttons = {IupFill(),help,IupFill(),quit,IupFill()}
Ihandle settings = IupHbox({IupLabel("Contestant name(s)"),
playerset,
IupLabel("Timer (seconds)"),
playtime},acp),
currbox = IupHbox({current,remain},acp),
numbox = IupHbox({turn,input},acp),
vbox = IupVbox({settings,
currbox,
numbox, hframe, btnbox}, "GAP=5,MARGIN=5x5")
dlg = IupDialog(vbox, `TITLE="%s", SIZE=500x220`, {title})
IupSetCallback(dlg, "K_ANY", Icallback("key_cb"))
IupShow(dlg)
if platform()!=JS then
IupMainLoop()
IupClose()
end if
```
Output:

A quick timed-out game:

```Initial word: farm
Pete's move: harm ok
Sue's move: warm ok
Pete's move: war ok
Sue's move: ward ok
Pete's move: hard ok
Sue's move: lard ok
Pete's move: card ok
GAME OVER:
You could have had bard, car, care, carl, carp, carr, cart, chard, cord, curd, yard
Pete: 2.519
Sue: 3.078
And the winner is: Pete
```

Computer playing against itself:

```Initial word: sage
Computer's move: sake
Computer's move: take
Computer's move: wake
Computer's move: wave
Computer's move: ware
Computer's move: aware
Computer's move: awake
Computer's move: awaken
Computer's move: waken
Computer's move: taken
Computer's move: oaken
no more moves possible
GAME OVER:
Computer: 0.000
And the winner is: Computer
```

## Python

This is without timing, but ends by showing some wordiffs from the dictionary that could have worked on failure.

`# -*- coding: utf-8 -*- from typing import List, Tuple, Dict, Setfrom itertools import cycle, islicefrom collections import Counterimport reimport randomimport urllib dict_fname = 'unixdict.txt'dict_url1 = 'http://wiki.puzzlers.org/pub/wordlists/unixdict.txt'    # ~25K wordsdict_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())     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`
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
Could have used: titfer, witter, tittery, totter, titler, kitter, twitter, tilter, gitter, jitter ...```

## Raku

`my @words = 'unixdict.txt'.IO.slurp.lc.words.grep(*.chars > 2); my @small = @words.grep(*.chars < 6); use Text::Levenshtein; my (\$rounds, \$word, \$guess, @used, @possibles) = 0; loop {    my \$lev;    \$word = @small.pick;    hyper for @words  -> \$this {        next if (\$word.chars - \$this.chars).abs > 1;        last if (\$lev = distance(\$word, \$this)) == 1;    }    last if \$lev;} my \$name = ','; #[[### Entirely unnecessary and unuseful "chatty repartee" but is required by the task run 'clear';\$name = prompt "Hello player one, what is your name? ";say "Cool. I'm going to call you Gomer.";\$name = ' Gomer,';sleep 1;say "\nPlayer two, what is your name?\nOh wait, this isn't a \"specified number of players\" game...";sleep 1;say "Nevermind.\n"; ################################################################################]] loop {    say "Word in play: \$word";    push @used, \$word;    @possibles = @words.hyper.map: -> \$this {        next if (\$word.chars - \$this.chars).abs > 1;        \$this if distance(\$word, \$this) == 1 and \$this ∉ @used;    }    \$guess = prompt "your word? ";    last unless \$guess ∈ @possibles;    ++\$rounds;    say qww<Ok! Woot! 'Way to go!' Nice! 👍 😀>.pick ~ "\n";    \$word = \$guess;} my \$already = (\$guess ∈ @used) ?? " \$guess was already played but" !! ''; if @possibles {    say "\nOops. Sorry{\$name}{\$already} one of [{@possibles}] would have continued the game."} else {    say "\nGood job{\$name}{\$already} there were no possible words to play."}say "You made it through \$rounds rounds.";`
Sample output:
```Hello player one, what is your name? Burtram Redneck
Cool. I'm going to call you Gomer.

Player two, what is your name?
Oh wait, this isn't a "specified number of players" game...
Nevermind.

Word in play: howe
Woot!

Word in play: how
👍

Word in play: show
Nice!

Word in play: shot
😀

Word in play: hot
Way to go!

Word in play: hit
Nice!

Word in play: mit
😀

Word in play: kit
Woot!

Word in play: nit
😀

Word in play: nip
Ok!

Word in play: snip
Ok!

Word in play: slip
Way to go!

Word in play: slap
Woot!

Word in play: lap
Woot!

Word in play: nap
Nice!

Word in play: nan
Nice!

Word in play: man
Woot!

Word in play: men
Nice!

Word in play: ben
👍

Word in play: ban

Oops. Sorry Gomer, man was already played but one of [bad bag bah bam band bane bang bank bar barn bat bay bean bin bon bran bun can dan fan han ian jan pan ran san tan van wan zan] would have continued the game.
You made it through 19 rounds.```

## REXX

`/*REXX program acts as a host and allows two or more people to play the   WORDIFF  game.*/signal on halt                                   /*allow the user(s) to halt the game.  */parse arg iFID seed .                            /*obtain optional arguments from the CL*/if iFID=='' | iFID=="," then iFID='unixdict.txt' /*Not specified?  Then use the default.*/if datatype(seed, 'W')  then call random ,,seed  /*If      "         "   "   "    seed. */call readcall IDsfirst= random(1, min(100000, starters) )         /*get a random start word for the game.*/list= \$\$\$.firstsay;                say eye  "OK, let's play the  WORDIFF  game.";   say;   say         do round=1                    do player=1  for players                    call show;   ou= o;   upper ou                    call CBLF  word(names, player)                    end   /*players*/         end              /*round*/ halt: say;  say;  say eye 'The  WORDIFF  game has been halted.'done: exit 0                                     /*stick a fork in it,  we're all done. */quit: say;  say;  say eye 'The  WORDDIF  game is quitting.';   signal done/*──────────────────────────────────────────────────────────────────────────────────────*/isMix: return datatype(arg(1), 'M')              /*return unity if arg has mixed letters*/ser:   say;   say eye '***error*** ' arg(1).;   say;    return  /*issue error message.  */last:  parse arg y;      return word(y, words(y) )              /*get last word in list.*/over:  call ser 'word ' _ x _ arg(1);  say eye 'game over,' you; signal done /*game over*/show:  o= last(list);   say;  call what;  say;   L= length(o);    returnverE:  m= 0;  do v=1  for L; m= m + (substr(ou,v,1)==substr(xu,v,1)); end;   return m==L-1verL:  do v=1  for L;  if space(overlay(' ', ou, v), 0)==xu  then return 1; end;  return 0verG:  do v=1  for w;  if space(overlay(' ', xu, v), 0)==ou  then return 1; end;  return 0what:  say eye 'The current word in play is: '   _   o   _;       return/*──────────────────────────────────────────────────────────────────────────────────────*/CBLF:  parse arg you                             /*ask carbon-based life form for a word*/          do getword=0  by 0  until x\==''          say eye "What's your word to be played, " you'?'          parse pull x;  x= space(x);   #= words(x);  if #==0  then iterate;  w= length(x)          if #>1  then do;  call ser 'too many words given: '   x                            x=;  iterate getword                       end          if \isMix(x)  then do;  call ser 'the name'   _  x  _  " isn't alphabetic"                                  x=;   iterate getword                             end          end   /*getword*/        if wordpos(x, list)>0  then call over " has already been used"       xu= x;  upper xu                          /*obtain an uppercase version of word. */       if \@.xu  then call over  " doesn't exist in the dictionary: " iFID       if length(x) <3            then call over  " must be at least three letters long."       if w <L  then  if \verL()  then call over  " isn't a legal letter deletion."       if w==L  then  if \verE()  then call over  " isn't a legal letter substitution."       if w >L  then  if \verG()  then call over  " isn't a legal letter addition."       list= list  x                             /*add word to the list of words used.  */       return/*──────────────────────────────────────────────────────────────────────────────────────*/IDs:   ?= "Enter the names of the people that'll be playing the WORDIFF game   (or Quit):"       names=                                    /*start with a clean slate (of names). */          do getIDs=0  by 0  until words(names)>1          say;  say eye ?          parse pull ids;  ids= space( translate(ids, , ',') )      /*elide any commas. */          if ids==''  then iterate;  q= ids;  upper q               /*use uppercase QUIT*/          if abbrev('QUIT', q, 1)  then signal quit            do j=1  for words(ids);    x= word(ids, j)            if \isMix(x)  then do;  call ser 'the name'    _ x _  " isn't alphabetic"                                    names=;   iterate getIDs                               end            if wordpos(x, names)>0  then do; call ser 'the name' _ x _ " is already taken"                                             names=;   iterate getIDs                                         end            names= space(names x)            end   /*j*/          end     /*getIDs*/       say       players= words(names)          do until ans\==''          say eye 'The '    players     " player's names are: "    names          say eye 'Is this correct?';   pull ans;  ans= space(ans)          end   /*until*/       yeahs= 'yah yeah yes ja oui si da';   upper yeahs          do ya=1  for words(yeahs)          if abbrev( word(yeahs, ya), ans, 2) | ans=='Y'  then return          end   /*ya*/       call IDS;                                               return/*──────────────────────────────────────────────────────────────────────────────────────*/read: _= '───';       eye= copies('─', 8)        /*define a couple of eye catchers.     */      say;   say eye eye eye  'Welcome to the  WORDIFF  word game.'  eye eye eye;    say      @.= 0;           starters= 0            do r=1  while lines(iFID)\==0        /*read each word in the file  (word=X).*/            x= strip(linein(iFID))               /*pick off a word from the input line. */            if \isMix(x)  then iterate           /*Not a suitable word for WORDIFF? Skip*/            y= x;    upper x                     /*pick off a word from the input line. */            @.x= 1;  L= length(x)                /*set a semaphore for uppercased word. */            if L<3 | L>4  then iterate           /*only use short words for the start.  */            starters= starters + 1               /*bump the count of starter words.     */            \$\$\$.starters= y                      /*save short words for the starter word*/            end   /*#*/      if r>100  &  starters> 10  then return     /*is the dictionary satisfactory ?     */      call ser 'Dictionary file ' _ iFID _ "wasn't found or isn't satisfactory.";  exit 13`
output   when using the default inputs:
```──────── ──────── ──────── Welcome to the  WORDIFF  word game. ──────── ──────── ────────

──────── Enter the names of the people that'll be playing the WORDIFF game   (or Quit):
Teddy Jasmine Cy                                            ◄■■■■■■■ user input

──────── The  3  player's names are:  Teddy Jasmine Cy
──────── Is this correct?
yes                                                         ◄■■■■■■■ user input

──────── OK, let's play the  WORDIFF  game.

──────── The current word in play is:  ─── tame ───

──────── What's your word to be played,  Teddy?
fame                                                        ◄■■■■■■■ user input

──────── The current word in play is:  ─── fame ───

──────── What's your word to be played,  Jasmine?
lame                                                        ◄■■■■■■■ user input

──────── The current word in play is:  ─── lame ───

──────── What's your word to be played,  Cy?
lam                                                         ◄■■■■■■■ user input

──────── The current word in play is:  ─── lam ───

──────── What's your word to be played,  Teddy?
lamb                                                        ◄■■■■■■■ user input

──────── The current word in play is:  ─── lamb ───

──────── What's your word to be played,  Jasmine?
limb                                                        ◄■■■■■■■ user input

──────── The current word in play is:  ─── limb ───

──────── What's your word to be played,  Cy?
climb                                                       ◄■■■■■■■ user input

──────── The current word in play is:  ─── climb ───

──────── What's your word to be played,  Teddy?
climbs                                                      ◄■■■■■■■ user input

──────── ***error***  word  ─── climbs ───  doesn't exist in the dictionary:  unixdict.txt.

──────── game over, Teddy
```

## Wren

Library: Wren-ioutil
Library: Wren-str
Library: Wren-sort

Due to a bug in the System.clock method (basically it gets suspended whilst waiting for user input), it is not currently possible to add timings.

`import "random" for Randomimport "/ioutil" for File, Inputimport "/str" for Strimport "/sort" for Find var rand = Random.new()var words = File.read("unixdict.txt").trim().split("\n") var player1 = Input.text("Player 1, please enter your name : ", 1)var player2 = Input.text("Player 2, please enter your name : ", 1)if (player2 == player1) player2 = player2 + "2" var words3or4 = words.where { |w| w.count == 3 || w.count == 4 }.toListvar n = words3or4.countvar firstWord = words3or4[rand.int(n)]var prevLen = firstWord.countvar prevWord = firstWordvar used = []var player = player1System.print("\nThe first word is %(firstWord)\n")while (true) {    var word = Str.lower(Input.text("%(player), enter your word : ", 1))    var len = word.count    var ok = false    if (len < 3) {        System.print("Words must be at least 3 letters long.")    } else if (Find.first(words, word) == -1) {        System.print("Not in dictionary.")    } else if (used.contains(word)) {        System.print("Word has been used before.")    } else if (word == prevWord) {        System.print("You must change the previous word.")    } else if (len == prevLen) {        var changes = 0        for (i in 0...len) {            if (word[i] != prevWord[i]) {                changes = changes + 1            }        }        if (changes > 1) {            System.print("Only one letter can be changed.")        } else ok = true    } else if (len == prevLen + 1) {        var addition = false        var temp = word        for (i in 0...prevLen) {            if (word[i] != prevWord[i]) {                addition = true                temp = Str.delete(temp, i)                if (temp == prevWord) {                    ok = true                }                break            }        }        if (!addition) ok = true        if (!ok) System.print("Invalid addition.")    } else if (len == prevLen - 1) {        var deletion = false        var temp = prevWord        for (i in 0...len) {            if (word[i] != prevWord[i]) {                deletion = true                temp = Str.delete(temp, i)                if (temp == word) {                    ok = true                }                break            }        }        if (!deletion) ok = true        if (!ok) System.print("Invalid deletion.")    } else {        System.print("Invalid change.")    }    if (ok) {        prevLen = word.count        prevWord = word        used.add(word)        player = (player == player1) ? player2 : player1    } else {        System.print("So, sorry %(player), you've lost!")        return    }}`
Output:

Sample game:

```Player 1, please enter your name : Paddy

The first word is pan