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.