2 min read

Solving Kakurasu using Z3

Table of Contents

Kakurasu

Kakurasu is played on a rectangular grid with no standard size. The goal is to make some of the cells black in such a way that:

  1. The black cells on each row sum up to the number on the right.
  2. The black cells on each column sum up to the number on the bottom.
  3. If a black cell is first on its row/column its value is 1. If it is second its value is 2 etc.

Here is a 9×99\times9 hard Kakurasu puzzle

Python code using Z3

from z3 import *
import seaborn as sns
sns.set()

class KakurasuSolver:
    def __init__(self, n, puzzle, outputfilename):
        self.n = n
        self.input = puzzle
        self.output = outputfilename
        self.X = [[Int("x_%s_%s" % (i+1, j+1)) for j in range(n)]
                      for i in range(n)]
        self.s = Solver()
        self.s.add([And(0 <= self.X[i][j], self.X[i][j]<= 1) for i in range(n) for j in range(n)])

    def set_constraints(self):
        for d, n, s in self.input:
            if d == "C":
                self.s.add(And(sum([(i+1)*self.X[i][n-1] for i in range(0,self.n)]) == s))
            if d == "R":
                self.s.add(And(sum([(i+1)*self.X[n-1][i] for i in range(0,self.n)]) == s))

    def output_solution(self):
        m = self.s.model()
        data = [[int(str(m.evaluate(self.X[i][j])))*-1 for j in range(self.n)] for i in range(self.n)]
        snsplot = sns.heatmap(data, square=True, linewidths=1.0, xticklabels=False, yticklabels=False, cbar=False)
        snsplot.get_figure().savefig(self.outputfilename + ".png")

    def solve(self):
        self.set_constraints()
        if self.s.check() == sat:
            self.output_solution()
        else:
            print(self.s)
            print("Failed to solve.")

puzzle = [
    ("C",1,18),
    ("C",2,4),
    ("C",3,14),
    ("C",4,23),
    ("C",5,8),
    ("C",6,20),
    ("C",7,24),
    ("C",8,39),
    ("C",9,33),
    ("R",1,8),
    ("R",2,27),
    ("R",3,23),
    ("R",4,33),
    ("R",5,34),
    ("R",6,1),
    ("R",7,28),
    ("R",8,24),
    ("R",9,30),
]

outputfilename = "kakurasu_sol"
ks = KakurasuSolver(9, puzzle, outputfilename)
ks.solve()

Solution

The solution for above puzzle is given below