\myheading{Cracking Minesweeper with SAT solver and sorting network}
\label{minesweeper_sorting_network}

\renewcommand{\CURPATH}{equations/minesweeper/5_SAT_SN}

The SAT version of this program used Mathematica-generated POPCNT functions: \ref{minesweeper_SAT}.

Now what if I locked on a desert island again, with no Internet and Wolfram Mathematica?

Here is another way of solving it using SAT solver.
The main problem is to count bits around a cell.

Here (\ref{sorting_network}) I described sorting networks shortly.

They can be used for sorting boolean values.
01101 will become 00111, 10001 -> 00011, etc.
We will count bits using sorting network.

My implementation is a simplest \emph{bubble sort} and not the one the most optimized I described earlier.
It's created recursively, as shown in Wikipedia\footnote{\url{https://en.wikipedia.org/wiki/Sorting_network}}.

\begin{figure}[H]
\centering
\frame{\includegraphics[scale=0.7]{\CURPATH/288px-Recursive-bubble-sorting-network.png}}
\end{figure}

The resulting 6-wire network is:

\begin{figure}[H]
\centering
\frame{\includegraphics[scale=0.7]{\CURPATH/257px-Six-wire-bubble-sorting-network.png}}
\end{figure}

Now the comparator/swapper. How do we compare/swap two boolean variables, A and B?

\begin{lstlisting}
A B out1 out2
0 0    0    0
0 1    0    1
1 0    0    1
1 1    1    1
\end{lstlisting}

As you can deduce effortlessly, out1 is just an AND, out2 is OR.

And here is all functions creating sorting network in my SAT\_lib Python library:

\begin{lstlisting}
    def sort_unit(self, a, b):
        return self.OR_list([a,b]), self.AND(a,b)

    def sorting_network(self, lst:List[int]) -> List[int]:
        lst=copy.deepcopy(lst)
        lst_len=len(lst)
        for i in range(lst_len):
            for j in range((lst_len-i)-1):
                x, y = self.sort_unit(lst[j], lst[j+1])
                lst[j], lst[j+1] = x, y
        return lst
\end{lstlisting}
( \url{\RepoURL/libs/SAT_lib.py} )

Now if you will look closely on the output of sorting network, it looks like a thermometer, isn't it?
This is indeed \emph{unary coding}, or \emph{thermometer code},
where 1 is encoded as 1, 2 as 11... 4 as 1111, etc.
Who need such a wasteful code?
For 9 inputs/outputs, we can afford it so far.

In other words, sorting network is a device counting input bits and giving output in unary coding.

Also, we don't need to add 9 constraints for each variable.
Only two will suffice, one False and one True, because we are only interesting in the \emph{level} of a thermometer.

\begin{lstlisting}
    def POPCNT(self, n:int, vars:List[str]):
        sorted=self.sorting_network(vars)
        self.fix_always_false(sorted[n])
        if n!=0:
            self.fix_always_true(sorted[n-1])
\end{lstlisting}
( \url{\RepoURL/libs/SAT_lib.py} )

And the whole source code:

\lstinputlisting[style=custompy]{\CURPATH/minesweeper_SAT_lib_SN.py}

As before, this is a list of Minesweeper cells you can safely click on:

\begin{lstlisting}
row=1, col=3, unsat!
row=6, col=2, unsat!
row=6, col=3, unsat!
row=7, col=4, unsat!
row=7, col=9, unsat!
row=8, col=9, unsat!
\end{lstlisting}

However, it performs several times slower than the version with Mathematica-generated POPCNT functions,
which is the fastest version so far...

Nevertheless, sorting networks has important place in SAT/SMT world.
By fixing a ``level'' of a thermometer using a single constraint, it's possible to add PB (pseudo-boolean) constraints, like, ``x>=10''
(you need just to force a ``level'' to be always higher or equal than 10).

