wordleDeducer.frink

Download or view wordleDeducer.frink in plain text format


use Deducer.frink

/** This plays Wordle using the Deducer framework.

    If you want to let it guess a target for you, you can just hit the "enter"
    key and watch it play.

    See Deducer.frink for the deducer framework.  To implement this game, we
    have to implement 3 interfaces from the Deducer framework:
     * DeducerProblem
     * DeducerRank
     * DeducerMove
*/

class WordleProblem implements DeducerProblem
{
   // Number of letters in the game
   var numChars

   // Create a new game with the specified number of letters.
   new[numChars=5] :=
   {
      this.numChars = numChars
   }
   
   /** Rank/score a particular move with respect to target and return an object
       of type WordleRank */

   rank[move is WordleMove, target is WordleMove] :=
   {
      return new WordleRank[move, target]
   }

   /** Return all possible moves as an array or enumerating expression of
       WordleMove appropriate for this problem. */

   allPossibleMoves[] :=
   {
      opts = new array
      for opt = select[lines["file:/home/eliasen/prog/mobydict/scrabble/sowpods.txt"], {|x, data| length[x] == data}, numChars]
         opts.push[new WordleMove[opt]]

      return opts
   }
}

/** This represents a move for a Wordle game. */
class WordleMove implements DeducerMove
{
   var word   // The word as a string
   var chars  // The word as an array of chars

   /** Construct a WordleMove for the specified word. */
   new[word] :=
   {
      this.word = word
      chars = charList[word]
   }

   /** Compares the goodness of this move to another move.  A move is considered
       "better" if it has more different letters. */

   compareTo[other is WordleMove] :=
   {
      return length[toSet[this.chars]] <=> length[toSet[other.chars]]
   }
   
   /** Returns a string representation of the move suitable for presenting to a
       human. */

   toString[] := word
}


/** This implements DeducerRank to represent a rank for a Wordle move.  Its
    data is an array of chars like ["B", "B", "Y", "G", "G"]
*/

class WordleRank implements DeducerRank
{
   var result // An array of chars like ["B", "B", "Y", "G", "G"]

   /** Create a new WordleRank by determining how good a move was at matching
       the specified target. */

   new[move is WordleMove, target is WordleMove] :=
   {
      targetCopy = target.chars.shallowCopy[]

      result = new array[[length[target.chars]], undef]

      for i = rangeOf[move.chars]
         if (move.chars)@i == (target.chars)@i
         {
            result@i = "G"
            targetCopy.removeValue[(move.chars)@i]
         }

      for i = rangeOf[move.chars]
         if result@i == undef
         {
            if targetCopy.removeValue[(move.chars)@i]
               result@i = "Y"
            else
               result@i = "B"
         }
   }

   /** Construct a WordleRank from a string like "BBYGG" */
   new[str] :=
   {
      result = charList[str]
   }
   
   /** Compares this rank with another rank and returns true if they are equal.
   */

   equals[other is DeducerRank] :=
   {
      return result == other.result
   }

   /** Returns a string representation of the rank for display to a human. */
   toString[] :=
   {
      return join["", result]
   }
}


/** Main play loop. */

chars = eval[input["Number of letters: ", 5]]
println["0.) Computer plays itself"]
println["1.) Human guesses computer word"]
println["2.) Computer plays outside puzzle"]
println["3.) Assistant mode"]

mode = eval[input["Mode: ", "0"]]
computerPicksWord = bitAnd[mode, 2] == 0
humanGuesses = bitAnd[mode, 1] != 0

// Play the game.
d = new Deducer[new WordleProblem[chars]]

// Pick a target play at random.
if computerPicksWord
   target = random[d.movesRemaining[]]

if mode == 0
   println["Computer picks: " + target.toString[]]

winCondition = repeat["G", chars]
guesses = 0

// The main loop.
do
{
   if humanGuesses
      move = new WordleMove[uc[trim[input["Enter guess: "]]]]
   else
      move = d.pickSmartMove[]    // Pick a smart move.

   if computerPicksWord
   {
      r1 = new WordleRank[move, target]    // Tentative rank against target
      if ! humanGuesses
         result = uc[input["Rank of " + move.toString[], r1.toString[]]]
      else
      {
         println["Rank is " + r1.toString[]]
         result = r1.toString[]
      }
   } else
      result = uc[input["Rank of " + move.toString[] + ": "]]
   
   guesses = guesses + 1
   rank = new WordleRank[result]
   d.doMove[move,rank]  // Eliminate options that can't match.

   println["\nPossible solutions remaining: " + d.numMovesRemaining[]]
   if mode == 3
   {
      for m1 = d.movesRemaining[]
         print[m1.toString[] + " "]
      println[]
   }
} while rank.toString[] != winCondition and d.numMovesRemaining[] > 1

// All green?  We guessed right last time!
if rank.toString[] == winCondition
   println["Guessed the solution correctly in $guesses guesses!"]
else                      // Otherwise, we know what the solution will be.
{
   if length[d.movesRemaining[]] == 0
      println["No words remaining.  Either the word is not in my list or I got bad feedback."]
   else
      println["Remaining solution is " + first[d.movesRemaining[]].toString[] + " after " + (guesses+1) + " guesses"]
}


Download or view wordleDeducer.frink in plain text format


This is a program written in the programming language Frink.
For more information, view the Frink Documentation or see More Sample Frink Programs.

Alan Eliasen was born 20217 days, 15 hours, 7 minutes ago.