So, you saw how we handled the last puzzle. It was flashy, watching the browser solve itself was a neat party trick, but it’s hella inefficient. Relying on Selenium to brute-force the DOM with clicks felt like using a sledgehammer for brain surgery. For this next stage, the puzzles got harder and the timer got drastically shorter. It was time to evolve.
As hinted before, we knew there was a backend API. It was time to stop playing with the frontend puppet and start pulling the strings directly.
API Abuse, In a cool way
Why scrape the HTML when you can just ask the server for the data directly? A quick look at the frontend javascript code revealed the endpoints we needed: /api/newpuzzle and /api/checkanswer.
The new methodology is surgical and stripped of all excess:
Direct Requisition: Send a GET request to
/api/newpuzzle. The server hands over a JSON object containing everything: puzzle dimensions, the hint URL, and a list of all the scrambled pieces, pre-encoded in Base64. No more parsing the CSS bullshit (thank God šš)Headless Hinting: The hint URL still points to the original image. We still need to resolve it. For this,
Seleniumis brought out of retirement to navigate to the URL, grab the final image source, and immediately shut down.Reference Generation: This remains the same. Download the full-resolution image, perform some calculations to crop and resize it to match the puzzle’s true dimensions, and slice it into a grid of perfect reference tiles.
The Toolbox: This is where things get interesting. Without having access to the source code (so far ;)), we don’t know the exact preprocessing the reference image went through before being sliced into tiles. This became evident with relentless testing that revealed subtle image variationsācompression artifacts and slight color and canvas shifts. These were present in the previous puzzle too, but with the pieces being even smaller in size, a single offset pixel will fool the template matching algorithm from earlier. A single strategy was no longer reliable, so why not adopt multiple of them, escalating in intensity:
- Attempt 1: Pixel Difference. A direct, pixel-by-pixel
cv2.absdiffcomparison. - Attempt 2: Perceptual Hashing. If pixel-perfect fails, switch to
imagehash. This ignores minor artifacts and compares the “fingerprints” of the images. - Attempt 3 & 4: The Nuclear Option. If both fail, we re-run them, but first apply a heavy
GaussianBlurto both the reference and current tiles. This blurs out fine details and noise, forcing the match to be based on the general color and shape of the tiles. It’s ugly, but it works when nothing else will.
- Attempt 1: Pixel Difference. A direct, pixel-by-pixel
The Verdict: Once a match is found, we don’t need to simulate clicks. We construct a JSON payload with the puzzle ID and the
answerāa simple list of integers representing the correct final positions of the initial piecesāand POST it directly to/api/checkanswer.
After submitting a solution, we can directly go onto the next puzzle, reducing the overhead time and drastically speeding up the solver.
Technical Dive
The core of the new solver is its ability to adapt its matching strategy. We cycle through four methods until one yields a correct solution from the API.
| |
This loop ensures that if a simple pixel difference fails, we escalate to perceptual hashing, and then to pre-processed (blurred) versions of both. This resilience was key to achieving a 100% solve rate against the varied puzzle sets.
The Final Submission
Gone are the ActionChains. The final step is a clean, simple API call. After linear_sum_assignment gives us the optimal mapping, we format it into the list the API expects and send it off.
| |
This new approach is brutally efficient. Each puzzle is now solved in seconds, bottlenecked only by the download speed of the source image. The diff_check method is the fastest one, usually clocking in at 5 seconds max to map each tile, making each puzzle take at most 20 seconds to breeze through.
Script
Click to expand solver code
| |
It also important to note that, for some odd reason, one of the images is slightly offset in position. I had to modify the script just for that particular puzzle
| |
Also, since this methode is entirely done without a GUI, we didn’t get the chance to admire the *cough, cough* gorgeous furry art xd, BUT YOU WILL SEE IT !

