Chess960 position generator

11

2

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.

jsedano

Posted 2013-06-20T04:11:55.437

Reputation: 1 607

When you say that it has to be capable of outputting any of the 960 positions, do they have to be equiprobable? – Peter Taylor – 2013-06-20T10:05:51.193

Interesting, I haven't really thought of that... I mean ideally it should be, I think... The answers so far offer this quality, ...right ? – jsedano – 2013-06-20T14:08:05.240

The two which are written in languages which have builtins that shuffle uniformly do; the two GolfScript ones are close but not quite uniform. – Peter Taylor – 2013-06-20T14:15:56.433

I would say that close is good enough – jsedano – 2013-06-20T14:25:48.033

This question inspired me to ask http://codegolf.stackexchange.com/questions/12322/chess960-position-lookup

– user123444555621 – 2013-08-17T11:26:58.193

Answers

6

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

Peter Taylor

Posted 2013-06-20T04:11:55.437

Reputation: 41 901

8

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)

Paul Prestidge

Posted 2013-06-20T04:11:55.437

Reputation: 2 390

1

In 1.9.3 you can spare the ~ with the cost of a warning, when available. http://pastebin.com/nuE9zWSw

– manatwork – 2013-06-20T06:56:34.617

@manatwork that's great, thanks! – Paul Prestidge – 2013-06-20T08:57:28.190

2the "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 :-) – John Dvorak – 2013-06-20T09:56:06.023

I think this is quite elegant. Can you explain why until/r.*k.*r/&&/b(..)*b/ actually works? It's easy enough to imagine what it does given the context, but I don't get why you assign to the $_ variable, which is supposed to be The last input line of string by gets or readline. Thread and scope local. - What has that to do with anything? Oh, mysterious Ruby. – daniero – 2013-06-20T15:37:16.657

1@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. – Paul Prestidge – 2013-06-20T22:43:03.307

Can't /b(..)*b/ be shortened to /^b.*b$/? – Mr. Llama – 2013-06-21T19:38:18.823

2@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. – John Dvorak – 2013-06-23T12:39:51.583

8

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

Cristian Lupascu

Posted 2013-06-20T04:11:55.437

Reputation: 8 369

1Your method for checking that there's an even number of letters between the bs seems very long. How about .'b'/1=,2%? – Peter Taylor – 2013-06-20T10:10:38.730

And you can avoid discarding failed attempts by pulling the 'qbbnnxxx' out of the loop and reshuffling the same string. – Peter Taylor – 2013-06-20T10:15:34.203

@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. – Cristian Lupascu – 2013-06-20T11:23:27.077

4

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"

John Dvorak

Posted 2013-06-20T04:11:55.437

Reputation: 9 048

4

K,69

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

tmartin

Posted 2013-06-20T04:11:55.437

Reputation: 3 917

3

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.

daniero

Posted 2013-06-20T04:11:55.437

Reputation: 17 193

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. – Peter Taylor – 2013-06-22T20:39:01.310

You're right about the regex, Peter. Thanks! Shuffle returns None though, so it's no good :( – daniero – 2013-06-22T20:41:39.563

1Missed 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. – Peter Taylor – 2013-07-04T20:58:24.237

@PeterTaylor Good catch! thanks. – daniero – 2013-07-05T06:19:46.727