/** This program cracks a lock from a puzzle posted here: https://twitter.com/vardi/status/1164204994624741376 The solution is based on the mastermind.frink program that plays the game Mastermind. Its algorithm is very simple: 1.) Make a list of all possible combinations 2.) Take one given result and eliminate all of the plays from the list that can't match that feedback. Repeat until done. */ // The possible numbers. You can change these to whatever you want, including // full names. You can also use more or fewer numbers and everything will // just work right. colors=array[0 to 9] // You can change from a 3-digit combination to an arbitrary number of pegs. numDigits = 3 // Create an array with all possible plays. opts = new array multifor x = makeArray[[numDigits], colors] opts.push[x] // This is a function that will be passed to "select". It just returns true // for each combination that matches the specified result. sfunc = {|combination, data| [target, result] = data rank[combination, target] == result } plays = [[[6,8,2], [1,0]], [[6,1,4], [0,1]], [[2,0,6], [0,2]], [[7,3,8], [0,0]], [[7,8,0], [0,1]]] // The main loop. for [combination, result] = plays { print["Possible solutions remaining: " + length[opts]] println["\tTesting \$combination"] // Now just go through the remaining possible solutions and return only // the ones that could have matching results. opts = array[select[opts, sfunc, [combination, result]]] } println["Solution[s] are " + opts] /** This function determines how good a combination was at matching the specified target. It returns the number of black and white pegs as the array [black, white] */ rank[combination, target] := { black = 0 white = 0 targetCopy = target.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 = combination if targetCopy.removeValue[mpiece] // Returns 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]-1 if combination@i == target@i { white = white - 1 black = black + 1 } return [black,white] }