エイト・クイーン、ついでに N-クイーン

エイト・クイーンを解くプログラムを Common Lisp で書いてみた。
関数 queen にボードのサイズ N を与えると、N×N のボードで N-クイーン問題を解く。
キーワード引数はふたつ。display に nil でない値を与えると、見つけたパターンをすべて表示する。base-only に nil でない値を与えると、回転・反転で同じになるものはそのうちの1つだけが解となる。


コード:

(defun queen (size &key display base-only)
  (if base-only
      (queen-base size :display display)
      (queen-all  size :display display)))


(defun queen-all (size &key display)
  (let ((board nil)
        (total 0))
    (labels ((rec (j)
               (dotimes (i size)
                 (unless (conflict i j board)
                   (if (= j (1- size))
                       (progn
                         (incf total)
                         (when display
                           (format t "~%[~A]~%" total)
                           (print-board (cons (list i j) board)))
                         (return))
                       (progn
                         (push (list i j) board)
                         (rec (1+ j))
                         (pop board)))))))
      (rec 0))
    (format t "~&~%Total: ~A~%" total)))


(defun queen-base (size &key display)
  (let ((board nil)
        (bases nil)
        (total 0)
        (base-total 0))
    (labels
        ((rec (j)
           (dotimes (i size)
             (unless (conflict i j board)
               (if (= j (1- size))
                   (let ((normalized
                          (sort-board (cons (list i j)
                                            (copy-tree board)))))
                     (incf total)
                     (when (base? normalized bases)
                       (incf base-total)
                       (push normalized bases)
                       (when display
                         (format t "~%[~A]~%" base-total)
                         (print-board normalized)))
                     (return))
                   (progn
                     (push (list i j) board)
                     (rec (1+ j))
                     (pop board)))))))
      (rec 0))
    (format t "~&Base/total: ~A/~A~%" base-total total)))



(defun print-board (board)
  (let* ((size (length board))
         (pos-table (make-array (list size size)
                               :initial-element nil)))
    (dolist (pos board)
      (setf (aref pos-table (first pos) (second pos)) t))
    (princ "*")
    (dotimes (i (1+ (* 2 size))) (princ "-"))
    (format t "*~%")
    (dotimes (i size)
      (princ "|")
      (dotimes (j size)
        (princ (if (aref pos-table i j) " Q" " X")))
      (format t " |~%"))
    (princ "*")
    (dotimes (i (1+ (* 2 size))) (princ "-"))
    (format t "*~%")))


(defun threat (i j a b)
  (or (= i a)
      (= j b)
      (= (+ i j) (+ a b))
      (= (- i j) (- a b))))


(defun conflict (i j board)
  (cond ((null board) nil)
        ((threat i
                 j
                 (first (car board))
                 (second (car board)))
         t)
        (t (conflict i j (cdr board)))))


(defun sort-board (board)
  (sort board #'< :key #'car))


(defun roll-board (board)
  (let ((new-board nil)
        (max-index (1- (length board))))
    (dolist (pos board)
      (push (list (second pos)
                  (- max-index (first pos)))
            new-board))
    (sort-board new-board)))


(defun mirror-board (board)
  (let ((new-board nil)
        (max-index (1- (length board))))
    (dolist (pos board)
      (push (list (first pos)
                  (- max-index (second pos)))
            new-board))
    (sort-board new-board)))


(defun base? (board bases)
  (let ((rolled board))
    (flet ((return-test ()
             (when (member rolled bases :test #'equal)
               (return-from base? nil))))
      (dotimes (i 3)
        (setq rolled (roll-board rolled))
        (return-test))
      (setq rolled (mirror-board board))
      (return-test)
      (dotimes (i 3)
        (setq rolled (roll-board rolled))
        (return-test))))
  t)


使用例:

CL-USER> (queen 4 :display t)

[1]
*---------*
| X X Q X |
| Q X X X |
| X X X Q |
| X Q X X |
*---------*

[2]
*---------*
| X Q X X |
| X X X Q |
| Q X X X |
| X X Q X |
*---------*

Total: 2
NIL
CL-USER> (queen 4 :display t :base-only t)

[1]
*---------*
| X X Q X |
| Q X X X |
| X X X Q |
| X Q X X |
*---------*
Base/total: 1/2
NIL
CL-USER> (queen 4 :base-only t)
Base/total: 1/2
NIL
CL-USER> (queen 8 :display t :base-only t)

[1]
*-----------------*
| Q X X X X X X X |
| X X X X X X Q X |
| X X X X Q X X X |
| X X X X X X X Q |
| X Q X X X X X X |
| X X X Q X X X X |
| X X X X X Q X X |
| X X Q X X X X X |
*-----------------*

[2]
*-----------------*
| Q X X X X X X X |
| X X X X X X Q X |
| X X X Q X X X X |
| X X X X X Q X X |
| X X X X X X X Q |
| X Q X X X X X X |
| X X X X Q X X X |
| X X Q X X X X X |
*-----------------*

[3]
*-----------------*
| X X X X X Q X X |
| Q X X X X X X X |
| X X X X Q X X X |
| X Q X X X X X X |
| X X X X X X X Q |
| X X Q X X X X X |
| X X X X X X Q X |
| X X X Q X X X X |
*-----------------*

[4]
*-----------------*
| X X X Q X X X X |
| Q X X X X X X X |
| X X X X Q X X X |
| X X X X X X X Q |
| X Q X X X X X X |
| X X X X X X Q X |
| X X Q X X X X X |
| X X X X X Q X X |
*-----------------*

[5]
*-----------------*
| X X X X Q X X X |
| Q X X X X X X X |
| X X X X X X X Q |
| X X X Q X X X X |
| X Q X X X X X X |
| X X X X X X Q X |
| X X Q X X X X X |
| X X X X X Q X X |
*-----------------*

[6]
*-----------------*
| X X Q X X X X X |
| Q X X X X X X X |
| X X X X X X Q X |
| X X X X Q X X X |
| X X X X X X X Q |
| X Q X X X X X X |
| X X X Q X X X X |
| X X X X X Q X X |
*-----------------*

[7]
*-----------------*
| X X X X Q X X X |
| Q X X X X X X X |
| X X X Q X X X X |
| X X X X X Q X X |
| X X X X X X X Q |
| X Q X X X X X X |
| X X X X X X Q X |
| X X Q X X X X X |
*-----------------*

[8]
*-----------------*
| X X X X X X Q X |
| Q X X X X X X X |
| X X Q X X X X X |
| X X X X X X X Q |
| X X X X X Q X X |
| X X X Q X X X X |
| X Q X X X X X X |
| X X X X Q X X X |
*-----------------*

[9]
*-----------------*
| X X X X Q X X X |
| Q X X X X X X X |
| X X X X X X X Q |
| X X X X X Q X X |
| X X Q X X X X X |
| X X X X X X Q X |
| X Q X X X X X X |
| X X X Q X X X X |
*-----------------*

[10]
*-----------------*
| X X X X Q X X X |
| X X Q X X X X X |
| Q X X X X X X X |
| X X X X X X Q X |
| X Q X X X X X X |
| X X X X X X X Q |
| X X X X X Q X X |
| X X X Q X X X X |
*-----------------*

[11]
*-----------------*
| X X X X Q X X X |
| X X X X X X Q X |
| Q X X X X X X X |
| X X X Q X X X X |
| X Q X X X X X X |
| X X X X X X X Q |
| X X X X X Q X X |
| X X Q X X X X X |
*-----------------*

[12]
*-----------------*
| X X X X X Q X X |
| X X Q X X X X X |
| Q X X X X X X X |
| X X X X X X X Q |
| X X X Q X X X X |
| X Q X X X X X X |
| X X X X X X Q X |
| X X X X Q X X X |
*-----------------*
Base/total: 12/92
NIL
CL-USER> (queen 12 :base-only t)
Base/total: 1787/14200
NIL