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.
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 | | | | | +----+----+----+----+
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.
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.