September Morn Update

This commit is contained in:
Ingy döt Net 2019-09-12 10:33:56 -07:00
parent 4e2d22a71d
commit aac6731f2c
6856 changed files with 141342 additions and 21127 deletions

View file

@ -15,6 +15,7 @@ For the number of solutions for small values of   '''N''',   see &nbsp
* [[Solve a Hidato puzzle]]
* [[Solve a Holy Knight's tour]]
* [[Knight's tour]]
* [[Peaceful chess queen armies]]
* [[Solve a Hopido puzzle]]
* [[Solve a Numbrix puzzle]]
* [[Solve the no connection puzzle]]

View file

@ -0,0 +1 @@
--- {}

View file

@ -0,0 +1,41 @@
Sub aux(n As Integer, i As Integer, a() As Integer, _
u() As Integer, v() As Integer, ByRef m As LongInt)
Dim As Integer j, k, p, q
If i > n Then
m += 1
For k = 1 To n : Print a(k); : Next : Print
Else
For j = i To n
k = a(j)
p = i - k + n
q = i + k - 1
If u(p) And v(q) Then
u(p) = 0 : v(q) = 0
a(j) = a(i) : a(i) = k
aux(n, i + 1, a(), u(), v(), m)
u(p) = 1 : v(q) = 1
a(i) = a(j) : a(j) = k
End If
Next
End If
End Sub
Dim As Integer n, i
Dim m As LongInt = 1
If Command(1) <> "" Then
n = CInt(Command(1))
ReDim a(1 To n) As Integer
ReDim u(1 To 2 * n - 1) As Integer
ReDim v(1 To 2 * n - 1) As Integer
For i = 1 To n
a(i) = i
Next
For i = 1 To 2 * n - 1
u(i) = 1
v(i) = 1
Next
m = 0
aux(n, 1, a(), u(), v(), m)
Print m
End If

View file

@ -0,0 +1,43 @@
Dim As Integer n, i, j, k, p, q
Dim m As LongInt = 0
If Command(1) <> "" Then
n = CInt(Command(1))
ReDim a(1 To n) As Integer
ReDim s(1 To n) As Integer
ReDim u(1 To 2 * n - 1) As Integer
ReDim v(1 To 2 * n - 1) As Integer
For i = 1 To n
a(i) = i
Next
For i = 1 To 2 * n - 1
u(i) = 1
v(i) = 1
Next
m = 0
i = 1
L1: If i > n Then
m += 1
For k = 1 To n : Print a(k); : Next : Print
Goto L4
End If
j = i
L2: k = a(j)
p = i - k + n
q = i + k - 1
If u(p) And v(q) Then
u(p) = 0 : v(q) = 0
a(j) = a(i) : a(i) = k
s(i) = j
i += 1
Goto L1
End If
L3: j += 1 : If j <= n Goto L2
L4: i -= 1 : If i = 0 Then Print m : End
j = s(i)
k = a(i) : a(i) = a(j) : a(j) = k
p = i - k + n
q = i + k - 1
u(p) = 1 : v(q) = 1
Goto L3
End If

View file

@ -1,136 +1,124 @@
/*
* N-Queens Problem
*
* For an NxN chess board, 'safely' place a chess queen in every column and row such that none can attack another.
* This solution is based Wirth Pascal solution, although a tad cleaner, thus easier to understand as it uses Go/C
* style indexing and naming, and also prints the Queen using a Unicode 'rune' (which other languages do not handle natively).
*
* N rows by N columns are number left to right top to bottom 0 - 7
*
* There are 2N-1 diagonals (showing an 8x8)
* the upper-right to lower-left are numbered row + col that is:
* 0 1 2 3 4 5 6 7
* 1 2 3 4 5 6 7 8
* 2 3 4 5 6 7 8 9
* 3 4 5 6 7 8 9 10
* 4 5 6 7 8 9 10 11
* 5 6 7 8 9 10 11 12
* 6 7 8 9 10 11 12 13
* 7 8 9 10 11 12 13 14
*
* the upper-left to lower-right are numbered N-1 + row - col
* 7 6 5 4 3 2 1 0
* 8 7 6 5 4 3 2 1
* 9 8 7 6 5 4 3 2
* 10 9 8 7 6 5 4 3
* 11 10 9 8 7 6 5 4
* 12 11 10 9 8 7 6 5
* 13 12 11 10 9 8 7 6
* 14 13 12 11 10 9 8 7
*/
package main
import (
"flag"
"fmt"
"log"
"os"
"time"
import "fmt"
"rosettacode.org/dlx" // or where ever you put the dlx package
)
const N = 8
const HAS_QUEEN = false
const EMPTY = true
const UNASSIGNED = -1
const white_queen = '\u2655'
var row_num[N]int // results, indexed by row will be the column where the queen lives (UNASSIGNED) is empty
var right_2_left_diag[(2*N-1)]bool // T if no queen in diag[idx]: row i, column col is diag i+col
var left_2_right_diag[(2*N-1)]bool // T is no queen in diag[idx], row i, column col is N-1 + i-col
func printresults() {
for col := 0; col < N; col++ {
if col != 0 {
fmt.Printf(" ");
}
fmt.Printf("%d,%d", col, row_num[col])
}
fmt.Printf("\n");
for row := 0; row < N; row++ {
for col := 0; col < N; col++ {
if col == row_num[row] {
fmt.Printf(" %c ", white_queen)
} else {
fmt.Printf(" . ")
}
}
fmt.Printf("\n")
}
}
/*
* save a queen on the board by saving where we think it should go, and marking the diagonals as occupied
*/
func savequeen(row int, col int) {
row_num[row] = col // save queen column for this row
right_2_left_diag[row+col] = HAS_QUEEN // mark forward diags as occupied
left_2_right_diag[row-col+(N-1)] = HAS_QUEEN // mark backward diags as occupied
}
/*
* backout a previously saved queen by clearing where we put it, and marking the diagonals as empty
*/
func clearqueen(row int, col int) {
row_num[row] = UNASSIGNED
right_2_left_diag[row+col] = EMPTY
left_2_right_diag[row-col+(N-1)] = EMPTY
}
/*
* for each column try the solutions
*/
func trycol(col int) bool {
// check each row to look for the first empty row that does not have a diagonal in use too
for row := 0; row < N; row++ {
if row_num[row] == UNASSIGNED && // has the row been used yet?
right_2_left_diag[row+col] == EMPTY && // check for the forward diags
left_2_right_diag[row-col+(N-1)] == EMPTY { // check for the backwards diags
savequeen(row, col) // this is a possible solution
// Tricky part here: going forward thru the col up to but not including the rightmost one
// if this fails, we are done, no need to search any more
if col < N-1 && !trycol(col+1) {
// ok this did not work - we need to try a different row, so undo the guess
clearqueen(row, col)
} else {
// we have a solution on this row/col, start popping the stack.
return true
}
}
}
return false // not a solution for this col, pop the stack, undo the last guess, and try the next one
}
func main() {
log.SetPrefix("N-queens: ")
log.SetFlags(0)
profile := flag.Bool("profile", false, "show DLX profile")
flag.Parse()
for N := 2; N <= 18; N++ {
err := nqueens(N, N == 8, *profile)
if err != nil {
log.Fatal(err)
}
}
}
func nqueens(N int, printFirst, profile bool) error {
// Build a new DLX matrix with 2N primary columns and 4N-6 secondary
// columns: R0..R(N-1), F0..F(N-1), A1..A(2N-3), B1..B(2N-3).
// We also know the number of cells and solution rows required.
m := dlx.NewWithHint(2*N, 4*N-6, N*N*4-4, 8)
s := solution{
N: N,
renumFwd: make([]int, 0, 2*N),
renumBack: make([]int, 2*N),
printFirst: printFirst,
}
// column indexes
iR0 := 0
iF0 := iR0 + N
iA1 := iF0 + N
iB1 := iA1 + 2*N - 3
// Use "organ-pipe" ordering. E.g. for N=8:
// R4 F4 R3 F3 R5 F5 R2 F2 R6 F6 R1 F1 R7 F7 R0 F0
// This can reduce the number of link updates required by
// almost half for large N; see Knuth's paper for details.
mid := N / 2
for off := 0; off <= N-mid; off++ {
i := mid - off
if i >= 0 {
s.renumBack[iR0+i] = len(s.renumFwd)
s.renumBack[iF0+i] = len(s.renumFwd) + 1
s.renumFwd = append(s.renumFwd, iR0+i, iF0+i)
}
if i = mid + off; off != 0 && i < N {
s.renumBack[iR0+i] = len(s.renumFwd)
s.renumBack[iF0+i] = len(s.renumFwd) + 1
s.renumFwd = append(s.renumFwd, iR0+i, iF0+i)
}
}
// Add constraint rows.
// TODO: pre-eliminate symetrical possibilities.
cols := make([]int, 4)
for i := 0; i < N; i++ {
for j := 0; j < N; j++ {
cols[0] = iR0 + i // Ri, rank i
cols[1] = iF0 + j // Fj, file j
a := (i + j) // A(i+j), diagonals
b := (N - 1 - i + j) // B(N-1-i+j), reverse diagonals
cols = cols[:2]
// Do organ-pipe reordering for R and F.
for i, c := range cols {
cols[i] = s.renumBack[c]
}
// Only add diagonals with more than one space; that
// is we omit the corners: A0, A(2N-2), B0, and B(2N-2)
if 0 < a && a < 2*N-2 {
cols = append(cols, iA1+a-1)
}
if 0 < b && b < 2*N-2 {
cols = append(cols, iB1+b-1)
}
m.AddRow(cols)
}
}
// Search for solutions.
start := time.Now()
err := m.Search(s.found)
if err != nil {
return err
}
elapsed := time.Since(start)
fmt.Printf("%d×%d queens has %2d solutions, found in %v\n", N, N, s.count, elapsed)
if profile {
m.ProfileWrite(os.Stderr)
}
return nil
}
type solution struct {
N int
count int
renumFwd []int // for "organ-pipe" column ordering
renumBack []int
printFirst bool
}
func (s *solution) found(m *dlx.Matrix) error {
s.count++
if s.printFirst && s.count == 1 {
fmt.Printf("First %d×%d queens solution:\n", s.N, s.N)
for _, cols := range m.SolutionIDs(nil) {
var r, f int
for _, c := range cols {
// Undo organ-pipe reodering
if c < len(s.renumFwd) {
c = s.renumFwd[c]
}
if c < s.N {
r = c + 1
} else if c < 2*s.N {
f = c - s.N + 1
}
}
fmt.Printf(" R%d F%d\n", r, f)
}
}
return nil
for i := 0; i < N ; i++ {
row_num[i] = UNASSIGNED
}
for i := 0; i < 2*N-1 ; i++ {
right_2_left_diag[i] = EMPTY
}
for i := 0; i < 2*N-1 ; i++ {
left_2_right_diag[i] = EMPTY
}
trycol(0)
printresults()
}

View file

@ -0,0 +1,136 @@
package main
import (
"flag"
"fmt"
"log"
"os"
"time"
"rosettacode.org/dlx" // or where ever you put the dlx package
)
func main() {
log.SetPrefix("N-queens: ")
log.SetFlags(0)
profile := flag.Bool("profile", false, "show DLX profile")
flag.Parse()
for N := 2; N <= 18; N++ {
err := nqueens(N, N == 8, *profile)
if err != nil {
log.Fatal(err)
}
}
}
func nqueens(N int, printFirst, profile bool) error {
// Build a new DLX matrix with 2N primary columns and 4N-6 secondary
// columns: R0..R(N-1), F0..F(N-1), A1..A(2N-3), B1..B(2N-3).
// We also know the number of cells and solution rows required.
m := dlx.NewWithHint(2*N, 4*N-6, N*N*4-4, 8)
s := solution{
N: N,
renumFwd: make([]int, 0, 2*N),
renumBack: make([]int, 2*N),
printFirst: printFirst,
}
// column indexes
iR0 := 0
iF0 := iR0 + N
iA1 := iF0 + N
iB1 := iA1 + 2*N - 3
// Use "organ-pipe" ordering. E.g. for N=8:
// R4 F4 R3 F3 R5 F5 R2 F2 R6 F6 R1 F1 R7 F7 R0 F0
// This can reduce the number of link updates required by
// almost half for large N; see Knuth's paper for details.
mid := N / 2
for off := 0; off <= N-mid; off++ {
i := mid - off
if i >= 0 {
s.renumBack[iR0+i] = len(s.renumFwd)
s.renumBack[iF0+i] = len(s.renumFwd) + 1
s.renumFwd = append(s.renumFwd, iR0+i, iF0+i)
}
if i = mid + off; off != 0 && i < N {
s.renumBack[iR0+i] = len(s.renumFwd)
s.renumBack[iF0+i] = len(s.renumFwd) + 1
s.renumFwd = append(s.renumFwd, iR0+i, iF0+i)
}
}
// Add constraint rows.
// TODO: pre-eliminate symetrical possibilities.
cols := make([]int, 4)
for i := 0; i < N; i++ {
for j := 0; j < N; j++ {
cols[0] = iR0 + i // Ri, rank i
cols[1] = iF0 + j // Fj, file j
a := (i + j) // A(i+j), diagonals
b := (N - 1 - i + j) // B(N-1-i+j), reverse diagonals
cols = cols[:2]
// Do organ-pipe reordering for R and F.
for i, c := range cols {
cols[i] = s.renumBack[c]
}
// Only add diagonals with more than one space; that
// is we omit the corners: A0, A(2N-2), B0, and B(2N-2)
if 0 < a && a < 2*N-2 {
cols = append(cols, iA1+a-1)
}
if 0 < b && b < 2*N-2 {
cols = append(cols, iB1+b-1)
}
m.AddRow(cols)
}
}
// Search for solutions.
start := time.Now()
err := m.Search(s.found)
if err != nil {
return err
}
elapsed := time.Since(start)
fmt.Printf("%d×%d queens has %2d solutions, found in %v\n", N, N, s.count, elapsed)
if profile {
m.ProfileWrite(os.Stderr)
}
return nil
}
type solution struct {
N int
count int
renumFwd []int // for "organ-pipe" column ordering
renumBack []int
printFirst bool
}
func (s *solution) found(m *dlx.Matrix) error {
s.count++
if s.printFirst && s.count == 1 {
fmt.Printf("First %d×%d queens solution:\n", s.N, s.N)
for _, cols := range m.SolutionIDs(nil) {
var r, f int
for _, c := range cols {
// Undo organ-pipe reodering
if c < len(s.renumFwd) {
c = s.renumFwd[c]
}
if c < s.N {
r = c + 1
} else if c < 2*s.N {
f = c - s.N + 1
}
}
fmt.Printf(" R%d F%d\n", r, f)
}
}
return nil
}

View file

@ -1,40 +1,39 @@
import Data.List (transpose, intercalate)
import Data.Bool (bool)
queenPuzzle :: Int -> Int -> [[Int]]
queenPuzzle nRows nCols
| nRows <= 0 = [[]]
| otherwise =
foldr
(\solution a ->
(\qs a ->
a ++
foldr
(\iCol b ->
if safe (nRows - 1) iCol solution
then b ++ [solution ++ [iCol]]
else b)
(\iCol b -> bool b (b ++ [qs ++ [iCol]]) (safe (nRows - 1) iCol qs))
[]
[1 .. nCols])
[]
(queenPuzzle (nRows - 1) nCols)
where
safe iRow iCol solution =
True `notElem`
zipWith
(\sc sr ->
(iCol == sc) || (sc + sr == iCol + iRow) || (sc - sr == iCol - iRow))
solution
[0 .. iRow - 1]
-- TEST ------------------------------------------------------------------------
safe :: Int -> Int -> [Int] -> Bool
safe iRow iCol qs =
(not . or) $
zipWith
(\sc sr ->
(iCol == sc) || (sc + sr == (iCol + iRow)) || (sc - sr == (iCol - iRow)))
qs
[0 .. iRow - 1]
-- TEST ---------------------------------------------------
-- 10 columns of solutions for the 7*7 board:
showSolutions :: Int -> Int -> [String]
showSolutions nCols nBoardSize =
showSolutions nCols nSize =
unlines <$>
(((intercalate " " <$>) . transpose . (boardLines <$>)) <$>
chunksOf nCols (queenPuzzle nBoardSize nBoardSize))
((fmap (intercalate " ") . transpose . fmap boardLines) <$>
chunksOf nCols (queenPuzzle nSize nSize))
where
boardLines rows =
(\r -> foldMap (\c -> if_ (c == r) "" ".") [1 .. (length rows)]) <$> rows
(\r -> (bool '.' '♛' . (== r)) <$> [1 .. (length rows)]) <$> rows
chunksOf :: Int -> [a] -> [[a]]
chunksOf i xs = take i <$> ($ (:)) (splits xs) []
@ -42,9 +41,5 @@ chunksOf i xs = take i <$> ($ (:)) (splits xs) []
splits [] _ n = []
splits l c n = l `c` splits (drop i l) c n
if_ :: Bool -> a -> a -> a
if_ True x _ = x
if_ False _ y = y
main :: IO ()
main = mapM_ putStrLn $ showSolutions 10 7
main = (putStrLn . unlines) $ showSolutions 10 7

View file

@ -0,0 +1,63 @@
import Control.Monad
import System.Environment
-- | data types for the puzzle
type Row = Int
type State = [Row]
type Thread = [Row]
-- | utility functions
empty = null
-- | Check for infeasible states
infeasible :: Int -> (State, Thread) -> Bool
infeasible n ([], _) = False
infeasible n ((r:rs),t) = length rs >= n || attack r rs || infeasible n (rs, t)
feasible n st = not $ infeasible n st
-- | Check if a row is attacking another row of a state
attack :: Row -> [Row] -> Bool
attack r rs = r `elem` rs
|| r `elem` (upperDiag rs)
|| r `elem` (lowerDiag rs)
where
upperDiag xs = zipWith (-) xs [1..]
lowerDiag xs = zipWith (+) xs [1..]
-- | Check if it is a goal state
isGoal :: Int -> (State, Thread) -> Bool
isGoal n (rs,t) = (feasible n (rs,t)) && (length rs == n)
-- | Perform a move
move :: Int -> (State, Thread) -> (State, Thread)
move x (s,t) = (x:s, x:t)
choices n = [1..n]
moves n = pure move <*> choices n
emptySt = ([],[])
-- | Breadth-first search
bfs :: Int -> [(State, Thread)] -> (State, Thread)
bfs n [] = error "Couldn't find a feasible solution"
bfs n sts | (not.empty) goal = head goal
| otherwise = bfs n sts'
where
goal = filter (isGoal n) sts'
sts' = filter (feasible n) $ (moves n) <*> sts
-- | Depth-first search
dfs :: Int -> (State, Thread) -> [(State, Thread)]
dfs n st | isGoal n st = [st]
| infeasible n st = [emptySt]
| otherwise = do x <- [1..n]
st' <- dfs n $ move x st
guard $ st' /= emptySt
return st'
main = do
[narg] <- getArgs
let n = read narg :: Int
print (bfs n [emptySt])
print (head $ dfs n emptySt)

View file

@ -0,0 +1,114 @@
; "eight queens problem" benchmark test
.radix 16
.loc 0
nop ;
mov #scr,@#E800
mov #88C6,@#E802
; clear the display RAM
mov #scr,r0
mov #1E0,r1
cls: clr (r0)+
sob r1,cls
; display the initial counter value
clr r3
mov #scr,r0
jsr pc,number
; perform the test
jsr pc,queens
; display the counter
mov #scr,r0
jsr pc,number
finish: br finish
; display the character R1 at the screen address R0,
; advance the pointer R0 to the next column
putc: mov r2,-(sp)
; R1 <- 6 * R1
asl r1 ;* 2
mov r1,-(sp)
asl r1 ;* 4
add (sp)+,r1 ;* 6
add #chars,r1
mov #6,r2
putc1: movb (r1)+,(r0)
add #1E,r0
sob r2,putc1
sub #B2,r0 ;6 * 1E - 2 = B2
mov (sp)+,r2
rts pc
print1: jsr pc,putc
; print a string pointed to by R2 at the screen address R0,
; advance the pointer R0 to the next column,
; the string should be terminated by a negative byte
print: movb (r2)+,r1
bpl print1
rts pc
; display the word R3 decimal at the screen address R0
number: mov sp,r1
mov #A0A,-(sp)
mov (sp),-(sp)
mov (sp),-(sp)
movb #80,-(r1)
numb1: clr r2
div #A,r2
movb r3,-(r1)
mov r2,r3
bne numb1
mov sp,r2
jsr pc,print
add #6,sp
rts pc
queens: mov #64,r5 ;100
l06: clr r3
clr r0
l00: cmp #8,r0
beq l05
inc r0
movb #8,ary(r0)
l01: inc r3
mov r0,r1
l02: dec r1
beq l00
movb ary(r0),r2
movb ary(r1),r4
sub r2,r4
beq l04
bcc l03
neg r4
l03: add r1,r4
sub r0,r4
bne l02
l04: decb ary(r0)
bne l01
sob r0,l04
l05: sob r5,l06
mov r3,cnt
rts pc
; characters, width = 8 pixels, height = 6 pixels
chars: .byte 3C, 46, 4A, 52, 62, 3C ;digit '0'
.byte 18, 28, 8, 8, 8, 3E ;digit '1'
.byte 3C, 42, 2, 3C, 40, 7E ;digit '2'
.byte 3C, 42, C, 2, 42, 3C ;digit '3'
.byte 8, 18, 28, 48, 7E, 8 ;digit '4'
.byte 7E, 40, 7C, 2, 42, 3C ;digit '5'
.byte 3C, 40, 7C, 42, 42, 3C ;digit '6'
.byte 7E, 2, 4, 8, 10, 10 ;digit '7'
.byte 3C, 42, 3C, 42, 42, 3C ;digit '8'
.byte 3C, 42, 42, 3E, 2, 3C ;digit '9'
.byte 0, 0, 0, 0, 0, 0 ;space
.even
cnt: .blkw 1
ary: .blkb 9
.loc 200
scr: ;display RAM

View file

@ -33,9 +33,8 @@ sub try_column {
}
}
$board_size = 12; # takes a minute or so, 14,200 solutions
$board_size = 12;
try_column(0);
local $" = "\n";
print @solutions;
print "total ", scalar(@solutions), " solutions\n";
#print for @solutions; # un-comment to see all solutions
print "total " . @solutions . " solutions\n";

View file

@ -0,0 +1,33 @@
import cp.
% CP approach
queens_cp(N, Q) =>
Q = new_list(N),
Q :: 1..N,
all_different(Q),
all_different([$Q[I]-I : I in 1..N]),
all_different([$Q[I]+I : I in 1..N]),
solve([ff],Q).
% SAT approach (using a N x N matrix)
queens_sat(N,Q) =>
Q = new_array(N,N),
Q :: 0..1,
foreach (K in 1-N..N-1)
sum([Q[I,J] : I in 1..N, J in 1..N, I-J==K]) #=< 1
end,
foreach (K in 2..2*N)
sum([Q[I,J] : I in 1..N, J in 1..N, I+J==K]) #=< 1
end,
foreach (I in 1..N)
sum([Q[I,J] : J in 1..N]) #= 1
end,
foreach (J in 1..N)
sum([Q[I,J] : I in 1..N]) #= 1
end,
solve([inout],Q).

View file

@ -0,0 +1,46 @@
#COMPILE EXE
#DIM ALL
SUB aux(n AS INTEGER, i AS INTEGER, a() AS INTEGER, _
u() AS INTEGER, v() AS INTEGER, m AS QUAD)
LOCAL j, k, p, q AS INTEGER
IF i > n THEN
INCR m
FOR k = 1 TO n : PRINT a(k); : NEXT : PRINT
ELSE
FOR j = i TO n
k = a(j)
p = i - k + n
q = i + k - 1
IF u(p) AND v(q) THEN
u(p) = 0 : v(q) = 0
a(j) = a(i) : a(i) = k
CALL aux(n, i + 1, a(), u(), v(), m)
u(p) = 1 : v(q) = 1
a(i) = a(j) : a(j) = k
END IF
NEXT
END IF
END SUB
FUNCTION PBMAIN () AS LONG
LOCAL n, i AS INTEGER
LOCAL m AS QUAD
IF COMMAND$(1) <> "" THEN
n = VAL(COMMAND$(1))
REDIM a(1 TO n) AS INTEGER
REDIM u(1 TO 2 * n - 1) AS INTEGER
REDIM v(1 TO 2 * n - 1) AS INTEGER
FOR i = 1 TO n
a(i) = i
NEXT
FOR i = 1 TO 2 * n - 1
u(i) = 1
v(i) = 1
NEXT
m = 0
CALL aux(n, 1, a(), u(), v(), m)
PRINT m
END IF
END FUNCTION

View file

@ -0,0 +1,47 @@
#COMPILE EXE
#DIM ALL
FUNCTION PBMAIN () AS LONG
LOCAL n, i, j, k, p, q AS INTEGER
LOCAL m AS QUAD
IF COMMAND$(1) <> "" THEN
n = VAL(COMMAND$(1))
REDIM a(1 TO n) AS INTEGER
REDIM s(1 TO n) AS INTEGER
REDIM u(1 TO 2 * n - 1) AS INTEGER
REDIM v(1 TO 2 * n - 1) AS INTEGER
FOR i = 1 TO n
a(i) = i
NEXT
FOR i = 1 TO 2 * n - 1
u(i) = 1
v(i) = 1
NEXT
m = 0
i = 1
1 IF i > n THEN
INCR m
FOR k = 1 TO n : PRINT a(k); : NEXT : PRINT
GOTO 4
END IF
j = i
2 k = a(j)
p = i - k + n
q = i + k - 1
IF u(p) AND v(q) THEN
u(p) = 0 : v(q) = 0
a(j) = a(i) : a(i) = k
s(i) = j
INCR i
GOTO 1
END IF
3 INCR j : IF j <= n GOTO 2
4 DECR i : IF i = 0 THEN PRINT m : EXIT FUNCTION
j = s(i)
k = a(i) : a(i) = a(j) : a(j) = k
p = i - k + n
q = i + k - 1
u(p) = 1 : v(q) = 1
GOTO 3
END IF
END FUNCTION

View file

@ -1,40 +0,0 @@
defint a-z
option base 1
input "n=",n
dim a(n), s(n), u(4*n-2)
for i=1 to n: a(i)=i: next
for i=1 to 4*n-2: u(i)=0: next
m=0
i=1
r=2*n-1
goto 20
10 s(i)=j
u(p)=1
u(q+r)=1
incr i
20 if i>n goto 60
j=i
30 z=a(i)
y=a(j)
p=i-y+n
q=i+y-1
a(i)=y
a(j)=z
if u(p)=0 and u(q+r)=0 goto 10
40 incr j
if j<=n goto 30
50 decr j
if j=i goto 70
swap a(i),a(j)
goto 50
60 incr m
for k=1 to n: print a(k);: next: print
70 decr i
if i=0 goto 80
p=i-a(i)+n
q=i+a(i)-1
j=s(i)
u(p)=0
u(q+r)=0
goto 40
80 print m

View file

@ -1,23 +1,11 @@
def queens(n):
a = list(range(n))
up = [True]*(2*n - 1)
down = [True]*(2*n - 1)
def sub(i):
if i == n:
yield tuple(a)
else:
for k in range(i, n):
j = a[k]
p = i + j
q = i - j + n - 1
if up[p] and down[q]:
up[p] = down[q] = False
a[i], a[k] = a[k], a[i]
yield from sub(i + 1)
up[p] = down[q] = True
a[i], a[k] = a[k], a[i]
yield from sub(0)
def solve(n, i, a, b, c):
if i < n:
for j in range(n):
if j not in a and i+j not in b and i-j not in c:
for solution in solve(n, i+1, a+[j], b+[i+j], c+[i-j]):
yield solution
else:
yield a
#Count solutions for n=8:
sum(1 for p in queens(8))
92
for solution in solve(8, 0, [], [], []):
print(solution)

View file

@ -1,4 +1,4 @@
def queens_lex(n):
def queens(n):
a = list(range(n))
up = [True]*(2*n - 1)
down = [True]*(2*n - 1)
@ -7,25 +7,17 @@ def queens_lex(n):
yield tuple(a)
else:
for k in range(i, n):
a[i], a[k] = a[k], a[i]
j = a[i]
j = a[k]
p = i + j
q = i - j + n - 1
if up[p] and down[q]:
up[p] = down[q] = False
a[i], a[k] = a[k], a[i]
yield from sub(i + 1)
up[p] = down[q] = True
x = a[i]
for k in range(i + 1, n):
a[k - 1] = a[k]
a[n - 1] = x
a[i], a[k] = a[k], a[i]
yield from sub(0)
next(queens(31))
(0, 2, 4, 1, 3, 8, 10, 12, 14, 6, 17, 21, 26, 28, 25, 27, 24, 30, 7, 5, 29, 15, 13, 11, 9, 18, 22, 19, 23, 16, 20)
next(queens_lex(31))
(0, 2, 4, 1, 3, 8, 10, 12, 14, 5, 17, 22, 25, 27, 30, 24, 26, 29, 6, 16, 28, 13, 9, 7, 19, 11, 15, 18, 21, 23, 20)
#Compare to A065188
#1, 3, 5, 2, 4, 9, 11, 13, 15, 6, 8, 19, 7, 22, 10, 25, 27, 29, 31, 12, 14, 35, 37, ...
#Count solutions for n=8:
sum(1 for p in queens(8))
92

View file

@ -0,0 +1,31 @@
def queens_lex(n):
a = list(range(n))
up = [True]*(2*n - 1)
down = [True]*(2*n - 1)
def sub(i):
if i == n:
yield tuple(a)
else:
for k in range(i, n):
a[i], a[k] = a[k], a[i]
j = a[i]
p = i + j
q = i - j + n - 1
if up[p] and down[q]:
up[p] = down[q] = False
yield from sub(i + 1)
up[p] = down[q] = True
x = a[i]
for k in range(i + 1, n):
a[k - 1] = a[k]
a[n - 1] = x
yield from sub(0)
next(queens(31))
(0, 2, 4, 1, 3, 8, 10, 12, 14, 6, 17, 21, 26, 28, 25, 27, 24, 30, 7, 5, 29, 15, 13, 11, 9, 18, 22, 19, 23, 16, 20)
next(queens_lex(31))
(0, 2, 4, 1, 3, 8, 10, 12, 14, 5, 17, 22, 25, 27, 30, 24, 26, 29, 6, 16, 28, 13, 9, 7, 19, 11, 15, 18, 21, 23, 20)
#Compare to A065188
#1, 3, 5, 2, 4, 9, 11, 13, 15, 6, 8, 19, 7, 22, 10, 25, 27, 29, 31, 12, 14, 35, 37, ...

View file

@ -0,0 +1,145 @@
'''N Queens problem'''
from functools import reduce
from itertools import chain
# queenPuzzle :: Int -> Int -> [[Int]]
def queenPuzzle(nRows, nCols):
'''All board patterns of this dimension
in which no two Queens share a row,
column, or diagonal.
'''
def go(nRows, nCols):
return reduce(
lambda a, xys: a + reduce(
lambda b, iCol: b + [xys + [iCol]] if (
safe(nRows - 1, iCol, xys)
) else b,
enumFromTo(1)(nCols),
[]
),
go(nRows - 1, nCols),
[]
) if nRows > 0 else [[]]
return go(nRows, nCols)
# safe :: Int -> Int -> [Int] -> Bool
def safe(iRow, iCol, pattern):
'''True if no two queens in the pattern
share a row, column or diagonal.
'''
def p(sc, sr):
return (iCol == sc) or (
sc + sr == (iCol + iRow)
) or (sc - sr == (iCol - iRow))
return not any(map(p, pattern, range(0, iRow)))
# TEST ----------------------------------------------------
# main :: IO ()
def main():
'''Number of solutions for boards of various sizes'''
n = 5
xs = queenPuzzle(n, n)
print(
str(len(xs)) + ' solutions for a {n} * {n} board:\n'.format(n=n)
)
print(showBoards(10)(xs))
print(
fTable(
'\n\n' + main.__doc__ + ':\n'
)(str)(lambda n: str(n).rjust(3, ' '))(
lambda n: len(queenPuzzle(n, n))
)(enumFromTo(1)(10))
)
# GENERIC -------------------------------------------------
# enumFromTo :: (Int, Int) -> [Int]
def enumFromTo(m):
'''Integer enumeration from m to n.'''
return lambda n: list(range(m, 1 + n))
# chunksOf :: Int -> [a] -> [[a]]
def chunksOf(n):
'''A series of lists of length n, subdividing the
contents of xs. Where the length of xs is not evenly
divible, the final list will be shorter than n.
'''
return lambda xs: reduce(
lambda a, i: a + [xs[i:n + i]],
range(0, len(xs), n), []
) if 0 < n else []
# intercalate :: [a] -> [[a]] -> [a]
# intercalate :: String -> [String] -> String
def intercalate(x):
'''The concatenation of xs
interspersed with copies of x.
'''
return lambda xs: x.join(xs) if isinstance(x, str) else list(
chain.from_iterable(
reduce(lambda a, v: a + [x, v], xs[1:], [xs[0]])
)
) if xs else []
# FORMATTING ----------------------------------------------
# showBoards :: Int -> [[Int]] -> String
def showBoards(nCols):
'''String representation, with N columns
of a set of board patterns.
'''
def showBlock(b):
return '\n'.join(map(intercalate(' '), zip(*b)))
def go(bs):
return '\n\n'.join(map(
showBlock,
chunksOf(nCols)(
list(map(showBoard, bs))
)
))
return lambda boards: go(boards)
# showBoard :: [Int] -> String
def showBoard(xs):
'''String representation of a Queens board.'''
lng = len(xs)
def showLine(n):
return ('.' * (n - 1)) + '' + ('.' * (lng - n))
return list(map(showLine, xs))
# fTable :: String -> (a -> String) ->
# (b -> String) -> (a -> b) -> [a] -> String
def fTable(s):
'''Heading -> x display function -> fx display function ->
f -> xs -> tabular string.
'''
def go(xShow, fxShow, f, xs):
ys = [xShow(x) for x in xs]
w = max(map(len, ys))
return s + '\n' + '\n'.join(map(
lambda x, y: y.rjust(w, ' ') + ' -> ' + fxShow(f(x)),
xs, ys
))
return lambda xShow: lambda fxShow: lambda f: lambda xs: go(
xShow, fxShow, f, xs
)
# MAIN ---
if __name__ == '__main__':
main()

View file

@ -1,48 +1,48 @@
/*REXX program places N queens on an NxN chessboard (the eight queens problem). */
parse arg N . /*obtain optional argument from the CL.*/
if N=='' | N=="," then N=8 /*Not specified: Then use the default.*/
if N<1 then signal nOK /*display a message, the board is bad. */
rank=1; file=1; #=0 /*starting rank&file; #≡number queens.*/
@.=0; pad=left('', 9* (N<18) ) /*define empty board; set indentation.*/
if N=='' | N=="," then N= 8 /*Not specified: Then use the default.*/
if N<1 then call nOK /*display a message, the board is bad. */
rank= 1; file= 1; #=0 /*starting rank&file; #≡number queens.*/
@.= 0; pad= left('', 9* (N<18) ) /*define empty board; set indentation.*/
/* [↓] rank&file ≡ chessboard row&cols*/
do while #<N; @.file.rank=1 /*keep placing queens until we're done.*/
if ok(file, rank) then do; #=#+1; file=1 /*Queen not being attacked? Then eureka*/
rank=rank+1 /*use another attempt at another rank. */
do while #<N; @.file.rank= 1 /*keep placing queens until we're done.*/
if ok(file, rank) then do; file= 1; #= # + 1 /*Queen not being attacked? Then eureka*/
rank= rank + 1 /*use another attempt at another rank. */
iterate /*go and try another queen placement. */
end /* [↑] found a good queen placement. */
@.file.rank=0 /*It isn't safe. So remove this queen.*/
file=file+1 /*So, try the next (higher) chess file.*/
do while file>N; rank=rank-1; if rank==0 then call nOK
do j=1 for N; if \@.j.rank then iterate /*¿ocupado?*/
@.j.rank=0; #=#-1; file=j+1; leave
end /*j*/
end /*while file>N*/
end /*while #<N*/
say 'A solution for ' N " queens:"; g=substr( copies("╬═══", N) ,2); say
say pad translate(''g"", '', "") /*display the top rank (of the board).*/
line = ''g""; dither= '' /*define a line (bar) for cell boundry.*/
bar = '' ; queen = "Q" /*kinds: horizontal, vertical, salad. */
Bqueen = dither || queen || dither /*glyph befitting a black─square queen.*/
Wqueen = ' 'queen" " /* " " " white─square " */
do rank=1 for N; if rank\==1 then say pad line; _= /*display sep for rank.*/
do file=1 for N; B = (file+rank) // 2 /*is the square black? */
Qgylph=Wqueen; if B then Qgylph=Bqueen /*use a dithered queen.*/
if @.file.rank then _=_ || bar || Qgylph /*3─char queen symbol. */
else if B then _=_ || bar || copies(dither,3) /*use dithering for sq.*/
else _=_ || bar || copies(' ' ,3) /* " 3 blanks " " */
end /*file*/ /* [↑] preserve square─ish chessboard.*/
say pad _ || bar /*show a single rank of the chessboard.*/
end /*rank*/ /*80 cols can view a 19x19 chessboard.*/
say pad translate(''g"", '', "") /*display the last rank (of the board).*/
@.file.rank= 0 /*It isn't safe. So remove this queen.*/
file= file+1 /*So, try the next (higher) chess file.*/
do while file>N; rank= rank - 1; if rank==0 then call nOK
do j=1 for N; if \@.j.rank then iterate /*¿ocupado?*/
@.j.rank= 0; #= # - 1; file= j + 1; leave
end /*j*/
end /*while file>N*/
end /*while #<N*/
call show
exit 1 /*stick a fork in it, we're all done. */
/*──────────────────────────────────────────────────────────────────────────────────────*/
nOK: say; say "No solution for" N 'queens.'; say; exit 0
/*──────────────────────────────────────────────────────────────────────────────────────*/
ok: parse arg f,r; fp=f+1; rm=r-1 /*if return≡0, then queen isn't safe. */
do k=1 for rm; if @.f.k then return 0; end
f=f-1; do k=rm by -1 for rm while f\==0; if @.f.k then return 0; f=f-1; end
f=fp; do k=rm by -1 for rm while f <=N; if @.f.k then return 0; f=f+1; end
ok: parse arg f,r; fp= f + 1; rm= r - 1 /*if return≡0, then queen isn't safe. */
do k=1 for rm; if @.f.k then return 0; end
f= f-1; do k=rm by -1 for rm while f\==0; if @.f.k then return 0; f= f-1; end
f= fp; do k=rm by -1 for rm while f <=N; if @.f.k then return 0; f= f+1; end
return 1 /*1≡queen is safe. */ /* ↑↑↑↑↑↑↑↑ is queen under attack? */
/*──────────────────────────────────────────────────────────────────────────────────────*/
show: say 'A solution for ' N " queens:" /*display a title to the terminal.*/
g= substr( copies("╬═══", N) ,2) /*start of all cells on chessboard*/
say; say pad translate(''g"", '', "") /*display top rank (of the board).*/
line = ''g""; dither= ""; ditherQ= '' /*define a line for cell boundary.*/
bar = '' ; queen = "Q" /*kinds: horiz., vert., salad.*/
Bqueen = ditherQ || queen || ditherQ /*glyph befitting a black square Q*/
Wqueen = ' 'queen" " /* " " " white " "*/
do rank=1 for N; if rank\==1 then say pad line; _= /*show rank sep. */
do file=1 for N; B = (file + rank) // 2 /*square black ? */
Qgylph= Wqueen; if B then Qgylph= Bqueen /*use dithered Q.*/
if @.file.rank then _= _ || bar || Qgylph /*3─char Q symbol*/
else if B then _=_ || bar || copies(dither,3) /*dithering */
else _=_ || bar || copies( ' ' ,3) /* 3 blanks */
end /*file*/ /* [↑] preserve square─ish board.*/
say pad _ || bar /*show a single rank of the board.*/
end /*rank*/ /*80 cols can view a 19x19 board.*/
say pad translate(''g"", '', ""); return /*display the last rank (of board)*/

View file

@ -11,6 +11,7 @@
# list, place the second-column queen in the row with the second number in
# the list, etc.
def n_queens(n)
if n == 1
return "Q"
@ -25,35 +26,32 @@ def n_queens(n)
rem = n % 12 # (1)
nums = evens # (2)
nums.push(nums.shift) if rem == 3 or rem == 9 # (3)
nums.rotate if rem == 3 or rem == 9 # (3)
# (4)
if rem == 8
odds = odds.each_slice(2).inject([]) {|ary, (a,b)| ary += [b,a]}
odds = odds.each_slice(2).flat_map(&:reverse)
end
nums.concat(odds)
# (5)
if rem == 2
idx = []
[1,3,5].each {|i| idx[i] = nums.index(i)}
nums[idx[1]], nums[idx[3]] = nums[idx[3]], nums[idx[1]]
nums.slice!(idx[5])
nums.push(5)
nums[nums.index(1)], nums[nums.index(3)] = nums[nums.index(3)], nums[nums.index(1)]
nums << nums.delete(5)
end
# (6)
if rem == 3 or rem == 9
[1,3].each do |i|
nums.slice!( nums.index(i) )
nums.push(i)
end
nums << nums.delete(1)
nums << nums.delete(3)
end
# (7)
board = Array.new(n) {Array.new(n) {"."}}
n.times {|i| board[i][nums[i] - 1] = "Q"}
board.inject("") {|str, row| str << row.join(" ") << "\n"}
nums.map do |q|
a = Array.new(n,".")
a[q-1] = "Q"
a*(" ")
end
end
(1 .. 15).each {|n| puts "n=#{n}"; puts n_queens(n); puts}

View file

@ -0,0 +1,49 @@
object NQueens {
private implicit class RichPair[T](
pair: (T,T))(
implicit num: Numeric[T]
) {
import num._
def safe(x: T, y: T): Boolean =
pair._1 - pair._2 != abs(x - y)
}
def solve(n: Int): Iterator[Seq[Int]] = {
(0 to n-1)
.permutations
.filter { v =>
(0 to n-1).forall { y =>
(y+1 to n-1).forall { x =>
(x,y).safe(v(x),v(y))
}
}
}
}
def main(args: Array[String]): Unit = {
val n = args.headOption.getOrElse("8").toInt
val (solns1, solns2) = solve(n).duplicate
solns1
.zipWithIndex
.foreach { case (soln, i) =>
Console.out.println(s"Solution #${i+1}")
output(n)(soln)
}
val n_solns = solns2.size
if (n_solns == 1) {
Console.out.println("Found 1 solution")
} else {
Console.out.println(s"Found $n_solns solutions")
}
}
def output(n: Int)(board: Seq[Int]): Unit = {
board.foreach { queen =>
val row =
"_|" * queen + "Q" + "|_" * (n-queen-1)
Console.out.println(row)
}
}
}

View file

@ -0,0 +1,56 @@
let maxn = 31
func nq(n: Int) -> Int {
var cols = Array(repeating: 0, count: maxn)
var diagl = Array(repeating: 0, count: maxn)
var diagr = Array(repeating: 0, count: maxn)
var posibs = Array(repeating: 0, count: maxn)
var num = 0
for q0 in 0...n-3 {
for q1 in q0+2...n-1 {
let bit0: Int = 1<<q0
let bit1: Int = 1<<q1
var d: Int = 0
cols[0] = bit0 | bit1 | (-1<<n)
diagl[0] = (bit0<<1|bit1)<<1
diagr[0] = (bit0>>1|bit1)>>1
var posib: Int = ~(cols[0] | diagl[0] | diagr[0])
while (d >= 0) {
while(posib != 0) {
let bit: Int = posib & -posib
let ncols: Int = cols[d] | bit
let ndiagl: Int = (diagl[d] | bit) << 1;
let ndiagr: Int = (diagr[d] | bit) >> 1;
let nposib: Int = ~(ncols | ndiagl | ndiagr);
posib^=bit
num += (ncols == -1 ? 1 : 0)
if (nposib != 0){
if(posib != 0) {
posibs[d] = posib
d += 1
}
cols[d] = ncols
diagl[d] = ndiagl
diagr[d] = ndiagr
posib = nposib
}
}
d -= 1
posib = d<0 ? n : posibs[d]
}
}
}
return num*2
}
if(CommandLine.arguments.count == 2) {
let board_size: Int = Int(CommandLine.arguments[1])!
print ("Number of solutions for board size \(board_size) is: \(nq(n:board_size))")
} else {
print("Usage: 8q <n>")
}

View file

@ -0,0 +1,78 @@
#!/bin/bash
# variable declaration
typeset -i BoardSize=8
typeset -i p=0
typeset -i total=0
typeset -i board
# initialization
function init
{
for (( i=0;i<$BoardSize;i++ ))
do
(( board[$i]=-1 ))
done
}
# check if queen can be placed
function place
{
typeset -i flag=1
for (( i=0;i<$1;i++ ))
do
if [[ (${board[$i]}-${board[$1]} -eq ${i}-${1}) || (${board[$i]}-${board[$1]} -eq ${1}-${i}) || (${board[$i]} -eq ${board[$1]}) ]]
then
(( flag=0 ))
fi
done
[[ $flag -eq 0 ]]
return $?
}
# print the result
function out
{
printf "Problem of queen %d:%d\n" $BoardSize $total
}
# free the variables
function depose
{
unset p
unset total
unset board
unset BoardSize
}
# back tracing
function work
{
while [[ $p -gt -1 ]]
do
(( board[$p]++ ))
if [[ ${board[$p]} -ge ${BoardSize} ]]
then # back tracing
(( p-- ))
else # try next position
place $p
if [[ $? -eq 1 ]]
then
(( p++ ))
if [[ $p -ge ${BoardSize} ]]
then
(( total++ ))
(( p-- ))
else
(( board[$p]=-1 ))
fi
fi
fi
done
}
# entry
init
work
out
depose