I’ve made a puzzle game about breaking open padlocks. If you just want to play the game, go play the game. Or read on for the how-and-why of its creation.
About three months ago, my friend Claire, in a WhatsApp group we both frequent, shared a brainteaser:
The puzzle was to be interpreted as follows: you have a three-digit combination lock with numbers 0-9; so 1,000 possible combinations in total. Bulls and Cows-style, a series of clues indicate how “close” each of several pre-established “guesses” are. In “bulls and cows” nomenclature, a “bull” is a correctly-guessed digit in the correct location and a “cow” is a correctly-guessed digit in the wrong location, so the puzzle’s clues are:
- 682 – one bull
- 614 – one cow
- 206 – two cows
- 738 – no bulls, no cows
- 380 – one cow
By the time I’d solved her puzzle the conventional way I was already interested in the possibility of implementing a general-case computerised solver for this kind of puzzle, so I did. My solver uses a simple “brute force” technique, as follows:
- Put all possible combinations into a search space.
- For each clue, remove from the search space all invalid combinations.
- Whatever combination is left is the correct answer.
Visualising the solver as a series of bisections of a search space got me thinking about something else: wouldn’t this be a perfectly reasonable way to programatically generate puzzles of this type, too? Something like this:
- Put all possible combinations into a search space.
- Randomly generate a clue such that the search space is bisected (within given parameters to ensure that neither too many nor too few clues are needed)
- Repeat until only one combination is left
Interestingly, this approach is almost the opposite of what a human would probably do. A human, tasked with creating a puzzle of this sort, would probably choose the answer first and then come up with clues that describe it. Instead, though, my solution would come up with clues, apply them, and then see what’s left-over at the end.
I expanded my generator to go beyond simple bulls-or-cows clues: it’s also capable of generating clues that make reference to the balance of odd and even digits (in a numeric lock), the number of different digits used in the combination, the sum of the digits of the combination, and whether or not the correct combination “ascends” or “descends”. I’ve ideas for other possible clue types too, which could be valuable to make even tougher combination locks: e.g. specifying how many numbers in the combination are adjacent to a consecutive number, specifying the types of number that the sum of the digits adds to (e.g. “the sum of the digits is a prime number”) and so on.
Next up, I wanted to make a based interface so that people could have a go at the puzzles in their web browser, track their progress through the levels, get a “score” based on the number and difficulty of the locks that they’d cracked (so they can compare it to their friends), and save their progress to carry on next time.
I implemented in pure vanilla HTML, CSS, SVG and JS, with no dependencies. Compressed, it delivers to your browser and is ready-to-play in a little under 10kB, most of which is the puzzles themselves (which are pregenerated and stored in a JSON file). Naturally, it lends itself well to running offline, so it’s PWA-enhanced with a service worker so it can be “installed” onto your device, too, and it’ll check for bonus puzzles and other updates periodically.
Honestly, the hardest bit of implementing the frontend was the “spinnable” digits: depending on your browser, these are an endless-scrolling <ul> implemented mostly in CSS and with snap points set, and then some JS to work out “what you meant” based on where you span to. Which feels like the right way to implement such a thing, but was a lot more work than putting together my own control, not least because of browser inconsistencies in the implementation of snap points.
Anyway: you should go and play the game, now, and let me know what you think. Is it worth expanding and improving? Should I leave it as it is? I’m open to ideas (and if you don’t like that I’m not implementing your suggestions, you can always fork a copy of the code and change it yourself)!
Or if you’d like to see some of the other JavaScript experiments I’ve done, you might enjoy my “cheating” hangman game, my recreation of the lunar lander game I wrote in college, or rediscover that time I was ill and came up the worst conceivable tool to calculate Pi.
This is a numeric equivalent of the Mastermind peg board game from the 70s – built a digital version of it for my 1st year computing module at university
en.wikipedia.org/wiki/Mastermin…
Read more →
@mattpep @Eskoala This is a numeric equivalent of the Mastermind peg board game from the 70s – built a… https://t.co/9WKtUsq0iO
Love it! Issue with the scoring though, if i get the hardest one just gimme all the points 😅
Read more →
@Eskoala Love it! Issue with the scoring though, if i get the hardest one just gimme all the points 😅
Read more →
Indeed, while it’s a different puzzle there’s a clear lineage.
Read more →
@ciphergoth @Eskoala https://t.co/MGQNPeiY6C
Nice! My wife and I just played through all the puzzles and really enjoyed it. A few comments:
1. The github repo is private. Otherwise I’d file issues there.
2. The ‘023: monster’ puzzle doesn’t complete for me. I have the right answer (I even checked the json) but the lock won’t open. Looks like the problem is that the inputs are `type=”number”`, but one of the answers is non-numeric.
3. How are difficulties computed? I tended to take longer on ‘medium’ puzzles with lots of bull/cow questions, whereas high-rated puzzles such as ‘insanity’ (66.6) were quick to solve because the arithmetic rules are easier for me to keep track of.
4. You should add emojis to your rules 🐂🐄. The text is a bit too verbose to easily grok.
5. I didn’t come across any rules like ‘one bull and one cow’. Is that deliberate?
1. Whoops. Public now. Can’t believe nobody mentioned before now!
2. Interesting. Shall take a look when I remember to!
3. Badly-developed heuristics. See the generator source!
4. Cool idea.
5. No; the original puzzle didn’t have any of these, and I couldn’t think of a phraseology that I thought would immediately make sense to everybody, so I didn’t bother. Not opposed to it in theory though!
I’ve added a PR fixing 2. Thanks for open sourcing!
I liked your Open The Lock Puzzle and I want you to publish (if you like it) the puzzle that appears in the cover of my new book: CAN YOU OPEN THE LOCK USING THESES CLUES? and you cand find it at: https://www.amazon.com/dp/B08L5MGYYP to see how many people can find the solution.
Thanks, Alexander.
Very nice! Much appreciated. I’m a novice at writing applications like these, and was about to try on my own, but can already see how this will be helpful if I get stuck. Puzzle games are usually what I start writing algorithms for, but I get side-tracked trying to analyze them in non-brute force ways, then feel content to not actually make anything if I do figure that out. (Of course nothing wrong with your method either; think I’m just more of a math than CS guy at heart. I should actually start making projects at some point, and your work here is inspiring.)
They’re rather fun puzzles too. Amazing how sometimes the answers just jump out at me, sometimes not so much. Some people on TikTok are amazingly quick with them.