Rudy’s OBTF Rudolf Adamkovič

Home / Computer science / N-queens / Solve with home-grown SAT solver


Constraints: Diagonal threats

Ensure at most one queen on every increasing and decreasing diagonal,

\begin{align*} \forall (i, j) \> \forall(i' > i, j' > j) \quad & (i - j = i' - j') \implies (\lnot x_{ij} \lor \lnot x_{i'j'}) \\ \forall (i, j) \> \forall(i' > i, j' > j) \quad & (i + j = j' + j') \implies (\lnot x_{ij} \lor \lnot x_{i'j'}), \end{align*}

derived from the constant difference between rows and columns on diagonals

\begin{equation*} \begin{aligned} i' - i & = j' - j \\ i' + i & = j' + j \end{aligned} \iff \begin{aligned} i - i' & = +j - j' \\ i - i' & = -j + j' \end{aligned} \iff \begin{aligned} \Delta i & = + \Delta j \\ \Delta i & = - \Delta j \end{aligned} \iff \Delta i = \pm \Delta j \, . \end{equation*}
(defun my-queens-not-sharing-diagonal-clauses (size &optional transpose)
  "Return CNF clauses enforcing at most one queen on decreasing diagonals.

Implements constraints for SIZE-queens puzzle.  With non-nil TRANSPOSE,
enforce at most most queen on increasing diagonals instead of decreasing
diagonals.

The result is a list of lists containing literals, non-zero integers
representing variables, possibly negated.  All returned clauses and
literals are sorted for testability and reproducibility."
  (sort (mapcan (pcase-lambda (`(,variable ,row ,column))
                  (mapcan (pcase-lambda (`(,variable* ,row* ,column*))
                            (when (or (and transpose
                                           (> row* row)
                                           (< column* column)
                                           (= (+ row column)
                                              (+ row* column*)))
                                      (and (not transpose)
                                           (> row* row)
                                           (> column* column)
                                           (= (- row column)
                                              (- row* column*))))
                              (list (sort (list (- variable)
                                                (- variable*))))))
                          (my-queens-variables size)))
                (my-queens-variables size))))

(ert-deftest my-queens-not-sharing-diagonal-clauses/size-2 ()
  (should (equal (my-queens-not-sharing-diagonal-clauses 2)
                 '((-4 -1)))))

(ert-deftest my-queens-not-sharing-diagonal-clauses/size-2/anti-diagonal ()
  (should (equal (my-queens-not-sharing-diagonal-clauses 2 t)
                 '((-3 -2)))))

(ert-deftest my-queens-not-sharing-diagonal-clauses/size-3 ()
  (should (equal (my-queens-not-sharing-diagonal-clauses 3)
                 '((-9 -5)
                   (-9 -1)
                   (-8 -4)
                   (-6 -2)
                   (-5 -1)))))

(ert-deftest my-queens-not-sharing-diagonal-clauses/size-3/anti-diagonal ()
  (should (equal (my-queens-not-sharing-diagonal-clauses 3 t)
                 '((-8 -6)
                   (-7 -5)
                   (-7 -3)
                   (-5 -3)
                   (-4 -2)))))

(ert-deftest my-queens-not-sharing-diagonal-clauses/size-4 ()
  (should (equal (my-queens-not-sharing-diagonal-clauses 4)
                 '((-16 -11)
                   (-16 -6)
                   (-16 -1)
                   (-15 -10)
                   (-15 -5)
                   (-14 -9)
                   (-12 -7)
                   (-12 -2)
                   (-11 -6)
                   (-11 -1)
                   (-10 -5)
                   (-8 -3)
                   (-7 -2)
                   (-6 -1)))))

© 2025 Rudolf Adamkovič under GNU General Public License version 3.
Made with Emacs and secret alien technologies of yesteryear.