Part 1: Decisions, Decisions
I’ve designed and released a few apps for iOS over the years, but I’ve never developed one by request – until now. Pam, my wife, likes brain game apps, from CandyCrush to Sudoku and more. One genre that has her hooked lately is Nonograms. However, none of the apps she tried were quite what she wanted for gameplay. When I retired in April, she asked if I might consider developing a better Nonogram for her. And thus I started work on my first commissioned app, which I would ultimately name LogiSketch.
While this would be an app dedicated to my wife, I also aimed to place it in the App Store so others could enjoy it as well. Whether designing for one customer or a million, this work provided an opportunity to think through practical implications of my Christian faith on engineering design.
For context, let me briefly describe nonograms. A grid of cells represents a picture once the correct cells are filled in. The picture is quite blocky, but a good puzzle produces a recognizable drawing. The challenge is knowing which cells to fill based on clues. Each row has a set of number clues and each column has a set of number clues. The numbers represent the number of cells in a row that are filled in. For example, if the row clues are “2 4 2” that means that row contains a run of 2 blocks, a run of 4 blocks, and a run of 2 blocks. We know there is at least one empty cell in between each, but do not know whether there is more than one. If the row happens to be 10 cells wide, then we would know for certain that only a single empty cell can separate them, since that adds up to 2 + 1 + 4 + 1 + 2 = 10, the total width. But if the row is 15 wide, now there are many possible combinations, requiring more sophisticated logic to solve.With that quick background, let’s look at three interesting obstacles I faced during development and how each turned into an opportunity.
Three Challenges
During development of the new app, I encountered plenty of issues to solve, but I’ll highlight just three of them here. One challenge was a problem of scale for the developer; another was a problem of scale for the user. The third challenge was the difficulty of measuring difficulty.
Challenge 1: Developer Scale
The challenge of scale for me, the developer, was designing a large number of interesting, solvable nonogram puzzles. A strong player could solve multiple puzzles a day, meaning I needed hundreds or probably thousands of puzzles available. Designing them all completely by hand would be onerous. Like any good software engineer, I turned to automating as much of that task as possible.
Solving the problem of generating a large number of puzzles by using automation also led to one of the distinguishing features of the app: all puzzles would be generated from real photos. This in turn addressed a deficiency Pam had noticed in some of the other nonogram apps. When she finished some puzzles, it was difficult to determine what the picture actually represented. The pictures are of course very blocky, so simply knowing what it should be often is enough to trigger visual recognition. So I would do both: reveal the source photograph and name the subject. This approach was also the genesis for the theme of the app: a museum. Each picture is revealed as if it is framed art. Collections of puzzles are called galleries, wings, and so forth.
Converting a high resolution image thousands of pixels across into a blocky image dozen cells across did not work well with my first attempt, nor my second. It took many tries to get it right. I wrote the puzzle generator code in Python, allowing a quick turn around for trying out ideas since it is an interpreted language that can be run immediately, rather than waiting for a compile. In addition, there were existing libraries for image processing, solving algorithms, and more. I experimented quite a few times to narrow down the sorts of image pre-processing, filtering, and post-processing that could do the job. I did some simple Design of Experiments (carefully selected variations of the driving parameters to identify which most influenced the end goal of a recognizable, uniquely solvable puzzle). Even with all this, I still ended up with dozens to hundreds of candidate puzzles from one image. So I also designed a Python tool to help me quickly review the candidates and down-select. This means the final puzzles are all still checked by hand.
I also wrote some Python code to use the Z3 solver to ensure the puzzle was uniquely solvable. Since the puzzles are created from a picture, I start with the solution of filled-in cells, then reverse engineer the row and column clues. If the solver cannot find a solution based on those clues, something is wrong (since we *know* there is a solution). After working out a few bugs, the solver consistently confirms the puzzle is solvable. But then I add that first solution as a constraint and have the solver try for a second solution that does not match the first. If it finds one, the solution is not unique and I throw out that particular puzzle variant.
Every puzzle is listed in a gallery with a title and description that intentionally avoids directly naming (giving away) the subject. This adds a bit of mystery for the player to uncover using the numerical clues at first, but perhaps the narrative clues as the subject comes into view. For thousands of puzzles, I used some support from AI to generate candidate clues, though it took a bit of prompt engineering to get strong candidates (fun, interesting facts about the subject without directly naming it). This worked well over 90% of the time, allowing me to quickly choose a candidate title/description and do a bit of final copy edit. Some of the AI descriptions surprised me or made me suspicious. I needed to carefully review and fact check in any questionable case, which occurred perhaps in 1 out of 10 descriptions. Where none of the AI-generated candidate descriptions were suitable, I filled those in myself.
I now had the means to generate thousands of quality puzzles, with a variety of sizes. The largest ones posed the next challenge, this one a customer-facing obstacle that my puzzle solving users would run into.
Challenge 2: User Scale
The second challenge was also of scale, but now for the user. Many of the existing nonogram apps are limited to puzzles that are at most 15 cells wide or high. I wanted workable game play on puzzles of that size, but also for much larger puzzles that did not fit on a small screen.
My first thought was to make the cell size configurable. That gave me only minimal benefit, going from a puzzle 10 cells across to a puzzle 15 across. Anything larger and the cells became unusably small. I kept the cell configurability for accessibility reasons, but continued looking for a better solution for large puzzles.
My puzzles would be as much as 100 cells in either or both directions. My next solution was to allow the puzzle to be scrolled, so that you still had a manageable 15x15 grid at any one time in view, but it could now be part of a larger puzzle. A small “minimap” feature then allowed the user to see the entire picture forming anytime they scrolled to another region of the puzzle. That solved the main problem.
However, sometimes solving one thing breaks another. Scrolling is intuitive on mobile devices: one simply drags with one finger on the screen. But for the apps with a fixed small puzzle, they usually used that one-finger drag gesture to allow the user to easily fill a string of cells along one direction. I tried a couple different approaches with feedback from my trusted tester. I ended up using the pinch-out gesture for filling many cells at once, and this also allowed filling a block, not just one row or one column at a time.
Now I could provide a nice variety of puzzle sizes. Two refinements arose during subsequent testing. First, Pam found that when scrolling across a large puzzle, it was hard to keep track of which row you really were looking at, since you could no longer see the edges where the clues were. I solved this by adding a feature to lock a row (or column) so that its clues were visible even when you scrolled away from an edge.
Even with this, I wondered: would some users still find the gargantuan puzzles just too daunting? It would be nice to give them an optional approach. This reminded me of an old riddle.
How do you eat an elephant?
One bite at a time.
This led to the other refinement, a “panel” feature. As an option, the user can solve the puzzle in panels that are 10 or 15 cells in each direction, with clues for only the active panel. That also gives a better sense of progress knowing you have some panels finished.
I now had a collection of quality puzzles with a wide variety of sizes. This is when I noticed that other nonogram apps treated large puzzles as harder. But that turned out to be only part of the story.
Challenge 3: Measuring True Difficulty
I became aware of the third challenge while working on the second challenge of large puzzles. Although large puzzles take longer to fill in, that does not mean they are intellectually more difficult. Some large puzzles can be solved with rudimentary methods; some small puzzles require much more sophisticated solution techniques.
I browsed several existing nonogram apps and found that most of them simply labeled larger puzzles as harder. This is partially true – a larger puzzle has more cells to fill. But that is not the whole story. I thus set out to find a better difficulty metric that was not tied solely to size.
My first attempt at a metric tried to re-use my existing code with the Z3 solver. I simply measured how hard the solver “tried” when solving the puzzle. But the Z3 solver is not all that smart; it is simply exhaustive. The number of possible solutions it tries can quickly number in the millions. On a decent laptop the solver can finish in sub-seconds for a small puzzle, minutes on a large puzzle. But exhaustive search of the solution space is not the way humans solve a puzzle. I needed something more realistic as my difficulty measure.
To get a more realistic measure of the human difficulty, I turned to some of the nonogram tutorials on the web. These provided the typical strategies that a beginner, intermediate, or advanced player might use. This was just what I needed. I coded these strategies and then categorized my puzzles based on whether they could be solved only with easy methods, only with easy + medium methods, or required the hard methods to solve. After several rounds of refinement, I found that very few puzzles could be completely solved with only the beginner methods. In order to roughly divide my puzzles into three buckets, I adjusted my metric so that if a large fraction of the puzzle could be solved using the beginner methods, then I would rate it as easy. If a large fraction could be solved with beginner + intermediate methods, I would rate it as medium. The remainder of the puzzles I then rated as hard.
Hidden in each of these challenges were decisions, selecting one approach among many possibilities. That selection was not based solely on what would work, but what would work best. Defining what we mean by “best” is the point of the rest of this article. Let’s start with the traditional way to compare and judge design alternatives: design criteria.
Decision Criteria
The prior section described three challenges that required some design decisions and refinement. And that is how any engineering design goes. Almost all problems we solve have more than one solution. We must thus weigh the different approaches based on something more than simply “it works”.
Beyond correctness, other factors we consider are sometimes more formally called design (or decision) criteria. These criteria typically include factors such as cost, time to market, maintainability, recyclability, safety, and more.
These criteria are all good things. But they sometimes conflict. Improving one can degrade another. For example, we can improve safety in an automobile design by including more metal in the frame, which acts like armor to protect occupants during a crash. But that extra armor increases the initial cost of the vehicle. It also has ongoing costs, for the driver via decreased gas mileage and for all of us through a bigger environmental footprint. And so it goes. The design team must balance the various “goods” in their design. Some engineering disciplines (such as software engineering) sometimes fall into the trap of thinking the first solution imagined is the only one and miss that there are important alternatives that should be considered. Every architectural pattern, library choice, and error-handling scheme involves tradeoffs. Choosing the first solution that comes to mind isn't the absence of a decision. It is simply an unexamined one.
Once we have identified the decisions we must make, and then identify the design criteria that should help us weigh and balance alternative decisions, the heavy lifting is done. The next step, actually making the choice, might seem rather mechanical and deterministic. Some might even use a decision matrix to tabularize the decision. But even if we use some math or spreadsheets to apply our weights, the decision is never purely mechanical and never predetermined. Those criteria represent our values, individually and collectively. The weight we put on factors such as safety or cost expose what we see as important in life, uncovering our world view as we make the more difficult trade-offs.
Because these engineering design trade-offs depend on our values, our Christian faith must come into play directly in our work. Not just how we behave around co-workers. Not just in how we talk. Directly in our engineering decisions. Since it is rare for a single engineer to make all decisions about a product, these value judgments usually get decided as a team. When we surface these decisions and the values that drive them, making them explicit, we also uncover an opportunity for witness. It is an opportunity to talk about values arising from faith.
This is the end of Part 1. I hope these ideas come to mind during the next design project you tackle. I encourage you to look for the decisions inherent in your work. Furthermore, recognize the opportunities to apply your faith and witness in a winsome way that benefits both your product and your co-workers. In part 2 I’ll suggest some additional criteria that you won’t often see in an engineering textbook, but are important to consider when looking at the full perspective of an engineering design. These are the so-called design norms. We will look at applying a few of them (justice, clarity, and more) to the development of the LogiSketch app.







![[Portcullis image Kevin King from Pensacola, FL, US of A, CC BY 2.0 <https://creativecommons.org/licenses/by/2.0>, via Wikimedia Commons]](https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhwaLCbEjProyZL-lhBRnerH6eCdfjKaZYvBpbCzvi62vMuCW1F2KpIpeQ61Ejf70datT7uKof41EBgQRoWZDDUxEN9sAdry1Q95W1EtntVOHOakAFh0r23CU_iysQ1Ko__EF6fyYAkio3RxK1kAIT8ghXFas-4wqqbYbb6pvJxyKrg8RROdA94n6D1eGVp/w320-h209/Cahir_Castle_Portcullis_by_Kevin_King.jpg)







