When I set out to create LocalOne Crosswords, I thought, "How hard can it be to make a crossword app?" Turns out, I was about to learn—a lot!
Finding the Words
First off, every LocalOneLabs app must work fully offline. No servers, no data tracking. That means I couldn't fetch puzzles from the internet daily. I needed a massive offline library of words and clues.
I had three choices:
- Buy a puzzle pack license
- Use a puzzle generator
- Gather public domain puzzles manually
I opted for puzzle generation. By bundling a large database of words and clues into the app, I could procedurally generate infinite puzzles offline. After some searching, I discovered Saul Pwanson's incredible XD dataset—a collection of over 6 million crossword clues and answers, sourced from various publications. (Thanks, Saul!)
UPDATE: March 5, 2025
After publishing this post, I learned about potential copyright concerns with using the XD dataset. To ensure LocalOne Crosswords respects intellectual property rights, I've completely replaced the dataset with 100% AI-generated clues. You can read about this process in my new blog post about creating AI-generated crossword clues.

LocalOne Crosswords
Daily crossword puzzles that work completely offline. No internet required!
Building My Crossword Engine
I wrote a C script to structure this huge dataset into a simple dictionary:
{
word1: [clue1, clue2, ...],
word2: [clue1, ...],
...
}
Next up: Puzzle difficulty. How do you decide what's easy or hard? Initially, I tried something straightforward—rank words based on length and usage frequency. Short and frequently-used words are easy, right? Well, not always. There were tons of edge cases.



Introducing AI for Difficulty Levels
To improve accuracy, I took each word-clue pair and fed them into a large language model (LLM), batch-processing 100 words at a time. After 10 hours running overnight, I had a robust classification of words into Easy, Medium, and Hard categories.
Generating the Puzzles
Now came the hardest part: generating puzzles dynamically. My first brute-force attempt used random longest words to start, built intersections, and filled the grid to about 80% density. After some tweaks, I released version 1.0.
Early User Feedback (and a Reality Check!)
Initial user feedback was eye-opening:
- "Crosswords should be symmetrical!"
- "Grid sizes should be 15x15, not 10x10!"
- "Density needs to be higher—we should have around 60 words, not 20!"

Before: 10x10 Grid

After: 15x15 Professional Grid
Clearly, crossword aficionados expected more! This was fantastic feedback that pushed me to do better.
Leveling Up the Algorithm
I revamped the grids to the standard 15x15, made UI/UX improvements, but my existing algorithm couldn't achieve symmetry or higher density.
After 10 frustrating hours researching different approaches, I discovered Michael Wehar's heuristic CSP algorithm—an advanced solution designed specifically for crossword puzzles. Thanks to Michael's excellent documentation, I transposed the Python logic into Swift.
Several more hours of coding and debugging later, LocalOne Crosswords 1.1 was finally complete—symmetrical, 90% density, and professional-grade puzzles!
Lessons Learned
Creating LocalOne Crosswords wasn't just a project—it was a journey filled with unexpected twists, deep learning, and valuable user insights. The result? An app I'm proud of, and a reminder that building something great is never as simple as it first seems—but always worth the effort.

Constantly Improving
User feedback has been crucial in making LocalOne Crosswords better with each update.
Thanks for reading!
Warmly,