mastermindDeducer.frink

Download or view mastermindDeducer.frink in plain text format


use Deducer.frink

/** This is an implementation of the Mastermind game using the Deducer class.
    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

    For an example that doesn't use the Deducer framework, see mastermind.frink
*/

class MastermindProblem implements DeducerProblem
{
   /* The possible colors.  You can change these to whatever you want, including
   full names.  You can also use more or fewer colors and everything will
   just work right. */

   class var colors=["G","B","Y","P","O","R"]

   // You can change from the normal 4-peg game to more or fewer pegs.
   class var numPegs = 4
   

   /** Rank/score a how a particular move would score against a target and
       return an object of type DeducerRank */

   rank[move is MastermindMove, target is MastermindMove] :=
   {
      return new MastermindRank[move, target]
   }

   /** Return all possible moves as an array or enumerating expression of
       DeducerMove appropriate for this problem.  In this case, returned values
       are of type MastermindMove which implements DeducerMove.
   */

   allPossibleMoves[] :=
   {
      // Create an array with all possible plays.
      opts = new array
      multifor x = makeArray[[numPegs], colors]
         opts.push[new MastermindMove[x]]

      return opts
   }
}


/** The DeducerRank interface describes the methods for ranking a particular
    move.  For example, a Mastermind ranking would include the count of black
    and white pegs.  For Wordle, this would indicate which letters are green,
    which are yellow, and which are black. */

class MastermindRank implements DeducerRank
{
   var black
   var white

   /** Construct a rank from a [black, white] array. */
   new[blackWhiteArray] :=
   {
      if isArray[blackWhiteArray] and length[blackWhiteArray] == 2
         [black, white] = blackWhiteArray
      else
      {
         black = blackWhiteArray.black
         white = blackWhiteArray.white
      }
   }

   /** Construct a rank by evaluating the move against the target. */
   new[move is MastermindMove, target is MastermindMove] :=
   {
      black = 0
      white = 0
      targetCopy = target.vals.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.vals
         if targetCopy.removeValue[mpiece]  // true if a piece was removed
            white = white + 1

      // Now count pieces in the correct positions.  For each one found, remove
      // one from the "white" count and add one to the "black" count.
      for i = 0 to length[target.vals]-1
         if move.vals@i == target.vals@i
         {
            white = white - 1
            black = black + 1
         }
   }
   
   /** Compares this rank with another rank and returns true if they are equal.
   */

   equals[other is DeducerRank] :=
   {
      return this.black == other.black and this.white == other.white
   }

   /** Returns a string representation of the rank for display to a human. */
   toString[] := "Black:$black  White:$white"
}


/** This represents a move for Mastermind.  The values are stored in the array
    vals.  These will have the same values as MastermindProblem.colors. */

class MastermindMove implements DeducerMove
{
   /** The array of values */
   var vals

   new[vals] :=
   {
      if isString[vals]      // Parse single-char names from a string badly
         this.vals = charList[vals]
      else
         this.vals = vals
   }
   
   /** Compares the goodness of this move to another move.  A move is considered
       "better" if it has more different colors. */

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

   toString[] := toString[vals]
}

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
guesses = 0

// Play the game.
d = new Deducer[new MastermindProblem]

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

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

// The main loop.
do
{
   if humanGuesses
      move = new MastermindMove[uc[trim[input["Enter guess: "]]]]
   else
      move = d.pickSmartMove[]    // Pick a smart move.
   
   if computerPicksWord
   {
      r1 = new MastermindRank[move, target]    // Tentative rank against target
      if ! humanGuesses
         result = eval[input["Move is " + move.toString[],
                       [["Black: ", r1.black], ["White: ", r1.white]]]]
      else
      {
         println["Rank is " + r1.toString[]]
         result = r1
      }
   } else
      result = eval[input["Move is " + move.toString[],
                       [["Black: "], ["White: "]]]]

   guesses = guesses + 1
   rank = new MastermindRank[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 result@0 != MastermindProblem.numPegs and d.numMovesRemaining[] > 1

// All green?  We guessed right last time!
if result@0 == MastermindProblem.numPegs
   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.  I may have gotten bad feedback."]
   else
      println["Remaining solution is " + first[d.movesRemaining[]].toString[] + " after " + (guesses+1) + " guesses"]
}



Download or view mastermindDeducer.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, 26 minutes ago.