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 19976 days, 6 hours, 25 minutes ago.