20-CS-4003-001 Organization of Programming Languages Fall 2017
Search and Abstract Thinking

Lambda calculus, Type theory, Formal semantics, Program analysis

    Prev     Next     All lectures        Code

The N Queens Problem

;; r - hypothesized row for queen in next 
;;     unassigned column
;; l - current queen assignments in leftmost
;;     consecutive columns
;; returns #t iff a queen can be placed in
;; row r of the next unassigned column
;; without attacking any queen that was
;; assigned in l.  Assumes there are no
;; two attacking queens in l.
(define check?
  (lambda (r l)
    (if (null? l)
        #t
        (let ((qp (car l)) (d (length l)))
          (if (or (= r qp)         ;; row
                  (= (+ r d) qp)   ;; diag↑
                  (= (- r d) qp))  ;; diag↓
              #f  
              (check? r (cdr l)))))))

;; extend queen assignment l by adding r in the
;; first unassigned column then return that list
;; if it is of size m or return all solutions 
;; that can be made by extending that list
(define extend
  (lambda (r l m)
    (let ((el (append l (list r))))
      (if (= m (length el))
          (list el)
          (queen 1 el m '())))))

;; returns all assignments of queens to squares
;; such that no two queens are attacking that
;; include the assignments of list l and 
;; assignments to rows r, r+1,... in the column
;; right after the last column of l. Param m
;; is the board size (m x m).
(define queen
  (lambda (r l m acc)
    (if (> r m)
        acc
        (if (check? r l)
            (let ((s (append acc (extend r l m))))
              (queen (+ r 1) l m s))
            (queen (+ r 1) l m acc))))) 

(define nqueen (lambda (m) (queen 1 '() m '())))
 -  The N-Queens problem:
Given a N x N chessboard and N queens, place the queens on the board so that no two are attacking. Two queens are attacking if they are on the same diagonal, on the same row, or on the same column. Examples:
  +---+---+---+---+      +---+---+---+---+
  |   | . | Q |   |      | Q | * | Q |   |
  |---+---+---+---+      +---+---+---+---+
  | Q |   | . |   |      |   | * |   |   |
  |---+---+---+---+      +---+---+---+---+
  |...|...|...| Q |      |   | Q | * |   |
  |---+---+---+---+      +---+---+---+---+
  |   | Q | . |   |      |   |   |   | Q |
  +---+---+---+---+      +---+---+---+---+
  no attacking pair      two attacking pairs
Setup:
representation:
  ()   - no queens placed
  (2)   - a queen in col 1 at row 2
  (2 4)   - two queens at col 1, row 2; col 2, row 4
  ()   - a solution (4x4)

Search:
Search for a solution, backtrack when fruitless to expand partial solution. Example:

 add x=1 to list:() success!
 add x=1 to list:(1) fail :-(
 add x=2 to list:(1) fail :-(
 add x=3 to list:(1) success!
 add x=1 to list:(1 3) fail :-(
 add x=2 to list:(1 3) fail :-(
 add x=3 to list:(1 3) fail :-(
 add x=4 to list:(1 3) fail :-(
 tried all possibilities: backtrack
  
 add x=4 to list:(1) success!
 add x=1 to list:(1 4) fail :-(
 add x=2 to list:(1 4) success!
 add x=1 to list:(1 4 2) fail :-(
 add x=2 to list:(1 4 2) fail :-(
 add x=3 to list:(1 4 2) fail :-(
 add x=4 to list:(1 4 2) fail :-(
 tried all possibilities: backtrack
 ... 
 add x=3 to list:(2 4 1) --> (2 4 1 3)
 ...
 add x=2 to list:(3 1 4) --> (3 1 4 2)
 ...
 ;Value 11: ((2 4 1 3) (3 1 4 2))

Check:
How to check whether to expand a current queen assignment? Examples:

                                          1   2   3   4
 (2 4 1)   <- cur-queen-assgn           +---+---+---+---+
 (3 2 1 0) <- dec-numbs               1 |   | . | Q |   |
 3         <- next-col-queen-pos        |---+---+---+---+
 ---------                            2 | Q |   | . |   |
 (or (= 3 2) (= 6 2) (= 0 2)) -> #f     |---+---+---+---+
 (or (= 3 4) (= 5 4) (= 1 4)) -> #f   3 |...|...|...| X | r=3
 (or (= 3 1) (= 4 1) (= 2 1)) -> #f     |---+---+---+---+
 =====================                4 |   | Q | . |   |
 (2 4 1)   <- cur-queen-assgn           +---+---+---+---+
 (3 2 1 0) <- dec-numbs                               o col 4
 1         <- next-col-queen-pos                      
 ---------                               \---------/
 (or (= 1 2) (= 4 2) (= -2 2)) -> #f       (2 4 1) <- assgn
 (or (= 1 4) (= 3 4) (= -1 4)) -> #f
 (or (= 1 1) (= 2 1) (= 0 1)) -> #t
 =====================
The list of decreasing numbers can be obtained by invoking dec-seq.

The check? function returns #t iff there is no attacking pair with a queen placed in row next-col-queen-pos of the next unassigned column. That is, it returns #t iff it is OK to put a queen in row r of column length(l) + 1. Examples:

  (check? 1 '(2 4 1)) -> #f
  (check? 2 '(2 4 1)) -> #f
  (check? 3 '(2 4 1)) -> #t
  (check? 4 '(2 4 1)) -> #f
Back to the search for a solution. Function queen takes as input number r - a queen placement in a row of the next unassigned column, a list l - a current assignment of queens to rows, a number m - the size of the board, and acc - to accumulate a list of queen assignments that are solutions. The action of queen is to append to the accumulator list all extensions of l that are solutions. Examples:
  (queen 1 '(2) 8 '())

  ((2 4 6 8 3 1 7 5) (2 5 7 1 3 8 6 4) (2 5 7 4 1 8 6 3) 
   (2 6 1 7 4 8 3 5) (2 6 8 3 1 4 7 5) (2 7 3 6 8 5 1 4) 
   (2 7 5 8 1 4 6 3) (2 8 6 1 3 5 7 4))

  (queen 1 '(2) 4 '())

  ((2 4 1 3))
If it is OK to add r to l (check? returns #t) then use a call to queen with l appended with the OK row position (nl below) for the next unassigned column. But if the newly filled column completes an assignment for a solution, then output that list (as (list nl)).
  (let ((nl (append l (list x))))
    (if (= m (length nl))
        (list nl)
        (queen 1 nl m '()))))) 
add all solutions that include current queen assignments with the new column assignment added

Run the thing:

  (define nqueens (lambda (n) (queen 1 '() m '())))
  (nqueens 8)