12
\$\begingroup\$

Context

Chess960 (or Fischer Random Chess) is a variant of chess invented and advocated by former World Chess Champion Bobby Fischer, publicly announced on June 19, 1996 in Buenos Aires, Argentina. It employs the same board and pieces as standard chess; however, the starting position of the pieces on the players' home ranks is randomized

Rules

  • White pawns are placed on the second rank as in standard chess
  • All remaining white pieces are placed randomly on the first rank
  • The bishops must be placed on opposite-color squares
  • The king must be placed on a square between the rooks.
  • Black's pieces are placed equal-and-opposite to White's pieces.

From: http://en.wikipedia.org/wiki/Chess960

For all the people that would like to post answers...

you have to make a Chess960 positions generator, capable of randomly generate one of the 960 positions following the rules described above (it has to be capable of outputting any of the 960, hardcoding one position is not accepted!), and you only need to output the white rank one pieces.

Example output:

rkrbnnbq

where:

  • k king
  • q queen
  • b bishop
  • n knight
  • r rook

This will be code golf, and the tie breaker will be the upvotes.

\$\endgroup\$
5
  • \$\begingroup\$ When you say that it has to be capable of outputting any of the 960 positions, do they have to be equiprobable? \$\endgroup\$ Jun 20, 2013 at 10:05
  • \$\begingroup\$ Interesting, I haven't really thought of that... I mean ideally it should be, I think... The answers so far offer this quality, ...right ? \$\endgroup\$
    – jsedano
    Jun 20, 2013 at 14:08
  • \$\begingroup\$ The two which are written in languages which have builtins that shuffle uniformly do; the two GolfScript ones are close but not quite uniform. \$\endgroup\$ Jun 20, 2013 at 14:15
  • \$\begingroup\$ I would say that close is good enough \$\endgroup\$
    – jsedano
    Jun 20, 2013 at 14:25
  • \$\begingroup\$ This question inspired me to ask codegolf.stackexchange.com/questions/12322/… \$\endgroup\$ Aug 17, 2013 at 11:26

8 Answers 8

8
\$\begingroup\$

Ruby 1.9, 67 65 characters

Ah, the old "keep randomizing until you generate something valid" technique...

$_=%w(r r n n b b q k).shuffle*''until/r.*k.*r/&&/b(..)*b/
$><<$_

(In Ruby 2.0, %w(r r n n b b q k) could be 'rrnnbbqk'.chars)

\$\endgroup\$
7
  • 1
    \$\begingroup\$ In 1.9.3 you can spare the ~ with the cost of a warning, when available. pastebin.com/nuE9zWSw \$\endgroup\$
    – manatwork
    Jun 20, 2013 at 6:56
  • \$\begingroup\$ @manatwork that's great, thanks! \$\endgroup\$ Jun 20, 2013 at 8:57
  • 2
    \$\begingroup\$ the "keep randomizing until you generate something valid" technique is still much faster than the "shuffle the list of possibilities, filter and take first" technique that purely functional languages like APL tend to produce :-) \$\endgroup\$ Jun 20, 2013 at 9:56
  • 1
    \$\begingroup\$ @Daniero that's definitely what the $_ variable is. It works because ruby has some neat methods such as Kernel#chop that work like the equivalent String#chop method but with $_ as their receiver. This saves a lot of time when (for example) you're writing a read/process/write loop using ruby -n or ruby -p. \$\endgroup\$ Jun 20, 2013 at 22:43
  • 2
    \$\begingroup\$ @GigaWatt no. The former matches if there's an even number of characters between some two B's. The latter matches only if the B'S are at the ends. \$\endgroup\$ Jun 23, 2013 at 12:39
8
\$\begingroup\$

GolfScript 60 49

;'qbbnnxxx'{{9rand*}$.'b'/1=,2%}do'x'/'rkr'1/]zip

(shortened to 49 chars thanks to Peter Taylor's great tips)

Online test here.

An explanation of the code:

;'qbbnnxxx'         # push the string 'qbbnnxxx' on the clean stack
{

    {9rand*}$       # shuffle the string

    .'b'/1=,2%      # count the number of places between the 'b's
                    # (including the 'b's themselves)
                    # if this count is even, the bishops are on
                    # squares of different colors, so place a 0
                    # on the stack to make the do loop stop

}do                 # repeat the procedure above until a 
                    # good string is encountered

'x'/                # split the string where the 'x's are

'rkr'1/]zip         # and put 'r', 'k' and then 'r' again
                    # where the 'x's used to be
\$\endgroup\$
3
  • 1
    \$\begingroup\$ Your method for checking that there's an even number of letters between the bs seems very long. How about .'b'/1=,2%? \$\endgroup\$ Jun 20, 2013 at 10:10
  • \$\begingroup\$ And you can avoid discarding failed attempts by pulling the 'qbbnnxxx' out of the loop and reshuffling the same string. \$\endgroup\$ Jun 20, 2013 at 10:15
  • \$\begingroup\$ @PeterTaylor Thank you for the great tips. For the "count between 'b's" issue I felt that there should be a shorter way, but I just couldn't find it. \$\endgroup\$ Jun 20, 2013 at 11:23
6
\$\begingroup\$

GolfScript (49 48 chars, or 47 for upper-case output)

'bbnnrrkq'{{;9rand}$.'b'/1=,1$'r'/1='k'?)!|1&}do

This uses the standard technique of permuting randomly until we meet the criteria. Unlike w0lf's GolfScript solution, this does both checks on the string, so it is likely to run through the loop more times.

Using upper case allows saving one char:

'BBNNRRKQ'{{;9rand}$.'B'/1=,1$'R'/1=75?)!|1&}do
\$\endgroup\$
5
\$\begingroup\$

J, 56 characters

{.(#~'(?=.*b(..)*b).*r.*k.*r.*'&rxeq"1)'kqbbnnrr'A.~?~!8

it takes several seconds on my machine due to the inefficient algorithm. Some speed may be gained by adding ~.(remove duplicates) before 'kqbbnnrr'.

explanation:

  • ?~!8 deals 8! random elements from 0 ... 8!
  • 'kqbbnnrr'A.~ uses them as anagram indexes to the string kqbbnnrr.
  • (#~'...'&rxeq"1)' filters them by the regex in quotes.
  • {. means "take the first element"
\$\endgroup\$
4
\$\begingroup\$

K,69

(-8?)/[{~*(*/~~':{m=_m:x%2}@&x="b")&(&x="k")within&"r"=x};"rrbbnnkq"]
\$\endgroup\$
3
\$\begingroup\$

Python, 105 chars

Basically chron's technique, minus the elegant Ruby stuff.

import re,random
a='rrbbnnkq'
while re.search('b.(..)*b|r[^k]*r',a):a=''.join(random.sample(a,8))
print a

Thanks to Peter Taylor for the shortening of the regex.

\$\endgroup\$
4
  • \$\begingroup\$ not s('b(..)*b',a) seems like a long-winded way of saying s('b.(..)*b',a). Also, sample may be one character shorter than shuffle, but it requires an extra argument. \$\endgroup\$ Jun 22, 2013 at 20:39
  • \$\begingroup\$ You're right about the regex, Peter. Thanks! Shuffle returns None though, so it's no good :( \$\endgroup\$
    – daniero
    Jun 22, 2013 at 20:41
  • 1
    \$\begingroup\$ Missed the forest for the trees. You don't need two regexes, because you're checking the same string and or is equivalent to regex alternation (|). Saves 13 chars. \$\endgroup\$ Jul 4, 2013 at 20:58
  • \$\begingroup\$ @PeterTaylor Good catch! thanks. \$\endgroup\$
    – daniero
    Jul 5, 2013 at 6:19
1
\$\begingroup\$

Retina, 36 30 bytes

6 bytes saved thanks to Neil


qbnbnrrr
/b.(..)*b/+V?`
1`r
k

Try it online!

Explanation


qbnbnrrr

Start with the list of pieces except we have three rooks and no king

/b.(..)*b/+V?`

Keep shuffling the list as long as it matches the regex b.(..)*b (odd number of pieces between bishops)

1`r
k

Replace the second rook with a king

\$\endgroup\$
2
  • 1
    \$\begingroup\$ Save 2 bytes each removing the .* from the loop condition. (Although I would have written it /b(..)*b/^+ for the same reduced byte count.) And you can just use a limit to replace the second r with a k, saving another 2 bytes. \$\endgroup\$
    – Neil
    Feb 20 at 9:11
  • \$\begingroup\$ Thanks @Neil! For some reason I thought the loop condition had to match the full input, I didn't know it could be a partial match. And I just didn't think of this much simpler way of replacing the second r \$\endgroup\$
    – Leo
    Feb 20 at 22:54
0
\$\begingroup\$

Perl, 69 chars

$_=rrbbnnkq;/r[^k]*r|b.(..)*b/||say while s/(.{@{[time%8]}})(.)/$2$1/

To test it, add $|=1; at the beginning to see progress and keep tabs by pipeing into another perl process like this:

perl -E'$|=1;$_=rrbbnnkq;/r[^k]*r|b.(..)*b/||say while s/(.{@{[time%7]}})(.)/$2$1/' \
| perl -nlE'$a{$_}++ or say"$_ ".keys%a'

This will e v e n t u a l l y get the 960 valid permutations.

To run the test fast, swap out time%8 with int rand 8 like this:

perl -E'$_=rrbbnnkq;/r[^k]*r|b.(..)*b/||say while s/(.{@{[int rand 8]}})(.)/$2$1/' \
| perl -nlE'$a{$_}++||say"$_ ".keys%a'
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.