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]
   }
   
   /** 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.movesRemaining[].removeRandom[]        // Choose a move at random

   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 20152 days, 21 hours, 40 minutes ago.