Download or view nerdleDeducer.frink in plain text format
use Deducer.frink
/** This plays Nerdle using the Deducer framework.
https://nerdlegame.com/
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
*/
class NerdleProblem implements DeducerProblem
{
// Number of letters in the game
var numChars
// Create a new game with the specified number of letters.
new[numChars=8] :=
{
this.numChars = numChars
}
/** Rank/score a particular move with respect to target and return an object
of type NerdleRank */
rank[move is NerdleMove, target is NerdleMove] :=
{
return new NerdleRank[move, target]
}
/** Return all possible moves as an array or enumerating expression of
NerdleMove appropriate for this problem. */
allPossibleMoves[] :=
{
filename = "nerdle$numChars.txt"
f = newJava["java.io.File", filename]
if ! f.exists[] or f.length[] == 0
generateMoves[]
opts = new array
for opt = lines[f]
opts.push[new NerdleMove[opt]]
return opts
}
/** If no moves have been calculated for this game, calculate them and
save them to a file. */
generateMoves[] :=
{
num = toArray["0" to "9"]
numNonZero = toArray["1" to "9"]
ops = ["+", "-", "*", "/"]
opSet = toSet[ops]
numOrOp = concat[num, ops]
opts = new array
w = new Writer["nerdle$numChars.txt", "UTF-8", false, 32768]
wd = new Writer["nerdle${numChars}d.txt", "UTF-8", false, 32768]
for len=2 to numChars-2
{
args = new array
args.push[numNonZero]
for i=1 to len-2
args.push[numOrOp]
args.push[num]
VAL:
multifor val = args
{
left = joinstr[val]
len1 = length[left]
lastop = false
for i=1 to len1-2
{
c = substrLen[left, i, 1]
if opSet.contains[c]
if lastop
next VAL i
else
lastop = true
else
lastop = false
}
for [numeric] = left =~ %r/([0-9]+)/g // No lone zero on left
if left[numeric, 1] == "0"
next VAL
right = eval[left]
if ! isInteger[right] or right < 0 // Positive integers only on right
next VAL
eq = left + "=" + right
if length[eq] != numChars // Make sure solution is right length
next VAL
if eq =~ %r/[^0-9]0[0-9]/ // Number won't start with 0
next VAL
opts = new array
w.println[eq]
print[eq]
if allDifferent[charList[eq]]
{
wd.println[eq]
println[" *"]
} else
println[]
opts.push[new NerdleMove[eq]]
}
}
w.close[]
wd.close[]
}
}
/** This represents a move for a Nerdle game. */
class NerdleMove implements DeducerMove
{
var word // The word as a string
var chars // The word as an array of chars
/** Construct a NerdleMove for the specified word. */
new[word] :=
{
this.word = word
chars = charList[word]
}
/** Compares the goodness of this move to another move. A move is considered
"better" if it has more different symbols. */
compareTo[other is NerdleMove] :=
{
return length[toSet[this.chars]] <=> length[toSet[other.chars]]
}
/** Returns a string representation of the move suitable for presenting to a
human. */
toString[] := word
}
/** This implements DeducerRank to represent a rank for a Nerdle move. Its
data is an array of chars like ["B", "B", "Y", "G", "G"]
*/
class NerdleRank implements DeducerRank
{
var result // An array of chars like ["B", "B", "Y", "G", "G"]
/** Create a new NerdleRank by determining how good a move was at matching
the specified target. */
new[move is NerdleMove, target is NerdleMove] :=
{
targetCopy = target.chars.shallowCopy[]
result = new array[[length[target.chars]], undef]
for i = rangeOf[move.chars]
if (move.chars)@i == (target.chars)@i
{
result@i = "G"
targetCopy.removeValue[(move.chars)@i]
}
for i = rangeOf[move.chars]
if result@i == undef
{
if targetCopy.removeValue[(move.chars)@i]
result@i = "Y"
else
result@i = "B"
}
}
/** Construct a NerdleRank from a string like "BBYGG" */
new[str] :=
{
result = charList[str]
}
/** Compares this rank with another rank and returns true if they are equal.
*/
equals[other is DeducerRank] :=
{
return result == other.result
}
/** Returns a string representation of the rank for display to a human. */
toString[] :=
{
return joinstr[result]
}
}
/** Main play loop. */
chars = eval[input["Number of letters: ", "8"]]
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
// Play the game.
d = new Deducer[new NerdleProblem[chars]]
// Pick a target play at random.
if computerPicksWord
target = random[d.movesRemaining[]]
if mode == 0
println["Computer picks: " + target.toString[]]
winCondition = repeat["G", chars]
guesses = 0
// The main loop.
do
{
if humanGuesses
move = new NerdleMove[uc[trim[input["Enter guess: "]]]]
else
move = d.pickSmartMove[]
if computerPicksWord
{
r1 = new NerdleRank[move, target] // Tentative rank against target
if ! humanGuesses
result = uc[input["Rank of " + move.toString[], r1.toString[]]]
else
{
println["Rank is " + r1.toString[]]
result = r1.toString[]
}
} else
result = uc[input["Rank of " + move.toString[] + ": "]]
guesses = guesses + 1
rank = new NerdleRank[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 rank.toString[] != winCondition and d.numMovesRemaining[] > 1
// All green? We guessed right last time!
if rank.toString[] == winCondition
println["Guessed the solution correctly in $guesses guesses!"]
else // Otherwise, we know what the solution will be.
{
if length[d.movesRemaining[]] == 0
println["No solutions remaining. Either the solution is not in my list or I got bad feedback."]
else
println["Remaining solution is " + first[d.movesRemaining[]].toString[] + " after " + (guesses+1) + " guesses"]
}
Download or view nerdleDeducer.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, eliasen@mindspring.com