A popular KenKen variant is the “no-op” puzzle, in which only the result of a cage is given. (In “standard” KenKen, both the result and an operator are given.) A reader wrote in with a question about a no-op puzzle, so I took a look at extending my Python solver to handle this variant. It turned out to be pretty easy.
An Example
Here’s an example of a 4×4 no-op puzzle. I’m using the notation I developed earlier, with the minor extension that “?” represents an unknown operator.
# 4
? 3 A1 A2
? 5 A3 A4
! 3 B1
? 8 B2 B3 B4
? 2 C2 C3
? 7 C4 D4
? 6 D1 D2 D3
This looks something like:
+----+----+----+----+
|3 |5 |
| | |
+----+----+----+----+
|3 |8 |
| | |
+----+----+----+----+
| |2 |7 |
| | | |
+----+----+----+ +
|6 | |
| | |
+----+----+----+----+
Constraints
Our solver uses constraints to answer the following question:
Given what we know (or assume) about the puzzle, is it possible, under any set of circumstances, for a given value to appear in a given cell?
We answer this question, for the mathematical Constraints
we’ve seen so far, with a _test_component()
member function, which takes as its arguments the component to be tested, and a tuple of tuples of all other possible values in the cage, grouped by cell. Each type of Constraint
has its own version of this function, customized for the Constraint
‘s mathematical operation.
We can handle no-op cages in much the same way, by asking a more liberal question; instead of asking whether the _test_component()
function associated with a particular operation would permit a given value in a given cell, we ask whether the _test_component()
function associated with any operation would permit the value to appear.
Code
I began with the last version of the solver that I published (in this article), moved a little code around, and added a new constraint. The new no-op constraint looks like this:
class Noop(Constraint):
def __init__(self, value, *cells):
Constraint.__init__(self, value, *cells)
if (len(self.cells) < 2): raise Exception('Noop constraints must be applied to 2 or more cells')
def _test_component(self, component, context):
return self._test_component_sum(component, context) or \
self._test_component_diff(component, context) or \
self._test_component_prod(component, context) or \
self._test_component_div(component, context)
You can also download the complete latest version of the solver here.
Pingback: Things that were not immediately obvious to me » Blog Archive » NekNek Update