Generate Chess960 starting position

From Rosetta Code
Revision as of 18:55, 14 May 2014 by rosettacode>TimToady (→‎{{header|Perl 6}}: final example isn't meant to do task, but demo precomp)
Task
Generate Chess960 starting position
You are encouraged to solve this task according to the task description, using any language you may know.

Chess960 is a variant of chess created by world champion Bobby Fisher. Unlike other variants of the game, Chess960 does not require a different material, but instead relies on a random initial position, with a few constraints:

  • as in the standard chess game, all eight white pawns must be placed on the second rank.
  • White pieces must stand on the first rank as in the standard game, in random column order but with the two following constraints:
    • the bishops must be placed on opposite color squares (i.e. they must be an odd number of spaces apart or there must be an even number of spaces between them)
    • the King must be between two rooks (with any number of other pieces between them all)
  • Black pawns and pieces must be placed respectively on the seventh and eighth ranks, mirroring the white pawns and pieces, just as in the standard game. (That is, their positions are not independently randomized.)

With those constraints there are 960 possible starting positions, thus the name of the variant.

Task

The purpose of this task is to write a program that can randomly generate any one of the 960 Chess960 initial positions. You will show the result as the first rank displayed with Chess symbols in Unicode: ♔♕♖♗♘ or with the letters King Queen Rook Bishop kNight.

D

Translation of: Python

D: Indexing

<lang d>void main() {

   import std.stdio, std.range, std.algorithm, std.string, permutations2;
   auto pieces = "KQRrBbNN";
   alias I = indexOf;
   auto starts = permutations(pieces).filter!(p =>
           I(p, 'B') % 2 != I(p, 'b') % 2 && // Bishop constraint.
           // King constraint.
           ((I(p, 'r') < I(p, 'K') && I(p, 'K') < I(p, 'R')) ||
            (I(p, 'R') < I(p, 'K') && I(p, 'K') < I(p, 'r'))))
       .map!toUpper.array.sort().uniq;
   writeln(starts.walkLength, "\n", starts.front);

}</lang>

Output:
960
BBNNQRKR

D: Regexp

<lang d>void main() {

   import std.stdio, std.regex, std.range, std.algorithm, permutations2;
   immutable pieces = "KQRRBBNN";
   immutable bish = r"B(|..|....|......)B";
   immutable king = r"R.*K.*R";
   auto starts3 = permutations(pieces)
                  .filter!(p => p.match(bish) && p.match(king))
                  .array.sort().uniq;
   writeln(starts3.walkLength, "\n", starts3.front);

}</lang> The output is the same.

D: Correct by construction

<lang d>void main() {

   import std.stdio, std.random, std.array, std.range;
   // Subsequent order unchanged by insertions.
   auto start = "RKR".dup;
   foreach (immutable piece; "QNN")
       start.insertInPlace(uniform(0, start.length), piece);
   immutable bishpos = uniform(0, start.length);
   start.insertInPlace(bishpos, 'B');
   start.insertInPlace(iota(bishpos % 2, start.length, 2)[uniform(0,$)], 'B');
   start.writeln;

}</lang>

Output:
QBNNBRKR

J

Build a table of the starting positions then pick one at random. There are 40320 distinct permutations of 8 items and 5040 distinct permutations of these chess pieces, so little memory is needed to generate the table. Also, since the table is built at "compile time", execution is fast.

<lang J>row0=: u: 9812+2}.5|i.10 king=: u:9812 rook=: u:9814 bish=: u:9815 pos=: I.@e. bishok=: 1=2+/ .| pos&bish rookok=: pos&rook -: (<./,>./)@pos&(rook,king) ok=: bishok*rookok perm=: A.&i.~ ! valid=: (#~ ok"1) ~.row0{"1~perm 8 gen=: valid {~ ? bind 960</lang>

Example use:

<lang J> gen ♘♗♖♔♗♕♖♘

  gen

♗♘♘♗♖♔♖♕

  gen

♖♗♔♘♘♕♗♖

  gen

♖♔♕♗♗♘♖♘</lang>

Java

Works with: Java version 1.5+

Regex inspired by (original) Python Regexp, prints ten examples. <lang java5>import java.util.Arrays; import java.util.Collections; import java.util.List;

public class Chess960{ private static List<Character> pieces = Arrays.asList('R','B','N','Q','K','N','B','R');

public static List<Character> generateFirstRank(){ do{ Collections.shuffle(pieces); }while(!check(pieces.toString().replaceAll("[^\\p{Upper}]", ""))); //List.toString adds some human stuff, remove that

return pieces; }

private static boolean check(String rank){ if(!rank.matches(".*R.*K.*R.*")) return false; //king between rooks if(!rank.matches(".*B(..|....|......|)B.*")) return false; //all possible ways bishops can be placed return true; }

public static void main(String[] args){ for(int i = 0; i < 10; i++){ System.out.println(generateFirstRank()); } } }</lang>

Output:
[R, N, K, N, R, B, B, Q]
[B, B, Q, R, N, K, N, R]
[R, K, Q, N, N, R, B, B]
[N, B, B, N, R, K, Q, R]
[R, Q, B, B, K, N, N, R]
[R, K, B, Q, N, B, N, R]
[N, N, R, K, Q, B, B, R]
[R, N, K, Q, N, B, B, R]
[N, R, B, K, Q, B, N, R]
[N, Q, N, R, K, B, B, R]

Perl

Directly generates a configuration by inserting pieces at random appropriate places. Each config has an equal chance of being produced. <lang perl>sub rnd($) { int(rand(shift)) }

sub empties { grep !$_[0][$_], 0 .. 7 }

sub chess960 { my @s = (undef) x 8; @s[2*rnd(4), 1 + 2*rnd(4)] = qw/B B/;

for (qw/Q N N/) { my @idx = empties \@s; $s[$idx[rnd(@idx)]] = $_; }

@s[empties \@s] = qw/R K R/; @s } print "@{[chess960]}\n" for 0 .. 10;</lang>

Output:
R N B K R N Q B
N N R K B R Q B
N N Q R K R B B
Q R N K B N R B
R K R B N Q B N
B R K B Q N R N
B R N B Q K N R
R B Q N N K B R
N R N Q K R B B
R Q N K R B B N
R K N Q B B R N

Perl 6

First, using a list with three rooks and no king, we keep generating a random piece order until the two bishops are on opposite colors. Then we sneakily promote the second of the three rooks to a king. <lang perl6>repeat until m/ '♗' [..]* '♗' / { $_ = < ♖ ♖ ♖ ♕ ♗ ♗ ♘ ♘ >.pick(*).join } s:2nd['♖'] = '♔'; say .comb;</lang>

Output:
♕ ♗ ♖ ♘ ♔ ♖ ♗ ♘

Here's a more "functional" solution that avoids side effects; it defines a "constant" infinite sequence of random boards. <lang perl6>constant chess960 =

   map *.subst(:nth(2), /'♜'/, '♚'),
       grep rx/ '♝' [..]* '♝' /,
            < ♛ ♜ ♜ ♜ ♝ ♝ ♞ ♞ >.pick(*).join xx *;

.say for chess960[^10];</lang>

Output:
♛♝♜♚♝♞♞♜
♞♛♝♞♜♝♚♜
♜♝♚♜♝♛♞♞
♝♜♞♝♛♞♚♜
♜♚♞♛♜♝♝♞
♝♜♞♚♛♜♞♝
♛♝♝♞♜♞♚♜
♞♝♜♛♝♚♞♜
♜♚♞♞♝♜♛♝
♝♝♛♞♜♚♜♞

We can also pregenerate the list of 960 positions, though the method we use below is a bit wasteful, since it generates 40320 candidates only to throw most of them away. This is essentially the same filtering algorithm but written in the form of a list comprehension rather than nested map and grep. (The list comprehension is actually faster currently.) Note that the constant is calculated at compile time, because, well, it's a constant. Just a big fancy one.

<lang perl6>constant chess960 = eager

   .subst(:nth(2), /'♜'/, '♚') 
       if / '♝' [..]* '♝' /
           for < ♛ ♜ ♜ ♜ ♝ ♝ ♞ ♞ >.permutations».join.uniq;

.say for chess960;</lang> Here's a much faster way (about 30x) to generate all 960 variants by construction. No need to filter for uniqueness, since it produces exactly 960 entries. <lang perl6>sub insert(@a,$p,$e) { @a[^$p], $e, @a[$p..*] }

constant chess960 = gather for 0..3 -> $q {

   my @q = insert <♜ ♚ ♜>, $q, '♛';
   for 0 .. @q -> $n1 {
       my @n1 = insert @q, $n1, '♞';
       for $n1 ^.. @n1 -> $n2 {
           my @n2 = insert @n1, $n2, '♞';
           for 0 .. @n2 -> $b1 {
               my @b1 = insert @n2, $b1, '♝';
               for $b1+1, $b1+3 ...^ * > @b1 -> $b2 {
                   my @b2 = insert @b1, $b2, '♝';
                   take @b2.join;
               }
           }
       }
   }

}

CHECK { note "done compiling" } note +chess960; say chess960.pick;</lang>

Output:
done compiling
960
♜♚♝♜♞♛♞♝

If you run this you'll see that most of the time is spent in compilation, so in the case of separate precompilation the table of 960 entries merely needs to be deserialized back into memory. Picking from those entries guarantees uniform distribution over all possible boards.

Python

Python: Indexing

This uses indexing rather than regexps. Rooks and bishops are in upper and lower case to start with so they can be individually indexed to apply the constraints. This would lead to some duplication of start positions if not for the use of a set comprehension to uniquify the, (upper-cased), start positions.

<lang python>>>> from itertools import permutations >>> pieces = 'KQRrBbNN' >>> starts = {.join(p).upper() for p in permutations(pieces)

                    if p.index('B') % 2 != p.index('b') % 2 		# Bishop constraint
                    and ( p.index('r') < p.index('K') < p.index('R')	# King constraint	
                          or p.index('R') < p.index('K') < p.index('r') ) }

>>> len(starts) 960 >>> starts.pop() 'QNBRNKRB' >>> </lang>

Python: Regexp

This uses regexps to filter permutations of the start position pieces rather than indexing. <lang python>>>> import re >>> pieces = 'KQRRBBNN' >>> bish = re.compile(r'B(|..|....|......)B').search >>> king = re.compile(r'R.*K.*R').search >>> starts3 = {p for p in (.join(q) for q in permutations(pieces))

           if bish(p) and king(p)}

>>> len(starts3) 960 >>> starts3.pop() 'QRNKBNRB' >>> </lang>

Python: Correct by construction

Follows Perl algorithm of constructing one start position randomly, according to the rules. (See talk page for tests). <lang python>from random import choice

def random960():

   start = ['R', 'K', 'R']         # Subsequent order unchanged by insertions.
   #
   for piece in ['Q', 'N', 'N']:
       start.insert(choice(range(len(start)+1)), piece)
   #
   bishpos = choice(range(len(start)+1))
   start.insert(bishpos, 'B')
   start.insert(choice(range(bishpos + 1, len(start) + 1, 2)), 'B')
   return start
   return .join(start).upper()

print(random960())</lang>

Output:
['N', 'R', 'K', 'N', 'B', 'Q', 'R', 'B']

Racket

Constructive:

<lang racket>#lang racket (define white (match-lambda ['P #\♙] ['R #\♖] ['B #\♗] ['N #\♘] ['Q #\♕] ['K #\♔])) (define black (match-lambda ['P #\♟] ['R #\♜] ['B #\♝] ['N #\♞] ['Q #\♛] ['K #\♚]))

(define (piece->unicode piece colour)

 (match colour ('w white) ('b black)) piece)

(define (find/set!-random-slot vec val k (f values))

 (define r (f (random k)))
 (cond
   [(vector-ref vec r)
    (find/set!-random-slot vec val k f)]
   [else
    (vector-set! vec r val)
    r]))

(define (chess960-start-position)

 (define v (make-vector 8 #f))  
 ;; Kings and Rooks
 (let ((k (find/set!-random-slot v (white 'K) 6 add1)))
   (find/set!-random-slot v (white 'R) k)
   (find/set!-random-slot v (white 'R) (- 7 k) (curry + k 1)))
 ;; Bishops -- so far only three squares allocated, so there is at least one of each colour left
 (find/set!-random-slot v (white 'B) 4 (curry * 2))
 (find/set!-random-slot v (white 'B) 4 (compose add1 (curry * 2)))
 ;; Everyone else
 (find/set!-random-slot v (white 'Q) 8)
 (find/set!-random-slot v (white 'N) 8)
 (find/set!-random-slot v (white 'N) 8)
 (list->string (vector->list v)))

(chess960-start-position)</lang>

Output:
"♖♘♗♕♔♗♘♖"

Well that's embarassing... the stupid thing has only gone and randomly generated a classic chess starting position.

Try again:

"♘♖♔♕♗♗♖♘"


REXX

Random starting position is correct by construction   (both REXX entries).

generates one random position

<lang rexx>/*REXX pgm generates a random starting position for the Chess960 game.*/ parse arg seed . /*allow for (RAND) repeatability.*/ if seed\== then call random ,,seed /*if SEED specified, use the seed*/ @.=. /*define the (empty) first rank. */ r1=random(1,6); @.r1='R' /*place the first rook on rank1.*/ rm=r1-1; rp=r1+1 /*used for faster comparisons. */

 do forever;  r2=random(1,8)          /*try to get a random second rook*/
 if r2==r1 | r2==rm | r2==rp  then iterate /*position of 2nd rook ¬good*/
 leave                                /*found a good 2nd rook placement*/
 end   /*forever*/                    /* [↑]  separate IFs for speed.  */

@.r2='r' /*place the second rook on rank1.*/ c1=min(r1,r2); c2=max(r1,r2) /*order the two rooks for RANDOM.*/ _ =random(c1+1,c2-1); @._ ='K' /* " " only king " " */

         do _=0      ; b1=random(1,8);  if @.b1\==. then iterate; c=b1//2
           do forever; b2=random(1,8) /* c=color of bishop ───────┘    */
           if @.b2\==. | b2==b1 | b2//2==c    then iterate;       leave _
           end   /*forever*/          /* [↑]  find a place: 1st bishop.*/
         end     /*_*/                /* [↑]    "  "   "    2nd    "   */

@.b1='B' /*place the 1st bishop on rank1*/ @.b2='b' /* " " 2nd " " " */

                                      /*place two knights on rank1.    */
  do  until @._='N'; _=random(1,8); if @._\==. then iterate; @._='N'; end
  do  until @.!='n'; !=random(1,8); if @.!\==. then iterate; @.!='n'; end

_= /*only the queen is left to place*/

  do i=1  for 8;  _=_ || @.i;   end   /*construct output:  first rank. */

say translate(translate(_, 'q', .)) /*stick a fork in it, we're done.*/</lang> output

NRQKBRNB

generates 960 random positions

This REXX version had some parts of the code optimized by short-circuiting two compound if statements. <lang rexx>/*REXX pgm generates all random starting positions for the Chess960 game*/ parse arg seed . /*allow for (RAND) repeatability.*/ if seed\== then call random ,,seed /*if SEED specified, use the seed*/ x.=0; #=0

do t=1 /*═══════════════════════════════════════════════════════════════*/ if t//1000==0 then say right(t,9) 'random generations: ' # " unique starting positions." @.=. /*define the (empty) first rank. */ r1=random(1,6); @.r1='R' /*place the first rook on rank1.*/ r1m=r1-1; r1p=r1+1 /*used for faster comparisons. */

  do forever;  r2=random(1,8)         /*try to get a random second rook*/
  if r2==r1    then iterate           /*position is the same as rook1. */
  if r2==r1m   then iterate           /*it's immediately before rook1. */
  if r2==r1p   then iterate           /*  "       "       after   "    */
  leave                               /*found a good 2nd rook placement*/
  end   /*forever*/                   /* [↑]  separate IFs for speed.  */

@.r2='r' /*place the second rook on rank1.*/ c1=min(r1,r2); c2=max(r1,r2) /*order the two rooks for RANDOM.*/ _ =random(c1+1,c2-1); @._ ='K' /* " " only king " " */

         do _=0      ; b1=random(1,8);  if @.b1\==. then iterate; c=b1//2
           do forever; b2=random(1,8) /* c=color of bishop ───────┘    */
           if b2//2==c  then iterate  /*bishop2 = same color as bishop1*/
           if @.b2\==.  then iterate  /*the position is already taken. */
           if b2==b1    then iterate  /*position is taken by bishop 1. */
           leave _                    /*we found a position for bishop2*/
           end   /*forever*/          /* [↑]  find a place: 1st bishop.*/
         end     /*_*/                /* [↑]    "  "   "    2nd    "   */

@.b1='B' /*place the 1st bishop on rank1*/ @.b2='b' /* " " 2nd " " " */

                                      /*place two knights on rank1.    */
  do  until @._='N'; _=random(1,8); if @._\==. then iterate; @._='N'; end
  do  until @.!='n'; !=random(1,8); if @.!\==. then iterate; @.!='n'; end

_= /*only the queen is left to place*/

  do i=1  for 8;  _=_ || @.i;   end   /*construct output:  first rank. */

upper _ /*uppercase all chess pieces. */ if x._ then iterate /*was this position found before?*/ x._=1 /*define this position as found. */

  1. =#+1 /*bump the unique positions found*/

if #==960 then leave end /*t ══════════════════════════════════════════════════════════════*/

say # 'unique starting positions found after ' t "generations."

                                      /*stick a fork in it, we're done.*/</lang>

output

     1000 random generations:  515  unique starting positions.
     2000 random generations:  707  unique starting positions.
     3000 random generations:  796  unique starting positions.
     4000 random generations:  849  unique starting positions.
     5000 random generations:  883  unique starting positions.
     6000 random generations:  900  unique starting positions.
     7000 random generations:  922  unique starting positions.
     8000 random generations:  935  unique starting positions.
     9000 random generations:  942  unique starting positions.
    10000 random generations:  946  unique starting positions.
    11000 random generations:  953  unique starting positions.
    12000 random generations:  957  unique starting positions.
    13000 random generations:  959  unique starting positions.
    14000 random generations:  959  unique starting positions.
960 unique starting positions found after  14639 generations.

Ruby

Ruby: shuffle pieces until all regexes match

Translation of Tcl. <lang ruby>pieces = %i(♔ ♕ ♘ ♘ ♗ ♗ ♖ ♖) regexes = [/♗(..)*♗/, /♖.*♔.*♖/] row = pieces.shuffle.join until regexes.all?{|re| re.match(row)} puts row</lang>

Output:
♕♖♗♘♔♖♘♗

Ruby: Construct

Uses the Perl idea of starting with [R,K,R] and inserting the rest: <lang ruby>row = [:♖, :♔, :♖] [:♕, :♘, :♘].each{|piece| row.insert(rand(row.size+1), piece)} [[0, 2, 4, 6].sample, [1, 3, 5, 7].sample].sort.each{|pos| row.insert(pos, :♗)}

puts row </lang>

Output:
♗♘♕♘♖♗♔♖

Tcl

Using regular expressions to filter a random permutation.

Library: Tcllib (Package: struct::list)

<lang tcl>package require struct::list

proc chess960 {} {

   while true {

set pos [join [struct::list shuffle {N N B B R R Q K}] ""] if {[regexp {R.*K.*R} $pos] && [regexp {B(..)*B} $pos]} { return $pos }

   }

}

  1. A simple renderer

proc chessRender {position} {

   string map {P ♙ N ♘ B ♗ R ♖ Q ♕ K ♔} $position

}

  1. Output multiple times just to show scope of positions

foreach - {1 2 3 4 5} {puts [chessRender [chess960]]}</lang>

Output:
♕♖♘♔♗♗♘♖
♖♔♘♘♗♕♖♗
♘♖♗♗♕♔♘♖
♘♕♗♖♔♖♘♗
♘♘♖♔♗♗♕♖