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
         println["MastermindRank.new: Invalid black-white array: $blackWhiteArray"]
   }

   /** 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
   }
   
   /** Returns a string representation of the move suitable for presenting to a
       human. */

   toString[] := toString[vals]
}


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

// Pick a target play at random.
target = random[d.movesRemaining[]]
println["Suggest: " + target.toString[]]       

// The main loop.
do
{
   println["Possible solutions remaining: " + d.numMovesRemaining[]]
   move = d.movesRemaining[].removeRandom[]        // Choose a move at random
   r1 = new MastermindRank[move, target]    // Tentative rank against target
   result = eval[input["Move is " + move.toString[],
                       [["Black: ", r1.black], ["White: ", r1.white]]]]
   rank = new MastermindRank[result]
   d.doMove[move,rank]  // Eliminate options that can't match.
} while result@0 != MastermindProblem.numPegs and d.numMovesRemaining[] > 1

// All black?  We guessed right last time!
if result@0 == MastermindProblem.numPegs
   println["Guessed the solution correctly!"]
else                      // Otherwise, we know what the solution will be.
   println["Solution is " + first[d.movesRemaining[]].toString[]]



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 19974 days, 18 hours, 46 minutes ago.