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[]]