No Let
- 13 Devlogs
- 38 Total hours
A JavaScript coding puzzle site where you aren't allowed to declare let variables or use anything with mutable state.
A JavaScript coding puzzle site where you aren't allowed to declare let variables or use anything with mutable state.
Final devlog before ship #2 
How the heck did this take 2 hours…
All that I did in this devlog was improve the Conway’s Game of Life puzzle a bit. I rewrote some of the starter and solution code for simplicity (I can’t believe I forgot about the .flatMap() method), and I also wrote a bunch of comments that explain the starter code and solution code.
Next, I think I’ll add a difficulty indicator for the puzzles. I think I’ll probably be ready to re-ship after that!
Hi! It’s been a while. Again.
The highlight of this devlog is the new puzzle: Conway’s Game of Life. In the feedback on my first ship, several people mentioned that they wanted more challenging levels. Well, here ya go! This puzzle is much more complex, and it also doesn’t lend itself well to functional programming, so you have to get creative in order to make the linter happy. Personally, after I designed the puzzle and wrote the starter code, it took me over 45 minutes to write the solution code. It’s pretty difficult, so hopefully it’ll satisfy the users who want a challenge.
I also made a few smaller changes:
Next, I think I’ll start adding comments to some of the starter code and solution code. I think it’d be helpful for users who aren’t as familiar with JavaScript or function programming, so that they can more easily get acquainted with the starter code, and so that they can learn how the solution works if they choose to use it.
I made a new puzzle! The puzzle involves implementing a Caesar cipher. That’s it, actually. It’s certainly not a very difficult puzzle, but it does contribute to puzzle variety.
Also, do you notice the new favicon? No more Svelte logo!
…i think the user would have more feedback/satisfaction if upon completion, something else happens to let them know they successfully completed a puzzle, something like confetti, or even just the “run” button changing color/text
When the puzzle states changes to “passed” (meaning the logic test and linter both passed) from some other status, I made it trigger a confetti effect via canvas-confetti and made the run button turn green.
Next, I’m probably going to make a favicon.
One thing: I can just cheese everything by changing the output manually. I recommend you use multiple test cases so i can not just type “return 6” and complete the task.
I added secretInput values to the rest of the puzzles to fix the hardcoding cheese. I also mentioned the anti-hardcoding in the README.
Next, I’m going to work on adding some confetti and whatnot for when the user’s code passes both the logic test and the linter.
Hi again! It’s been a while. I’m coming back to No Let because when I shipped it the first time, the lovely people who rated it took the time to write really thoughtful feedback and suggest great actionable improvements. So i thought it’d be fun to implement your suggestions (and also I was bored).
Here are some excerpts from the feedback I got:
One thing: I can just cheese everything by changing the output manually. I recommend you use multiple test cases so i can not just type “return 6” and complete the task.
Anyway, my biggest critique would be the lack of levels. I think that adding tiers of difficulty, along with 10-15 more levels would take this project to the highest possible level.
…svelte favicon ;)…
…i think the user would have more feedback/satisfaction if upon completion, something else happens to let them know they successfully completed a puzzle, something like confetti, or even just the “run” button changing color/text
I intend to fix/implement each one of these. With the exception of the “10-15 levels” one; that’s a bit more than I’m willing to make, but I will add some new puzzles.
So far, I’ve started implementing the first suggestion. I’m creating secretInput values for each puzzle, and in the test function, after it evaluates the input (which is publicly displayed), it runs the code again using the secretInput.
If the code passes the input check but fails the secretInput check, it fails the logic test with this message:
Failed secret anti-hardcoding check. You need to generalize your logic.
I’ve only added secretInputs for two puzzles so far, so the work is still ongoing.
I wrote the README and also fixed a minor performance bug.
Because Prism code editor only initializes once Svelte finishes hydrating, there’s a delay of a few hundred milliseconds between when the page becomes visible and when the editor initializes. This caused a layout shift on each puzzle page, which was making Lighthouse take away a few points in the performance category. So I precalculated the height of each puzzle editor in advance and applied that minimum height to the placeholder text. This fixed the layout shift, so now Lighthouse gives the site perfect 100s in performance, best practices, and SEO, which is cool.
I just added some error handling for infinite loops and infinite recursion.
Before, if you wrote while (true) {} in the editor and pressed run, it would permanently lock up the UI and crash the browser tab. Now the logic test just interrupts the execution after 1000ms and fails your code for taking too long.
Likewise, if you wrote const r=()=>r();r(); and ran it, QuickJS would silently throw an uncaught error and the logic test would be stuck at “Waiting…” forever. Now it just instantly fails the code because it recurses too deeply and QuickJS can’t execute it.
I’m probably going to ship No Let soon, unless I have more puzzle ideas, find more bugs to patch, or think of more features to add.
I spent the last 3.5 hours making a single puzzle. It’s the hardest puzzle yet and took the most time to create, and by a lot.
The puzzle is called Headcount, and it involves extracting a total count from a deeply-nested data object. I considered using nested arrays instead, but that would have a pretty trivial solution: return input.flat(Infinity).length. JS has far fewer built-in utilities for manipulating objects than it does for arrays, so I figured that using objects would pose more of a challenge, which I think it does.
You can play the new puzzle here: https://ethmarks.github.io/nolet/headcount.
I added more puzzles and wrote a congrats page for users who finish the whole site.
The congrats page finally gave me the excuse to reference xkcd 1312, which I’d been dying to do since I started this project.
Next, I just need to make more puzzles. I have 4 so far, which I don’t think is nearly enough. Puzzle design is really hard though.
I’ve made a lot of progress since my last devlog. I’d say that No Let is basically feature-complete now. All that’s left to do is adding polish and creating a bunch more puzzles.
You can try No Let right now at https://ethmarks.github.io/nolet/.
Here are some of the highlights of the changes in this devlog.
Output.svelte component that runs the logic test and the linter and outputs their results into a human-readable HTML list. I also made it use Svelte’s slide transition, which required a lot of refactoring.But most of the work I did has been refactors, fixes, and creating new puzzles.
So my idea for this project is that it’s a JavaScript puzzle site which forces you to use pure functional programming, which means that you aren’t allowed to use let or anything else with mutable state.
Basically, pure functional programming says that you should never ever reassign variables after you declare them. Ever. Because you can’t reassign variables, you also can’t increment counters, which means that it’s impossible to use for loops. The same logic applies to while loops, and to a few other things. These extremely restrictive self-imposed limitations force you to do things the functional programming way, which involves lambda calculus, recursive functions, currying, etc.
I think that this project will have four main parts. I’ve been working on it for about 7 hours and I’ve made some good progress, but there’s still a lot to do.
Math.random() that are considered “impure” by functional programming because they’re non-deterministic.let. I can use acornjs to parse the code into an AST, then applying a bunch of custom rules to it.let and whatnot.Right now I have minimal versions of parts 1, 2, and 3. They do work (mostly), but they’re missing features and don’t integrate well with each other.