# Mu - nqueens.mu

      1 # http://rosettacode.org/wiki/N-queens_problem
      2 # port of the Arc solution at http://arclanguage.org/item?id=19743
      3 # run with tracing turned on:
      4 #   ./mu --trace nqueens.mu
      5
      6 container square [
      7   rank:num
      8   file:num
      9 ]
     10
     11 def nqueens n:num, queens:&:list:square -> result:num, queens:&:list:square [
     12   local-scope
     13   load-inputs
     14   # if 'queens' is already long enough, print it and return
     15   added-so-far:num <- length queens
     16   {
     17     done?:bool <- greater-or-equal added-so-far, n
     18     break-unless done?
     19     stash queens
     20     return 1
     21   }
     22   # still work to do
     23   next-rank:num <- copy 0
     24   {
     25     break-unless queens
     26     first:square <- first queens
     27     existing-rank:num <- get first, rank:offset
     28     next-rank <- add existing-rank, 1
     29   }
     30   result <- copy 0
     31   next-file:num <- copy 0
     32   {
     33     done?:bool <- greater-or-equal next-file, n
     34     break-if done?
     35     curr:square <- merge next-rank, next-file
     36     {
     37       curr-conflicts?:bool <- conflict? curr, queens
     38       break-if curr-conflicts?
     39       new-queens:&:list:square <- push curr, queens
     40       sub-result:num <- nqueens n, new-queens
     41       result <- add result, sub-result
     42     }
     43     next-file <- add next-file, 1
     44     loop
     45   }
     46 ]
     47
     48 # check if putting a queen on 'curr' conflicts with any of the existing
     49 # queens
     50 # assumes that 'curr' is on a non-conflicting rank, and checks for conflict
     51 # only in files and diagonals
     52 def conflict? curr:square, queens:&:list:square -> result:bool [
     53   local-scope
     54   load-inputs
     55   result <- conflicting-file? curr, queens
     56   return-if result
     57   result <- conflicting-diagonal? curr, queens
     58 ]
     59
     60 def conflicting-file? curr:square, queens:&:list:square -> result:bool [
     61   local-scope
     62   load-inputs
     63   curr-file:num <- get curr, file:offset
     64   {
     65     break-unless queens
     66     q:square <- first queens
     67     qfile:num <- get q, file:offset
     68     file-match?:bool <- equal curr-file, qfile
     69     return-if file-match?, true/conflict-found
     70     queens <- rest queens
     71     loop
     72   }
     73   return false/no-conflict-found
     74 ]
     75
     76 def conflicting-diagonal? curr:square, queens:&:list:square -> result:bool [
     77   local-scope
     78   load-inputs
     79   curr-rank:num <- get curr, rank:offset
     80   curr-file:num <- get curr, file:offset
     81   {
     82     break-unless queens
     83     q:square <- first queens
     84     qrank:num <- get q, rank:offset
     85     qfile:num <- get q, file:offset
     86     rank-delta:num <- subtract qrank, curr-rank
     87     file-delta:num <- subtract qfile, curr-file
     88     rank-delta <- abs rank-delta
     89     file-delta <- abs file-delta
     90     diagonal-match?:bool <- equal rank-delta, file-delta
     91     return-if diagonal-match?, true/conflict-found
     92     queens <- rest queens
     93     loop
     94   }
     95   return false/no-conflict-found
     96 ]
     97
     98 def main [
     99   nqueens 4
    100   $dump-trace [app]
    101 ]