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 was born 20217 days, 15 hours, 38 minutes ago.