This version has been expanded to handle any board size up to the number of bits in a cell.

\ 8 queens

\ Triangular array of bitmasks, one bit per square
\  recursion depth 0, ranks 0..N-1 are at offsets 0..N-1
\  recursion depth 1, ranks 1..N-1 are at offsets N..N+(N-1)-1
\  etc.
12 constant maxN
8 value N
create ranks maxN dup 1+ * 2/ cells allot
: init_ranks
  1 N lshift 1-  N 0 do dup ranks I cells + ! loop drop ;

: lowBit  ( mask -- bit ) dup negate and ;
: lowBit- ( mask -- bits ) dup 1- and ;

: .sq   1 and if [char] Q else [char] . then space emit ;
: .rank ( mask -- ) N 0 do dup .sq 2/ loop drop cr ;
: .solution	\ a solution is encoded in ranks
  N ranks begin
    dup @ lowBit .rank
    over cells +  swap 1- swap
  over 0= until 2drop cr ;
: .ranks ( addr count -- )
  0 do dup I cells + @ .rank loop drop cr ;

: dmask ( fm r -- fdm ) \ mask for file and diagonals
  >R  dup R@ lshift or  dup R> rshift or ;

\ Copy the square availability from the current ranks
\ to the next ranks, excluding attacks by the new queen
\ at nextBit of ^rank.
\ Aborts if there is no possible solution from here.

variable excludes  \ N=8 should be 4380
variable nodes     \ N=8 should be 1073
variable solutions \ N=8 should be 92

: exclude ( ranksLeft ^rank -- tf )
  over 1- cells over + swap rot ( dest src ranksLeft )
  1 do   1 excludes +!
    2dup dup @ lowBit ( dest src mask ) \ file
     dup I lshift or		\ left diagonal
     dup I rshift or invert	\ right diagonal
    swap I cells + @ and        ( dest masked )
    dup 0= if 2drop 2drop unloop false exit then
    swap I cells + !
  loop 2drop true ;

: tryRank ( ranksLeft ^rank -- ) 1 nodes +!
  begin
    over 1- if
      2dup exclude if
        over 1- 2dup 1+ cells + recurse
      then
    else ( .solution)  1 solutions +! then
    dup @ lowBit-  dup
  while  over !
  repeat drop 2drop ;

: queens ( n -- ) to N  cr
  init_ranks  0 solutions ! 0 nodes ! 0 excludes !
  N ranks tryRank
  N . ." queens: " solutions @ . ." solutions, "
  nodes @ . ." nodes, " excludes @ . ." exclude loops" ;

: test-queens   maxN 0 do I 1+ queens loop ;

Here is a version that fits the whole chessboard into two 32-bit cells.

\ 8 queens, bitboard version (requires 32-bit cells)

: 2AND ( d d -- d ) rot and >R and R> ;
: 2lshift ( d 0..7 -- d ) tuck lshift >R lshift R> ;

: .sq ( n -- n/2 )
  dup 1 and if [char] Q else [char] . then space emit 2/ ;
: .rank ( n -- n>>8 )
  .sq .sq .sq .sq .sq .sq .sq .sq  cr ;
: .halfboard ( n -- ) .rank .rank .rank .rank drop ;
: .board ( bh bl -- ) .halfboard .halfboard  cr ;

hex
\ Masks for eliminating possible queen positions from future tries
\ indexed by rank, then shifted by file
create attacks
 090503ff invert 81412111 invert 2,
 0503ff03 invert 41211109 invert 2,
 03ff0305 invert 21110905 invert 2,
 ff030509 invert 11090503 invert 2,
 03050911 invert 090503ff invert 2,
 05091121 invert 0503ff03 invert 2,
 09112141 invert 03ff0305 invert 2,
 11214181 invert ff030509 invert 2,
decimal

variable solutions variable nodes \ 92 1965

\ Try placing queens (q) along file and recurse to subsequent files.
\ boardL/H accumulates a solution, dleft on stack tracks
\  possible future queen positions

variable boardL variable boardH
variable file
defer tryFile

: nextFile ( dleft att q -- dleft att q )
  file @ 7 < if
    2over 2over drop 2@ file @ 2lshift 2and ( dleft' )
    1 file +!  tryFile  -1 file +!
  else
    boardH @ boardL @ .board  1 solutions +!
  then ;
: nextRank ( att q -- att++ q<<8) >R 2 cells + R> 8 lshift ;
: tryRankL ( dleft att q -- dleft att++ q<<8 )
  2over DROP over and if
    dup boardL +!  nextFile  dup negate boardL +!
  then nextRank ;
: tryRankH ( dleft att q -- dleft att++ q<<8 )
  2over NIP  over and if
    dup boardH +!  nextFile  dup negate boardH +!
  then nextRank ;
: (tryFile) ( dleft -- ) 1 nodes +!
  attacks 1 file @ lshift  ( dleft att q )
  tryRankL tryRankL tryRankL tryRankL
  drop 1 file @ lshift
  tryRankH tryRankH tryRankH tryRankH
  2drop 2drop ;

' (tryFile) is tryFile

: queens
  0 solutions ! 0 nodes ! 0 boardH ! 0 boardL !
  cr 0 file ! -1. tryFile
  solutions @ . ." solutions, " nodes @ . ." nodes" cr ;

This version is inspired by an elegant approach on the Portland Pattern Repository written in MCPL. Instead of tracking all future ranks in memory, the attacks of placed queens along the files and both diagonals relative to the current rank are carried forward on the stack through the recursion.

8 value N
variable solutions variable nodes \ N=8: 92 1965
: .sq   1 and if [char] Q else [char] . then space emit ;
: .rank ( mask -- ) N 0 do dup .sq 2/ loop drop cr ;
: .sol ( fn ... f1 x x f0 -- unchanged )
  dup N 0 do
    I 3 * 4 + pick ( fi fi+1 )
    2dup xor .rank nip
  loop drop cr ;
: lowBit  ( mask -- bit ) dup negate and ;
: lowBit- ( mask -- bits ) dup 1- and ;
: third ( a b c -- a b c a ) 2 pick ;    \ >r over r> swap ;
: poss ( a b c -- a b c a&b&c ) dup 2over and and ;
: next3 ( dl dr f Qfilebit -- dl dr f dl' dr' f' )
  invert >r third r@ and 2* 1+  third r@ and 2/  third r> and ;
: try ( dl dr f -- )
  dup if 1 nodes +!  poss
    begin ?dup while
      dup >r lowBit next3 recurse r> lowBit-
    repeat
  else .sol 1 solutions +! then drop 2drop ;
: queens ( n -- )   dup to N 0 solutions ! 0 nodes ! cr
  1 over lshift 1-  -1 dup rot try
  . ." queens: " solutions @ . ." solutions, " nodes @ . ." nodes" ;

This is a simple brute force approach, only interesting because it codes checking for vertical and diagonals attack positions with the same logic and loop:

\ ===== depict board and queens =====

: .border  ( -- )     cr 8 0 do  ." +---" loop [char] + emit ;
: |        ( -- )  [char] | emit ;

: .fields  ( pos -- )
   cr 8 0 do  | space
      bl over i = if drop [char] Q then emit space
   loop | drop ;

: .board  ( board -- )
   8 0 do  .border
      8 /mod swap .fields
   loop drop
   .border ;

\ ===== test whether board is solution =====

create attackline 16 allot

: solution? ( board -- f )
   0 3 0 do                \ test vertical, diagonal1, diagonal2
      attackline 16 erase
      over 8 0 do      
         8 /mod swap   
         j if i j 1- if - 8 then + then attackline +
         dup c@ if drop -1 or leave then        
         1 swap c!                               
      loop
      or dup ?leave     
   loop nip 0=  ;  

\ ===== run through all boards, display results =====

8 base !                            
100000000 constant #boards
decimal

: 8queens
  0 #boards 0 do 
     i solution? if
        1+ cr ." solution #" dup . i .board cr
     then
  loop drop ;