/** This program plays the game Jotto. Its algorithm is very simple:
1.) Make a list of all possible plays.
2.) Choose one play at random and play it.
3.) Using the feedback your opponent gives you, eliminate all of the
plays from the list that can't match that feedback. Repeat until done.
This is intended to be used interactively, but if you just want to watch it
play, it will automatically pick a random secret pattern for you and
play for you. Just hit the "enter" key every time and the computer will
provide the correct defaults for you.
*/
allowRepeated = false
// You can change from the normal 5-letter game to more or fewer pegs.
[letters, repeated] = input["Game configuration", [["Number of letters ", 5], ["Allow repeated letters", "n"]]]
numLetters = parseInt[letters]
if repeated =~ %r/y/i
allowRepeated = true
opts = new array
// Create an array with all possible plays.
for opt = select[lines["file:/home/eliasen/prog/mobydict/scrabble/sowpods.txt"], {|x, data| length[x] == data}, numLetters]
{
letters = charList[opt]
// Allow or disallow repeated letters in word
if (allowRepeated == true) OR (allowRepeated == false and length[toSet[letters]] == numLetters)
opts.push[letters]
}
// Pick a target play at random.
target = random[opts]
target = charList[uc[input["Your secret word: ", join["", target]]]]
if length[toSet[target]] != numLetters and allowRepeated == false
{
println["Your word violates the rules of the game because it has repeated letters."]
exit[]
}
// This is a function that will be passed to "select". It just returns true
// for each move that matches the specified result pegs.
sfunc = {|move, data|
[target, result] = data
rank[move, target] == result
}
// The main loop.
do
{
println["Possible solutions remaining: " + length[opts]]
move = opts.removeRandom[] // Choose a move at random
jots = rank[move,target] // Get default results for user.
if move == target
{
println["Solved with " + join["", move]]
exit[]
}
result = input["Move is " + join["", move], jots]
if result =~ %r/y/i
{
println["Solved with " + join["", move]]
exit[]
} else
jots = eval[result]
//println["Jots is $jots"]
// Now just go through the remaining possible solutions and return only
// the ones that could have matching results.
opts = array[select[opts,
sfunc,
[move, jots]]]
// println[/*"result is $opts, " + */length[opts] + " items remaining"]
// TODO: Fix lower bailout condition
} until result == target or length[opts] == 1
if result == target // We guessed right!
println["Guessed the solution correctly!"]
else // Otherwise, we know what the solution will be.
println["Only remaining solution is " + join["",opts@0]]
/** This function determines how good a move was at matching the specified
target. It returns the number of matched letters (jots) */
rank[move, target] :=
{
jots = 0
targetCopy = target.shallowCopy[]
// First, count total number of matches in any position. As a match is
// found in any position, remove it from the list so it's not counted twice.
for mpiece = move
if targetCopy.removeValue[mpiece] // Returns true if a piece was removed
jots = jots + 1
return jots
}