Generate Chess960 starting position: Difference between revisions
(70 intermediate revisions by 38 users not shown) | |||
Line 1: | Line 1: | ||
{{task}} |
{{task}} [[Category:Chess960]] |
||
'''[[wp:Chess960|Chess960]]''' is a variant of chess created by world champion [[wp:Bobby Fischer|Bobby Fischer]]. 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: |
|||
'''[[wp:Chess960|Chess960]]''' is a variant of chess created by world champion [[wp:Bobby Fisher|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. |
* as in the standard chess game, all eight white pawns must be placed on the second rank. |
||
Line 14: | Line 13: | ||
;Task: |
;Task: |
||
The purpose of this task is to write a program that can randomly generate any one of the 960 Chess960 initial positions. |
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 using either the [[wp:Chess symbols in Unicode|chess symbols in Unicode (♔♕♖♗♘)]], the letters '''K'''ing '''Q'''ueen '''R'''ook '''B'''ishop k'''N'''ight, or the corresponding letters in a language other than English. |
||
<br><br> |
<br><br> |
||
=={{header|11l}}== |
|||
{{trans|Python: Correct by construction}} |
|||
<syntaxhighlight lang="11l">F random960() |
|||
V start = [‘R’, ‘K’, ‘R’] |
|||
L(piece) [‘Q’, ‘N’, ‘N’] |
|||
start.insert(random:(start.len + 1), piece) |
|||
V bishpos = random:(start.len + 1) |
|||
start.insert(bishpos, Char(‘B’)) |
|||
start.insert(random:(bishpos + 1), Char(‘B’)) |
|||
R start |
|||
print(random960())</syntaxhighlight> |
|||
{{out}} |
|||
<pre> |
|||
[Q, B, N, R, K, B, N, R] |
|||
</pre> |
|||
=={{header|Action!}}== |
|||
<syntaxhighlight lang="action!">DEFINE MAX_NUMBERS="200" |
|||
DEFINE MAX_LEN="20" |
|||
DEFINE MAX_FACTORS="5" |
|||
DEFINE PTR="CARD" |
|||
PROC PrintResult(BYTE max,n BYTE ARRAY factors PTR ARRAY texts) |
|||
BYTE i,j,t |
|||
BYTE ARRAY values(MAX_FACTORS) |
|||
FOR j=0 TO n-1 |
|||
DO |
|||
values(j)=1 |
|||
OD |
|||
FOR i=1 TO max |
|||
DO |
|||
t=0 |
|||
FOR j=0 TO n-1 |
|||
DO |
|||
IF values(j)=0 THEN |
|||
t=1 Print(texts(j)) |
|||
FI |
|||
values(j)==+1 |
|||
IF values(j)=factors(j) THEN |
|||
values(j)=0 |
|||
FI |
|||
OD |
|||
IF t=0 THEN PrintB(i) FI |
|||
Put(32) |
|||
OD |
|||
RETURN |
|||
BYTE FUNC Find(CHAR ARRAY s CHAR c BYTE POINTER err) |
|||
BYTE i |
|||
FOR i=1 TO s(0) |
|||
DO |
|||
IF s(i)=c THEN |
|||
err^=0 RETURN (i) |
|||
FI |
|||
OD |
|||
err^=1 |
|||
RETURN (0) |
|||
PROC Main() |
|||
BYTE max,i,n,pos,err |
|||
BYTE ARRAY factors(MAX_FACTORS) |
|||
PTR ARRAY texts(MAX_FACTORS) |
|||
CHAR ARRAY |
|||
s(100),tmp(100), |
|||
t0(MAX_LEN),t1(MAX_LEN),t2(MAX_LEN), |
|||
t3(MAX_LEN),t4(MAX_LEN) |
|||
texts(0)=t0 texts(1)=t1 texts(2)=t2 |
|||
texts(3)=t3 texts(4)=t4 |
|||
DO |
|||
PrintF("Max number (1-%B): ",MAX_NUMBERS) |
|||
max=InputB() |
|||
UNTIL max>=1 AND max<=MAX_NUMBERS |
|||
OD |
|||
n=0 |
|||
DO |
|||
PrintF("Number of rules (1-%B): ",MAX_FACTORS) |
|||
n=InputB() |
|||
UNTIL n>=1 AND n<=MAX_FACTORS |
|||
OD |
|||
FOR i=0 TO n-1 |
|||
DO |
|||
DO |
|||
PrintF("Rule #%B (number space text):",i+1) |
|||
InputS(s) |
|||
pos=Find(s,' ,@err) |
|||
IF pos=1 OR pos=s(0) THEN |
|||
err=1 |
|||
FI |
|||
IF err=0 THEN |
|||
SCopyS(tmp,s,1,pos-1) |
|||
factors(i)=ValB(tmp) |
|||
IF factors(i)<2 THEN |
|||
err=1 |
|||
FI |
|||
SCopyS(texts(i),s,pos+1,s(0)) |
|||
FI |
|||
UNTIL err=0 |
|||
OD |
|||
OD |
|||
PutE() |
|||
PrintResult(max,n,factors,texts) |
|||
RETURN</syntaxhighlight> |
|||
{{out}} |
|||
[https://gitlab.com/amarok8bit/action-rosetta-code/-/raw/master/images/Generate_Chess960_starting_position_.png Screenshot from Atari 8-bit computer] |
|||
<pre> |
|||
NBBQRNKR |
|||
NRKBRQBN |
|||
BNRBKRNQ |
|||
QBRKRNBN |
|||
RBKNRQBN |
|||
BNNBRKQR |
|||
NQNBRKBR |
|||
BRNKRBQN |
|||
NRKRQNBB |
|||
RNQBKRBN |
|||
</pre> |
|||
=={{header|APL}}== |
|||
This function accepts a SP-ID and generates the corresponding position; to generate a random one, just pass in roll 960 (<tt>?960</tt>). (It's written for index origin 1, but reduces the passed-in SP-ID modulo 960 so that works without having to subtract 1 from the roll result.) |
|||
{{works with|Dyalog APL}} |
|||
{{works with|GNU APL}} |
|||
<syntaxhighlight lang="apl">⍝ Utility functions |
|||
divmod ← {(⌊⍺÷⍵),⍵|⍺} |
|||
indices ← {(⍺∊⍵)/⍳⍴⍺} |
|||
∇result ← place placement; array; index; piece; result |
|||
(array piece index) ← placement |
|||
array[(array indices '-')[index]] ← piece |
|||
result ← array |
|||
∇ |
|||
∇result ← chess960 spid; array; n; b1; b2; n1; n2; q |
|||
spid ← 960 | spid |
|||
array ← 8/'-' |
|||
(n b1) ← spid divmod 4 |
|||
array[2+2×b1] ← 'B' |
|||
(n b2) ← n divmod 4 |
|||
array[1+2×b2] ← 'B' |
|||
(n q) ← n divmod 6 |
|||
array ← place array 'Q' (1+q) |
|||
n1 ← 1⍳⍨n<4 7 9 10 |
|||
array ← place array 'N' n1 |
|||
n2 ← (1 2 3 4 2 3 4 3 4 4)[n+1] |
|||
array ← place array 'N' n2 |
|||
array ← place array 'R' 1 |
|||
array ← place array 'K' 1 |
|||
array ← place array 'R' 1 |
|||
result ← spid, array |
|||
∇</syntaxhighlight> |
|||
{{Out}} |
|||
<pre> chess960 518 |
|||
518 RNBQKBNR |
|||
chess960 ?960 |
|||
377 NRKBBRNQ |
|||
chess960 ?960 |
|||
487 QRBNKNRB</pre> |
|||
=={{header|Arturo}}== |
|||
<syntaxhighlight lang="arturo">; Using Edward Collins' single-die method |
|||
; http://www.edcollins.com/chess/fischer-random.htm |
|||
chess960: function [][ |
|||
result: array.of: 8 ø |
|||
vacant: @0..7 ; open squares available to put pieces |
|||
result\[remove 'vacant <= 2 * random 0 3]: 'bishop ; place on random black square |
|||
result\[remove 'vacant <= 1 + 2 * random 0 3]: 'bishop ; place on random white square |
|||
loop ['queen 'knight 'knight] 'piece [ |
|||
result\[remove 'vacant <= sample vacant]: piece ; place on random open square |
|||
] |
|||
result\[vacant\0]: 'rook ; place king between rooks on remaining open squares |
|||
result\[vacant\1]: 'king |
|||
result\[vacant\2]: 'rook |
|||
result |
|||
] |
|||
do.times:5 -> print chess960</syntaxhighlight> |
|||
{{out}} |
|||
<pre>bishop knight rook queen king knight rook bishop |
|||
knight rook queen bishop bishop king knight rook |
|||
rook bishop bishop knight knight king rook queen |
|||
bishop knight rook queen king knight rook bishop |
|||
rook king knight knight bishop queen rook bishop</pre> |
|||
=={{header|AutoHotkey}}== |
=={{header|AutoHotkey}}== |
||
{{works with|AutoHotkey 1.1}} |
{{works with|AutoHotkey 1.1}} |
||
< |
<syntaxhighlight lang="autohotkey">Loop, 5 |
||
Out .= Chess960() "`n" |
Out .= Chess960() "`n" |
||
MsgBox, % RTrim(Out, "`n") |
MsgBox, % RTrim(Out, "`n") |
||
Line 51: | Line 249: | ||
Random, n, Min, Max |
Random, n, Min, Max |
||
return n |
return n |
||
}</ |
}</syntaxhighlight> |
||
{{Output}} |
{{Output}} |
||
<big><big><pre>♕♘♖♗♗♘♔♖ |
<big><big><pre>♕♘♖♗♗♘♔♖ |
||
Line 58: | Line 256: | ||
♗♗♘♖♔♕♘♖ |
♗♗♘♖♔♕♘♖ |
||
♘♗♖♔♗♘♕♖</pre></big></big> |
♘♗♖♔♗♘♕♖</pre></big></big> |
||
=={{header|BASIC}}== |
|||
==={{header|BASIC256}}=== |
|||
{{trans|Yabasic}} |
|||
<syntaxhighlight lang="basic256">for i = 1 to 10 |
|||
inicio$ = "RKR" |
|||
pieza$ = "QNN" |
|||
for n = 1 to length(pieza$) |
|||
posic = int(rand*(length(inicio$) + 1)) + 1 |
|||
inicio$ = left(inicio$, posic-1) + mid(pieza$, n, 1) + right(inicio$, length(inicio$) - posic + 1) |
|||
next n |
|||
posic = int(rand*(length(inicio$) + 1)) + 1 |
|||
inicio$ = left(inicio$, posic-1) + "B" + right(inicio$, length(inicio$) - posic + 1) |
|||
posic += 1 + 2 * int(int(rand*(length(inicio$) - posic)) / 2) |
|||
inicio$ = left(inicio$, posic-1) + "B" + right(inicio$, length(inicio$) - posic + 1) |
|||
print inicio$ |
|||
next i |
|||
end</syntaxhighlight> |
|||
==={{header|BBC BASIC}}=== |
|||
{{works with|BBC BASIC for Windows}} |
|||
<syntaxhighlight lang="bbcbasic"> VDU 23, 22, 240; 360; 8, 16, 16, 136 |
|||
*FONT Arial, 20 |
|||
FOR I% = 1 TO 10 |
|||
Rank1$ = "202" |
|||
FOR Piece = 1 TO 3 |
|||
P% = RND(LENRank1$ + 1) |
|||
Rank1$ = LEFT$(Rank1$, P% - 1) + MID$("144", Piece, 1) + MID$(Rank1$, P%) |
|||
NEXT |
|||
P% = RND(7) |
|||
Rank1$ = LEFT$(Rank1$, P% - 1) + "3" + MID$(Rank1$, P%) |
|||
IF P% > 5 P% += 1 ELSE P% += RND(4 - (P% >> 1)) * 2 - 1 |
|||
Rank1$ = LEFT$(Rank1$, P% - 1) + "3" + MID$(Rank1$, P%) |
|||
FOR Piece = 1 TO 8 |
|||
VDU &E2, &99, &94 + VALMID$(Rank1$, Piece, 1) |
|||
NEXT |
|||
PRINT |
|||
NEXT</syntaxhighlight> |
|||
{{out}} |
|||
<pre>♘ ♖ ♗ ♔ ♖ ♘ ♕ ♗ |
|||
♘ ♖ ♔ ♘ ♕ ♖ ♗ ♗ |
|||
♕ ♗ ♖ ♔ ♗ ♘ ♖ ♘ |
|||
♖ ♘ ♔ ♗ ♖ ♕ ♗ ♘ |
|||
♖ ♗ ♗ ♘ ♔ ♖ ♘ ♕ |
|||
♘ ♖ ♗ ♗ ♕ ♔ ♘ ♖ |
|||
♖ ♔ ♕ ♘ ♗ ♘ ♖ ♗ |
|||
♘ ♘ ♖ ♔ ♖ ♗ ♗ ♕ |
|||
♖ ♕ ♔ ♘ ♗ ♗ ♖ ♘ |
|||
♖ ♕ ♔ ♘ ♘ ♗ ♗ ♖</pre> |
|||
==={{header|Commodore BASIC}}=== |
|||
{{works with|Commodore BASIC|3.5,7.0}} |
|||
Besides the admittedly-trivial use of <tt>DO</tt>/<tt>LOOP</tt>, this implementation also exploits the BASIC 3.5+ ability to use <tt>MID$</tt> as an lvalue. |
|||
<syntaxhighlight lang="basic">100 REM CHESS 960 |
|||
110 PRINT "SPID (-1 FOR RANDOM):"; |
|||
120 OPEN 1,0:INPUT#1, SP$:CLOSE 1 |
|||
130 SP=VAL(SP$) |
|||
140 IF SP<0 THEN SP=INT(RND(.)*960) |
|||
150 PRINT |
|||
160 DO WHILE SP>959: SP=SP-960: LOOP |
|||
170 AR$="--------" |
|||
180 P=SP |
|||
190 N=P AND 3:P=INT(P/4) |
|||
200 MID$(AR$,2*N+2,1)="B" |
|||
210 N=P AND 3:P=INT(P/4) |
|||
220 MID$(AR$,2*N+1,1)="B" |
|||
230 N=P-6*INT(P/6):P=INT(P/6) |
|||
240 P$="Q":GOSUB 420 |
|||
250 N=P-10*INT(P/10):P=INT(P/10) |
|||
260 FOR N1=0 TO 3 |
|||
270 : FOR N2=N1+1 TO 4 |
|||
280 : IF N<>0 THEN 340 |
|||
290 : P$="N":N=N1:GOSUB 420 |
|||
300 : P$="N":N=N2-1:GOSUB 420 |
|||
310 : N1=3 |
|||
320 : N2=4 |
|||
340 : N=N-1 |
|||
350 : NEXT N2 |
|||
360 NEXT N1 |
|||
370 P$="R":N=0:GOSUB 420 |
|||
380 P$="K":N=0:GOSUB 420 |
|||
390 P$="R":N=0:GOSUB 420 |
|||
400 PRINT STR$(SP);":";AR$ |
|||
410 END |
|||
420 FOR I=1 TO LEN(AR$) |
|||
430 : IF MID$(AR$,I,1)<>"-" THEN 510 |
|||
440 : IF N<>0 THEN 480 |
|||
450 : MID$(AR$,I,1)=P$ |
|||
460 : I=LEN(AR$) |
|||
470 : GOTO 510 |
|||
480 : N=N-1 |
|||
510 NEXT I |
|||
520 RETURN |
|||
</syntaxhighlight> |
|||
Here's a version that doesn't use the advanced features: |
|||
{{works with|Commodore BASIC|2.0}} |
|||
<syntaxhighlight lang="basic">100 REM CHESS 960 |
|||
110 PRINT "SPID (-1 FOR RANDOM):"; |
|||
120 OPEN 1,0:INPUT#1, SP$:CLOSE 1 |
|||
130 SP=VAL(SP$) |
|||
140 IF SP<0 THEN SP=INT(RND(.)*960) |
|||
150 PRINT |
|||
160 IF SP>959 THEN SP=SP-960: GOTO 160 |
|||
170 AR$="--------" |
|||
180 P=SP |
|||
190 N=P AND 3:P=INT(P/4) |
|||
200 AR$=LEFT$(AR$,2*N+1)+"B"+MID$(AR$,2*N+3) |
|||
210 N=P AND 3:P=INT(P/4) |
|||
220 AR$=LEFT$(AR$,2*N)+"B"+MID$(AR$,2*N+2) |
|||
230 N=P-6*INT(P/6):P=INT(P/6) |
|||
240 P$="Q":GOSUB 420 |
|||
250 N=P-10*INT(P/10):P=INT(P/10) |
|||
260 FOR N1=0 TO 3 |
|||
270 : FOR N2=N1+1 TO 4 |
|||
280 : IF N<>0 THEN 340 |
|||
290 : P$="N":N=N1:GOSUB 420 |
|||
300 : P$="N":N=N2-1:GOSUB 420 |
|||
310 : N1=3 |
|||
320 : N2=4 |
|||
340 : N=N-1 |
|||
350 : NEXT N2 |
|||
360 NEXT N1 |
|||
370 P$="R":N=0:GOSUB 420 |
|||
380 P$="K":N=0:GOSUB 420 |
|||
390 P$="R":N=0:GOSUB 420 |
|||
400 PRINT STR$(SP);":";AR$ |
|||
410 END |
|||
420 FOR I=1 TO LEN(AR$) |
|||
430 : IF MID$(AR$,I,1)<>"-" THEN 510 |
|||
440 : IF N<>0 THEN 480 |
|||
450 : AR$=LEFT$(AR$,I-1)+P$+MID$(AR$,I+1) |
|||
460 : I=LEN(AR$) |
|||
470 : GOTO 510 |
|||
480 : N=N-1 |
|||
510 NEXT I |
|||
520 RETURN</syntaxhighlight> |
|||
{{Out}} |
|||
The output is the same for both versions: |
|||
<pre> |
|||
READY. |
|||
RUN |
|||
SPID (-1 FOR RANDOM):518 |
|||
518:RNBQKBNR |
|||
READY. |
|||
RUN |
|||
SPID (-1 FOR RANDOM):-1 |
|||
926:RKRQNBBN |
|||
READY.</pre> |
|||
==={{header|FreeBASIC}}=== |
|||
{{trans|Yabasic}} |
|||
<syntaxhighlight lang="freebasic"> |
|||
Randomize Timer |
|||
For i As Byte = 1 To 10 |
|||
Dim As String inicio = "RKR", pieza = "QNN" |
|||
Dim As Byte posic |
|||
For n As Byte = 1 To Len(pieza) |
|||
posic = Int(Rnd*(Len(inicio) + 1)) + 1 |
|||
inicio = Left(inicio, posic-1) + _ |
|||
Mid(pieza, n, 1) +_ |
|||
Right(inicio, Len(inicio) - posic + 1) |
|||
Next n |
|||
posic = Int(Rnd*(Len(inicio) + 1)) + 1 |
|||
inicio = Left(inicio, posic-1) + "B" + Right(inicio, Len(inicio) - posic + 1) |
|||
posic = posic + 1 + 2 * Int(Int(Rnd*(Len(inicio) - posic)) / 2) |
|||
inicio = Left(inicio, posic-1) + "B" + Right(inicio, Len(inicio) - posic + 1) |
|||
Print inicio |
|||
Next i |
|||
</syntaxhighlight> |
|||
==={{header|QBasic}}=== |
|||
{{trans|Yabasic}} |
|||
<syntaxhighlight lang="qbasic">RANDOMIZE TIMER |
|||
FOR i = 1 TO 10 |
|||
inicio$ = "RKR" |
|||
pieza$ = "QNN" |
|||
'Dim posic |
|||
FOR n = 1 TO LEN(pieza$) |
|||
posic = INT(RND * (LEN(inicio$) + 1)) + 1 |
|||
inicio$ = LEFT$(inicio$, posic - 1) + MID$(pieza$, n, 1) + RIGHT$(inicio$, LEN(inicio$) - posic + 1) |
|||
NEXT n |
|||
posic = INT(RND * (LEN(inicio$) + 1)) + 1 |
|||
inicio$ = LEFT$(inicio$, posic - 1) + "B" + RIGHT$(inicio$, LEN(inicio$) - posic + 1) |
|||
posic = posic + 1 + 2 * INT(INT(RND * (LEN(inicio$) - posic)) / 2) |
|||
inicio$ = LEFT$(inicio$, posic - 1) + "B" + RIGHT$(inicio$, LEN(inicio$) - posic + 1) |
|||
PRINT inicio$ |
|||
NEXT i |
|||
END</syntaxhighlight> |
|||
==={{header|Yabasic}}=== |
|||
{{trans|Seed7}} |
|||
<syntaxhighlight lang="yabasic">start$ = "RKR" |
|||
piece$ = "QNN" |
|||
for piece = 1 to len(piece$) |
|||
pos = int(ran(len(start$) + 1)) + 1 |
|||
start$ = left$(start$, pos-1) + mid$(piece$, piece, 1) + right$(start$, len(start$) - pos + 1) |
|||
next |
|||
pos = int(ran(len(start$) + 1)) + 1 |
|||
start$ = left$(start$, pos-1) + "B" + right$(start$, len(start$) - pos + 1) |
|||
pos = pos + 1 + 2 * int(int(ran(len(start$) - pos)) / 2) |
|||
start$ = left$(start$, pos-1) + "B" + right$(start$, len(start$) - pos + 1) |
|||
print start$ |
|||
</syntaxhighlight> |
|||
=={{header|Befunge}}== |
=={{header|Befunge}}== |
||
Similar to the [[Generate_Chess960_starting_position#Ruby:_Generate_from_SP-ID|Ruby SP-ID solution]], this generates the start position for a random number in the [[wp:Chess960 numbering scheme|Chess960 numbering scheme]]. |
Similar to the [[Generate_Chess960_starting_position#Ruby:_Generate_from_SP-ID|Ruby SP-ID solution]], this generates the start position for a random number in the [[wp:Chess960 numbering scheme|Chess960 numbering scheme]]. |
||
< |
<syntaxhighlight lang="befunge">#.#.#.#.065*0#v_1-\>>?1v |
||
v,":".:%*8"x"$<^!:\*2<+< |
v,":".:%*8"x"$<^!:\*2<+< |
||
>48*,:4%2*1#v+#02#\3#g<< |
>48*,:4%2*1#v+#02#\3#g<< |
||
Line 71: | Line 482: | ||
>"RKRNN"11g:!#v_\$\$\$\v |
>"RKRNN"11g:!#v_\$\$\$\v |
||
v _v#!`*86:g0:<^!:-1$\$< |
v _v#!`*86:g0:<^!:-1$\$< |
||
>$\>,1+ :7`#@_^> v960v <</ |
>$\>,1+ :7`#@_^> v960v <</syntaxhighlight> |
||
{{out}} |
{{out}} |
||
<pre>856 : RBKNBRNQ</pre> |
<pre>856 : RBKNBRNQ</pre> |
||
=={{header|C}}== |
|||
As noted in the C implementation for the [[Sparkline in unicode]] task, unicode output is reliable only on Linux/Unix systems. This implementation thus has compiler directives to check whether the underlying system is Windows or Linux, if Windows, only letters are printed, otherwise Unicode output is displayed. 9 rows are displayed. |
|||
<syntaxhighlight lang="c">#include<stdlib.h> |
|||
#include<locale.h> |
|||
#include<wchar.h> |
|||
#include<stdio.h> |
|||
#include<time.h> |
|||
char rank[9]; |
|||
int pos[8]; |
|||
void swap(int i,int j){ |
|||
int temp = pos[i]; |
|||
pos[i] = pos[j]; |
|||
pos[j] = temp; |
|||
} |
|||
void generateFirstRank(){ |
|||
int kPos,qPos,bPos1,bPos2,rPos1,rPos2,nPos1,nPos2,i; |
|||
for(i=0;i<8;i++){ |
|||
rank[i] = 'e'; |
|||
pos[i] = i; |
|||
} |
|||
do{ |
|||
kPos = rand()%8; |
|||
rPos1 = rand()%8; |
|||
rPos2 = rand()%8; |
|||
}while((rPos1-kPos<=0 && rPos2-kPos<=0)||(rPos1-kPos>=0 && rPos2-kPos>=0)||(rPos1==rPos2 || kPos==rPos1 || kPos==rPos2)); |
|||
rank[pos[rPos1]] = 'R'; |
|||
rank[pos[kPos]] = 'K'; |
|||
rank[pos[rPos2]] = 'R'; |
|||
swap(rPos1,7); |
|||
swap(rPos2,6); |
|||
swap(kPos,5); |
|||
do{ |
|||
bPos1 = rand()%5; |
|||
bPos2 = rand()%5; |
|||
}while(((pos[bPos1]-pos[bPos2])%2==0)||(bPos1==bPos2)); |
|||
rank[pos[bPos1]] = 'B'; |
|||
rank[pos[bPos2]] = 'B'; |
|||
swap(bPos1,4); |
|||
swap(bPos2,3); |
|||
do{ |
|||
qPos = rand()%3; |
|||
nPos1 = rand()%3; |
|||
}while(qPos==nPos1); |
|||
rank[pos[qPos]] = 'Q'; |
|||
rank[pos[nPos1]] = 'N'; |
|||
for(i=0;i<8;i++) |
|||
if(rank[i]=='e'){ |
|||
rank[i] = 'N'; |
|||
break; |
|||
} |
|||
} |
|||
void printRank(){ |
|||
int i; |
|||
#ifdef _WIN32 |
|||
printf("%s\n",rank); |
|||
#else |
|||
{ |
|||
setlocale(LC_ALL,""); |
|||
printf("\n"); |
|||
for(i=0;i<8;i++){ |
|||
if(rank[i]=='K') |
|||
printf("%lc",(wint_t)9812); |
|||
else if(rank[i]=='Q') |
|||
printf("%lc",(wint_t)9813); |
|||
else if(rank[i]=='R') |
|||
printf("%lc",(wint_t)9814); |
|||
else if(rank[i]=='B') |
|||
printf("%lc",(wint_t)9815); |
|||
if(rank[i]=='N') |
|||
printf("%lc",(wint_t)9816); |
|||
} |
|||
} |
|||
#endif |
|||
} |
|||
int main() |
|||
{ |
|||
int i; |
|||
srand((unsigned)time(NULL)); |
|||
for(i=0;i<9;i++){ |
|||
generateFirstRank(); |
|||
printRank(); |
|||
} |
|||
return 0; |
|||
} |
|||
</syntaxhighlight> |
|||
Output on Linux : |
|||
<pre>♗♗♖♕♘♘♔♖ |
|||
♘♖♕♔♗♗♖♘ |
|||
♖♘♔♖♕♘♗♗ |
|||
♘♘♖♗♗♔♖♕ |
|||
♗♘♖♘♕♔♖♗ |
|||
♕♗♗♘♖♔♘♖ |
|||
♕♘♖♔♗♖♘♗ |
|||
♗♘♘♕♖♔♖♗ |
|||
♖♘♘♕♗♔♖♗</pre> |
|||
Output on Windows : |
|||
<pre>BRKNNQRB |
|||
RBNQNKBR |
|||
RNQKNBBR |
|||
RQNKNBBR |
|||
QBBNRKNR |
|||
BNRBQKNR |
|||
BRQKNNRB |
|||
RNKBNRBQ |
|||
QNRBBNKR</pre> |
|||
=={{header|C#}}== |
|||
{{trans|Go}} |
|||
<syntaxhighlight lang="C#"> |
|||
using System; |
|||
class Program |
|||
{ |
|||
struct Symbols |
|||
{ |
|||
public char K, Q, R, B, N; |
|||
public Symbols(char k, char q, char r, char b, char n) |
|||
{ |
|||
K = k; Q = q; R = r; B = b; N = n; |
|||
} |
|||
} |
|||
private static Symbols A = new Symbols('K', 'Q', 'R', 'B', 'N'); |
|||
private static Symbols W = new Symbols('♔', '♕', '♖', '♗', '♘'); |
|||
private static Symbols B = new Symbols('♚', '♛', '♜', '♝', '♞'); |
|||
private static string[] krn = new string[] |
|||
{ |
|||
"nnrkr", "nrnkr", "nrknr", "nrkrn", |
|||
"rnnkr", "rnknr", "rnkrn", |
|||
"rknnr", "rknrn", |
|||
"rkrnn" |
|||
}; |
|||
private static string Chess960(Symbols sym, int id) |
|||
{ |
|||
char[] pos = new char[8]; |
|||
int q = id / 4, r = id % 4; |
|||
pos[r * 2 + 1] = sym.B; |
|||
r = q % 4; q /= 4; |
|||
pos[r * 2] = sym.B; |
|||
r = q % 6; q /= 6; |
|||
int placementIndex = 0; // Adjusted variable name to prevent conflict |
|||
for (int i = 0; ; i++) |
|||
{ |
|||
if (pos[i] != '\0') continue; |
|||
if (r == 0) |
|||
{ |
|||
pos[i] = sym.Q; |
|||
break; |
|||
} |
|||
r--; |
|||
} |
|||
while (pos[placementIndex] != '\0') placementIndex++; // Adjusted loop to prevent conflict |
|||
foreach (char f in krn[q]) |
|||
{ |
|||
while (pos[placementIndex] != '\0') placementIndex++; |
|||
switch (f) |
|||
{ |
|||
case 'k': |
|||
pos[placementIndex] = sym.K; |
|||
break; |
|||
case 'r': |
|||
pos[placementIndex] = sym.R; |
|||
break; |
|||
case 'n': |
|||
pos[placementIndex] = sym.N; |
|||
break; |
|||
} |
|||
} |
|||
return new string(pos); |
|||
} |
|||
static void Main(string[] args) |
|||
{ |
|||
Console.WriteLine(" ID Start position"); |
|||
foreach (int id in new int[] { 0, 518, 959 }) |
|||
{ |
|||
Console.WriteLine($"{id,3} {Chess960(A, id)}"); |
|||
} |
|||
Console.WriteLine("\nRandom"); |
|||
Random rand = new Random(); |
|||
for (int i = 0; i < 5; i++) |
|||
{ |
|||
Console.WriteLine(Chess960(W, rand.Next(960))); |
|||
} |
|||
} |
|||
} |
|||
</syntaxhighlight> |
|||
{{out}} |
|||
<pre> |
|||
ID Start position |
|||
0 BBQNNRKR |
|||
518 RNBQKBNR |
|||
959 RKRNNQBB |
|||
Random |
|||
♘♕♖♔♗♖♘♗ |
|||
♗♗♖♘♔♖♕♘ |
|||
♖♕♘♔♖♗♗♘ |
|||
♘♖♗♔♘♗♖♕ |
|||
♖♘♕♗♔♘♗♖ |
|||
</pre> |
|||
=={{header|C++}}== |
=={{header|C++}}== |
||
<syntaxhighlight lang="cpp">#include <iostream> |
|||
<lang cpp> |
|||
#include <iostream> |
|||
#include <string> |
#include <string> |
||
#include <time.h> |
#include <time.h> |
||
Line 135: | Line 771: | ||
return system( "pause" ); |
return system( "pause" ); |
||
} |
} |
||
</syntaxhighlight> |
|||
</lang> |
|||
{{out}} |
{{out}} |
||
<pre> |
<pre>NQBRNBKR |
||
NQBRNBKR |
|||
RKBQNBNR |
RKBQNBNR |
||
RKBRNNQB |
RKBRNNQB |
||
Line 147: | Line 782: | ||
RNBKQBNR |
RNBKQBNR |
||
QRNKBBRN |
QRNKBBRN |
||
QRBKNBRN |
QRBKNBRN</pre> |
||
</pre> |
|||
=={{header|Clojure}}== |
=={{header|Clojure}}== |
||
< |
<syntaxhighlight lang="clojure">(ns c960.core |
||
(ns c960.core |
|||
(:gen-class) |
(:gen-class) |
||
(:require [clojure.string :as s])) |
(:require [clojure.string :as s])) |
||
Line 189: | Line 822: | ||
;; => "♖, ♕, ♘, ♔, ♗, ♗, ♘, ♖" |
;; => "♖, ♕, ♘, ♔, ♗, ♗, ♘, ♖" |
||
(c960 4) |
(c960 4) |
||
;; => ("♘, ♖, ♔, ♘, ♗, ♗, ♖, ♕" "♗, ♖, ♔, ♘, ♘, ♕, ♖, ♗" "♘, ♕, ♗, ♖, ♔, ♗, ♘, ♖" "♖, ♔, ♘, ♘, ♕, ♖, ♗, ♗") |
;; => ("♘, ♖, ♔, ♘, ♗, ♗, ♖, ♕" "♗, ♖, ♔, ♘, ♘, ♕, ♖, ♗" "♘, ♕, ♗, ♖, ♔, ♗, ♘, ♖" "♖, ♔, ♘, ♘, ♕, ♖, ♗, ♗")</syntaxhighlight> |
||
=={{header|Common Lisp}}== |
|||
===Common Lisp: generate from SP-ID=== |
|||
</lang> |
|||
{{trans|Raku}} |
|||
<syntaxhighlight lang="lisp">(defun chess960-from-sp-id |
|||
(&optional (sp-id (random 360 (make-random-state t)))) |
|||
(labels |
|||
((combinations (lst r) |
|||
(cond |
|||
((numberp lst) |
|||
(combinations (loop for i from 0 while (< i lst) collect i) r)) |
|||
((= r 1) |
|||
(mapcar #'list lst)) |
|||
(t |
|||
(loop for i in lst append |
|||
(let ((left (loop for j in lst if (< i j) collect j))) |
|||
(mapcar (lambda (c) (cons i c)) |
|||
(combinations left (1- r)))))))) |
|||
(enumerate (ary) |
|||
(loop for item across ary for index from 0 |
|||
collect (list index item)))) |
|||
(let* |
|||
((first-bishop -1) |
|||
(knight-combo '()) |
|||
(placements (list |
|||
;divisor function to get position piece symbol |
|||
(list 4 (lambda (n) (setq first-bishop n) |
|||
(1+ (* 2 n))) '♝) |
|||
(list 4 (lambda (n) ( - (* 2 n) (if (> n first-bishop) 1 0))) '♝) |
|||
(list 6 #'identity '♛) |
|||
(list 10 (lambda (n) |
|||
(setq knight-combo (nth n (combinations 5 2))) |
|||
(car knight-combo)) '♞) |
|||
(list 1 (lambda (n) (1- (cadr knight-combo))) '♞) |
|||
(list 1 (lambda (n) 0) '♜) |
|||
(list 1 (lambda (n) 0) '♚) |
|||
(list 1 (lambda (n) 0) '♜))) |
|||
(p sp-id) |
|||
(ary (make-array 8 :initial-element '-))) |
|||
(loop for (divisor func piece) in placements doing |
|||
(let* ((n (mod p divisor)) |
|||
(square (funcall func n))) |
|||
(setq p (floor p divisor)) |
|||
(setq index |
|||
(car (nth square (remove-if-not (lambda (p) (eq (cadr p) '-)) |
|||
(enumerate ary))))) |
|||
(setf (aref ary index) piece))) |
|||
(list sp-id ary)))) |
|||
;; demo |
|||
(format t "~a~%" (chess960-from-sp-id 518)) |
|||
(format t "~a~%" (chess960-from-sp-id))</syntaxhighlight> |
|||
{{Out}}<pre>(518 #(♜ ♞ ♝ ♛ ♚ ♝ ♞ ♜)) |
|||
(246 #(♞ ♜ ♝ ♚ ♛ ♝ ♞ ♜))</pre> |
|||
=={{header|D}}== |
=={{header|D}}== |
||
Line 198: | Line 889: | ||
===D: Indexing=== |
===D: Indexing=== |
||
< |
<syntaxhighlight lang="d">void main() { |
||
import std.stdio, std.range, std.algorithm, std.string, permutations2; |
import std.stdio, std.range, std.algorithm, std.string, permutations2; |
||
Line 210: | Line 901: | ||
.map!toUpper.array.sort().uniq; |
.map!toUpper.array.sort().uniq; |
||
writeln(starts.walkLength, "\n", starts.front); |
writeln(starts.walkLength, "\n", starts.front); |
||
}</ |
}</syntaxhighlight> |
||
{{out}} |
{{out}} |
||
<pre>960 |
<pre>960 |
||
Line 216: | Line 907: | ||
===D: Regexp=== |
===D: Regexp=== |
||
< |
<syntaxhighlight lang="d">void main() { |
||
import std.stdio, std.regex, std.range, std.algorithm, permutations2; |
import std.stdio, std.regex, std.range, std.algorithm, permutations2; |
||
Line 226: | Line 917: | ||
.array.sort().uniq; |
.array.sort().uniq; |
||
writeln(starts3.walkLength, "\n", starts3.front); |
writeln(starts3.walkLength, "\n", starts3.front); |
||
}</ |
}</syntaxhighlight> |
||
The output is the same. |
The output is the same. |
||
===D: Correct by construction=== |
===D: Correct by construction=== |
||
< |
<syntaxhighlight lang="d">void main() { |
||
import std.stdio, std.random, std.array, std.range; |
import std.stdio, std.random, std.array, std.range; |
||
Line 242: | Line 933: | ||
start.insertInPlace(iota(bishpos % 2, start.length, 2)[uniform(0,$)], 'B'); |
start.insertInPlace(iota(bishpos % 2, start.length, 2)[uniform(0,$)], 'B'); |
||
start.writeln; |
start.writeln; |
||
}</ |
}</syntaxhighlight> |
||
{{out}} |
{{out}} |
||
<pre>QBNNBRKR</pre> |
<pre>QBNNBRKR</pre> |
||
=={{header|EasyLang}}== |
|||
{{trans|Lua}} |
|||
<syntaxhighlight> |
|||
len t$[] 8 |
|||
proc randins c$ l r . pos . |
|||
repeat |
|||
pos = randint (r - l + 1) + l - 1 |
|||
until t$[pos] = "" |
|||
. |
|||
t$[pos] = c$ |
|||
. |
|||
randins "K" 2 7 king |
|||
randins "R" 1 (king - 1) h |
|||
randins "R" (king + 1) 8 h |
|||
randins "B" 1 8 b1 |
|||
repeat |
|||
randins "B" 1 8 b2 |
|||
until (b2 - b1) mod 2 <> 0 |
|||
t$[b2] = "" |
|||
. |
|||
randins "Q" 1 8 b1 |
|||
randins "N" 1 8 b1 |
|||
randins "N" 1 8 b1 |
|||
print strjoin t$[] |
|||
</syntaxhighlight> |
|||
{{out}} |
|||
<pre> |
|||
RBBNQNKR |
|||
</pre> |
|||
=={{header|EchoLisp}}== |
=={{header|EchoLisp}}== |
||
<syntaxhighlight lang="lisp">(define-values (K Q R B N) (iota 5)) |
|||
<lang lisp> |
|||
(define-values (K Q R B N) (iota 5)) |
|||
(define *pos* (list R N B Q K B N R)) ;; standard starter |
(define *pos* (list R N B Q K B N R)) ;; standard starter |
||
Line 263: | Line 983: | ||
(if (legal-pos *pos*) |
(if (legal-pos *pos*) |
||
(map unicode-piece *pos*) (c960))) |
(map unicode-piece *pos*) (c960))) |
||
</syntaxhighlight> |
|||
</lang> |
|||
{{out}} |
{{out}} |
||
<pre>(define (unicode-piece i) (unicode->string (+ 0x2654 i))) |
|||
<pre> |
|||
(define (unicode-piece i) (unicode->string (+ 0x2654 i))) |
|||
(legal-pos *pos*) → #t ;; starter is OK |
(legal-pos *pos*) → #t ;; starter is OK |
||
Line 275: | Line 994: | ||
(c960) |
(c960) |
||
(♖ ♘ ♗ ♘ ♔ ♕ ♖ ♗) |
(♖ ♘ ♗ ♘ ♔ ♕ ♖ ♗) |
||
;; etc. |
;; etc.</pre> |
||
</pre> |
|||
=={{header|Elixir}}== |
=={{header|Elixir}}== |
||
Line 282: | Line 1,000: | ||
{{works with|Elixir|1.1}} |
{{works with|Elixir|1.1}} |
||
===Elixir: shuffle pieces until all regexes match=== |
===Elixir: shuffle pieces until all regexes match=== |
||
< |
<syntaxhighlight lang="elixir">defmodule Chess960 do |
||
@pieces ~w(♔ ♕ ♘ ♘ ♗ ♗ ♖ ♖) # ~w(K Q N N B B R R) |
@pieces ~w(♔ ♕ ♘ ♘ ♗ ♗ ♖ ♖) # ~w(K Q N N B B R R) |
||
@regexes [~r/♗(..)*♗/, ~r/♖.*♔.*♖/] # [~r/B(..)*B/, ~r/R.*K.*R/] |
@regexes [~r/♗(..)*♗/, ~r/♖.*♔.*♖/] # [~r/B(..)*B/, ~r/R.*K.*R/] |
||
Line 292: | Line 1,010: | ||
end |
end |
||
Enum.each(1..5, fn _ -> IO.puts Chess960.shuffle end)</ |
Enum.each(1..5, fn _ -> IO.puts Chess960.shuffle end)</syntaxhighlight> |
||
{{out}} |
{{out}} |
||
<pre>♘♗♘♖♗♔♕♖ |
|||
<pre> |
|||
♘♗♘♖♗♔♕♖ |
|||
♗♖♔♗♕♘♘♖ |
♗♖♔♗♕♘♘♖ |
||
♗♗♕♖♔♖♘♘ |
♗♗♕♖♔♖♘♘ |
||
♘♗♖♔♗♘♕♖ |
♘♗♖♔♗♘♕♖ |
||
♖♕♘♘♗♗♔♖ |
♖♕♘♘♗♗♔♖</pre> |
||
</pre> |
|||
===Elixir: Construct=== |
===Elixir: Construct=== |
||
< |
<syntaxhighlight lang="elixir">defmodule Chess960 do |
||
def construct do |
def construct do |
||
row = Enum.reduce(~w[♕ ♘ ♘], ~w[♖ ♔ ♖], fn piece,acc -> |
row = Enum.reduce(~w[♕ ♘ ♘], ~w[♖ ♔ ♖], fn piece,acc -> |
||
Line 316: | Line 1,031: | ||
end |
end |
||
Enum.each(1..5, fn _ -> IO.puts Chess960.construct end)</ |
Enum.each(1..5, fn _ -> IO.puts Chess960.construct end)</syntaxhighlight> |
||
{{out}} |
{{out}} |
||
<pre>♖♔♗♘♖♕♘♗ |
|||
<pre> |
|||
♖♔♗♘♖♕♘♗ |
|||
♘♗♘♕♖♔♗♖ |
♘♗♘♕♖♔♗♖ |
||
♗♖♔♘♘♗♖♕ |
♗♖♔♘♘♗♖♕ |
||
♖♗♘♘♕♔♗♖ |
♖♗♘♘♕♔♗♖ |
||
♖♕♗♘♘♗♔♖ |
♖♕♗♘♘♗♔♖</pre> |
||
</pre> |
|||
===Elixir: Generate from SP-ID=== |
===Elixir: Generate from SP-ID=== |
||
< |
<syntaxhighlight lang="elixir">defmodule Chess960 do |
||
@krn ~w(NNRKR NRNKR NRKNR NRKRN RNNKR RNKNR RNKRN RKNNR RKNRN RKRNN) |
@krn ~w(NNRKR NRNKR NRKNR NRKRN RNNKR RNKNR RNKRN RKNNR RKNRN RKRNN) |
||
Line 358: | Line 1,070: | ||
end) |
end) |
||
IO.puts "\nGenerate random Start Position" |
IO.puts "\nGenerate random Start Position" |
||
Enum.each(1..5, fn _ -> IO.puts Chess960.start_position end)</ |
Enum.each(1..5, fn _ -> IO.puts Chess960.start_position end)</syntaxhighlight> |
||
{{out}} |
{{out}} |
||
<pre>Generate Start Position from ID number |
|||
<pre> |
|||
Generate Start Position from ID number |
|||
0 : BBQNNRKR |
0 : BBQNNRKR |
||
518 : BRNKNBRQ |
518 : BRNKNBRQ |
||
Line 372: | Line 1,082: | ||
RQKNNRBB |
RQKNNRBB |
||
RKRQBBNN |
RKRQBBNN |
||
RNBNKQRB |
RNBNKQRB</pre> |
||
=={{header|Factor}}== |
|||
===Single die method=== |
|||
Using the single die method: https://en.wikipedia.org/wiki/Chess960_starting_position#Single_die_method |
|||
<syntaxhighlight lang="factor">USING: io kernel math random sequences ; |
|||
IN: rosetta-code.chess960 |
|||
: empty ( seq -- n ) 32 swap indices random ; ! return a random empty index (i.e. equal to 32) of seq |
|||
: next ( seq -- n ) 32 swap index ; ! return the leftmost empty index of seq |
|||
: place ( seq elt n -- seq' ) rot [ set-nth ] keep ; ! set nth member of seq to elt, keeping seq on the stack |
|||
: white-bishop ( -- elt n ) CHAR: ♗ 4 random 2 * ; |
|||
: black-bishop ( -- elt n ) white-bishop 1 + ; |
|||
: queen ( seq -- seq elt n ) CHAR: ♕ over empty ; |
|||
: knight ( seq -- seq elt n ) CHAR: ♘ over empty ; |
|||
: rook ( seq -- seq elt n ) CHAR: ♖ over next ; |
|||
: king ( seq -- seq elt n ) CHAR: ♔ over next ; |
|||
: chess960 ( -- str ) |
|||
" " clone |
|||
black-bishop place |
|||
white-bishop place |
|||
queen place |
|||
knight place |
|||
knight place |
|||
rook place |
|||
king place |
|||
rook place ; |
|||
: chess960-demo ( -- ) 5 [ chess960 print ] times ; |
|||
MAIN: chess960-demo</syntaxhighlight> |
|||
{{out}} |
|||
<pre> |
|||
♕♖♗♘♔♘♖♗ |
|||
♕♗♖♘♗♘♔♖ |
|||
♗♘♕♖♔♗♖♘ |
|||
♘♖♗♕♔♗♘♖ |
|||
♗♗♘♖♘♔♕♖ |
|||
</pre> |
|||
===Built-in=== |
|||
Factor comes with a chess960 position generator: |
|||
<syntaxhighlight lang="factor">USING: chess960 prettyprint ; |
|||
chess960-position .</syntaxhighlight> |
|||
{{out}} |
|||
<pre> |
|||
{ rook bishop king knight bishop queen rook knight } |
|||
</pre> |
</pre> |
||
=={{header|Forth}}== |
=={{header|Forth}}== |
||
< |
<syntaxhighlight lang="forth">\ make starting position for Chess960, constructive |
||
\ 0 1 2 3 4 5 6 7 8 9 |
\ 0 1 2 3 4 5 6 7 8 9 |
||
Line 396: | Line 1,155: | ||
959 chess960 \ RKRNNQBB ok |
959 chess960 \ RKRNNQBB ok |
||
960 choose chess960 \ random position |
960 choose chess960 \ random position</syntaxhighlight> |
||
</lang> |
|||
=={{header|Fortran}}== |
=={{header|Fortran}}== |
||
This implementation simply iterates through all 960 positions. |
This implementation simply iterates through all 960 positions. |
||
< |
<syntaxhighlight lang="fortran">program chess960 |
||
program chess960 |
|||
implicit none |
implicit none |
||
Line 459: | Line 1,216: | ||
end program chess960 |
end program chess960 |
||
</syntaxhighlight> |
|||
</lang> |
|||
{{out}} |
{{out}} |
||
The first ten positions: |
The first ten positions: |
||
<pre> |
<pre>R K R B B Q N N |
||
R K R B B Q N N |
|||
R K R B B N Q N |
R K R B B N Q N |
||
R K R B B N N Q |
R K R B B N N Q |
||
Line 472: | Line 1,228: | ||
R K R N B Q N B |
R K R N B Q N B |
||
R K R N B N Q B |
R K R N B N Q B |
||
R K R B Q N B N |
R K R B Q N B N</pre> |
||
</pre> |
|||
=={{header|Go}}== |
=={{header|Go}}== |
||
{{trans|Ruby}} |
{{trans|Ruby}} |
||
< |
<syntaxhighlight lang="go">package main |
||
import ( |
import ( |
||
Line 539: | Line 1,294: | ||
fmt.Println(W.chess960(rand.Intn(960))) |
fmt.Println(W.chess960(rand.Intn(960))) |
||
} |
} |
||
}</ |
}</syntaxhighlight> |
||
{{out}} |
{{out}} |
||
<pre> |
<pre> ID Start position |
||
ID Start position |
|||
0 BBQNNRKR |
0 BBQNNRKR |
||
518 RNBQKBNR |
518 RNBQKBNR |
||
Line 552: | Line 1,306: | ||
♖♘♗♔♖♕♘♗ |
♖♘♗♔♖♕♘♗ |
||
♘♘♖♕♗♔♖♗ |
♘♘♖♕♗♔♖♗ |
||
♗♕♘♗♘♖♔♖ |
♗♕♘♗♘♖♔♖</pre> |
||
</pre> |
|||
=={{header|Haskell}}== |
=={{header|Haskell}}== |
||
< |
<syntaxhighlight lang="haskell">import Data.List |
||
import qualified Data.Set as Set |
import qualified Data.Set as Set |
||
Line 570: | Line 1,323: | ||
main :: IO () |
main :: IO () |
||
main = mapM_ (putStrLn . concatMap show) . Set.toList . Set.fromList |
main = mapM_ (putStrLn . concatMap show) . Set.toList . Set.fromList |
||
. filter isChess960 $ permutations [R,N,B,Q,K,B,N,R]</ |
. filter isChess960 $ permutations [R,N,B,Q,K,B,N,R]</syntaxhighlight> |
||
{{out}} |
{{out}} |
||
<pre>QRKRBBNN |
<pre>QRKRBBNN |
||
Line 580: | Line 1,333: | ||
=={{header|J}}== |
=={{header|J}}== |
||
The simplest J approach for this task is to initially build a table of the starting positions. Then, for the task, we pick one position from the table at random. There are 40320 distinct permutations of 8 items and 5040 distinct permutations of these chess pieces and (as the task name points out) only 960 permutations which also satisfy the constraints on bishop and rook position, so little memory is needed to generate the table. Also, since the table is built at "compile time", execution is fast (though "compilation" is reasonably fast also). |
|||
< |
<syntaxhighlight lang="j">row0=: u: 9812+2}.5|i.10 |
||
king=: u:9812 |
king=: u:9812 |
||
rook=: u:9814 |
rook=: u:9814 |
||
Line 592: | Line 1,345: | ||
perm=: A.&i.~ ! |
perm=: A.&i.~ ! |
||
valid=: (#~ ok"1) ~.row0{"1~perm 8 |
valid=: (#~ ok"1) ~.row0{"1~perm 8 |
||
gen=: valid {~ ? bind 960</ |
gen=: valid {~ ? bind 960</syntaxhighlight> |
||
Example use: |
Example use: |
||
< |
<syntaxhighlight lang="j"> gen'' |
||
♘♗♖♔♗♕♖♘ |
♘♗♖♔♗♕♖♘ |
||
gen'' |
gen'' |
||
Line 603: | Line 1,356: | ||
♖♗♔♘♘♕♗♖ |
♖♗♔♘♘♕♗♖ |
||
gen'' |
gen'' |
||
♖♔♕♗♗♘♖♘</ |
♖♔♕♗♗♘♖♘</syntaxhighlight> |
||
=={{header|Java}}== |
=={{header|Java}}== |
||
{{works with|Java|1.5+}} |
{{works with|Java|1.5+}} |
||
Regex inspired by (original) [[#Python: Regexp|Python Regexp]], prints ten examples. |
Regex inspired by (original) [[#Python: Regexp|Python Regexp]], prints ten examples. |
||
< |
<syntaxhighlight lang="java5">import java.util.Arrays; |
||
import java.util.Collections; |
import java.util.Collections; |
||
import java.util.List; |
import java.util.List; |
||
Line 634: | Line 1,387: | ||
} |
} |
||
} |
} |
||
}</ |
}</syntaxhighlight> |
||
{{out}} |
{{out}} |
||
<pre>[R, N, K, N, R, B, B, Q] |
<pre>[R, N, K, N, R, B, B, Q] |
||
Line 649: | Line 1,402: | ||
=={{header|JavaScript}}== |
=={{header|JavaScript}}== |
||
This conforms to Altendörfer's single die method[https://en.wikipedia.org/wiki/Chess960_starting_position#Single_die_method], though the die will give no "needless" numbers. |
This conforms to Altendörfer's single die method[https://en.wikipedia.org/wiki/Chess960_starting_position#Single_die_method], though the die will give no "needless" numbers. |
||
< |
<syntaxhighlight lang="javascript">function ch960startPos() { |
||
var rank = new Array(8), |
var rank = new Array(8), |
||
// randomizer (our die) |
// randomizer (our die) |
||
Line 673: | Line 1,426: | ||
} |
} |
||
// testing (10 times) |
|||
// test |
|||
for (var x = 1; x <= 10; x++) console.log(ch960startPos().join(" | "));</ |
for (var x = 1; x <= 10; x++) console.log(ch960startPos().join(" | "));</syntaxhighlight> |
||
{{out}} |
{{out}} |
||
<p>The test-output (exemplary each):</p> |
<p>The test-output (exemplary each):</p> |
||
Line 688: | Line 1,441: | ||
♘ | ♗ | ♖ | ♔ | ♗ | ♘ | ♖ | ♕<br/> |
♘ | ♗ | ♖ | ♔ | ♗ | ♘ | ♖ | ♕<br/> |
||
=={{header| |
=={{header|jq}}== |
||
'''Adapted from [[#Wren|Wren]]''' |
|||
<lang Julia># placeholder knights |
|||
{{works with|jq}} |
|||
rank1 = ['♘', '♘', '♘', '♘', '♘', '♘', '♘', '♘'] |
|||
'''Also works with gojq, the Go implementation of jq''' |
|||
'''Also works with fq, a Go implementation of a large subset of jq''' |
|||
# function to check if a space is available |
|||
isfree(x::Int) = rank1[x] == '♘' |
|||
<syntaxhighlight lang=jq> |
|||
# place king |
|||
### Utilities |
|||
king = rand(2:7) |
|||
# The glyphs in . |
|||
rank1[king] = '♔' |
|||
def chars: explode[] | [.] | implode; |
|||
# input: an array |
|||
# place rooks |
|||
# $keys : an array of strings |
|||
rook1 = rand(filter(isfree, 1:8)) |
|||
def objectify($keys): |
|||
rank1[rook1] = '♖' |
|||
with_entries(.key = $keys[.key]) ; |
|||
def lpad($len): tostring | ($len - length) as $l | (" " * $l)[:$l] + .; |
|||
if rook1 > king |
|||
rank1[rand(filter(x -> isfree(x) && x < king, 1:8))] = '♖' |
|||
else |
|||
rank1[rand(filter(x -> isfree(x) && x > king, 1:8))] = '♖' |
|||
end |
|||
# place bishops |
|||
bishop1 = rand(filter(isfree, 1:8)) |
|||
rank1[bishop1] = '♗' |
|||
rank1[rand(filter(x -> isfree(x) && iseven(x) != iseven(bishop1), 1:8))] = '♗' |
|||
def Symbols: ["k", "q", "r", "b", "n"]; |
|||
# place queen |
|||
rank1[rand(filter(isfree, 1:8))] = '♕' |
|||
def A: ["K", "Q", "R", "B", "N"] | objectify(Symbols); |
|||
# print first rank |
|||
def W: ["♔", "♕", "♖", "♗", "♘"] | objectify(Symbols); |
|||
println(join(rank1))</lang> |
|||
def B: ["♚", "♛", "♜", "♝", "♞"] | objectify(Symbols); |
|||
def krn: [ |
|||
{{out}} |
|||
"nnrkr", "nrnkr", "nrknr", "nrkrn", |
|||
"rnnkr", "rnknr", "rnkrn", |
|||
"rknnr", "rknrn", |
|||
"rkrnn" |
|||
]; |
|||
# $sym specifies the Symbols |
|||
<big><big><big><big><pre>♘♗♘♖♗♕♔♖</pre></big></big></big></big> |
|||
# $id specifies the position |
|||
Of course since the program is stochastic this is just one possible outcome. |
|||
def chess960($sym): |
|||
. as $id |
|||
| { q: (($id/4)|floor), |
|||
r: ($id % 4) |
|||
} |
|||
| .pos[.r*2+1] = $sym.b |
|||
| .t = .q |
|||
| .q |= ((./4)|floor) |
|||
| .r = (.t % 4) |
|||
| .pos[.r*2] = $sym.b |
|||
| .t = .q |
|||
| .q |= ((./6)|floor) |
|||
| .r = .t % 6 |
|||
| .i = 0 |
|||
| .break = false |
|||
| until( .break; |
|||
if .pos[.i] == null |
|||
then if .r == 0 |
|||
then .pos[.i] = $sym.q |
|||
| .break = true |
|||
else .r += -1 |
|||
end |
|||
else . |
|||
end |
|||
| .i += 1 ) |
|||
| .i = 0 |
|||
| reduce (krn[.q]|chars) as $f (.; |
|||
# find next insertion point |
|||
until(.pos[.i] == null; .i += 1) |
|||
| if $f | IN("k", "r", "n") |
|||
then .pos[.i] = $sym[$f] |
|||
else . |
|||
end ) |
|||
| .pos |
|||
| join(" ") ; |
|||
def display960($sym): |
|||
"\(lpad(3)) \(chess960($sym))"; |
|||
" ID Start position", |
|||
( 0, 518, 959 | display960(A) ), |
|||
"\nPseudo-random starting positions:", |
|||
(699, 889, 757, 645, 754 | display960(W)) |
|||
</syntaxhighlight> |
|||
{{output}} |
|||
<pre> |
|||
ID Start position |
|||
0 B B Q N N R K R |
|||
518 R N B Q K B N R |
|||
959 R K R N N Q B B |
|||
Pseudo-random starting positions: |
|||
699 ♖ ♕ ♔ ♘ ♗ ♘ ♖ ♗ |
|||
889 ♖ ♕ ♔ ♗ ♗ ♖ ♘ ♘ |
|||
757 ♖ ♔ ♗ ♗ ♘ ♘ ♖ ♕ |
|||
645 ♖ ♘ ♗ ♗ ♔ ♖ ♕ ♘ |
|||
754 ♗ ♖ ♔ ♘ ♘ ♗ ♖ ♕ |
|||
</pre> |
|||
=={{header|Julia}}== |
|||
{{works with|Julia|0.6}} |
|||
<syntaxhighlight lang="julia">function generateposition() |
|||
# Placeholder knights |
|||
rank = ['♘', '♘', '♘', '♘', '♘', '♘', '♘', '♘'] |
|||
lrank = length(rank) |
|||
# Check if a space is available |
|||
isfree(x::Int) = rank[x] == '♘' |
|||
# Place the King |
|||
rank[indking = rand(2:lrank-1)] = '♔' |
|||
# Place rooks |
|||
rank[indrook = rand(filter(isfree, 1:lrank))] = '♖' |
|||
if indrook > indking |
|||
rank[rand(filter(isfree, 1:indking-1))] = '♖' |
|||
else |
|||
rank[rand(filter(isfree, indking+1:lrank))] = '♖' |
|||
end |
|||
# Place bishops |
|||
rank[indbish = rand(filter(isfree, 1:8))] = '♗' |
|||
pbish = filter(iseven(indbish) ? isodd : iseven, 1:lrank) |
|||
rank[rand(filter(isfree, pbish))] = '♗' |
|||
# Place queen |
|||
rank[rand(filter(isfree, 1:lrank))] = '♕' |
|||
return rank |
|||
end |
|||
@show generateposition()</syntaxhighlight> |
|||
{{out}} |
|||
<pre>generateposition() = ['♘', '♗', '♗', '♖', '♕', '♔', '♘', '♖']</pre> |
|||
=={{header|Kotlin}}== |
=={{header|Kotlin}}== |
||
< |
<syntaxhighlight lang="scala">object Chess960 : Iterable<String> { |
||
override fun iterator() = patterns.iterator() |
override fun iterator() = patterns.iterator() |
||
Line 733: | Line 1,579: | ||
val s = b + e |
val s = b + e |
||
if (s.is_valid()) patterns += s |
if (s.is_valid()) patterns += s |
||
} else |
} else { |
||
for (i in 0 |
for (i in 0 until e.length) { |
||
invoke(b + e[i], e.substring(0, i) + e.substring(i + 1)) |
invoke(b + e[i], e.substring(0, i) + e.substring(i + 1)) |
||
} |
|||
} |
|||
} |
} |
||
Line 741: | Line 1,589: | ||
val k = indexOf('K') |
val k = indexOf('K') |
||
return indexOf('R') < k && k < lastIndexOf('R') && |
return indexOf('R') < k && k < lastIndexOf('R') && |
||
indexOf('B') % 2 != lastIndexOf('B') % 2 |
|||
} |
} |
||
private val patterns = sortedSetOf<String>() |
private val patterns = sortedSetOf<String>() |
||
init { |
init { |
||
invoke("", "KQRRNNBB") |
|||
} |
|||
} |
} |
||
fun main(args: Array<String>) { |
fun main(args: Array<String>) { |
||
Chess960.forEachIndexed { i, s -> println("$i: $s") } |
Chess960.forEachIndexed { i, s -> println("$i: $s") } |
||
}</ |
}</syntaxhighlight> |
||
{{Out}} |
{{Out}} |
||
<pre>0: BBNNQRKR |
<pre>0: BBNNQRKR |
||
Line 762: | Line 1,612: | ||
=={{header|Lua}}== |
=={{header|Lua}}== |
||
< |
<syntaxhighlight lang="lua">-- Insert 'str' into 't' at a random position from 'left' to 'right' |
||
function randomInsert (t, str, left, right) |
function randomInsert (t, str, left, right) |
||
local pos |
local pos |
||
Line 790: | Line 1,640: | ||
-- Main procedure |
-- Main procedure |
||
math.randomseed(os.time()) |
math.randomseed(os.time()) |
||
print(table.concat(chess960()))</ |
print(table.concat(chess960()))</syntaxhighlight> |
||
{{out}} |
{{out}} |
||
<pre>NNRQBBKR</pre> |
<pre>NNRQBBKR</pre> |
||
=={{header|M2000 Interpreter}}== |
|||
<syntaxhighlight lang="m2000 interpreter"> |
|||
Module Chess960 { |
|||
function OneFrom960$ { |
|||
def q$="♕", h$="♘", t$="♖", b$="♗", k$="♔" |
|||
def integer b1, b2, t1, t2, k, q |
|||
buffer p as integer *8 |
|||
return p, 0:=string$(h$, 8) |
|||
k=random(1, 6) |
|||
t1=random(0,k-1) |
|||
t2=random(k+1, 7) |
|||
used=list:=k, t1, t2 |
|||
do : b1=random(0,7): until not exist(used, b1) |
|||
append used, b1 |
|||
n=b1 mod 2 |
|||
do : b2=random(0,7): until not exist(used, b2) and b2 mod 2 <> n |
|||
append used, b2 |
|||
do : q=random(0,7): until not exist(used, q) |
|||
// place pawns to positions |
|||
return p, k:=k$, t1:=t$, t2:=t$, b1:=b$, b2:=b$, q:=q$ |
|||
=Eval$(p)+{ |
|||
} // append new line to every solution |
|||
} |
|||
document doc$ |
|||
For i=0 to 7:doc$+=OneFrom960$(): next |
|||
clipboard doc$ |
|||
report doc$ |
|||
} |
|||
Chess960 |
|||
</syntaxhighlight> |
|||
{{out}} |
|||
<pre> |
|||
♘♗♗♘♖♕♔♖ |
|||
♗♕♖♘♘♔♖♗ |
|||
♗♗♕♖♔♘♖♘ |
|||
♘♖♕♗♗♔♖♘ |
|||
♖♔♘♗♖♕♗♘ |
|||
♖♔♖♗♘♘♗♕ |
|||
♘♘♗♕♖♗♔♖ |
|||
♖♔♕♘♘♗♗♖ |
|||
</pre> |
|||
=={{header|Mathematica}} / {{header|Wolfram Language}}== |
=={{header|Mathematica}} / {{header|Wolfram Language}}== |
||
{{Output?}} |
|||
Generates all possible initial conditions, filters for validity, and chooses a random element. |
Generates all possible initial conditions, filters for validity, and chooses a random element. |
||
< |
<syntaxhighlight lang="mathematica">Print[StringJoin[ |
||
RandomChoice[ |
RandomChoice[ |
||
Select[Union[ |
Select[Union[ |
||
Line 804: | Line 1,698: | ||
MatchQ[#, {___, "\[WhiteRook]", ___, "\[WhiteKing]", ___, |
MatchQ[#, {___, "\[WhiteRook]", ___, "\[WhiteKing]", ___, |
||
"\[WhiteRook]", ___}] && |
"\[WhiteRook]", ___}] && |
||
OddQ[Subtract @@ Flatten[Position[#, "\[WhiteBishop]"]]] &]]]];</ |
OddQ[Subtract @@ Flatten[Position[#, "\[WhiteBishop]"]]] &]]]];</syntaxhighlight> |
||
=={{header| |
=={{header|MiniScript}}== |
||
This version uses the Unicode piece characters. If running in [[Mini Micro]] — which supports Unicode but does not have these characters in its font — just replace the the piece characters with letters. |
|||
<lang parigp>chess960() = |
|||
<syntaxhighlight lang="miniscript">// placeholder knights |
|||
{ |
|||
rank = ["♘"] * 8 |
|||
my (C = vector(8), i, j, r); |
|||
// function to get a random free space from a to b, inclusive |
|||
C[random(4) * 2 + 1] = C[random(4) * 2 + 2] = "B"; |
|||
randFree = function(a, b) |
|||
for (i = 1, 3, while (C[r = random(8) + 1],); C[r] = Vec("NNQ")[i]); |
|||
free = [] |
|||
for (i = 1, 3, while (C[j++],); C[j] = Vec("RKR")[i]); |
|||
for i in range(a, b) |
|||
C |
|||
if rank[i] == "♘" then free.push i |
|||
}</lang> |
|||
end for |
|||
return free[rnd * free.len] |
|||
end function |
|||
// place the king |
|||
Output:<pre>gp > for(i=1, 10, print(chess960())); |
|||
kingIdx = randFree(1, 6) |
|||
["N", "R", "Q", "K", "N", "R", "B", "B"] |
|||
rank[kingIdx] = "♔" |
|||
["R", "K", "N", "B", "N", "R", "B", "Q"] |
|||
["B", "R", "K", "N", "R", "B", "Q", "N"] |
|||
["R", "B", "Q", "K", "B", "N", "R", "N"] |
|||
["R", "B", "K", "N", "N", "Q", "B", "R"] |
|||
["N", "Q", "N", "R", "B", "K", "R", "B"] |
|||
["N", "Q", "R", "B", "K", "R", "B", "N"] |
|||
["N", "R", "B", "K", "R", "N", "Q", "B"] |
|||
["R", "K", "Q", "N", "B", "N", "R", "B"] |
|||
["B", "B", "R", "N", "K", "R", "N", "Q"] |
|||
</pre> |
|||
// place rooks |
|||
Alternatively with recent version of PARI/GP >= 2.9:<pre>gp > M=Map(["B","♗";"K","♔";"N","♘";"Q","♕";"R","♖"]); |
|||
rank[randFree(0, kingIdx - 1)] = "♖" |
|||
gp > for(i=1,10,print(concat(apply((c)->mapget(M,c),chess960())))); |
|||
rank[randFree(kingIdx + 1, 7)] = "♖" |
|||
♗♖♘♔♕♗♖♘ |
|||
♕♖♘♔♖♗♗♘ |
|||
♖♕♘♔♘♖♗♗ |
|||
♘♘♗♖♕♗♔♖ |
|||
♘♘♖♗♗♔♖♕ |
|||
♗♖♘♔♘♕♖♗ |
|||
♖♔♘♗♗♕♘♖ |
|||
♘♖♗♘♔♗♕♖ |
|||
♖♔♕♗♗♘♖♘ |
|||
♕♗♖♔♗♘♘♖</pre> |
|||
// place bishops |
|||
=={{header|Perl}}== |
|||
bishIdx = randFree(0, 7) |
|||
Directly generates a configuration by inserting pieces at random appropriate places. Each config has an equal chance of being produced. |
|||
rank[bishIdx] = "♗" |
|||
<lang perl>sub rnd($) { int(rand(shift)) } |
|||
while true |
|||
i = randFree(0, 7) |
|||
if i % 2 != bishIdx % 2 then break |
|||
end while |
|||
rank[i] = "♗" |
|||
// place queen |
|||
sub empties { grep !$_[0][$_], 0 .. 7 } |
|||
rank[randFree(0, 7)] = "♕" |
|||
print join(rank, " ")</syntaxhighlight> |
|||
sub chess960 { |
|||
{{out}} |
|||
my @s = (undef) x 8; |
|||
<pre>♘ ♖ ♕ ♔ ♖ ♘ ♗ ♗</pre> |
|||
@s[2*rnd(4), 1 + 2*rnd(4)] = qw/B B/; |
|||
=={{header|Nim}}== |
|||
for (qw/Q N N/) { |
|||
<syntaxhighlight lang="nim">import random, strutils |
|||
my @idx = empties \@s; |
|||
$s[$idx[rnd(@idx)]] = $_; |
|||
} |
|||
type |
|||
@s[empties \@s] = qw/R K R/; |
|||
@s |
|||
} |
|||
print "@{[chess960]}\n" for 0 .. 10;</lang> |
|||
{{out}} |
|||
<pre> |
|||
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 |
|||
</pre> |
|||
# Chess pieces on first row. |
|||
=={{header|Perl 6}}== |
|||
Pieces {.pure.} = enum |
|||
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. |
|||
King = "♔", |
|||
<lang perl6>repeat until m/ '♗' [..]* '♗' / { $_ = < ♖ ♖ ♖ ♕ ♗ ♗ ♘ ♘ >.pick(*).join } |
|||
Queen = "♕", |
|||
s:2nd['♖'] = '♔'; |
|||
Rook1 = "♖", |
|||
say .comb;</lang> |
|||
Rook2 = "♖", |
|||
{{out}} |
|||
Bishop1 = "♗", |
|||
<big><big><big><big><pre>♕ ♗ ♖ ♘ ♔ ♖ ♗ ♘</pre></big></big></big></big> |
|||
Bishop2 = "♗", |
|||
Here's a more "functional" solution that avoids side effects |
|||
Knight1 = "♘", |
|||
<lang perl6>sub chess960 { |
|||
Knight2 = "♘" |
|||
.subst(:nth(2), /'♜'/, '♚') given |
|||
first rx/ '♝' [..]* '♝' /, |
|||
< ♛ ♜ ♜ ♜ ♝ ♝ ♞ ♞ >.pick(*).join xx *; |
|||
} |
|||
# Position counted from 0. |
|||
say chess960;</lang> |
|||
Position = range[0..7] |
|||
{{out}} |
|||
<big><big><big><big><pre>♛♝♜♚♝♞♞♜</pre></big></big></big></big> |
|||
# Position of pieces. |
|||
We can also pregenerate the list of 960 positions, though the method we use below is a bit wasteful, since it |
|||
Positions = array[Pieces, Position] |
|||
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; |
|||
func pop[T](s: var set[T]): T = |
|||
.say for chess960;</lang> |
|||
## Remove and return the first element of a set. |
|||
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. |
|||
for val in s: |
|||
<lang perl6>constant chess960 = eager gather for 0..3 -> $q { |
|||
result = val |
|||
(my @q = <♜ ♚ ♜>).splice($q, 0, '♛'); |
|||
break |
|||
for 0 .. @q -> $n1 { |
|||
s.excl(result) |
|||
(my @n1 = @q).splice($n1, 0, '♞'); |
|||
for $n1 ^.. @n1 -> $n2 { |
|||
(my @n2 = @n1).splice($n2, 0, '♞'); |
|||
proc choose[T](s: var set[T]): T = |
|||
for 0 .. @n2 -> $b1 { |
|||
## Choose randomly a value from a set and remove it from the set. |
|||
(my @b1 = @n2).splice($b1, 0, '♝'); |
|||
result = sample(s) |
|||
for $b1+1, $b1+3 ...^ * > @b1 -> $b2 { |
|||
s.excl(result) |
|||
(my @b2 = @b1).splice($b2, 0, '♝'); |
|||
take @b2.join; |
|||
} |
|||
proc positions(): Positions = |
|||
} |
|||
## Return a randomly chosen list of piece positions for the first row. |
|||
} |
|||
} |
|||
var pos = {Position.low..Position.high} |
|||
} |
|||
# Set bishops. |
|||
result[Bishop1] = sample([0, 2, 4, 6]) # Black squares. |
|||
result[Bishop2] = sample([1, 3, 5, 7]) # White squares. |
|||
pos = pos - {result[Bishop1], result[Bishop2]} |
|||
# Set queen. |
|||
result[Queen] = pos.choose() |
|||
# Set knights. |
|||
result[Knight1] = pos.choose() |
|||
result[Knight2] = pos.choose() |
|||
# In the remaining three pieces, the king must be between the two rooks. |
|||
result[Rook1] = pos.pop() |
|||
result[King] = pos.pop() |
|||
result[Rook2] = pos.pop() |
|||
#——————————————————————————————————————————————————————————————————————————————————————————————————— |
|||
randomize() |
|||
for _ in 1..10: |
|||
var row: array[8, string] |
|||
let pos = positions() |
|||
for piece in Pieces: |
|||
row[pos[piece]] = $piece |
|||
echo row.join(" ")</syntaxhighlight> |
|||
CHECK { note "done compiling" } |
|||
note +chess960; |
|||
say chess960.pick;</lang> |
|||
{{out}} |
{{out}} |
||
<pre>♘ ♘ ♕ ♖ ♔ ♗ ♗ ♖ |
|||
<pre>done compiling |
|||
♖ ♗ ♗ ♔ ♕ ♘ ♘ ♖ |
|||
960 |
|||
♕ ♗ ♖ ♔ ♘ ♘ ♗ ♖ |
|||
♜♚♝♜♞♛♞♝</pre> |
|||
♖ ♘ ♗ ♕ ♔ ♘ ♖ ♗ |
|||
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. |
|||
♖ ♘ ♗ ♗ ♘ ♕ ♔ ♖ |
|||
♕ ♘ ♖ ♗ ♗ ♔ ♘ ♖ |
|||
♗ ♘ ♖ ♔ ♖ ♗ ♘ ♕ |
|||
♖ ♔ ♕ ♘ ♗ ♖ ♘ ♗ |
|||
♘ ♖ ♗ ♔ ♕ ♗ ♘ ♖ |
|||
♗ ♕ ♖ ♗ ♘ ♔ ♖ ♘</pre> |
|||
=={{header|Objeck}}== |
=={{header|Objeck}}== |
||
{{trans|C++}} |
{{trans|C++}} |
||
< |
<syntaxhighlight lang="objeck">class Chess960 { |
||
function : Main(args : String[]) ~ Nil { |
function : Main(args : String[]) ~ Nil { |
||
Generate(10); |
Generate(10); |
||
Line 1,003: | Line 1,892: | ||
return (Float->Random() * 1000)->As(Int) % 8; |
return (Float->Random() * 1000)->As(Int) % 8; |
||
} |
} |
||
}</ |
}</syntaxhighlight> |
||
Output: |
Output: |
||
Line 1,016: | Line 1,905: | ||
♖♕♔♖♘♘♗♗ |
♖♕♔♖♘♘♗♗ |
||
♗♖♘♔♘♖♕♗</pre></big></big> |
♗♖♘♔♘♖♕♗</pre></big></big> |
||
=={{header|PARI/GP}}== |
|||
<syntaxhighlight lang="parigp">chess960() = |
|||
{ |
|||
my (C = vector(8), i, j, r); |
|||
C[random(4) * 2 + 1] = C[random(4) * 2 + 2] = "B"; |
|||
for (i = 1, 3, while (C[r = random(8) + 1],); C[r] = Vec("NNQ")[i]); |
|||
for (i = 1, 8, if (!C[i], C[i] = Vec("RKR")[j++])); |
|||
C |
|||
}</syntaxhighlight> |
|||
Output:<pre>gp > for(i=1, 10, print(chess960())); |
|||
["N", "R", "Q", "K", "N", "R", "B", "B"] |
|||
["R", "K", "N", "B", "N", "R", "B", "Q"] |
|||
["B", "R", "K", "N", "R", "B", "Q", "N"] |
|||
["R", "B", "Q", "K", "B", "N", "R", "N"] |
|||
["R", "B", "K", "N", "N", "Q", "B", "R"] |
|||
["N", "Q", "N", "R", "B", "K", "R", "B"] |
|||
["N", "Q", "R", "B", "K", "R", "B", "N"] |
|||
["N", "R", "B", "K", "R", "N", "Q", "B"] |
|||
["R", "K", "Q", "N", "B", "N", "R", "B"] |
|||
["B", "B", "R", "N", "K", "R", "N", "Q"] |
|||
</pre> |
|||
Alternatively with recent version of PARI/GP >= 2.9:<pre>gp > M=Map(["B","♗";"K","♔";"N","♘";"Q","♕";"R","♖"]); |
|||
gp > for(i=1,10,print(concat(apply((c)->mapget(M,c),chess960())))); |
|||
♗♖♘♔♕♗♖♘ |
|||
♕♖♘♔♖♗♗♘ |
|||
♖♕♘♔♘♖♗♗ |
|||
♘♘♗♖♕♗♔♖ |
|||
♘♘♖♗♗♔♖♕ |
|||
♗♖♘♔♘♕♖♗ |
|||
♖♔♘♗♗♕♘♖ |
|||
♘♖♗♘♔♗♕♖ |
|||
♖♔♕♗♗♘♖♘ |
|||
♕♗♖♔♗♘♘♖</pre> |
|||
=={{header|Perl}}== |
|||
Directly generates a configuration by inserting pieces at random appropriate places. Each config has an equal chance of being produced. |
|||
<syntaxhighlight 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;</syntaxhighlight> |
|||
{{out}} |
|||
<pre>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</pre> |
|||
=={{header|Phix}}== |
|||
Examines all 40320 permutations for validity and saves them in a list, which is easy to pick random entries from.<br> |
|||
Using a dictionary (as commented out) is a little faster, but harder to extract random entries from.<br> |
|||
For something faster, and truer to the task description, just use the commented out permute(rand(factorial(8) line, |
|||
and quit as soon as you find a valid one (but I wanted to check that I had found exactly 960). |
|||
<!--<syntaxhighlight lang="phix">(phixonline)--> |
|||
<span style="color: #008080;">with</span> <span style="color: #008080;">javascript_semantics</span> |
|||
<span style="color: #004080;">sequence</span> <span style="color: #000000;">solutions</span> <span style="color: #0000FF;">=</span> <span style="color: #0000FF;">{}</span> |
|||
<span style="color: #000080;font-style:italic;">--integer d = new_dict()</span> |
|||
<span style="color: #008080;">for</span> <span style="color: #000000;">i</span><span style="color: #0000FF;">=</span><span style="color: #000000;">1</span> <span style="color: #008080;">to</span> <span style="color: #7060A8;">factorial</span><span style="color: #0000FF;">(</span><span style="color: #000000;">8</span><span style="color: #0000FF;">)</span> <span style="color: #008080;">do</span> |
|||
<span style="color: #004080;">sequence</span> <span style="color: #000000;">s</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">permute</span><span style="color: #0000FF;">(</span><span style="color: #000000;">i</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"RNBQKBNR"</span><span style="color: #0000FF;">)</span> |
|||
<span style="color: #000080;font-style:italic;">-- sequence s = permute(rand(factorial(8),"RNBQKBNR")</span> |
|||
<span style="color: #004080;">integer</span> <span style="color: #000000;">b1</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">find</span><span style="color: #0000FF;">(</span><span style="color: #008000;">'B'</span><span style="color: #0000FF;">,</span><span style="color: #000000;">s</span><span style="color: #0000FF;">),</span> |
|||
<span style="color: #000000;">b2</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">find</span><span style="color: #0000FF;">(</span><span style="color: #008000;">'B'</span><span style="color: #0000FF;">,</span><span style="color: #000000;">s</span><span style="color: #0000FF;">,</span><span style="color: #000000;">b1</span><span style="color: #0000FF;">+</span><span style="color: #000000;">1</span><span style="color: #0000FF;">)</span> |
|||
<span style="color: #008080;">if</span> <span style="color: #7060A8;">and_bits</span><span style="color: #0000FF;">(</span><span style="color: #000000;">b2</span><span style="color: #0000FF;">-</span><span style="color: #000000;">b1</span><span style="color: #0000FF;">,</span><span style="color: #000000;">1</span><span style="color: #0000FF;">)=</span><span style="color: #000000;">1</span> <span style="color: #008080;">then</span> |
|||
<span style="color: #004080;">integer</span> <span style="color: #000000;">k</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">find</span><span style="color: #0000FF;">(</span><span style="color: #008000;">'K'</span><span style="color: #0000FF;">,</span><span style="color: #000000;">s</span><span style="color: #0000FF;">)</span> |
|||
<span style="color: #004080;">integer</span> <span style="color: #000000;">r1</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">find</span><span style="color: #0000FF;">(</span><span style="color: #008000;">'R'</span><span style="color: #0000FF;">,</span><span style="color: #000000;">s</span><span style="color: #0000FF;">)</span> |
|||
<span style="color: #004080;">integer</span> <span style="color: #000000;">r2</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">find</span><span style="color: #0000FF;">(</span><span style="color: #008000;">'R'</span><span style="color: #0000FF;">,</span><span style="color: #000000;">s</span><span style="color: #0000FF;">,</span><span style="color: #000000;">r1</span><span style="color: #0000FF;">+</span><span style="color: #000000;">1</span><span style="color: #0000FF;">)</span> |
|||
<span style="color: #008080;">if</span> <span style="color: #000000;">r1</span><span style="color: #0000FF;"><</span><span style="color: #000000;">k</span> <span style="color: #008080;">and</span> <span style="color: #000000;">k</span><span style="color: #0000FF;"><</span><span style="color: #000000;">r2</span> <span style="color: #008080;">then</span> |
|||
<span style="color: #008080;">if</span> <span style="color: #7060A8;">find</span><span style="color: #0000FF;">(</span><span style="color: #000000;">s</span><span style="color: #0000FF;">,</span><span style="color: #000000;">solutions</span><span style="color: #0000FF;">)=</span><span style="color: #000000;">0</span> <span style="color: #008080;">then</span> |
|||
<span style="color: #000080;font-style:italic;">-- if getd_index(s,d)=0 then |
|||
-- setd(s,0,d)</span> |
|||
<span style="color: #000000;">solutions</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">append</span><span style="color: #0000FF;">(</span><span style="color: #000000;">solutions</span><span style="color: #0000FF;">,</span><span style="color: #000000;">s</span><span style="color: #0000FF;">)</span> |
|||
<span style="color: #008080;">end</span> <span style="color: #008080;">if</span> |
|||
<span style="color: #008080;">end</span> <span style="color: #008080;">if</span> |
|||
<span style="color: #008080;">end</span> <span style="color: #008080;">if</span> |
|||
<span style="color: #008080;">end</span> <span style="color: #008080;">for</span> |
|||
<span style="color: #7060A8;">printf</span><span style="color: #0000FF;">(</span><span style="color: #000000;">1</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"Found %d solutions\n"</span><span style="color: #0000FF;">,{</span><span style="color: #7060A8;">length</span><span style="color: #0000FF;">(</span><span style="color: #000000;">solutions</span><span style="color: #0000FF;">)})</span> |
|||
<span style="color: #008080;">for</span> <span style="color: #000000;">i</span><span style="color: #0000FF;">=</span><span style="color: #000000;">1</span> <span style="color: #008080;">to</span> <span style="color: #000000;">5</span> <span style="color: #008080;">do</span> |
|||
<span style="color: #0000FF;">?</span><span style="color: #000000;">solutions</span><span style="color: #0000FF;">[</span><span style="color: #7060A8;">rand</span><span style="color: #0000FF;">(</span><span style="color: #7060A8;">length</span><span style="color: #0000FF;">(</span><span style="color: #000000;">solutions</span><span style="color: #0000FF;">))]</span> |
|||
<span style="color: #008080;">end</span> <span style="color: #008080;">for</span> |
|||
<!--</syntaxhighlight>--> |
|||
{{out}} |
|||
<pre>Found 960 solutions |
|||
"QRNNKRBB" |
|||
"BQRNKBNR" |
|||
"BRQBNNKR" |
|||
"QBNRBKRN" |
|||
"RNKBBQRN"</pre> |
|||
=={{header|PicoLisp}}== |
=={{header|PicoLisp}}== |
||
{{Output?}} |
|||
<lang PicoLisp>(load "@lib/simul.l") |
|||
<syntaxhighlight lang="picolisp">(load "@lib/simul.l") |
|||
(seed (in "/dev/urandom" (rd 8))) |
(seed (in "/dev/urandom" (rd 8))) |
||
Line 1,033: | Line 2,033: | ||
(prinl) ) |
(prinl) ) |
||
(bye)</ |
(bye)</syntaxhighlight> |
||
=={{header|PowerShell}}== |
=={{header|PowerShell}}== |
||
{{works with|powershell|2}} |
{{works with|powershell|2}} |
||
<syntaxhighlight lang="powershell">function Get-RandomChess960Start |
|||
<lang PowerShell> |
|||
function Get-RandomChess960Start |
|||
{ |
{ |
||
$Starts = @() |
$Starts = @() |
||
Line 1,066: | Line 2,066: | ||
Get-RandomChess960Start |
Get-RandomChess960Start |
||
Get-RandomChess960Start |
Get-RandomChess960Start |
||
</syntaxhighlight> |
|||
</lang> |
|||
{{out}} |
{{out}} |
||
<pre>♘♕♖♔♖♘♗♗ |
|||
<pre> |
|||
♘♕♖♔♖♘♗♗ |
|||
♗♕♘♖♔♗♖♘ |
♗♕♘♖♔♗♖♘ |
||
♖♗♔♕♗♖♘♘ |
♖♗♔♕♗♖♘♘ |
||
♘♖♔♖♕♗♗♘ |
♘♖♔♖♕♗♗♘</pre> |
||
</pre> |
|||
=={{header|Prolog}}== |
|||
Uses <code lang="prolog">random_permutation/2</code> defined in SWI-Prolog. |
|||
<syntaxhighlight lang="prolog"> |
|||
check(Row) :- |
|||
nth1(King, Row, ♔), |
|||
nth1(Rook1, Row, ♖), |
|||
nth1(Rook2, Row, ♖), |
|||
nth1(Bishop1, Row, ♗), |
|||
nth1(Bishop2, Row, ♗), |
|||
Rook1 < King, King < Rook2, |
|||
(Bishop1 + Bishop2) mod 2 =:= 1. |
|||
generate(Row) :- |
|||
random_permutation([♖,♘,♗,♕,♔,♗,♘,♖], Row), |
|||
check(Row) ; generate(Row). |
|||
</syntaxhighlight> |
|||
Example run: |
|||
<syntaxhighlight lang="prolog"> |
|||
?- generate(X). |
|||
X = [♘, ♗, ♖, ♕, ♘, ♔, ♗, ♖] ; |
|||
X = [♘, ♗, ♖, ♕, ♘, ♔, ♗, ♖] ; |
|||
X = [♘, ♖, ♘, ♕, ♔, ♖, ♗, ♗] ; |
|||
X = [♘, ♖, ♘, ♕, ♔, ♖, ♗, ♗] |
|||
</syntaxhighlight> |
|||
=={{header|Python}}== |
=={{header|Python}}== |
||
Line 1,079: | Line 2,104: | ||
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. |
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. |
||
< |
<syntaxhighlight lang="python">>>> from itertools import permutations |
||
>>> pieces = 'KQRrBbNN' |
>>> pieces = 'KQRrBbNN' |
||
>>> starts = {''.join(p).upper() for p in permutations(pieces) |
>>> starts = {''.join(p).upper() for p in permutations(pieces) |
||
Line 1,089: | Line 2,114: | ||
>>> starts.pop() |
>>> starts.pop() |
||
'QNBRNKRB' |
'QNBRNKRB' |
||
>>> |
>>></syntaxhighlight> |
||
===Python: Regexp=== |
===Python: Regexp=== |
||
This uses regexps to filter permutations of the start position pieces rather than indexing. |
This uses regexps to filter permutations of the start position pieces rather than indexing. |
||
< |
<syntaxhighlight lang="python">>>> import re |
||
>>> pieces = 'KQRRBBNN' |
>>> pieces = 'KQRRBBNN' |
||
>>> bish = re.compile(r'B(|..|....|......)B').search |
>>> bish = re.compile(r'B(|..|....|......)B').search |
||
Line 1,103: | Line 2,128: | ||
>>> starts3.pop() |
>>> starts3.pop() |
||
'QRNKBNRB' |
'QRNKBNRB' |
||
>>> |
>>></syntaxhighlight> |
||
===Python: Correct by construction=== |
===Python: Correct by construction=== |
||
Follows Perl algorithm of constructing one start position randomly, according to the rules. |
Follows Perl algorithm of constructing one start position randomly, according to the rules. |
||
(See talk page for tests). |
(See talk page for tests). |
||
< |
<syntaxhighlight lang="python">from random import choice |
||
def random960(): |
def random960(): |
||
Line 1,122: | Line 2,147: | ||
return ''.join(start).upper() |
return ''.join(start).upper() |
||
print(random960())</ |
print(random960())</syntaxhighlight> |
||
{{out}} |
{{out}} |
||
<pre>['N', 'R', 'K', 'N', 'B', 'Q', 'R', 'B']</pre> |
<pre>['N', 'R', 'K', 'N', 'B', 'Q', 'R', 'B']</pre> |
||
===Python: Generate all positions then choose one randomly=== |
===Python: Generate all positions then choose one randomly=== |
||
< |
<syntaxhighlight lang="python">from random import choice |
||
def generate960(): |
def generate960(): |
||
Line 1,158: | Line 2,182: | ||
gen = generate960() |
gen = generate960() |
||
print(''.join(choice(gen)))</ |
print(''.join(choice(gen)))</syntaxhighlight> |
||
{{out}} |
{{out}} |
||
<pre>NRBQNKRB</pre> |
<pre>NRBQNKRB</pre> |
||
=={{header|R}}== |
|||
<syntaxhighlight lang="rsplus"> |
|||
pieces <- c("R","B","N","Q","K","N","B","R") |
|||
generateFirstRank <- function() { |
|||
attempt <- paste0(sample(pieces), collapse = "") |
|||
while (!check_position(attempt)) { |
|||
attempt <- paste0(sample(pieces), collapse = "") |
|||
} |
|||
return(attempt) |
|||
} |
|||
check_position <- function(position) { |
|||
if (regexpr('.*R.*K.*R.*', position) == -1) return(FALSE) |
|||
if (regexpr('.*B(..|....|......|)B.*', position) == -1) return(FALSE) |
|||
TRUE |
|||
} |
|||
convert_to_unicode <- function(s) { |
|||
s <- sub("K","\u2654", s) |
|||
s <- sub("Q","\u2655", s) |
|||
s <- gsub("R","\u2656", s) |
|||
s <- gsub("B","\u2657", s) |
|||
s <- gsub("N","\u2658", s) |
|||
} |
|||
cat(convert_to_unicode(generateFirstRank()), "\n") |
|||
</syntaxhighlight> |
|||
{{out}} |
|||
<pre> |
|||
♘♗♘♖♗♕♔♖ |
|||
</pre> |
|||
=={{header|Racket}}== |
=={{header|Racket}}== |
||
Line 1,167: | Line 2,223: | ||
Constructive: |
Constructive: |
||
< |
<syntaxhighlight lang="racket">#lang racket |
||
(define white (match-lambda ['P #\♙] ['R #\♖] ['B #\♗] ['N #\♘] ['Q #\♕] ['K #\♔])) |
(define white (match-lambda ['P #\♙] ['R #\♖] ['B #\♗] ['N #\♘] ['Q #\♕] ['K #\♔])) |
||
(define black (match-lambda ['P #\♟] ['R #\♜] ['B #\♝] ['N #\♞] ['Q #\♛] ['K #\♚])) |
(define black (match-lambda ['P #\♟] ['R #\♜] ['B #\♝] ['N #\♞] ['Q #\♛] ['K #\♚])) |
||
Line 1,198: | Line 2,254: | ||
(list->string (vector->list v))) |
(list->string (vector->list v))) |
||
(chess960-start-position)</ |
(chess960-start-position)</syntaxhighlight> |
||
{{out}} |
{{out}} |
||
<pre>"♖♘♗♕♔♗♘♖"</pre> |
<pre>"♖♘♗♕♔♗♘♖"</pre> |
||
Line 1,205: | Line 2,261: | ||
Try again: |
Try again: |
||
<pre>"♘♖♔♕♗♗♖♘"</pre> |
<pre>"♘♖♔♕♗♗♖♘"</pre> |
||
=={{header|Raku}}== |
|||
(formerly 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. |
|||
<syntaxhighlight lang="raku" line>repeat until m/ '♗' [..]* '♗' / { $_ = < ♖ ♖ ♖ ♕ ♗ ♗ ♘ ♘ >.pick(*).join } |
|||
s:2nd['♖'] = '♔'; |
|||
say .comb;</syntaxhighlight> |
|||
{{out}} |
|||
<big><big><big><big><pre>♕ ♗ ♖ ♘ ♔ ♖ ♗ ♘</pre></big></big></big></big> |
|||
Here's a more "functional" solution that avoids side effects |
|||
<syntaxhighlight lang="raku" line>sub chess960 { |
|||
.subst(:nth(2), /'♜'/, '♚') given |
|||
first rx/ '♝' [..]* '♝' /, |
|||
< ♛ ♜ ♜ ♜ ♝ ♝ ♞ ♞ >.pick(*).join xx *; |
|||
} |
|||
say chess960;</syntaxhighlight> |
|||
{{out}} |
|||
<big><big><big><big><pre>♛♝♜♚♝♞♞♜</pre></big></big></big></big> |
|||
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. |
|||
<syntaxhighlight lang="raku" line>constant chess960 = |
|||
< ♛ ♜ ♜ ♜ ♝ ♝ ♞ ♞ >.permutations».join.unique.grep( / '♝' [..]* '♝' / )».subst(:nth(2), /'♜'/, '♚'); |
|||
.say for chess960;</syntaxhighlight> |
|||
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. |
|||
<syntaxhighlight lang="raku" line>constant chess960 = gather for 0..3 -> $q { |
|||
(my @q = <♜ ♚ ♜>).splice($q, 0, '♛'); |
|||
for 0 .. @q -> $n1 { |
|||
(my @n1 = @q).splice($n1, 0, '♞'); |
|||
for $n1 ^.. @n1 -> $n2 { |
|||
(my @n2 = @n1).splice($n2, 0, '♞'); |
|||
for 0 .. @n2 -> $b1 { |
|||
(my @b1 = @n2).splice($b1, 0, '♝'); |
|||
for $b1+1, $b1+3 ...^ * > @b1 -> $b2 { |
|||
(my @b2 = @b1).splice($b2, 0, '♝'); |
|||
take @b2.join; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
CHECK { note "done compiling" } |
|||
note +chess960; |
|||
say chess960.pick;</syntaxhighlight> |
|||
{{out}} |
|||
<pre>done compiling |
|||
960 |
|||
♜♚♝♜♞♛♞♝</pre> |
|||
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. |
|||
<big><big><big><big><pre>♛♝♜♚♝♞♞♜</pre></big></big></big></big> |
|||
=== Raku: Generate from SP-ID === |
|||
There is a [https://en.wikipedia.org/wiki/Fischer_random_chess_numbering_scheme standard numbering scheme] for Chess960 positions, assigning each an index in the range 0..959. This function will generate the corresponding position from a given index number (or fall back to a random one if no index is specified, making it yet another solution to the general problem). |
|||
<syntaxhighlight lang="raku" line>subset Pos960 of Int where { $_ ~~ ^960 }; |
|||
sub chess960(Pos960 $position = (^960).pick) { |
|||
# We remember the remainder used to place first bishop in order to place the |
|||
# second |
|||
my $b1; |
|||
# And likewise remember the chosen combination for knights between those |
|||
# placements |
|||
my @n; |
|||
# Piece symbols and positioning rules in order. Start with the position |
|||
# number. At each step, divide by the divisor; the quotient becomes the |
|||
# dividend for the next step. Feed the remainder into the specified code block |
|||
# to get a space number N, then place the piece in the Nth empty space left in |
|||
# the array. |
|||
my @rules = ( |
|||
#divisor, mapping function, piece |
|||
( 4, { $b1 = $_; 2 * $_ + 1 }, '♝' ), |
|||
( 4, { 2 * $_ - ($_ > $b1 ?? 1 !! 0) }, '♝' ), |
|||
( 6, { $_ }, '♛' ), |
|||
(10, { @n = combinations(5,2)[$_]; @n[0] }, '♞' ), |
|||
( 1, { @n[1]-1 }, '♞' ), |
|||
( 1, { 0 }, '♜' ), |
|||
( 1, { 0 }, '♚' ), |
|||
( 1, { 0 }, '♜' ) |
|||
); |
|||
# Initial array, using '-' to represent empty spaces |
|||
my @array = «-» xx 8; |
|||
# Working value that starts as the position number but is divided by the |
|||
# divisor at each placement step. |
|||
my $p = $position; |
|||
# Loop over the placement rules |
|||
for @rules -> ($divisor, $block, $piece) { |
|||
# get remainder when divided by divisor |
|||
(my $remainder, $p) = $p.polymod($divisor); |
|||
# apply mapping function |
|||
my $space = $block($remainder); |
|||
# find index of the $space'th element of the array that's still empty |
|||
my $index = @array.kv.grep(-> $i,$v { $v eq '-' })[$space][0]; |
|||
# and place the piece |
|||
@array[$index] = $piece; |
|||
} |
|||
return @array; |
|||
} |
|||
# demo code |
|||
say chess960(518); #standard optning position |
|||
say chess960; # (it happened to pick #300)</syntaxhighlight> |
|||
{{Out}} |
|||
<pre>♜ ♞ ♝ ♛ ♚ ♝ ♞ ♜ |
|||
♛ ♝ ♞ ♜ ♚ ♜ ♝ ♞</pre> |
|||
=={{header|REXX}}== |
=={{header|REXX}}== |
||
Random starting position is correct by construction (both REXX entries). |
Random starting position is correct by construction (both REXX entries). |
||
===generates one random position=== |
===generates one random position=== |
||
< |
<syntaxhighlight lang="rexx">/*REXX program generates a random starting position for the Chess960 game. */ |
||
parse arg seed . /*allow for (RANDOM BIF) repeatability.*/ |
parse arg seed . /*allow for (RANDOM BIF) repeatability.*/ |
||
if seed\=='' then call random ,,seed /*if SEED was specified, use the seed.*/ |
if seed\=='' then call random ,,seed /*if SEED was specified, use the seed.*/ |
||
Line 1,234: | Line 2,410: | ||
_= /*only the queen is left to be placed. */ |
_= /*only the queen is left to be placed. */ |
||
do i=1 for 8; _=_ || @.i; end /*construct the output: first rank only*/ |
do i=1 for 8; _=_ || @.i; end /*construct the output: first rank only*/ |
||
say translate(translate(_, 'q', .)) /*stick a fork in it, we're all done. */</ |
say translate(translate(_, 'q', .)) /*stick a fork in it, we're all done. */</syntaxhighlight> |
||
'''output''' |
'''output''' |
||
<pre> |
<pre>NRQKBRNB</pre> |
||
NRQKBRNB |
|||
</pre> |
|||
===generates all 960 positions randomly=== |
===generates all 960 positions randomly=== |
||
< |
<syntaxhighlight lang="rexx">/*REXX program generates all random starting positions for the Chess960 game. */ |
||
parse arg seed . /*allow for (RANDOM BIF) repeatability.*/ |
parse arg seed . /*allow for (RANDOM BIF) repeatability.*/ |
||
if seed\=='' then call random ,,seed /*if SEED was specified, use the seed.*/ |
if seed\=='' then call random ,,seed /*if SEED was specified, use the seed.*/ |
||
Line 1,278: | Line 2,452: | ||
say # 'unique starting positions found after ' t "generations." |
say # 'unique starting positions found after ' t "generations." |
||
/*stick a fork in it, we're all done. */ /**/</ |
/*stick a fork in it, we're all done. */ /**/</syntaxhighlight> |
||
'''output''' |
'''output''' |
||
<pre> 1000 random generations: 515 unique starting positions. |
|||
<pre> |
|||
1000 random generations: 515 unique starting positions. |
|||
2000 random generations: 707 unique starting positions. |
2000 random generations: 707 unique starting positions. |
||
3000 random generations: 796 unique starting positions. |
3000 random generations: 796 unique starting positions. |
||
Line 1,295: | Line 2,468: | ||
13000 random generations: 959 unique starting positions. |
13000 random generations: 959 unique starting positions. |
||
14000 random generations: 959 unique starting positions. |
14000 random generations: 959 unique starting positions. |
||
960 unique starting positions found after 14639 generations. |
960 unique starting positions found after 14639 generations.</pre> |
||
</pre> |
|||
===version 3 COMPUTE all possibilities=== |
===version 3 COMPUTE all possibilities=== |
||
< |
<syntaxhighlight lang="rexx">/*--------------------------------------------------------------- |
||
* Compute the 960 possible solutions |
* Compute the 960 possible solutions |
||
* There must be at least one field between the rooks |
* There must be at least one field between the rooks |
||
Line 1,314: | Line 2,486: | ||
Call rest |
Call rest |
||
End |
End |
||
End |
|||
End |
End |
||
say cnt.1 'solutions' |
say cnt.1 'solutions' |
||
Line 1,351: | Line 2,524: | ||
ol=ol||pos.k |
ol=ol||pos.k |
||
End |
End |
||
cnt.1 |
cnt.1=cnt.1+1 |
||
If cnt.1<4 |, |
If cnt.1<4 |, |
||
cnt.1>957 Then |
cnt.1>957 Then |
||
Line 1,357: | Line 2,530: | ||
If cnt.1=4 Then |
If cnt.1=4 Then |
||
Say ' ...' |
Say ' ...' |
||
Return</ |
Return</syntaxhighlight> |
||
{{out}} |
{{out}} |
||
<pre> 1 45678 1 2 3 RKRQBBNN |
<pre> 1 45678 1 2 3 RKRQBBNN |
||
Line 1,367: | Line 2,540: | ||
960 12345 6 7 8 NNBBQRKR |
960 12345 6 7 8 NNBBQRKR |
||
960 solutions</pre> |
960 solutions</pre> |
||
=={{header|RPL}}== |
|||
We use here the single die method, starting with a list of rooks that are gradually replaced by other pieces. |
|||
{{works with|HP|48G}} |
|||
≪ SWAP 1 ≪ 'R' SAME ≫ DOLIST 0 SWAP |
|||
1 OVER SIZE '''FOR''' j |
|||
DUP j GET ROT + SWAP |
|||
j 3 PICK PUT |
|||
'''NEXT''' |
|||
SWAP DROP SWAP POS |
|||
≫ '<span style="color:blue">ROOKPOS</span>' STO |
|||
≪ { } 1 8 '''START''' 'R' + '''NEXT''' |
|||
RAND 4 * FLOOR 2 * 1 + 'B' PUT |
|||
RAND 4 * CEIL 2 * 'B' PUT |
|||
DUP RAND 6 * CEIL <span style="color:blue">ROOKPOS</span> 'Q' PUT |
|||
DUP RAND 5 * CEIL <span style="color:blue">ROOKPOS</span> 'N' PUT |
|||
DUP RAND 4 * CEIL <span style="color:blue">ROOKPOS</span> 'N' PUT |
|||
DUP 2 <span style="color:blue">ROOKPOS</span> 'K' PUT |
|||
≫ '<span style="color:blue">→CH360</span>' STO |
|||
{{out}} |
|||
<pre> |
|||
3: { N Q R K B B R N } |
|||
2: { R K B B R Q N N } |
|||
1: { Q N R B K N B R } |
|||
</pre> |
|||
=={{header|Ruby}}== |
=={{header|Ruby}}== |
||
===Ruby: shuffle pieces until all regexes match=== |
===Ruby: shuffle pieces until all regexes match=== |
||
Translation of Tcl. |
Translation of Tcl. |
||
< |
<syntaxhighlight lang="ruby">pieces = %i(♔ ♕ ♘ ♘ ♗ ♗ ♖ ♖) |
||
regexes = [/♗(..)*♗/, /♖.*♔.*♖/] |
regexes = [/♗(..)*♗/, /♖.*♔.*♖/] |
||
row = pieces.shuffle.join until regexes.all?{|re| re.match(row)} |
row = pieces.shuffle.join until regexes.all?{|re| re.match(row)} |
||
puts row</ |
puts row</syntaxhighlight> |
||
{{output}} |
{{output}} |
||
<big><big><big><big><pre>♕♖♗♘♔♖♘♗</pre></big></big></big></big> |
<big><big><big><big><pre>♕♖♗♘♔♖♘♗</pre></big></big></big></big> |
||
Line 1,380: | Line 2,579: | ||
===Ruby: Construct=== |
===Ruby: Construct=== |
||
Uses the Perl idea of starting with [R,K,R] and inserting the rest: |
Uses the Perl idea of starting with [R,K,R] and inserting the rest: |
||
< |
<syntaxhighlight lang="ruby">row = [:♖, :♔, :♖] |
||
[:♕, :♘, :♘].each{|piece| row.insert(rand(row.size+1), piece)} |
[:♕, :♘, :♘].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, :♗)} |
[[0, 2, 4, 6].sample, [1, 3, 5, 7].sample].sort.each{|pos| row.insert(pos, :♗)} |
||
puts row</ |
puts row</syntaxhighlight> |
||
{{output}} |
{{output}} |
||
<big><big><big><big><pre>♗♘♕♘♖♗♔♖</pre></big></big></big></big> |
<big><big><big><big><pre>♗♘♕♘♖♗♔♖</pre></big></big></big></big> |
||
Line 1,390: | Line 2,589: | ||
===Ruby: Generate from SP-ID=== |
===Ruby: Generate from SP-ID=== |
||
'''[[wp:Chess960 numbering scheme|Chess960 numbering scheme]]''' |
'''[[wp:Chess960 numbering scheme|Chess960 numbering scheme]]''' |
||
< |
<syntaxhighlight lang="ruby">KRN = %w(NNRKR NRNKR NRKNR NRKRN RNNKR RNKNR RNKRN RKNNR RKNRN RKRNN) |
||
def chess960(id=rand(960)) |
def chess960(id=rand(960)) |
||
Line 1,411: | Line 2,610: | ||
puts "\nGenerate random Start Position" |
puts "\nGenerate random Start Position" |
||
5.times {puts chess960}</ |
5.times {puts chess960}</syntaxhighlight> |
||
{{out}} |
{{out}} |
||
<pre>Generate Start Position from id number |
|||
<pre> |
|||
Generate Start Position from id number |
|||
0 : BBQNNRKR |
0 : BBQNNRKR |
||
518 : RNBQKBNR |
518 : RNBQKBNR |
||
Line 1,424: | Line 2,622: | ||
BBRNQKNR |
BBRNQKNR |
||
NBRKNRBQ |
NBRKNRBQ |
||
BRKQNNRB |
BRKQNNRB</pre> |
||
</pre> |
|||
=={{header|Rust}}== |
=={{header|Rust}}== |
||
{{Output?}} |
|||
{{trans|Kotlin}} |
{{trans|Kotlin}} |
||
< |
<syntaxhighlight lang="rust">use std::collections::BTreeSet; |
||
struct Chess960 ( BTreeSet<String> ); |
struct Chess960 ( BTreeSet<String> ); |
||
Line 1,460: | Line 2,658: | ||
chess960.invoke("", "KQRRNNBB"); |
chess960.invoke("", "KQRRNNBB"); |
||
for (i, p) in chess960.0.iter().enumerate() { |
|||
let mut i = 0; |
|||
for p in chess960.0 { |
|||
println!("{}: {}", i, p); |
println!("{}: {}", i, p); |
||
i += 1; |
|||
} |
} |
||
}</ |
}</syntaxhighlight> |
||
===Rust 1.57 nightly=== |
|||
<syntaxhighlight lang="rust"> |
|||
// Chess960: regex and unicode version, create 5 valid random positions. |
|||
use rand::{seq::SliceRandom, thread_rng}; |
|||
use regex::Regex; |
|||
fn vec_to_string(v: Vec<&str>) -> String { |
|||
let mut is_string = String::new(); |
|||
for ele in v { |
|||
is_string.push_str(ele) |
|||
} |
|||
is_string |
|||
} |
|||
fn is_rook_king_ok(str_to_check: Vec<&str>) -> bool { |
|||
Regex::new(r"(.*♖.*♔.*♖.*)") |
|||
.unwrap() |
|||
.is_match(vec_to_string(str_to_check.clone()).as_str()) |
|||
} |
|||
fn is_two_bishops_ok(str_to_check: Vec<&str>) -> bool { |
|||
Regex::new(r"(.*♗.{0}♗.*|.*♗.{2}♗.*|.*♗.{4}♗.*|.*♗.{6}♗.*)") |
|||
.unwrap() |
|||
.is_match(vec_to_string(str_to_check.clone()).as_str()) |
|||
} |
|||
fn create_rnd_candidate() -> [&'static str; 8] { |
|||
let mut rng = thread_rng(); |
|||
let mut chaine = ["♖", "♘", "♗", "♔", "♕", "♗", "♘", "♖"]; |
|||
loop { |
|||
chaine.shuffle(&mut rng); |
|||
if is_candidate_valide(chaine) { |
|||
break chaine; |
|||
} |
|||
} |
|||
} |
|||
fn is_candidate_valide(s: [&str; 8]) -> bool { |
|||
is_rook_king_ok(s.to_vec()) && is_two_bishops_ok(s.to_vec()) |
|||
} |
|||
fn main() { |
|||
for _ in 0..5 { |
|||
println!("{:?}", create_rnd_candidate()); |
|||
} |
|||
}</syntaxhighlight> |
|||
{{out}} |
|||
<pre>["♕", "♘", "♗", "♗", "♖", "♘", "♔", "♖"] |
|||
["♖", "♔", "♗", "♕", "♘", "♗", "♖", "♘"] |
|||
["♗", "♖", "♕", "♔", "♘", "♗", "♖", "♘"] |
|||
["♘", "♖", "♘", "♕", "♔", "♖", "♗", "♗"] |
|||
["♖", "♗", "♕", "♔", "♗", "♘", "♖", "♘"]</pre> |
|||
=={{header|Scala}}== |
=={{header|Scala}}== |
||
===Functional Programming, tail recursive, Unicode, RegEx=== |
|||
<syntaxhighlight lang="scala">import scala.annotation.tailrec |
|||
object Chess960 extends App { |
|||
private val pieces = List('♖', '♗', '♘', '♕', '♔', '♘', '♗', '♖') |
|||
@tailrec |
|||
private def generateFirstRank(pieces: List[Char]): List[Char] = { |
|||
def check(rank: String) = |
|||
rank.matches(".*♖.*♔.*♖.*") && rank.matches(".*♗(..|....|......|)♗.*") |
|||
val p = scala.util.Random.shuffle(pieces) |
|||
if (check(p.toString.replaceAll("[^\\p{Upper}]", ""))) |
|||
generateFirstRank(pieces) |
|||
else p |
|||
} |
|||
loop(10) |
|||
@tailrec |
|||
private def loop(n: Int): Unit = { |
|||
println(generateFirstRank(pieces)) |
|||
if (n <= 0) () else loop(n - 1) |
|||
} |
|||
}</syntaxhighlight> |
|||
{{Out}}See it running in your browser by [https://scalafiddle.io/sf/AkvVAlG/0 ScalaFiddle (JavaScript, non JVM)] or by [https://scastie.scala-lang.org/qpKdhOc4SkuAbze8kgU6zQ Scastie (JVM)]. |
|||
===Imperative Programming=== |
|||
{{trans|Kotlin}} |
{{trans|Kotlin}} |
||
< |
<syntaxhighlight lang="scala">object Chess960 extends App { |
||
private def apply(b: String, e: String) { |
private def apply(b: String, e: String) { |
||
if (e.length <= 1) { |
if (e.length <= 1) { |
||
Line 1,489: | Line 2,764: | ||
apply("", "KQRRNNBB") |
apply("", "KQRRNNBB") |
||
for ((s, i) <- patterns.zipWithIndex) println(s"$i: $s") |
for ((s, i) <- patterns.zipWithIndex) println(s"$i: $s") |
||
}</ |
}</syntaxhighlight> |
||
=={{header|Scheme}}== |
|||
{{libheader|Scheme/SRFIs}} |
|||
<syntaxhighlight lang="scheme">(import (scheme base) (scheme write) |
|||
(srfi 1) ; list library |
|||
(srfi 27)) ; random numbers |
|||
(random-source-randomize! default-random-source) |
|||
;; Random integer in [start, end) |
|||
(define (random-between start end) |
|||
(let ((len (- end start 1))) |
|||
(if (< len 2) |
|||
start |
|||
(+ start (random-integer len))))) |
|||
;; Random item in list |
|||
(define (random-pick lst) |
|||
(if (= 1 (length lst)) |
|||
(car lst) |
|||
(list-ref lst (random-integer (length lst))))) |
|||
;; Construct a random piece placement for Chess960 |
|||
(define (random-piece-positions) |
|||
(define (free-indices positions) ; return list of empty slot indices |
|||
(let loop ((i 0) |
|||
(free '())) |
|||
(if (= 8 i) |
|||
free |
|||
(loop (+ 1 i) |
|||
(if (string=? "." (vector-ref positions i)) |
|||
(cons i free) |
|||
free))))) |
|||
; |
|||
(define (place-king+rooks positions) |
|||
(let ((king-posn (random-between 1 8))) |
|||
(vector-set! positions king-posn "K") |
|||
; left-rook is between left-edge and king |
|||
(vector-set! positions (random-between 0 king-posn) "R") |
|||
; right-rook is between right-edge and king |
|||
(vector-set! positions (random-between (+ 1 king-posn) 8) "R"))) |
|||
; |
|||
(define (place-bishops positions) |
|||
(let-values (((evens odds) (partition even? (free-indices positions)))) |
|||
(vector-set! positions (random-pick evens) "B") |
|||
(vector-set! positions (random-pick odds) "B"))) |
|||
; |
|||
(let ((positions (make-vector 8 "."))) |
|||
(place-king+rooks positions) |
|||
(place-bishops positions) |
|||
;; place the queen in a random remaining slot |
|||
(vector-set! positions (random-pick (free-indices positions)) "Q") |
|||
;; place the two knights in the remaining slots |
|||
(for-each (lambda (idx) (vector-set! positions idx "N")) |
|||
(free-indices positions)) |
|||
positions)) |
|||
(display "First rank: ") (display (random-piece-positions)) (newline) |
|||
</syntaxhighlight> |
|||
{{out}} |
|||
Ten sample runs: |
|||
<pre>First rank: #(R N N Q K R B B) |
|||
First rank: #(R K N N Q R B B) |
|||
First rank: #(Q R B N N B K R) |
|||
First rank: #(Q R B N N K R B) |
|||
First rank: #(R K N Q R B B N) |
|||
First rank: #(R K N B Q R B N) |
|||
First rank: #(R N K N B B R Q) |
|||
First rank: #(R B K Q B N R N) |
|||
First rank: #(B Q R N K N R B) |
|||
First rank: #(R B B Q N N K R)</pre> |
|||
=={{header|Seed7}}== |
=={{header|Seed7}}== |
||
< |
<syntaxhighlight lang="seed7">$ include "seed7_05.s7i"; |
||
const proc: main is func |
const proc: main is func |
||
Line 1,509: | Line 2,857: | ||
start := start[.. pred(pos)] & "B" & start[pos ..]; |
start := start[.. pred(pos)] & "B" & start[pos ..]; |
||
writeln(start); |
writeln(start); |
||
end func;</ |
end func;</syntaxhighlight> |
||
{{out}} |
|||
<pre>NQBNRBKR</pre> |
|||
=={{header|Sidef}}== |
|||
<syntaxhighlight lang="ruby">func is_valid_960 (backrank) { |
|||
var king = backrank.index('♚') |
|||
var (rook1, rook2) = backrank.indices_of('♜')... |
|||
king.is_between(rook1, rook2) || return false |
|||
var (bishop1, bishop2) = backrank.indices_of('♝')... |
|||
bishop1+bishop2 -> is_odd |
|||
} |
|||
func random_960_position(pieces = <♛ ♚ ♜ ♜ ♝ ♝ ♞ ♞>) { |
|||
pieces.shuffle.permutations {|*a| |
|||
return a if is_valid_960(a) |
|||
} |
|||
} |
|||
say random_960_position().join(' ')</syntaxhighlight> |
|||
{{out}} |
{{out}} |
||
<pre> |
<pre> |
||
♝ ♝ ♜ ♚ ♞ ♛ ♜ ♞ |
|||
NQBNRBKR |
|||
</pre> |
</pre> |
||
=={{header|Swift}}== |
|||
<syntaxhighlight lang="swift">func isValid960Position(_ firstRank: String) -> Bool { |
|||
var rooksPlaced = 0 |
|||
var bishopColor = -1 |
|||
for (i, piece) in firstRank.enumerated() { |
|||
switch piece { |
|||
case "♚" where rooksPlaced != 1: |
|||
return false |
|||
case "♜": |
|||
rooksPlaced += 1 |
|||
case "♝" where bishopColor == -1: |
|||
bishopColor = i & 1 |
|||
case "♝" where bishopColor == i & 1: |
|||
return false |
|||
case _: |
|||
continue |
|||
} |
|||
} |
|||
return true |
|||
} |
|||
struct Chess960Counts { |
|||
var king = 0, queen = 0, rook = 0, bishop = 0, knight = 0 |
|||
subscript(_ piece: String) -> Int { |
|||
get { |
|||
switch piece { |
|||
case "♚": return king |
|||
case "♛": return queen |
|||
case "♜": return rook |
|||
case "♝": return bishop |
|||
case "♞": return knight |
|||
case _: fatalError() |
|||
} |
|||
} |
|||
set { |
|||
switch piece { |
|||
case "♚": king = newValue |
|||
case "♛": queen = newValue |
|||
case "♜": rook = newValue |
|||
case "♝": bishop = newValue |
|||
case "♞": knight = newValue |
|||
case _: fatalError() |
|||
} |
|||
} |
|||
} |
|||
} |
|||
func get960Position() -> String { |
|||
var counts = Chess960Counts() |
|||
var bishopColor = -1 // 0 - white 1 - black |
|||
var output = "" |
|||
for i in 1...8 { |
|||
let validPieces = [ |
|||
counts["♜"] == 1 && counts["♚"] == 0 ? "♚" : nil, // king |
|||
i == 1 || (counts["♛"] == 0) ? "♛" : nil, // queen |
|||
i == 1 || (counts["♜"] == 0 || counts["♜"] < 2 && counts["♚"] == 1) ? "♜" : nil, // rook |
|||
i == 1 || (counts["♝"] < 2 && bishopColor == -1 || bishopColor != i & 1) ? "♝" : nil, // bishop |
|||
i == 1 || (counts["♞"] < 2) ? "♞" : nil // knight |
|||
].lazy.compactMap({ $0 }) |
|||
guard let chosenPiece = validPieces.randomElement() else { |
|||
// Need to swap last piece with a bishop |
|||
output.insert("♝", at: output.index(before: output.endIndex)) |
|||
break |
|||
} |
|||
counts[chosenPiece] += 1 |
|||
output += chosenPiece |
|||
if bishopColor == -1 && chosenPiece == "♝" { |
|||
bishopColor = i & 1 |
|||
} |
|||
} |
|||
assert(isValid960Position(output), "invalid 960 position \(output)") |
|||
return output |
|||
} |
|||
var positions = Set<String>() |
|||
while positions.count != 960 { |
|||
positions.insert(get960Position()) |
|||
} |
|||
print(positions.count, positions.randomElement()!)</syntaxhighlight> |
|||
{{out}} |
|||
<pre>960 ♞♛♜♞♚♝♝♜</pre> |
|||
=={{header|Tcl}}== |
=={{header|Tcl}}== |
||
Using regular expressions to filter a random permutation. |
Using regular expressions to filter a random permutation. |
||
{{tcllib|struct::list}} |
{{tcllib|struct::list}} |
||
< |
<syntaxhighlight lang="tcl">package require struct::list |
||
proc chess960 {} { |
proc chess960 {} { |
||
Line 1,536: | Line 3,000: | ||
# Output multiple times just to show scope of positions |
# Output multiple times just to show scope of positions |
||
foreach - {1 2 3 4 5} {puts [chessRender [chess960]]}</ |
foreach - {1 2 3 4 5} {puts [chessRender [chess960]]}</syntaxhighlight> |
||
{{out}} |
{{out}} |
||
<pre>♕♖♘♔♗♗♘♖ |
|||
<pre> |
|||
♕♖♘♔♗♗♘♖ |
|||
♖♔♘♘♗♕♖♗ |
♖♔♘♘♗♕♖♗ |
||
♘♖♗♗♕♔♘♖ |
♘♖♗♗♕♔♘♖ |
||
♘♕♗♖♔♖♘♗ |
♘♕♗♖♔♖♘♗ |
||
♘♘♖♔♗♗♕♖ |
♘♘♖♔♗♗♕♖</pre> |
||
=={{header|UNIX Shell}}== |
|||
{{trans|raku}} |
|||
{{works with|bash}} |
|||
<syntaxhighlight lang=bash>declare -a pieces=(♖ ♖ ♖ ♕ ♗ ♗ ♘ ♘) |
|||
declare -i i pick index |
|||
declare -ai picking_history |
|||
declare attempt |
|||
until [[ "$attempt" =~ ♗(..)*♗ ]] |
|||
do |
|||
attempt='' |
|||
picking_history=() |
|||
for _ in {1..8} |
|||
do |
|||
while ((picking_history[pick=RANDOM%8]++)) |
|||
do : |
|||
done |
|||
attempt+="${pieces[pick]}" |
|||
done |
|||
done |
|||
for i in {0..7} |
|||
do |
|||
if [[ "${attempt:i:1}" = ♖ ]] && ((index++)) |
|||
then echo "${attempt:0:i}♔${attempt:i+1}"; break; |
|||
fi |
|||
done</syntaxhighlight> |
|||
=={{header|Wren}}== |
|||
{{trans|Go}} |
|||
{{libheader|Wren-dynamic}} |
|||
{{libheader|Wren-fmt}} |
|||
<syntaxhighlight lang="wren">import "random" for Random |
|||
import "./dynamic" for Tuple |
|||
import "./fmt" for Fmt |
|||
var Symbols = Tuple.create("Symbols", ["k", "q", "r", "b", "n"]) |
|||
var A = Symbols.new("K", "Q", "R", "B", "N") |
|||
var W = Symbols.new("♔", "♕", "♖", "♗", "♘") |
|||
var B = Symbols.new("♚", "♛", "♜", "♝", "♞") |
|||
var krn = [ |
|||
"nnrkr", "nrnkr", "nrknr", "nrkrn", |
|||
"rnnkr", "rnknr", "rnkrn", |
|||
"rknnr", "rknrn", |
|||
"rkrnn" |
|||
] |
|||
var NUL = "\0" |
|||
var chess960 = Fn.new { |sym, id| |
|||
var pos = List.filled(8, NUL) |
|||
var q = (id/4).floor |
|||
var r = id % 4 |
|||
pos[r*2+1]= sym.b |
|||
var t = q |
|||
q = (q/4).floor |
|||
r = t % 4 |
|||
pos[r*2] = sym.b |
|||
t = q |
|||
q = (q/6).floor |
|||
r = t % 6 |
|||
var i = 0 |
|||
while (true) { |
|||
if (pos[i] == NUL) { |
|||
if (r == 0) { |
|||
pos[i] = sym.q |
|||
break |
|||
} |
|||
r = r - 1 |
|||
} |
|||
i = i + 1 |
|||
} |
|||
i = 0 |
|||
for (f in krn[q]) { |
|||
while (pos[i] != NUL) i = i + 1 |
|||
pos[i] = (f == "k") ? sym.k : |
|||
(f == "r") ? sym.r : |
|||
(f == "n") ? sym.n : pos[i] |
|||
} |
|||
return pos.join(" ") |
|||
} |
|||
System.print(" ID Start position") |
|||
for (id in [0, 518, 959]) Fmt.print("$3d $s", id, chess960.call(A, id)) |
|||
System.print("\nRandom") |
|||
var rand = Random.new() |
|||
for (i in 0..4) System.print(chess960.call(W, rand.int(960)))</syntaxhighlight> |
|||
{{out}} |
|||
<pre> |
|||
ID Start position |
|||
0 B B Q N N R K R |
|||
518 R N B Q K B N R |
|||
959 R K R N N Q B B |
|||
Random |
|||
♘ ♗ ♖ ♘ ♔ ♕ ♗ ♖ |
|||
♖ ♘ ♗ ♔ ♕ ♘ ♖ ♗ |
|||
♕ ♗ ♗ ♖ ♘ ♔ ♘ ♖ |
|||
♖ ♔ ♗ ♕ ♘ ♗ ♘ ♖ |
|||
♖ ♔ ♘ ♗ ♕ ♘ ♗ ♖ |
|||
</pre> |
|||
=={{header|XPL0}}== |
|||
<syntaxhighlight lang="xpl0">char Col; |
|||
func ColNum(Start, Piece); \Return column number of Piece |
|||
int Start, Piece, I; |
|||
[for I:= Start to 7 do |
|||
if Col(I) = Piece then return I; |
|||
return -1; |
|||
]; |
|||
proc Shuffle; \Randomly rearrange pieces in columns |
|||
int I, J, T; |
|||
[for I:= 8-1 downto 1 do |
|||
[J:= Ran(I); \range [0..I-1] (Sattolo cycle) |
|||
T:= Col(I); Col(I):= Col(J); Col(J):= T; |
|||
]; |
|||
]; |
|||
int N, B1, B2, BOK, R1, R2, K, KOK; |
|||
[for N:= 1 to 5 do |
|||
[Col:= "RNBQKBNR "; |
|||
repeat Shuffle; |
|||
B1:= ColNum(0, ^B); |
|||
B2:= ColNum(B1+1, ^B); |
|||
BOK:= ((B1 xor B2) and 1) # 0; |
|||
R1:= ColNum(0, ^R); |
|||
R2:= ColNum(R1+1, ^R); |
|||
K:= ColNum(0, ^K); |
|||
KOK:= R1<K and K<R2; |
|||
until BOK and KOK; |
|||
Text(0, Col); CrLf(0); |
|||
]; |
|||
]</syntaxhighlight> |
|||
{{out}} |
|||
<pre> |
|||
BNRBQKRN |
|||
RBKNNQBR |
|||
BQRBNKNR |
|||
NRBBQKNR |
|||
RNKNBQRB |
|||
</pre> |
</pre> |
||
=={{header|zkl}}== |
=={{header|zkl}}== |
||
{{trans|D}} |
{{trans|D}} |
||
< |
<syntaxhighlight lang="zkl">const pieces="KQRrBbNN"; |
||
starts:=pieces:Utils.Helpers.permuteW(_).filter(fcn(p){ |
starts:=pieces:Utils.Helpers.permuteW(_).filter(fcn(p){ |
||
I:=p.index; |
I:=p.index; |
||
Line 1,555: | Line 3,164: | ||
((I("r") < I("K") and I("K") < I("R")) or |
((I("r") < I("K") and I("K") < I("R")) or |
||
(I("R") < I("K") and I("K") < I("r"))) |
(I("R") < I("K") and I("K") < I("r"))) |
||
}).pump(List,"concat","toUpper"):Utils.Helpers.listUnique(_);</ |
}).pump(List,"concat","toUpper"):Utils.Helpers.listUnique(_);</syntaxhighlight> |
||
< |
<syntaxhighlight lang="zkl">N:=starts.len(); println(N); |
||
glyphs:=Dictionary("K","\u2654", "Q","\u2655", "R","\u2656", "B","\u2657", "N","\u2658"); |
glyphs:=Dictionary("K","\u2654", "Q","\u2655", "R","\u2656", "B","\u2657", "N","\u2658"); |
||
// pick some random starts and transform BBNRKQRN to glyphs |
// pick some random starts and transform BBNRKQRN to glyphs |
||
do(10){ starts[(0).random(N)].apply(glyphs.find).println() }</ |
do(10){ starts[(0).random(N)].apply(glyphs.find).println() }</syntaxhighlight> |
||
{{out}} |
{{out}} |
||
<pre> |
<pre>960 |
||
960 |
|||
♗♕♘♖♘♔♖♗ |
♗♕♘♖♘♔♖♗ |
||
♖♘♗♔♖♗♘♕ |
♖♘♗♔♖♗♘♕ |
||
Line 1,572: | Line 3,180: | ||
♖♘♗♔♕♘♖♗ |
♖♘♗♔♕♘♖♗ |
||
♖♔♖♕♘♘♗♗ |
♖♔♖♕♘♘♗♗ |
||
♕♗♖♘♗♔♘♖ |
♕♗♖♘♗♔♘♖</pre> |
||
</pre> |
Latest revision as of 12:16, 17 March 2024
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 Fischer. 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 using either the chess symbols in Unicode (♔♕♖♗♘), the letters King Queen Rook Bishop kNight, or the corresponding letters in a language other than English.
11l
F random960()
V start = [‘R’, ‘K’, ‘R’]
L(piece) [‘Q’, ‘N’, ‘N’]
start.insert(random:(start.len + 1), piece)
V bishpos = random:(start.len + 1)
start.insert(bishpos, Char(‘B’))
start.insert(random:(bishpos + 1), Char(‘B’))
R start
print(random960())
- Output:
[Q, B, N, R, K, B, N, R]
Action!
DEFINE MAX_NUMBERS="200"
DEFINE MAX_LEN="20"
DEFINE MAX_FACTORS="5"
DEFINE PTR="CARD"
PROC PrintResult(BYTE max,n BYTE ARRAY factors PTR ARRAY texts)
BYTE i,j,t
BYTE ARRAY values(MAX_FACTORS)
FOR j=0 TO n-1
DO
values(j)=1
OD
FOR i=1 TO max
DO
t=0
FOR j=0 TO n-1
DO
IF values(j)=0 THEN
t=1 Print(texts(j))
FI
values(j)==+1
IF values(j)=factors(j) THEN
values(j)=0
FI
OD
IF t=0 THEN PrintB(i) FI
Put(32)
OD
RETURN
BYTE FUNC Find(CHAR ARRAY s CHAR c BYTE POINTER err)
BYTE i
FOR i=1 TO s(0)
DO
IF s(i)=c THEN
err^=0 RETURN (i)
FI
OD
err^=1
RETURN (0)
PROC Main()
BYTE max,i,n,pos,err
BYTE ARRAY factors(MAX_FACTORS)
PTR ARRAY texts(MAX_FACTORS)
CHAR ARRAY
s(100),tmp(100),
t0(MAX_LEN),t1(MAX_LEN),t2(MAX_LEN),
t3(MAX_LEN),t4(MAX_LEN)
texts(0)=t0 texts(1)=t1 texts(2)=t2
texts(3)=t3 texts(4)=t4
DO
PrintF("Max number (1-%B): ",MAX_NUMBERS)
max=InputB()
UNTIL max>=1 AND max<=MAX_NUMBERS
OD
n=0
DO
PrintF("Number of rules (1-%B): ",MAX_FACTORS)
n=InputB()
UNTIL n>=1 AND n<=MAX_FACTORS
OD
FOR i=0 TO n-1
DO
DO
PrintF("Rule #%B (number space text):",i+1)
InputS(s)
pos=Find(s,' ,@err)
IF pos=1 OR pos=s(0) THEN
err=1
FI
IF err=0 THEN
SCopyS(tmp,s,1,pos-1)
factors(i)=ValB(tmp)
IF factors(i)<2 THEN
err=1
FI
SCopyS(texts(i),s,pos+1,s(0))
FI
UNTIL err=0
OD
OD
PutE()
PrintResult(max,n,factors,texts)
RETURN
- Output:
Screenshot from Atari 8-bit computer
NBBQRNKR NRKBRQBN BNRBKRNQ QBRKRNBN RBKNRQBN BNNBRKQR NQNBRKBR BRNKRBQN NRKRQNBB RNQBKRBN
APL
This function accepts a SP-ID and generates the corresponding position; to generate a random one, just pass in roll 960 (?960). (It's written for index origin 1, but reduces the passed-in SP-ID modulo 960 so that works without having to subtract 1 from the roll result.)
⍝ Utility functions
divmod ← {(⌊⍺÷⍵),⍵|⍺}
indices ← {(⍺∊⍵)/⍳⍴⍺}
∇result ← place placement; array; index; piece; result
(array piece index) ← placement
array[(array indices '-')[index]] ← piece
result ← array
∇
∇result ← chess960 spid; array; n; b1; b2; n1; n2; q
spid ← 960 | spid
array ← 8/'-'
(n b1) ← spid divmod 4
array[2+2×b1] ← 'B'
(n b2) ← n divmod 4
array[1+2×b2] ← 'B'
(n q) ← n divmod 6
array ← place array 'Q' (1+q)
n1 ← 1⍳⍨n<4 7 9 10
array ← place array 'N' n1
n2 ← (1 2 3 4 2 3 4 3 4 4)[n+1]
array ← place array 'N' n2
array ← place array 'R' 1
array ← place array 'K' 1
array ← place array 'R' 1
result ← spid, array
∇
- Output:
chess960 518 518 RNBQKBNR chess960 ?960 377 NRKBBRNQ chess960 ?960 487 QRBNKNRB
Arturo
; Using Edward Collins' single-die method
; http://www.edcollins.com/chess/fischer-random.htm
chess960: function [][
result: array.of: 8 ø
vacant: @0..7 ; open squares available to put pieces
result\[remove 'vacant <= 2 * random 0 3]: 'bishop ; place on random black square
result\[remove 'vacant <= 1 + 2 * random 0 3]: 'bishop ; place on random white square
loop ['queen 'knight 'knight] 'piece [
result\[remove 'vacant <= sample vacant]: piece ; place on random open square
]
result\[vacant\0]: 'rook ; place king between rooks on remaining open squares
result\[vacant\1]: 'king
result\[vacant\2]: 'rook
result
]
do.times:5 -> print chess960
- Output:
bishop knight rook queen king knight rook bishop knight rook queen bishop bishop king knight rook rook bishop bishop knight knight king rook queen bishop knight rook queen king knight rook bishop rook king knight knight bishop queen rook bishop
AutoHotkey
Loop, 5
Out .= Chess960() "`n"
MsgBox, % RTrim(Out, "`n")
Chess960() {
P := {}
P[K := Rand(2, 7)] := Chr(0x2654) ; King
P[Rand(1, K - 1)] := Chr(0x2656) ; Rook 1
P[Rand(K + 1, 8)] := Chr(0x2656) ; Rook 2
Loop, 8
Remaining .= P[A_Index] ? "" : A_Index "`n"
Sort, Remaining, Random N
P[Bishop1 := SubStr(Remaining, 1, 1)] := Chr(0x2657) ; Bishop 1
Remaining := SubStr(Remaining, 3)
Loop, Parse, Remaining, `n
if (Mod(Bishop1 - A_LoopField, 2))
Odd .= A_LoopField "`n"
else
Even .= A_LoopField "`n"
X := StrSplit(Odd Even, "`n")
P[X.1] := Chr(0x2657) ; Bishop 2
P[X.2] := Chr(0x2655) ; Queen
P[X.3] := Chr(0x2658) ; Knight 1
P[X.4] := Chr(0x2658) ; Knight 2
for Key, Val in P
Out .= Val
return Out
}
Rand(Min, Max) {
Random, n, Min, Max
return n
}
- Output:
♕♘♖♗♗♘♔♖ ♗♖♔♕♘♖♘♗ ♖♗♘♘♗♔♖♕ ♗♗♘♖♔♕♘♖ ♘♗♖♔♗♘♕♖
BASIC
BASIC256
for i = 1 to 10
inicio$ = "RKR"
pieza$ = "QNN"
for n = 1 to length(pieza$)
posic = int(rand*(length(inicio$) + 1)) + 1
inicio$ = left(inicio$, posic-1) + mid(pieza$, n, 1) + right(inicio$, length(inicio$) - posic + 1)
next n
posic = int(rand*(length(inicio$) + 1)) + 1
inicio$ = left(inicio$, posic-1) + "B" + right(inicio$, length(inicio$) - posic + 1)
posic += 1 + 2 * int(int(rand*(length(inicio$) - posic)) / 2)
inicio$ = left(inicio$, posic-1) + "B" + right(inicio$, length(inicio$) - posic + 1)
print inicio$
next i
end
BBC BASIC
VDU 23, 22, 240; 360; 8, 16, 16, 136
*FONT Arial, 20
FOR I% = 1 TO 10
Rank1$ = "202"
FOR Piece = 1 TO 3
P% = RND(LENRank1$ + 1)
Rank1$ = LEFT$(Rank1$, P% - 1) + MID$("144", Piece, 1) + MID$(Rank1$, P%)
NEXT
P% = RND(7)
Rank1$ = LEFT$(Rank1$, P% - 1) + "3" + MID$(Rank1$, P%)
IF P% > 5 P% += 1 ELSE P% += RND(4 - (P% >> 1)) * 2 - 1
Rank1$ = LEFT$(Rank1$, P% - 1) + "3" + MID$(Rank1$, P%)
FOR Piece = 1 TO 8
VDU &E2, &99, &94 + VALMID$(Rank1$, Piece, 1)
NEXT
PRINT
NEXT
- Output:
♘ ♖ ♗ ♔ ♖ ♘ ♕ ♗ ♘ ♖ ♔ ♘ ♕ ♖ ♗ ♗ ♕ ♗ ♖ ♔ ♗ ♘ ♖ ♘ ♖ ♘ ♔ ♗ ♖ ♕ ♗ ♘ ♖ ♗ ♗ ♘ ♔ ♖ ♘ ♕ ♘ ♖ ♗ ♗ ♕ ♔ ♘ ♖ ♖ ♔ ♕ ♘ ♗ ♘ ♖ ♗ ♘ ♘ ♖ ♔ ♖ ♗ ♗ ♕ ♖ ♕ ♔ ♘ ♗ ♗ ♖ ♘ ♖ ♕ ♔ ♘ ♘ ♗ ♗ ♖
Commodore BASIC
Besides the admittedly-trivial use of DO/LOOP, this implementation also exploits the BASIC 3.5+ ability to use MID$ as an lvalue.
100 REM CHESS 960
110 PRINT "SPID (-1 FOR RANDOM):";
120 OPEN 1,0:INPUT#1, SP$:CLOSE 1
130 SP=VAL(SP$)
140 IF SP<0 THEN SP=INT(RND(.)*960)
150 PRINT
160 DO WHILE SP>959: SP=SP-960: LOOP
170 AR$="--------"
180 P=SP
190 N=P AND 3:P=INT(P/4)
200 MID$(AR$,2*N+2,1)="B"
210 N=P AND 3:P=INT(P/4)
220 MID$(AR$,2*N+1,1)="B"
230 N=P-6*INT(P/6):P=INT(P/6)
240 P$="Q":GOSUB 420
250 N=P-10*INT(P/10):P=INT(P/10)
260 FOR N1=0 TO 3
270 : FOR N2=N1+1 TO 4
280 : IF N<>0 THEN 340
290 : P$="N":N=N1:GOSUB 420
300 : P$="N":N=N2-1:GOSUB 420
310 : N1=3
320 : N2=4
340 : N=N-1
350 : NEXT N2
360 NEXT N1
370 P$="R":N=0:GOSUB 420
380 P$="K":N=0:GOSUB 420
390 P$="R":N=0:GOSUB 420
400 PRINT STR$(SP);":";AR$
410 END
420 FOR I=1 TO LEN(AR$)
430 : IF MID$(AR$,I,1)<>"-" THEN 510
440 : IF N<>0 THEN 480
450 : MID$(AR$,I,1)=P$
460 : I=LEN(AR$)
470 : GOTO 510
480 : N=N-1
510 NEXT I
520 RETURN
Here's a version that doesn't use the advanced features:
100 REM CHESS 960
110 PRINT "SPID (-1 FOR RANDOM):";
120 OPEN 1,0:INPUT#1, SP$:CLOSE 1
130 SP=VAL(SP$)
140 IF SP<0 THEN SP=INT(RND(.)*960)
150 PRINT
160 IF SP>959 THEN SP=SP-960: GOTO 160
170 AR$="--------"
180 P=SP
190 N=P AND 3:P=INT(P/4)
200 AR$=LEFT$(AR$,2*N+1)+"B"+MID$(AR$,2*N+3)
210 N=P AND 3:P=INT(P/4)
220 AR$=LEFT$(AR$,2*N)+"B"+MID$(AR$,2*N+2)
230 N=P-6*INT(P/6):P=INT(P/6)
240 P$="Q":GOSUB 420
250 N=P-10*INT(P/10):P=INT(P/10)
260 FOR N1=0 TO 3
270 : FOR N2=N1+1 TO 4
280 : IF N<>0 THEN 340
290 : P$="N":N=N1:GOSUB 420
300 : P$="N":N=N2-1:GOSUB 420
310 : N1=3
320 : N2=4
340 : N=N-1
350 : NEXT N2
360 NEXT N1
370 P$="R":N=0:GOSUB 420
380 P$="K":N=0:GOSUB 420
390 P$="R":N=0:GOSUB 420
400 PRINT STR$(SP);":";AR$
410 END
420 FOR I=1 TO LEN(AR$)
430 : IF MID$(AR$,I,1)<>"-" THEN 510
440 : IF N<>0 THEN 480
450 : AR$=LEFT$(AR$,I-1)+P$+MID$(AR$,I+1)
460 : I=LEN(AR$)
470 : GOTO 510
480 : N=N-1
510 NEXT I
520 RETURN
- Output:
The output is the same for both versions:
READY. RUN SPID (-1 FOR RANDOM):518 518:RNBQKBNR READY. RUN SPID (-1 FOR RANDOM):-1 926:RKRQNBBN READY.
FreeBASIC
Randomize Timer
For i As Byte = 1 To 10
Dim As String inicio = "RKR", pieza = "QNN"
Dim As Byte posic
For n As Byte = 1 To Len(pieza)
posic = Int(Rnd*(Len(inicio) + 1)) + 1
inicio = Left(inicio, posic-1) + _
Mid(pieza, n, 1) +_
Right(inicio, Len(inicio) - posic + 1)
Next n
posic = Int(Rnd*(Len(inicio) + 1)) + 1
inicio = Left(inicio, posic-1) + "B" + Right(inicio, Len(inicio) - posic + 1)
posic = posic + 1 + 2 * Int(Int(Rnd*(Len(inicio) - posic)) / 2)
inicio = Left(inicio, posic-1) + "B" + Right(inicio, Len(inicio) - posic + 1)
Print inicio
Next i
QBasic
RANDOMIZE TIMER
FOR i = 1 TO 10
inicio$ = "RKR"
pieza$ = "QNN"
'Dim posic
FOR n = 1 TO LEN(pieza$)
posic = INT(RND * (LEN(inicio$) + 1)) + 1
inicio$ = LEFT$(inicio$, posic - 1) + MID$(pieza$, n, 1) + RIGHT$(inicio$, LEN(inicio$) - posic + 1)
NEXT n
posic = INT(RND * (LEN(inicio$) + 1)) + 1
inicio$ = LEFT$(inicio$, posic - 1) + "B" + RIGHT$(inicio$, LEN(inicio$) - posic + 1)
posic = posic + 1 + 2 * INT(INT(RND * (LEN(inicio$) - posic)) / 2)
inicio$ = LEFT$(inicio$, posic - 1) + "B" + RIGHT$(inicio$, LEN(inicio$) - posic + 1)
PRINT inicio$
NEXT i
END
Yabasic
start$ = "RKR"
piece$ = "QNN"
for piece = 1 to len(piece$)
pos = int(ran(len(start$) + 1)) + 1
start$ = left$(start$, pos-1) + mid$(piece$, piece, 1) + right$(start$, len(start$) - pos + 1)
next
pos = int(ran(len(start$) + 1)) + 1
start$ = left$(start$, pos-1) + "B" + right$(start$, len(start$) - pos + 1)
pos = pos + 1 + 2 * int(int(ran(len(start$) - pos)) / 2)
start$ = left$(start$, pos-1) + "B" + right$(start$, len(start$) - pos + 1)
print start$
Befunge
Similar to the Ruby SP-ID solution, this generates the start position for a random number in the Chess960 numbering scheme.
#.#.#.#.065*0#v_1-\>>?1v
v,":".:%*8"x"$<^!:\*2<+<
>48*,:4%2*1#v+#02#\3#g<<
v"B"*2%4:/4p<vg0:+1<\-1<
>\0p4/:6%0:0g>68*`#^_\:|
v"RKRNN"p11/6$p0\ "Q" \<
>"NRNKRRNNKRNRKNRRNKNR"v
v"NRNKRNRKNRNRKRNRNNKR"<
>"RKRNN"11g:!#v_\$\$\$\v
v _v#!`*86:g0:<^!:-1$\$<
>$\>,1+ :7`#@_^> v960v <
- Output:
856 : RBKNBRNQ
C
As noted in the C implementation for the Sparkline in unicode task, unicode output is reliable only on Linux/Unix systems. This implementation thus has compiler directives to check whether the underlying system is Windows or Linux, if Windows, only letters are printed, otherwise Unicode output is displayed. 9 rows are displayed.
#include<stdlib.h>
#include<locale.h>
#include<wchar.h>
#include<stdio.h>
#include<time.h>
char rank[9];
int pos[8];
void swap(int i,int j){
int temp = pos[i];
pos[i] = pos[j];
pos[j] = temp;
}
void generateFirstRank(){
int kPos,qPos,bPos1,bPos2,rPos1,rPos2,nPos1,nPos2,i;
for(i=0;i<8;i++){
rank[i] = 'e';
pos[i] = i;
}
do{
kPos = rand()%8;
rPos1 = rand()%8;
rPos2 = rand()%8;
}while((rPos1-kPos<=0 && rPos2-kPos<=0)||(rPos1-kPos>=0 && rPos2-kPos>=0)||(rPos1==rPos2 || kPos==rPos1 || kPos==rPos2));
rank[pos[rPos1]] = 'R';
rank[pos[kPos]] = 'K';
rank[pos[rPos2]] = 'R';
swap(rPos1,7);
swap(rPos2,6);
swap(kPos,5);
do{
bPos1 = rand()%5;
bPos2 = rand()%5;
}while(((pos[bPos1]-pos[bPos2])%2==0)||(bPos1==bPos2));
rank[pos[bPos1]] = 'B';
rank[pos[bPos2]] = 'B';
swap(bPos1,4);
swap(bPos2,3);
do{
qPos = rand()%3;
nPos1 = rand()%3;
}while(qPos==nPos1);
rank[pos[qPos]] = 'Q';
rank[pos[nPos1]] = 'N';
for(i=0;i<8;i++)
if(rank[i]=='e'){
rank[i] = 'N';
break;
}
}
void printRank(){
int i;
#ifdef _WIN32
printf("%s\n",rank);
#else
{
setlocale(LC_ALL,"");
printf("\n");
for(i=0;i<8;i++){
if(rank[i]=='K')
printf("%lc",(wint_t)9812);
else if(rank[i]=='Q')
printf("%lc",(wint_t)9813);
else if(rank[i]=='R')
printf("%lc",(wint_t)9814);
else if(rank[i]=='B')
printf("%lc",(wint_t)9815);
if(rank[i]=='N')
printf("%lc",(wint_t)9816);
}
}
#endif
}
int main()
{
int i;
srand((unsigned)time(NULL));
for(i=0;i<9;i++){
generateFirstRank();
printRank();
}
return 0;
}
Output on Linux :
♗♗♖♕♘♘♔♖ ♘♖♕♔♗♗♖♘ ♖♘♔♖♕♘♗♗ ♘♘♖♗♗♔♖♕ ♗♘♖♘♕♔♖♗ ♕♗♗♘♖♔♘♖ ♕♘♖♔♗♖♘♗ ♗♘♘♕♖♔♖♗ ♖♘♘♕♗♔♖♗
Output on Windows :
BRKNNQRB RBNQNKBR RNQKNBBR RQNKNBBR QBBNRKNR BNRBQKNR BRQKNNRB RNKBNRBQ QNRBBNKR
C#
using System;
class Program
{
struct Symbols
{
public char K, Q, R, B, N;
public Symbols(char k, char q, char r, char b, char n)
{
K = k; Q = q; R = r; B = b; N = n;
}
}
private static Symbols A = new Symbols('K', 'Q', 'R', 'B', 'N');
private static Symbols W = new Symbols('♔', '♕', '♖', '♗', '♘');
private static Symbols B = new Symbols('♚', '♛', '♜', '♝', '♞');
private static string[] krn = new string[]
{
"nnrkr", "nrnkr", "nrknr", "nrkrn",
"rnnkr", "rnknr", "rnkrn",
"rknnr", "rknrn",
"rkrnn"
};
private static string Chess960(Symbols sym, int id)
{
char[] pos = new char[8];
int q = id / 4, r = id % 4;
pos[r * 2 + 1] = sym.B;
r = q % 4; q /= 4;
pos[r * 2] = sym.B;
r = q % 6; q /= 6;
int placementIndex = 0; // Adjusted variable name to prevent conflict
for (int i = 0; ; i++)
{
if (pos[i] != '\0') continue;
if (r == 0)
{
pos[i] = sym.Q;
break;
}
r--;
}
while (pos[placementIndex] != '\0') placementIndex++; // Adjusted loop to prevent conflict
foreach (char f in krn[q])
{
while (pos[placementIndex] != '\0') placementIndex++;
switch (f)
{
case 'k':
pos[placementIndex] = sym.K;
break;
case 'r':
pos[placementIndex] = sym.R;
break;
case 'n':
pos[placementIndex] = sym.N;
break;
}
}
return new string(pos);
}
static void Main(string[] args)
{
Console.WriteLine(" ID Start position");
foreach (int id in new int[] { 0, 518, 959 })
{
Console.WriteLine($"{id,3} {Chess960(A, id)}");
}
Console.WriteLine("\nRandom");
Random rand = new Random();
for (int i = 0; i < 5; i++)
{
Console.WriteLine(Chess960(W, rand.Next(960)));
}
}
}
- Output:
ID Start position 0 BBQNNRKR 518 RNBQKBNR 959 RKRNNQBB Random ♘♕♖♔♗♖♘♗ ♗♗♖♘♔♖♕♘ ♖♕♘♔♖♗♗♘ ♘♖♗♔♘♗♖♕ ♖♘♕♗♔♘♗♖
C++
#include <iostream>
#include <string>
#include <time.h>
using namespace std;
namespace
{
void placeRandomly(char* p, char c)
{
int loc = rand() % 8;
if (!p[loc])
p[loc] = c;
else
placeRandomly(p, c); // try again
}
int placeFirst(char* p, char c, int loc = 0)
{
while (p[loc]) ++loc;
p[loc] = c;
return loc;
}
string startPos()
{
char p[8]; memset( p, 0, 8 );
// bishops on opposite color
p[2 * (rand() % 4)] = 'B';
p[2 * (rand() % 4) + 1] = 'B';
// queen knight knight, anywhere
for (char c : "QNN")
placeRandomly(p, c);
// rook king rook, in that order
placeFirst(p, 'R', placeFirst(p, 'K', placeFirst(p, 'R')));
return string(p, 8);
}
} // leave local
namespace chess960
{
void generate( int c )
{
for( int x = 0; x < c; x++ )
cout << startPos() << "\n";
}
}
int main( int argc, char* argv[] )
{
srand( time( NULL ) );
chess960::generate( 10 );
cout << "\n\n";
return system( "pause" );
}
- Output:
NQBRNBKR RKBQNBNR RKBRNNQB QRBNNKRB BRKNRBQN QNRBBKNR BQRBKNRN RNBKQBNR QRNKBBRN QRBKNBRN
Clojure
(ns c960.core
(:gen-class)
(:require [clojure.string :as s]))
;; legal starting rank - unicode chars for rook, knight, bishop, queen, king, bishop, knight, rook
(def starting-rank [\♖ \♘ \♗ \♕ \♔ \♗ \♘ \♖])
(defn bishops-legal?
"True if Bishops are odd number of indicies apart"
[rank]
(odd? (apply - (cons 0 (sort > (keep-indexed #(when (= \♗ %2) %1) rank))))))
(defn king-legal?
"True if the king is between two rooks"
[rank]
(let [king-&-rooks (filter #{\♔ \♖} rank)]
(and
(= 3 (count king-&-rooks))
(= \u2654 (second king-&-rooks)))))
(defn c960
"Return a legal rank for c960 chess"
([] (c960 1))
([n]
(->> #(shuffle starting-rank)
repeatedly
(filter #(and (king-legal? %) (bishops-legal? %)))
(take n)
(map #(s/join ", " %)))))
(c960)
;; => "♗, ♖, ♔, ♕, ♘, ♘, ♖, ♗"
(c960)
;; => "♖, ♕, ♘, ♔, ♗, ♗, ♘, ♖"
(c960 4)
;; => ("♘, ♖, ♔, ♘, ♗, ♗, ♖, ♕" "♗, ♖, ♔, ♘, ♘, ♕, ♖, ♗" "♘, ♕, ♗, ♖, ♔, ♗, ♘, ♖" "♖, ♔, ♘, ♘, ♕, ♖, ♗, ♗")
Common Lisp
Common Lisp: generate from SP-ID
(defun chess960-from-sp-id
(&optional (sp-id (random 360 (make-random-state t))))
(labels
((combinations (lst r)
(cond
((numberp lst)
(combinations (loop for i from 0 while (< i lst) collect i) r))
((= r 1)
(mapcar #'list lst))
(t
(loop for i in lst append
(let ((left (loop for j in lst if (< i j) collect j)))
(mapcar (lambda (c) (cons i c))
(combinations left (1- r))))))))
(enumerate (ary)
(loop for item across ary for index from 0
collect (list index item))))
(let*
((first-bishop -1)
(knight-combo '())
(placements (list
;divisor function to get position piece symbol
(list 4 (lambda (n) (setq first-bishop n)
(1+ (* 2 n))) '♝)
(list 4 (lambda (n) ( - (* 2 n) (if (> n first-bishop) 1 0))) '♝)
(list 6 #'identity '♛)
(list 10 (lambda (n)
(setq knight-combo (nth n (combinations 5 2)))
(car knight-combo)) '♞)
(list 1 (lambda (n) (1- (cadr knight-combo))) '♞)
(list 1 (lambda (n) 0) '♜)
(list 1 (lambda (n) 0) '♚)
(list 1 (lambda (n) 0) '♜)))
(p sp-id)
(ary (make-array 8 :initial-element '-)))
(loop for (divisor func piece) in placements doing
(let* ((n (mod p divisor))
(square (funcall func n)))
(setq p (floor p divisor))
(setq index
(car (nth square (remove-if-not (lambda (p) (eq (cadr p) '-))
(enumerate ary)))))
(setf (aref ary index) piece)))
(list sp-id ary))))
;; demo
(format t "~a~%" (chess960-from-sp-id 518))
(format t "~a~%" (chess960-from-sp-id))
- Output:
(518 #(♜ ♞ ♝ ♛ ♚ ♝ ♞ ♜)) (246 #(♞ ♜ ♝ ♚ ♛ ♝ ♞ ♜))
D
D: Indexing
void main() {
import std.stdio, std.range, std.algorithm, std.string, permutations2;
const pieces = "KQRrBbNN";
alias I = indexOf;
auto starts = pieces.dup.permutations.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);
}
- Output:
960 BBNNQRKR
D: Regexp
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.dup)
.filter!(p => p.match(bish) && p.match(king))
.array.sort().uniq;
writeln(starts3.walkLength, "\n", starts3.front);
}
The output is the same.
D: Correct by construction
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;
}
- Output:
QBNNBRKR
EasyLang
len t$[] 8
proc randins c$ l r . pos .
repeat
pos = randint (r - l + 1) + l - 1
until t$[pos] = ""
.
t$[pos] = c$
.
randins "K" 2 7 king
randins "R" 1 (king - 1) h
randins "R" (king + 1) 8 h
randins "B" 1 8 b1
repeat
randins "B" 1 8 b2
until (b2 - b1) mod 2 <> 0
t$[b2] = ""
.
randins "Q" 1 8 b1
randins "N" 1 8 b1
randins "N" 1 8 b1
print strjoin t$[]
- Output:
RBBNQNKR
EchoLisp
(define-values (K Q R B N) (iota 5))
(define *pos* (list R N B Q K B N R)) ;; standard starter
;; check opposite color bishops, and King between rooks
(define (legal-pos p)
(and
(> (list-index K p) (list-index R p))
(> (list-index K (reverse p)) (list-index R (reverse p)))
(even? (+ (list-index B p) (list-index B (reverse p))))))
;; random shuffle current position until a legal one is found
(define (c960)
(set! *pos* (shuffle *pos*))
(if (legal-pos *pos*)
(map unicode-piece *pos*) (c960)))
- Output:
(define (unicode-piece i) (unicode->string (+ 0x2654 i))) (legal-pos *pos*) → #t ;; starter is OK (c960) (♗ ♖ ♔ ♗ ♕ ♘ ♘ ♖) (c960) (♘ ♗ ♗ ♕ ♖ ♘ ♔ ♖) (c960) (♖ ♘ ♗ ♘ ♔ ♕ ♖ ♗) ;; etc.
Elixir
Elixir: shuffle pieces until all regexes match
defmodule Chess960 do
@pieces ~w(♔ ♕ ♘ ♘ ♗ ♗ ♖ ♖) # ~w(K Q N N B B R R)
@regexes [~r/♗(..)*♗/, ~r/♖.*♔.*♖/] # [~r/B(..)*B/, ~r/R.*K.*R/]
def shuffle do
row = Enum.shuffle(@pieces) |> Enum.join
if Enum.all?(@regexes, &Regex.match?(&1, row)), do: row, else: shuffle
end
end
Enum.each(1..5, fn _ -> IO.puts Chess960.shuffle end)
- Output:
♘♗♘♖♗♔♕♖ ♗♖♔♗♕♘♘♖ ♗♗♕♖♔♖♘♘ ♘♗♖♔♗♘♕♖ ♖♕♘♘♗♗♔♖
Elixir: Construct
defmodule Chess960 do
def construct do
row = Enum.reduce(~w[♕ ♘ ♘], ~w[♖ ♔ ♖], fn piece,acc ->
List.insert_at(acc, :rand.uniform(length(acc)+1)-1, piece)
end)
[Enum.random([0, 2, 4, 6]), Enum.random([1, 3, 5, 7])]
|> Enum.sort
|> Enum.reduce(row, fn pos,acc -> List.insert_at(acc, pos, "♗") end)
|> Enum.join
end
end
Enum.each(1..5, fn _ -> IO.puts Chess960.construct end)
- Output:
♖♔♗♘♖♕♘♗ ♘♗♘♕♖♔♗♖ ♗♖♔♘♘♗♖♕ ♖♗♘♘♕♔♗♖ ♖♕♗♘♘♗♔♖
Elixir: Generate from SP-ID
defmodule Chess960 do
@krn ~w(NNRKR NRNKR NRKNR NRKRN RNNKR RNKNR RNKRN RKNNR RKNRN RKRNN)
def start_position, do: start_position(:rand.uniform(960)-1)
def start_position(id) do
pos = List.duplicate(nil, 8)
q = div(id, 4)
r = rem(id, 4)
pos = List.replace_at(pos, r * 2 + 1, "B")
q = div(q, 4)
r = rem(q, 4)
pos = List.replace_at(pos, r * 2, "B")
q = div(q, 6)
r = rem(q, 6)
i = Enum.reject(0..7, &Enum.at(pos,&1)) |> Enum.at(r)
pos = List.replace_at(pos, i, "Q")
krn = Enum.at(@krn, q) |> String.codepoints
Enum.reject(0..7, &Enum.at(pos,&1))
|> Enum.zip(krn)
|> Enum.reduce(pos, fn {i,x},acc -> List.replace_at(acc,i,x) end)
|> Enum.join
end
end
IO.puts "Generate Start Position from ID number"
Enum.each([0,518,959], fn id ->
:io.format "~3w : ~s~n", [id, Chess960.start_position(id)]
end)
IO.puts "\nGenerate random Start Position"
Enum.each(1..5, fn _ -> IO.puts Chess960.start_position end)
- Output:
Generate Start Position from ID number 0 : BBQNNRKR 518 : BRNKNBRQ 959 : RKRQNNBB Generate random Start Position RQKBBNNR RBBQKNNR RQKNNRBB RKRQBBNN RNBNKQRB
Factor
Single die method
Using the single die method: https://en.wikipedia.org/wiki/Chess960_starting_position#Single_die_method
USING: io kernel math random sequences ;
IN: rosetta-code.chess960
: empty ( seq -- n ) 32 swap indices random ; ! return a random empty index (i.e. equal to 32) of seq
: next ( seq -- n ) 32 swap index ; ! return the leftmost empty index of seq
: place ( seq elt n -- seq' ) rot [ set-nth ] keep ; ! set nth member of seq to elt, keeping seq on the stack
: white-bishop ( -- elt n ) CHAR: ♗ 4 random 2 * ;
: black-bishop ( -- elt n ) white-bishop 1 + ;
: queen ( seq -- seq elt n ) CHAR: ♕ over empty ;
: knight ( seq -- seq elt n ) CHAR: ♘ over empty ;
: rook ( seq -- seq elt n ) CHAR: ♖ over next ;
: king ( seq -- seq elt n ) CHAR: ♔ over next ;
: chess960 ( -- str )
" " clone
black-bishop place
white-bishop place
queen place
knight place
knight place
rook place
king place
rook place ;
: chess960-demo ( -- ) 5 [ chess960 print ] times ;
MAIN: chess960-demo
- Output:
♕♖♗♘♔♘♖♗ ♕♗♖♘♗♘♔♖ ♗♘♕♖♔♗♖♘ ♘♖♗♕♔♗♘♖ ♗♗♘♖♘♔♕♖
Built-in
Factor comes with a chess960 position generator:
USING: chess960 prettyprint ;
chess960-position .
- Output:
{ rook bishop king knight bishop queen rook knight }
Forth
\ make starting position for Chess960, constructive
\ 0 1 2 3 4 5 6 7 8 9
create krn S" NNRKRNRNKRNRKNRNRKRNRNNKRRNKNRRNKRNRKNNRRKNRNRKRNN" mem,
create pieces 8 allot
: chess960 ( n -- )
pieces 8 erase
4 /mod swap 2* 1+ pieces + 'B swap c!
4 /mod swap 2* pieces + 'B swap c!
6 /mod swap pieces swap bounds begin dup c@ if swap 1+ swap then 2dup > while 1+ repeat drop 'Q swap c!
5 * krn + pieces 8 bounds do i c@ 0= if dup c@ i c! 1+ then loop drop
cr pieces 8 type ;
0 chess960 \ BBQNNRKR ok
518 chess960 \ RNBQKBNR ok
959 chess960 \ RKRNNQBB ok
960 choose chess960 \ random position
Fortran
This implementation simply iterates through all 960 positions.
program chess960
implicit none
integer, pointer :: a,b,c,d,e,f,g,h
integer, target :: p(8)
a => p(1)
b => p(2)
c => p(3)
d => p(4)
e => p(5)
f => p(6)
g => p(7)
h => p(8)
king: do a=2,7 ! King on an internal square
r1: do b=1,a-1 ! R1 left of the King
r2: do c=a+1,8 ! R2 right of the King
b1: do d=1,7,2 ! B1 on an odd square
if (skip_pos(d,4)) cycle
b2: do e=2,8,2 ! B2 on an even square
if (skip_pos(e,5)) cycle
queen: do f=1,8 ! Queen anywhere else
if (skip_pos(f,6)) cycle
n1: do g=1,7 ! First knight
if (skip_pos(g,7)) cycle
n2: do h=g+1,8 ! Second knight (indistinguishable from first)
if (skip_pos(h,8)) cycle
if (sum(p) /= 36) stop 'Loop error' ! Sanity check
call write_position
end do n2
end do n1
end do queen
end do b2
end do b1
end do r2
end do r1
end do king
contains
logical function skip_pos(i, n)
integer, intent(in) :: i, n
skip_pos = any(p(1:n-1) == i)
end function skip_pos
subroutine write_position
integer :: i, j
character(len=15) :: position = ' '
character(len=1), parameter :: names(8) = ['K','R','R','B','B','Q','N','N']
do i=1,8
j = 2*p(i)-1
position(j:j) = names(i)
end do
write(*,'(a)') position
end subroutine write_position
end program chess960
- Output:
The first ten positions:
R K R B B Q N N R K R B B N Q N R K R B B N N Q R K R Q B B N N R K R N B B Q N R K R N B B N Q R K R Q B N N B R K R N B Q N B R K R N B N Q B R K R B Q N B N
Go
package main
import (
"fmt"
"math/rand"
)
type symbols struct{ k, q, r, b, n rune }
var A = symbols{'K', 'Q', 'R', 'B', 'N'}
var W = symbols{'♔', '♕', '♖', '♗', '♘'}
var B = symbols{'♚', '♛', '♜', '♝', '♞'}
var krn = []string{
"nnrkr", "nrnkr", "nrknr", "nrkrn",
"rnnkr", "rnknr", "rnkrn",
"rknnr", "rknrn",
"rkrnn"}
func (sym symbols) chess960(id int) string {
var pos [8]rune
q, r := id/4, id%4
pos[r*2+1] = sym.b
q, r = q/4, q%4
pos[r*2] = sym.b
q, r = q/6, q%6
for i := 0; ; i++ {
if pos[i] != 0 {
continue
}
if r == 0 {
pos[i] = sym.q
break
}
r--
}
i := 0
for _, f := range krn[q] {
for pos[i] != 0 {
i++
}
switch f {
case 'k':
pos[i] = sym.k
case 'r':
pos[i] = sym.r
case 'n':
pos[i] = sym.n
}
}
return string(pos[:])
}
func main() {
fmt.Println(" ID Start position")
for _, id := range []int{0, 518, 959} {
fmt.Printf("%3d %s\n", id, A.chess960(id))
}
fmt.Println("\nRandom")
for i := 0; i < 5; i++ {
fmt.Println(W.chess960(rand.Intn(960)))
}
}
- Output:
ID Start position 0 BBQNNRKR 518 RNBQKBNR 959 RKRNNQBB Random ♗♘♖♗♘♔♕♖ ♕♘♖♔♘♖♗♗ ♖♘♗♔♖♕♘♗ ♘♘♖♕♗♔♖♗ ♗♕♘♗♘♖♔♖
Haskell
import Data.List
import qualified Data.Set as Set
data Piece = K | Q | R | B | N deriving (Eq, Ord, Show)
isChess960 :: [Piece] -> Bool
isChess960 rank =
(odd . sum $ findIndices (== B) rank) && king > rookA && king < rookB
where
Just king = findIndex (== K) rank
[rookA, rookB] = findIndices (== R) rank
main :: IO ()
main = mapM_ (putStrLn . concatMap show) . Set.toList . Set.fromList
. filter isChess960 $ permutations [R,N,B,Q,K,B,N,R]
- Output:
QRKRBBNN QRKRBNNB QRKRNBBN QRKRNNBB QRKBRNBN ...
J
The simplest J approach for this task is to initially build a table of the starting positions. Then, for the task, we pick one position from the table at random. There are 40320 distinct permutations of 8 items and 5040 distinct permutations of these chess pieces and (as the task name points out) only 960 permutations which also satisfy the constraints on bishop and rook position, so little memory is needed to generate the table. Also, since the table is built at "compile time", execution is fast (though "compilation" is reasonably fast also).
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
Example use:
gen''
♘♗♖♔♗♕♖♘
gen''
♗♘♘♗♖♔♖♕
gen''
♖♗♔♘♘♕♗♖
gen''
♖♔♕♗♗♘♖♘
Java
Regex inspired by (original) Python Regexp, prints ten examples.
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());
}
}
}
- 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]
JavaScript
This conforms to Altendörfer's single die method[1], though the die will give no "needless" numbers.
function ch960startPos() {
var rank = new Array(8),
// randomizer (our die)
d = function(num) { return Math.floor(Math.random() * ++num) },
emptySquares = function() {
var arr = [];
for (var i = 0; i < 8; i++) if (rank[i] == undefined) arr.push(i);
return arr;
};
// place one bishop on any black square
rank[d(2) * 2] = "♗";
// place the other bishop on any white square
rank[d(2) * 2 + 1] = "♗";
// place the queen on any empty square
rank[emptySquares()[d(5)]] = "♕";
// place one knight on any empty square
rank[emptySquares()[d(4)]] = "♘";
// place the other knight on any empty square
rank[emptySquares()[d(3)]] = "♘";
// place the rooks and the king on the squares left, king in the middle
for (var x = 1; x <= 3; x++) rank[emptySquares()[0]] = x==2 ? "♔" : "♖";
return rank;
}
// testing (10 times)
for (var x = 1; x <= 10; x++) console.log(ch960startPos().join(" | "));
- Output:
The test-output (exemplary each):
♖ | ♗ | ♗ | ♔ | ♘ | ♖ | ♘ | ♕
♗ | ♗ | ♕ | ♖ | ♔ | ♘ | ♘ | ♖
♖ | ♕ | ♘ | ♗ | ♗ | ♔ | ♘ | ♖
♖ | ♗ | ♔ | ♘ | ♗ | ♕ | ♘ | ♖
♗ | ♖ | ♕ | ♔ | ♘ | ♗ | ♘ | ♖
♖ | ♗ | ♗ | ♕ | ♔ | ♘ | ♖ | ♘
♗ | ♘ | ♖ | ♗ | ♔ | ♘ | ♕ | ♖
♕ | ♘ | ♗ | ♖ | ♔ | ♗ | ♖ | ♘
♗ | ♘ | ♖ | ♘ | ♕ | ♗ | ♔ | ♖
♘ | ♗ | ♖ | ♔ | ♗ | ♘ | ♖ | ♕
jq
Adapted from Wren
Also works with gojq, the Go implementation of jq
Also works with fq, a Go implementation of a large subset of jq
### Utilities
# The glyphs in .
def chars: explode[] | [.] | implode;
# input: an array
# $keys : an array of strings
def objectify($keys):
with_entries(.key = $keys[.key]) ;
def lpad($len): tostring | ($len - length) as $l | (" " * $l)[:$l] + .;
def Symbols: ["k", "q", "r", "b", "n"];
def A: ["K", "Q", "R", "B", "N"] | objectify(Symbols);
def W: ["♔", "♕", "♖", "♗", "♘"] | objectify(Symbols);
def B: ["♚", "♛", "♜", "♝", "♞"] | objectify(Symbols);
def krn: [
"nnrkr", "nrnkr", "nrknr", "nrkrn",
"rnnkr", "rnknr", "rnkrn",
"rknnr", "rknrn",
"rkrnn"
];
# $sym specifies the Symbols
# $id specifies the position
def chess960($sym):
. as $id
| { q: (($id/4)|floor),
r: ($id % 4)
}
| .pos[.r*2+1] = $sym.b
| .t = .q
| .q |= ((./4)|floor)
| .r = (.t % 4)
| .pos[.r*2] = $sym.b
| .t = .q
| .q |= ((./6)|floor)
| .r = .t % 6
| .i = 0
| .break = false
| until( .break;
if .pos[.i] == null
then if .r == 0
then .pos[.i] = $sym.q
| .break = true
else .r += -1
end
else .
end
| .i += 1 )
| .i = 0
| reduce (krn[.q]|chars) as $f (.;
# find next insertion point
until(.pos[.i] == null; .i += 1)
| if $f | IN("k", "r", "n")
then .pos[.i] = $sym[$f]
else .
end )
| .pos
| join(" ") ;
def display960($sym):
"\(lpad(3)) \(chess960($sym))";
" ID Start position",
( 0, 518, 959 | display960(A) ),
"\nPseudo-random starting positions:",
(699, 889, 757, 645, 754 | display960(W))
- Output:
ID Start position 0 B B Q N N R K R 518 R N B Q K B N R 959 R K R N N Q B B Pseudo-random starting positions: 699 ♖ ♕ ♔ ♘ ♗ ♘ ♖ ♗ 889 ♖ ♕ ♔ ♗ ♗ ♖ ♘ ♘ 757 ♖ ♔ ♗ ♗ ♘ ♘ ♖ ♕ 645 ♖ ♘ ♗ ♗ ♔ ♖ ♕ ♘ 754 ♗ ♖ ♔ ♘ ♘ ♗ ♖ ♕
Julia
function generateposition()
# Placeholder knights
rank = ['♘', '♘', '♘', '♘', '♘', '♘', '♘', '♘']
lrank = length(rank)
# Check if a space is available
isfree(x::Int) = rank[x] == '♘'
# Place the King
rank[indking = rand(2:lrank-1)] = '♔'
# Place rooks
rank[indrook = rand(filter(isfree, 1:lrank))] = '♖'
if indrook > indking
rank[rand(filter(isfree, 1:indking-1))] = '♖'
else
rank[rand(filter(isfree, indking+1:lrank))] = '♖'
end
# Place bishops
rank[indbish = rand(filter(isfree, 1:8))] = '♗'
pbish = filter(iseven(indbish) ? isodd : iseven, 1:lrank)
rank[rand(filter(isfree, pbish))] = '♗'
# Place queen
rank[rand(filter(isfree, 1:lrank))] = '♕'
return rank
end
@show generateposition()
- Output:
generateposition() = ['♘', '♗', '♗', '♖', '♕', '♔', '♘', '♖']
Kotlin
object Chess960 : Iterable<String> {
override fun iterator() = patterns.iterator()
private operator fun invoke(b: String, e: String) {
if (e.length <= 1) {
val s = b + e
if (s.is_valid()) patterns += s
} else {
for (i in 0 until e.length) {
invoke(b + e[i], e.substring(0, i) + e.substring(i + 1))
}
}
}
private fun String.is_valid(): Boolean {
val k = indexOf('K')
return indexOf('R') < k && k < lastIndexOf('R') &&
indexOf('B') % 2 != lastIndexOf('B') % 2
}
private val patterns = sortedSetOf<String>()
init {
invoke("", "KQRRNNBB")
}
}
fun main(args: Array<String>) {
Chess960.forEachIndexed { i, s -> println("$i: $s") }
}
- Output:
0: BBNNQRKR 1: BBNNRKQR 2: BBNNRKRQ ... 957: RQNNBKRB 958: RQNNKBBR 959: RQNNKRBB
Lua
-- Insert 'str' into 't' at a random position from 'left' to 'right'
function randomInsert (t, str, left, right)
local pos
repeat pos = math.random(left, right) until not t[pos]
t[pos] = str
return pos
end
-- Generate a random Chess960 start position for white major pieces
function chess960 ()
local t, b1, b2 = {}
local kingPos = randomInsert(t, "K", 2, 7)
randomInsert(t, "R", 1, kingPos - 1)
randomInsert(t, "R", kingPos + 1, 8)
b1 = randomInsert(t, "B", 1, 8)
b2 = randomInsert(t, "B", 1, 8)
while (b2 - b1) % 2 == 0 do
t[b2] = false
b2 = randomInsert(t, "B", 1, 8)
end
randomInsert(t, "Q", 1, 8)
randomInsert(t, "N", 1, 8)
randomInsert(t, "N", 1, 8)
return t
end
-- Main procedure
math.randomseed(os.time())
print(table.concat(chess960()))
- Output:
NNRQBBKR
M2000 Interpreter
Module Chess960 {
function OneFrom960$ {
def q$="♕", h$="♘", t$="♖", b$="♗", k$="♔"
def integer b1, b2, t1, t2, k, q
buffer p as integer *8
return p, 0:=string$(h$, 8)
k=random(1, 6)
t1=random(0,k-1)
t2=random(k+1, 7)
used=list:=k, t1, t2
do : b1=random(0,7): until not exist(used, b1)
append used, b1
n=b1 mod 2
do : b2=random(0,7): until not exist(used, b2) and b2 mod 2 <> n
append used, b2
do : q=random(0,7): until not exist(used, q)
// place pawns to positions
return p, k:=k$, t1:=t$, t2:=t$, b1:=b$, b2:=b$, q:=q$
=Eval$(p)+{
} // append new line to every solution
}
document doc$
For i=0 to 7:doc$+=OneFrom960$(): next
clipboard doc$
report doc$
}
Chess960
- Output:
♘♗♗♘♖♕♔♖ ♗♕♖♘♘♔♖♗ ♗♗♕♖♔♘♖♘ ♘♖♕♗♗♔♖♘ ♖♔♘♗♖♕♗♘ ♖♔♖♗♘♘♗♕ ♘♘♗♕♖♗♔♖ ♖♔♕♘♘♗♗♖
Mathematica / Wolfram Language
Generates all possible initial conditions, filters for validity, and chooses a random element.
Print[StringJoin[
RandomChoice[
Select[Union[
Permutations[{"\[WhiteKing]", "\[WhiteQueen]", "\[WhiteRook]",
"\[WhiteRook]", "\[WhiteBishop]", "\[WhiteBishop]",
"\[WhiteKnight]", "\[WhiteKnight]"}]],
MatchQ[#, {___, "\[WhiteRook]", ___, "\[WhiteKing]", ___,
"\[WhiteRook]", ___}] &&
OddQ[Subtract @@ Flatten[Position[#, "\[WhiteBishop]"]]] &]]]];
MiniScript
This version uses the Unicode piece characters. If running in Mini Micro — which supports Unicode but does not have these characters in its font — just replace the the piece characters with letters.
// placeholder knights
rank = ["♘"] * 8
// function to get a random free space from a to b, inclusive
randFree = function(a, b)
free = []
for i in range(a, b)
if rank[i] == "♘" then free.push i
end for
return free[rnd * free.len]
end function
// place the king
kingIdx = randFree(1, 6)
rank[kingIdx] = "♔"
// place rooks
rank[randFree(0, kingIdx - 1)] = "♖"
rank[randFree(kingIdx + 1, 7)] = "♖"
// place bishops
bishIdx = randFree(0, 7)
rank[bishIdx] = "♗"
while true
i = randFree(0, 7)
if i % 2 != bishIdx % 2 then break
end while
rank[i] = "♗"
// place queen
rank[randFree(0, 7)] = "♕"
print join(rank, " ")
- Output:
♘ ♖ ♕ ♔ ♖ ♘ ♗ ♗
Nim
import random, strutils
type
# Chess pieces on first row.
Pieces {.pure.} = enum
King = "♔",
Queen = "♕",
Rook1 = "♖",
Rook2 = "♖",
Bishop1 = "♗",
Bishop2 = "♗",
Knight1 = "♘",
Knight2 = "♘"
# Position counted from 0.
Position = range[0..7]
# Position of pieces.
Positions = array[Pieces, Position]
func pop[T](s: var set[T]): T =
## Remove and return the first element of a set.
for val in s:
result = val
break
s.excl(result)
proc choose[T](s: var set[T]): T =
## Choose randomly a value from a set and remove it from the set.
result = sample(s)
s.excl(result)
proc positions(): Positions =
## Return a randomly chosen list of piece positions for the first row.
var pos = {Position.low..Position.high}
# Set bishops.
result[Bishop1] = sample([0, 2, 4, 6]) # Black squares.
result[Bishop2] = sample([1, 3, 5, 7]) # White squares.
pos = pos - {result[Bishop1], result[Bishop2]}
# Set queen.
result[Queen] = pos.choose()
# Set knights.
result[Knight1] = pos.choose()
result[Knight2] = pos.choose()
# In the remaining three pieces, the king must be between the two rooks.
result[Rook1] = pos.pop()
result[King] = pos.pop()
result[Rook2] = pos.pop()
#———————————————————————————————————————————————————————————————————————————————————————————————————
randomize()
for _ in 1..10:
var row: array[8, string]
let pos = positions()
for piece in Pieces:
row[pos[piece]] = $piece
echo row.join(" ")
- Output:
♘ ♘ ♕ ♖ ♔ ♗ ♗ ♖ ♖ ♗ ♗ ♔ ♕ ♘ ♘ ♖ ♕ ♗ ♖ ♔ ♘ ♘ ♗ ♖ ♖ ♘ ♗ ♕ ♔ ♘ ♖ ♗ ♖ ♘ ♗ ♗ ♘ ♕ ♔ ♖ ♕ ♘ ♖ ♗ ♗ ♔ ♘ ♖ ♗ ♘ ♖ ♔ ♖ ♗ ♘ ♕ ♖ ♔ ♕ ♘ ♗ ♖ ♘ ♗ ♘ ♖ ♗ ♔ ♕ ♗ ♘ ♖ ♗ ♕ ♖ ♗ ♘ ♔ ♖ ♘
Objeck
class Chess960 {
function : Main(args : String[]) ~ Nil {
Generate(10);
}
function : Generate(c : Int) ~ Nil {
for(x := 0; x < c; x += 1;) {
StartPos()->PrintLine();
};
}
function : StartPos() ~ String {
p := Char->New[8];
# bishops
b1 : Int; b2 : Int;
while(true) {
b1 := GetPosition(); b2 := GetPosition();
b1c := b1 and 1; b2c := b2 and 1;
c := b1c = 0 & b2c <> 0;
if(c) {
break;
};
};
p[b1] := 0x2657; p[b2] := 0x2657;
# queen, knight, knight
q := false;
for(x := 0; x < 3; x += 1;) {
do {
b1 := GetPosition();
} while( p[b1] <> '\0');
if(<>q) {
p[b1] := 0x2655; q := true;
}
else {
p[b1] := 0x2658;
};
};
# rook king rook
q := false;
for(x := 0; x < 3; x += 1;) {
a := 0;
while(a < 8) {
if(p[a] = '\0') {
break;
};
a += 1;
};
if(<>q) {
p[a] := 0x2656; q := true;
}
else {
p[a] := 0x2654; q := false;
};
};
s := "";
for(x := 0; x < 8; x += 1;) { s->Append(p[x]); };
return s;
}
function : GetPosition() ~ Int {
return (Float->Random() * 1000)->As(Int) % 8;
}
}
Output:
♗♖♕♔♖♗♘♘ ♕♗♖♔♗♘♖♘ ♖♘♔♘♕♖♗♗ ♖♗♔♘♖♘♗♕ ♖♔♖♘♕♗♗♘ ♗♘♖♕♔♘♖♗ ♗♖♔♕♖♘♘♗ ♗♖♔♘♘♖♕♗ ♖♕♔♖♘♘♗♗ ♗♖♘♔♘♖♕♗
PARI/GP
chess960() =
{
my (C = vector(8), i, j, r);
C[random(4) * 2 + 1] = C[random(4) * 2 + 2] = "B";
for (i = 1, 3, while (C[r = random(8) + 1],); C[r] = Vec("NNQ")[i]);
for (i = 1, 8, if (!C[i], C[i] = Vec("RKR")[j++]));
C
}
Output:
gp > for(i=1, 10, print(chess960())); ["N", "R", "Q", "K", "N", "R", "B", "B"] ["R", "K", "N", "B", "N", "R", "B", "Q"] ["B", "R", "K", "N", "R", "B", "Q", "N"] ["R", "B", "Q", "K", "B", "N", "R", "N"] ["R", "B", "K", "N", "N", "Q", "B", "R"] ["N", "Q", "N", "R", "B", "K", "R", "B"] ["N", "Q", "R", "B", "K", "R", "B", "N"] ["N", "R", "B", "K", "R", "N", "Q", "B"] ["R", "K", "Q", "N", "B", "N", "R", "B"] ["B", "B", "R", "N", "K", "R", "N", "Q"]
Alternatively with recent version of PARI/GP >= 2.9:
gp > M=Map(["B","♗";"K","♔";"N","♘";"Q","♕";"R","♖"]); gp > for(i=1,10,print(concat(apply((c)->mapget(M,c),chess960())))); ♗♖♘♔♕♗♖♘ ♕♖♘♔♖♗♗♘ ♖♕♘♔♘♖♗♗ ♘♘♗♖♕♗♔♖ ♘♘♖♗♗♔♖♕ ♗♖♘♔♘♕♖♗ ♖♔♘♗♗♕♘♖ ♘♖♗♘♔♗♕♖ ♖♔♕♗♗♘♖♘ ♕♗♖♔♗♘♘♖
Perl
Directly generates a configuration by inserting pieces at random appropriate places. Each config has an equal chance of being produced.
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;
- 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
Phix
Examines all 40320 permutations for validity and saves them in a list, which is easy to pick random entries from.
Using a dictionary (as commented out) is a little faster, but harder to extract random entries from.
For something faster, and truer to the task description, just use the commented out permute(rand(factorial(8) line,
and quit as soon as you find a valid one (but I wanted to check that I had found exactly 960).
with javascript_semantics sequence solutions = {} --integer d = new_dict() for i=1 to factorial(8) do sequence s = permute(i,"RNBQKBNR") -- sequence s = permute(rand(factorial(8),"RNBQKBNR") integer b1 = find('B',s), b2 = find('B',s,b1+1) if and_bits(b2-b1,1)=1 then integer k = find('K',s) integer r1 = find('R',s) integer r2 = find('R',s,r1+1) if r1<k and k<r2 then if find(s,solutions)=0 then -- if getd_index(s,d)=0 then -- setd(s,0,d) solutions = append(solutions,s) end if end if end if end for printf(1,"Found %d solutions\n",{length(solutions)}) for i=1 to 5 do ?solutions[rand(length(solutions))] end for
- Output:
Found 960 solutions "QRNNKRBB" "BQRNKBNR" "BRQBNNKR" "QBNRBKRN" "RNKBBQRN"
PicoLisp
(load "@lib/simul.l")
(seed (in "/dev/urandom" (rd 8)))
(loop
(match
'(@A B @B B @C)
(shuffle '(Q B B N N 0 0 0)) )
(NIL (bit? 1 (length @B))) )
(let Rkr '(R K R)
(for I (append @A '(B) @B '(B) @C)
(prin (if (=0 I) (pop 'Rkr) I)) )
(prinl) )
(bye)
PowerShell
function Get-RandomChess960Start
{
$Starts = @()
ForEach ( $Q in 0..3 ) {
ForEach ( $N1 in 0..4 ) {
ForEach ( $N2 in ($N1+1)..5 ) {
ForEach ( $B1 in 0..3 ) {
ForEach ( $B2 in 0..3 ) {
$BB = $B1 * 2 + ( $B1 -lt $B2 )
$BW = $B2 * 2
$Start = [System.Collections.ArrayList]( '♖', '♔', '♖' )
$Start.Insert( $Q , '♕' )
$Start.Insert( $N1, '♘' )
$Start.Insert( $N2, '♘' )
$Start.Insert( $BB, '♗' )
$Start.Insert( $BW, '♗' )
$Starts += ,$Start
}}}}}
$Index = Get-Random 960
$StartString = $Starts[$Index] -join ''
return $StartString
}
Get-RandomChess960Start
Get-RandomChess960Start
Get-RandomChess960Start
Get-RandomChess960Start
- Output:
♘♕♖♔♖♘♗♗ ♗♕♘♖♔♗♖♘ ♖♗♔♕♗♖♘♘ ♘♖♔♖♕♗♗♘
Prolog
Uses random_permutation/2
defined in SWI-Prolog.
check(Row) :-
nth1(King, Row, ♔),
nth1(Rook1, Row, ♖),
nth1(Rook2, Row, ♖),
nth1(Bishop1, Row, ♗),
nth1(Bishop2, Row, ♗),
Rook1 < King, King < Rook2,
(Bishop1 + Bishop2) mod 2 =:= 1.
generate(Row) :-
random_permutation([♖,♘,♗,♕,♔,♗,♘,♖], Row),
check(Row) ; generate(Row).
Example run:
?- generate(X).
X = [♘, ♗, ♖, ♕, ♘, ♔, ♗, ♖] ;
X = [♘, ♗, ♖, ♕, ♘, ♔, ♗, ♖] ;
X = [♘, ♖, ♘, ♕, ♔, ♖, ♗, ♗] ;
X = [♘, ♖, ♘, ♕, ♔, ♖, ♗, ♗]
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.
>>> 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'
>>>
Python: Regexp
This uses regexps to filter permutations of the start position pieces rather than indexing.
>>> 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'
>>>
Python: Correct by construction
Follows Perl algorithm of constructing one start position randomly, according to the rules. (See talk page for tests).
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())
- Output:
['N', 'R', 'K', 'N', 'B', 'Q', 'R', 'B']
Python: Generate all positions then choose one randomly
from random import choice
def generate960():
start = ('R', 'K', 'R') # Subsequent order unchanged by insertions.
# Insert QNN in all combinations of places
starts = {start}
for piece in ['Q', 'N', 'N']:
starts2 = set()
for s in starts:
for pos in range(len(s)+1):
s2 = list(s)
s2.insert(pos, piece)
starts2.add(tuple(s2))
starts = starts2
# For each of the previous starting positions insert the bishops in their 16 positions
starts2 = set()
for s in starts:
for bishpos in range(len(s)+1):
s2 = list(s)
s2.insert(bishpos, 'B')
for bishpos2 in range(bishpos+1, len(s)+2, 2):
s3 = s2[::]
s3.insert(bishpos2, 'B')
starts2.add(tuple(s3))
return list(starts2)
gen = generate960()
print(''.join(choice(gen)))
- Output:
NRBQNKRB
R
pieces <- c("R","B","N","Q","K","N","B","R")
generateFirstRank <- function() {
attempt <- paste0(sample(pieces), collapse = "")
while (!check_position(attempt)) {
attempt <- paste0(sample(pieces), collapse = "")
}
return(attempt)
}
check_position <- function(position) {
if (regexpr('.*R.*K.*R.*', position) == -1) return(FALSE)
if (regexpr('.*B(..|....|......|)B.*', position) == -1) return(FALSE)
TRUE
}
convert_to_unicode <- function(s) {
s <- sub("K","\u2654", s)
s <- sub("Q","\u2655", s)
s <- gsub("R","\u2656", s)
s <- gsub("B","\u2657", s)
s <- gsub("N","\u2658", s)
}
cat(convert_to_unicode(generateFirstRank()), "\n")
- Output:
♘♗♘♖♗♕♔♖
Racket
Constructive:
#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)
- Output:
"♖♘♗♕♔♗♘♖"
Well that's embarassing... the stupid thing has only gone and randomly generated a classic chess starting position.
Try again:
"♘♖♔♕♗♗♖♘"
Raku
(formerly 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.
repeat until m/ '♗' [..]* '♗' / { $_ = < ♖ ♖ ♖ ♕ ♗ ♗ ♘ ♘ >.pick(*).join }
s:2nd['♖'] = '♔';
say .comb;
- Output:
♕ ♗ ♖ ♘ ♔ ♖ ♗ ♘
Here's a more "functional" solution that avoids side effects
sub chess960 {
.subst(:nth(2), /'♜'/, '♚') given
first rx/ '♝' [..]* '♝' /,
< ♛ ♜ ♜ ♜ ♝ ♝ ♞ ♞ >.pick(*).join xx *;
}
say chess960;
- 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.
constant chess960 =
< ♛ ♜ ♜ ♜ ♝ ♝ ♞ ♞ >.permutations».join.unique.grep( / '♝' [..]* '♝' / )».subst(:nth(2), /'♜'/, '♚');
.say for chess960;
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.
constant chess960 = gather for 0..3 -> $q {
(my @q = <♜ ♚ ♜>).splice($q, 0, '♛');
for 0 .. @q -> $n1 {
(my @n1 = @q).splice($n1, 0, '♞');
for $n1 ^.. @n1 -> $n2 {
(my @n2 = @n1).splice($n2, 0, '♞');
for 0 .. @n2 -> $b1 {
(my @b1 = @n2).splice($b1, 0, '♝');
for $b1+1, $b1+3 ...^ * > @b1 -> $b2 {
(my @b2 = @b1).splice($b2, 0, '♝');
take @b2.join;
}
}
}
}
}
CHECK { note "done compiling" }
note +chess960;
say chess960.pick;
- 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.
♛♝♜♚♝♞♞♜
Raku: Generate from SP-ID
There is a standard numbering scheme for Chess960 positions, assigning each an index in the range 0..959. This function will generate the corresponding position from a given index number (or fall back to a random one if no index is specified, making it yet another solution to the general problem).
subset Pos960 of Int where { $_ ~~ ^960 };
sub chess960(Pos960 $position = (^960).pick) {
# We remember the remainder used to place first bishop in order to place the
# second
my $b1;
# And likewise remember the chosen combination for knights between those
# placements
my @n;
# Piece symbols and positioning rules in order. Start with the position
# number. At each step, divide by the divisor; the quotient becomes the
# dividend for the next step. Feed the remainder into the specified code block
# to get a space number N, then place the piece in the Nth empty space left in
# the array.
my @rules = (
#divisor, mapping function, piece
( 4, { $b1 = $_; 2 * $_ + 1 }, '♝' ),
( 4, { 2 * $_ - ($_ > $b1 ?? 1 !! 0) }, '♝' ),
( 6, { $_ }, '♛' ),
(10, { @n = combinations(5,2)[$_]; @n[0] }, '♞' ),
( 1, { @n[1]-1 }, '♞' ),
( 1, { 0 }, '♜' ),
( 1, { 0 }, '♚' ),
( 1, { 0 }, '♜' )
);
# Initial array, using '-' to represent empty spaces
my @array = «-» xx 8;
# Working value that starts as the position number but is divided by the
# divisor at each placement step.
my $p = $position;
# Loop over the placement rules
for @rules -> ($divisor, $block, $piece) {
# get remainder when divided by divisor
(my $remainder, $p) = $p.polymod($divisor);
# apply mapping function
my $space = $block($remainder);
# find index of the $space'th element of the array that's still empty
my $index = @array.kv.grep(-> $i,$v { $v eq '-' })[$space][0];
# and place the piece
@array[$index] = $piece;
}
return @array;
}
# demo code
say chess960(518); #standard optning position
say chess960; # (it happened to pick #300)
- Output:
♜ ♞ ♝ ♛ ♚ ♝ ♞ ♜ ♛ ♝ ♞ ♜ ♚ ♜ ♝ ♞
REXX
Random starting position is correct by construction (both REXX entries).
generates one random position
/*REXX program generates a random starting position for the Chess960 game. */
parse arg seed . /*allow for (RANDOM BIF) repeatability.*/
if seed\=='' then call random ,,seed /*if SEED was specified, use the seed.*/
@.=. /*define the first rank as being empty.*/
r1=random(1,6) /*generate the first rook: rank 1. */
@.r1='R' /*place the first rook on rank1. */
do until r2\==r1 & r2\==r1-1 & r2\==r1+1
r2=random(1,8) /*find placement for the 2nd rook. */
end /*forever*/
@.r2='r' /*place the second rook on rank 1. */
k=random(min(r1, r2)+1, max(r1, r2)-1) /*find a random position for the king. */
@.k='K' /*place king between the two rooks. */
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 /*is a bad position?*/
leave _ /*found position for the 2 clergy*/
end /*forever*/ /* [↑] find a place for the 1st bishop*/
end /* _ */ /* [↑] " " " " " 2nd " */
@.b1='B' /*place the 1st bishop on rank 1. */
@.b2='b' /* " " 2nd " " " " */
/*place the two knights on rank 1. */
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 be placed. */
do i=1 for 8; _=_ || @.i; end /*construct the output: first rank only*/
say translate(translate(_, 'q', .)) /*stick a fork in it, we're all done. */
output
NRQKBRNB
generates all 960 positions randomly
/*REXX program generates all random starting positions for the Chess960 game. */
parse arg seed . /*allow for (RANDOM BIF) repeatability.*/
if seed\=='' then call random ,,seed /*if SEED was specified, use the seed.*/
x.=0; #=0; rg='random generations: ' /*initialize game placeholder; # games.*/
/*▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒*/
do t=1 /* [↓] display every 1,000 generations*/ /*▒*/
if t//1000==0 then say right(t,9) rg # " unique starting positions." /*▒*/
@.=. /*define the first rank as being empty.*/ /*▒*/
r1=random(1,6) /*generate the first rook: rank 1. */ /*▒*/
@.r1='R' /*place the first rook on rank1. */ /*▒*/
do until r2\==r1 & r2\==r1-1 & r2\==r1+1 /*▒*/
r2=random(1,8) /*find placement for the 2nd rook. */ /*▒*/
end /*forever*/ /*▒*/
@.r2='r' /*place the second rook on rank 1. */ /*▒*/
k=random(min(r1, r2)+1, max(r1, r2)-1) /*find a random position for the king. */ /*▒*/
@.k='K' /*place king between the two rooks. */ /*▒*/
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 /*is a bad position?*/ /*▒*/
leave _ /*found position for the 2 clergy*/ /*▒*/
end /*forever*/ /* [↑] find a place for the 1st bishop*/ /*▒*/
end /* _ */ /* [↑] " " " " " 2nd " */ /*▒*/
@.b1='B' /*place the 1st bishop on rank 1. */ /*▒*/
@.b2='b' /* " " 2nd " " " " */ /*▒*/
/*place the two knights on rank 1. */ /*▒*/
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 be placed. */ /*▒*/
do i=1 for 8; _=_ || @.i; end /*construct the output: first rank only*/ /*▒*/
upper _ /*uppercase all the chess pieces. */ /*▒*/
if x._ then iterate /*This position found before? Skip it.*/ /*▒*/
x._=1 /*define this position as being found. */ /*▒*/
#=#+1 /*bump the # of unique positions found,*/ /*▒*/
if #==960 then leave /*▒*/
end /*t ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒*/
say # 'unique starting positions found after ' t "generations."
/*stick a fork in it, we're all done. */ /**/
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.
version 3 COMPUTE all possibilities
/*---------------------------------------------------------------
* Compute the 960 possible solutions
* There must be at least one field between the rooks
* The king is positioned on any field between the rooks
* The queen is placed on any unoccupied field
* bishops are placed so that they are on different colored fields
* what remains are the kNights...
*--------------------------------------------------------------*/
cnt.=0
Call time 'R'
Do r1=1 To 6
Do r2=r1+1 To 8
Do kk=r1+1 To r2-1
poss=space(translate('12345678',' ',r1||kk||r2),0)
Call rest
End
End
End
say cnt.1 'solutions'
Say time('E')
Exit
rest:
Do i=1 To 5
q=substr(poss,i,1)
br=space(translate(poss,' ',q),0)
Do b1i=1 To 3
Do b2i=b1i+1 To 4
Call finish
End
End
End
Return
finish:
b1=substr(br,b1i,1)
b2=substr(br,b2i,1)
If (b1+b2)//2>0 Then
Call out
Return
out:
pos.='N'
pos.r1='R'
pos.r2='R'
pos.kk='K'
pos.q='Q'
pos.b1='B'
pos.b2='B'
ol=''
Do k=1 To 8
ol=ol||pos.k
End
cnt.1=cnt.1+1
If cnt.1<4 |,
cnt.1>957 Then
Say format(cnt.1,3) poss r1 kk r2 ol
If cnt.1=4 Then
Say ' ...'
Return
- Output:
1 45678 1 2 3 RKRQBBNN 2 45678 1 2 3 RKRQBNNB 3 45678 1 2 3 RKRQNBBN ... 958 12345 6 7 8 BNNBQRKR 959 12345 6 7 8 NBBNQRKR 960 12345 6 7 8 NNBBQRKR 960 solutions
RPL
We use here the single die method, starting with a list of rooks that are gradually replaced by other pieces.
≪ SWAP 1 ≪ 'R' SAME ≫ DOLIST 0 SWAP 1 OVER SIZE FOR j DUP j GET ROT + SWAP j 3 PICK PUT NEXT SWAP DROP SWAP POS ≫ 'ROOKPOS' STO ≪ { } 1 8 START 'R' + NEXT RAND 4 * FLOOR 2 * 1 + 'B' PUT RAND 4 * CEIL 2 * 'B' PUT DUP RAND 6 * CEIL ROOKPOS 'Q' PUT DUP RAND 5 * CEIL ROOKPOS 'N' PUT DUP RAND 4 * CEIL ROOKPOS 'N' PUT DUP 2 ROOKPOS 'K' PUT ≫ '→CH360' STO
- Output:
3: { N Q R K B B R N } 2: { R K B B R Q N N } 1: { Q N R B K N B R }
Ruby
Ruby: shuffle pieces until all regexes match
Translation of Tcl.
pieces = %i(♔ ♕ ♘ ♘ ♗ ♗ ♖ ♖)
regexes = [/♗(..)*♗/, /♖.*♔.*♖/]
row = pieces.shuffle.join until regexes.all?{|re| re.match(row)}
puts row
- Output:
♕♖♗♘♔♖♘♗
Ruby: Construct
Uses the Perl idea of starting with [R,K,R] and inserting the rest:
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
- Output:
♗♘♕♘♖♗♔♖
Ruby: Generate from SP-ID
KRN = %w(NNRKR NRNKR NRKNR NRKRN RNNKR RNKNR RNKRN RKNNR RKNRN RKRNN)
def chess960(id=rand(960))
pos = Array.new(8)
q, r = id.divmod(4)
pos[r * 2 + 1] = "B"
q, r = q.divmod(4)
pos[r * 2] = "B"
q, r = q.divmod(6)
pos[pos.each_index.reject{|i| pos[i]}[r]] = "Q"
krn = KRN[q].each_char
pos.each_index {|i| pos[i] ||= krn.next}
pos.join
end
puts "Generate Start Position from id number"
[0,518,959].each do |id|
puts "%3d : %s" % [id, chess960(id)]
end
puts "\nGenerate random Start Position"
5.times {puts chess960}
- Output:
Generate Start Position from id number 0 : BBQNNRKR 518 : RNBQKBNR 959 : RKRNNQBB Generate random Start Position RNBNKBRQ RKRNBBNQ BBRNQKNR NBRKNRBQ BRKQNNRB
Rust
use std::collections::BTreeSet;
struct Chess960 ( BTreeSet<String> );
impl Chess960 {
fn invoke(&mut self, b: &str, e: &str) {
if e.len() <= 1 {
let s = b.to_string() + e;
if Chess960::is_valid(&s) { self.0.insert(s); }
} else {
for (i, c) in e.char_indices() {
let mut b = b.to_string();
b.push(c);
let mut e = e.to_string();
e.remove(i);
self.invoke(&b, &e);
}
}
}
fn is_valid(s: &str) -> bool {
let k = s.find('K').unwrap();
k > s.find('R').unwrap() && k < s.rfind('R').unwrap() && s.find('B').unwrap() % 2 != s.rfind('B').unwrap() % 2
}
}
// Program entry point.
fn main() {
let mut chess960 = Chess960(BTreeSet::new());
chess960.invoke("", "KQRRNNBB");
for (i, p) in chess960.0.iter().enumerate() {
println!("{}: {}", i, p);
}
}
Rust 1.57 nightly
// Chess960: regex and unicode version, create 5 valid random positions.
use rand::{seq::SliceRandom, thread_rng};
use regex::Regex;
fn vec_to_string(v: Vec<&str>) -> String {
let mut is_string = String::new();
for ele in v {
is_string.push_str(ele)
}
is_string
}
fn is_rook_king_ok(str_to_check: Vec<&str>) -> bool {
Regex::new(r"(.*♖.*♔.*♖.*)")
.unwrap()
.is_match(vec_to_string(str_to_check.clone()).as_str())
}
fn is_two_bishops_ok(str_to_check: Vec<&str>) -> bool {
Regex::new(r"(.*♗.{0}♗.*|.*♗.{2}♗.*|.*♗.{4}♗.*|.*♗.{6}♗.*)")
.unwrap()
.is_match(vec_to_string(str_to_check.clone()).as_str())
}
fn create_rnd_candidate() -> [&'static str; 8] {
let mut rng = thread_rng();
let mut chaine = ["♖", "♘", "♗", "♔", "♕", "♗", "♘", "♖"];
loop {
chaine.shuffle(&mut rng);
if is_candidate_valide(chaine) {
break chaine;
}
}
}
fn is_candidate_valide(s: [&str; 8]) -> bool {
is_rook_king_ok(s.to_vec()) && is_two_bishops_ok(s.to_vec())
}
fn main() {
for _ in 0..5 {
println!("{:?}", create_rnd_candidate());
}
}
- Output:
["♕", "♘", "♗", "♗", "♖", "♘", "♔", "♖"] ["♖", "♔", "♗", "♕", "♘", "♗", "♖", "♘"] ["♗", "♖", "♕", "♔", "♘", "♗", "♖", "♘"] ["♘", "♖", "♘", "♕", "♔", "♖", "♗", "♗"] ["♖", "♗", "♕", "♔", "♗", "♘", "♖", "♘"]
Scala
Functional Programming, tail recursive, Unicode, RegEx
import scala.annotation.tailrec
object Chess960 extends App {
private val pieces = List('♖', '♗', '♘', '♕', '♔', '♘', '♗', '♖')
@tailrec
private def generateFirstRank(pieces: List[Char]): List[Char] = {
def check(rank: String) =
rank.matches(".*♖.*♔.*♖.*") && rank.matches(".*♗(..|....|......|)♗.*")
val p = scala.util.Random.shuffle(pieces)
if (check(p.toString.replaceAll("[^\\p{Upper}]", "")))
generateFirstRank(pieces)
else p
}
loop(10)
@tailrec
private def loop(n: Int): Unit = {
println(generateFirstRank(pieces))
if (n <= 0) () else loop(n - 1)
}
}
- Output:
See it running in your browser by ScalaFiddle (JavaScript, non JVM) or by Scastie (JVM).
Imperative Programming
object Chess960 extends App {
private def apply(b: String, e: String) {
if (e.length <= 1) {
val s = b + e
if (is_valid(s)) patterns += s
} else
for (i <- 0 until e.length)
apply(b + e(i), e.substring(0, i) + e.substring(i + 1))
}
private def is_valid(s: String) = {
val k = s.indexOf('K')
if (k < s.indexOf('R')) false
else k < s.lastIndexOf('R') && s.indexOf('B') % 2 != s.lastIndexOf('B') % 2
}
private val patterns = scala.collection.mutable.SortedSet[String]()
apply("", "KQRRNNBB")
for ((s, i) <- patterns.zipWithIndex) println(s"$i: $s")
}
Scheme
(import (scheme base) (scheme write)
(srfi 1) ; list library
(srfi 27)) ; random numbers
(random-source-randomize! default-random-source)
;; Random integer in [start, end)
(define (random-between start end)
(let ((len (- end start 1)))
(if (< len 2)
start
(+ start (random-integer len)))))
;; Random item in list
(define (random-pick lst)
(if (= 1 (length lst))
(car lst)
(list-ref lst (random-integer (length lst)))))
;; Construct a random piece placement for Chess960
(define (random-piece-positions)
(define (free-indices positions) ; return list of empty slot indices
(let loop ((i 0)
(free '()))
(if (= 8 i)
free
(loop (+ 1 i)
(if (string=? "." (vector-ref positions i))
(cons i free)
free)))))
;
(define (place-king+rooks positions)
(let ((king-posn (random-between 1 8)))
(vector-set! positions king-posn "K")
; left-rook is between left-edge and king
(vector-set! positions (random-between 0 king-posn) "R")
; right-rook is between right-edge and king
(vector-set! positions (random-between (+ 1 king-posn) 8) "R")))
;
(define (place-bishops positions)
(let-values (((evens odds) (partition even? (free-indices positions))))
(vector-set! positions (random-pick evens) "B")
(vector-set! positions (random-pick odds) "B")))
;
(let ((positions (make-vector 8 ".")))
(place-king+rooks positions)
(place-bishops positions)
;; place the queen in a random remaining slot
(vector-set! positions (random-pick (free-indices positions)) "Q")
;; place the two knights in the remaining slots
(for-each (lambda (idx) (vector-set! positions idx "N"))
(free-indices positions))
positions))
(display "First rank: ") (display (random-piece-positions)) (newline)
- Output:
Ten sample runs:
First rank: #(R N N Q K R B B) First rank: #(R K N N Q R B B) First rank: #(Q R B N N B K R) First rank: #(Q R B N N K R B) First rank: #(R K N Q R B B N) First rank: #(R K N B Q R B N) First rank: #(R N K N B B R Q) First rank: #(R B K Q B N R N) First rank: #(B Q R N K N R B) First rank: #(R B B Q N N K R)
Seed7
$ include "seed7_05.s7i";
const proc: main is func
local
var string: start is "RKR";
var char: piece is ' ';
var integer: pos is 0;
begin
for piece range "QNN" do
pos := rand(1, succ(length(start)));
start := start[.. pred(pos)] & str(piece) & start[pos ..];
end for;
pos := rand(1, succ(length(start)));
start := start[.. pred(pos)] & "B" & start[pos ..];
pos := succ(pos) + 2 * rand(0, (length(start) - pos) div 2);
start := start[.. pred(pos)] & "B" & start[pos ..];
writeln(start);
end func;
- Output:
NQBNRBKR
Sidef
func is_valid_960 (backrank) {
var king = backrank.index('♚')
var (rook1, rook2) = backrank.indices_of('♜')...
king.is_between(rook1, rook2) || return false
var (bishop1, bishop2) = backrank.indices_of('♝')...
bishop1+bishop2 -> is_odd
}
func random_960_position(pieces = <♛ ♚ ♜ ♜ ♝ ♝ ♞ ♞>) {
pieces.shuffle.permutations {|*a|
return a if is_valid_960(a)
}
}
say random_960_position().join(' ')
- Output:
♝ ♝ ♜ ♚ ♞ ♛ ♜ ♞
Swift
func isValid960Position(_ firstRank: String) -> Bool {
var rooksPlaced = 0
var bishopColor = -1
for (i, piece) in firstRank.enumerated() {
switch piece {
case "♚" where rooksPlaced != 1:
return false
case "♜":
rooksPlaced += 1
case "♝" where bishopColor == -1:
bishopColor = i & 1
case "♝" where bishopColor == i & 1:
return false
case _:
continue
}
}
return true
}
struct Chess960Counts {
var king = 0, queen = 0, rook = 0, bishop = 0, knight = 0
subscript(_ piece: String) -> Int {
get {
switch piece {
case "♚": return king
case "♛": return queen
case "♜": return rook
case "♝": return bishop
case "♞": return knight
case _: fatalError()
}
}
set {
switch piece {
case "♚": king = newValue
case "♛": queen = newValue
case "♜": rook = newValue
case "♝": bishop = newValue
case "♞": knight = newValue
case _: fatalError()
}
}
}
}
func get960Position() -> String {
var counts = Chess960Counts()
var bishopColor = -1 // 0 - white 1 - black
var output = ""
for i in 1...8 {
let validPieces = [
counts["♜"] == 1 && counts["♚"] == 0 ? "♚" : nil, // king
i == 1 || (counts["♛"] == 0) ? "♛" : nil, // queen
i == 1 || (counts["♜"] == 0 || counts["♜"] < 2 && counts["♚"] == 1) ? "♜" : nil, // rook
i == 1 || (counts["♝"] < 2 && bishopColor == -1 || bishopColor != i & 1) ? "♝" : nil, // bishop
i == 1 || (counts["♞"] < 2) ? "♞" : nil // knight
].lazy.compactMap({ $0 })
guard let chosenPiece = validPieces.randomElement() else {
// Need to swap last piece with a bishop
output.insert("♝", at: output.index(before: output.endIndex))
break
}
counts[chosenPiece] += 1
output += chosenPiece
if bishopColor == -1 && chosenPiece == "♝" {
bishopColor = i & 1
}
}
assert(isValid960Position(output), "invalid 960 position \(output)")
return output
}
var positions = Set<String>()
while positions.count != 960 {
positions.insert(get960Position())
}
print(positions.count, positions.randomElement()!)
- Output:
960 ♞♛♜♞♚♝♝♜
Tcl
Using regular expressions to filter a random permutation.
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
}
}
}
# A simple renderer
proc chessRender {position} {
string map {P ♙ N ♘ B ♗ R ♖ Q ♕ K ♔} $position
}
# Output multiple times just to show scope of positions
foreach - {1 2 3 4 5} {puts [chessRender [chess960]]}
- Output:
♕♖♘♔♗♗♘♖ ♖♔♘♘♗♕♖♗ ♘♖♗♗♕♔♘♖ ♘♕♗♖♔♖♘♗ ♘♘♖♔♗♗♕♖
UNIX Shell
declare -a pieces=(♖ ♖ ♖ ♕ ♗ ♗ ♘ ♘)
declare -i i pick index
declare -ai picking_history
declare attempt
until [[ "$attempt" =~ ♗(..)*♗ ]]
do
attempt=''
picking_history=()
for _ in {1..8}
do
while ((picking_history[pick=RANDOM%8]++))
do :
done
attempt+="${pieces[pick]}"
done
done
for i in {0..7}
do
if [[ "${attempt:i:1}" = ♖ ]] && ((index++))
then echo "${attempt:0:i}♔${attempt:i+1}"; break;
fi
done
Wren
import "random" for Random
import "./dynamic" for Tuple
import "./fmt" for Fmt
var Symbols = Tuple.create("Symbols", ["k", "q", "r", "b", "n"])
var A = Symbols.new("K", "Q", "R", "B", "N")
var W = Symbols.new("♔", "♕", "♖", "♗", "♘")
var B = Symbols.new("♚", "♛", "♜", "♝", "♞")
var krn = [
"nnrkr", "nrnkr", "nrknr", "nrkrn",
"rnnkr", "rnknr", "rnkrn",
"rknnr", "rknrn",
"rkrnn"
]
var NUL = "\0"
var chess960 = Fn.new { |sym, id|
var pos = List.filled(8, NUL)
var q = (id/4).floor
var r = id % 4
pos[r*2+1]= sym.b
var t = q
q = (q/4).floor
r = t % 4
pos[r*2] = sym.b
t = q
q = (q/6).floor
r = t % 6
var i = 0
while (true) {
if (pos[i] == NUL) {
if (r == 0) {
pos[i] = sym.q
break
}
r = r - 1
}
i = i + 1
}
i = 0
for (f in krn[q]) {
while (pos[i] != NUL) i = i + 1
pos[i] = (f == "k") ? sym.k :
(f == "r") ? sym.r :
(f == "n") ? sym.n : pos[i]
}
return pos.join(" ")
}
System.print(" ID Start position")
for (id in [0, 518, 959]) Fmt.print("$3d $s", id, chess960.call(A, id))
System.print("\nRandom")
var rand = Random.new()
for (i in 0..4) System.print(chess960.call(W, rand.int(960)))
- Output:
ID Start position 0 B B Q N N R K R 518 R N B Q K B N R 959 R K R N N Q B B Random ♘ ♗ ♖ ♘ ♔ ♕ ♗ ♖ ♖ ♘ ♗ ♔ ♕ ♘ ♖ ♗ ♕ ♗ ♗ ♖ ♘ ♔ ♘ ♖ ♖ ♔ ♗ ♕ ♘ ♗ ♘ ♖ ♖ ♔ ♘ ♗ ♕ ♘ ♗ ♖
XPL0
char Col;
func ColNum(Start, Piece); \Return column number of Piece
int Start, Piece, I;
[for I:= Start to 7 do
if Col(I) = Piece then return I;
return -1;
];
proc Shuffle; \Randomly rearrange pieces in columns
int I, J, T;
[for I:= 8-1 downto 1 do
[J:= Ran(I); \range [0..I-1] (Sattolo cycle)
T:= Col(I); Col(I):= Col(J); Col(J):= T;
];
];
int N, B1, B2, BOK, R1, R2, K, KOK;
[for N:= 1 to 5 do
[Col:= "RNBQKBNR ";
repeat Shuffle;
B1:= ColNum(0, ^B);
B2:= ColNum(B1+1, ^B);
BOK:= ((B1 xor B2) and 1) # 0;
R1:= ColNum(0, ^R);
R2:= ColNum(R1+1, ^R);
K:= ColNum(0, ^K);
KOK:= R1<K and K<R2;
until BOK and KOK;
Text(0, Col); CrLf(0);
];
]
- Output:
BNRBQKRN RBKNNQBR BQRBNKNR NRBBQKNR RNKNBQRB
zkl
const pieces="KQRrBbNN";
starts:=pieces:Utils.Helpers.permuteW(_).filter(fcn(p){
I:=p.index;
I("B") % 2 != I("b") % 2 and // Bishop constraint.
// King constraint.
((I("r") < I("K") and I("K") < I("R")) or
(I("R") < I("K") and I("K") < I("r")))
}).pump(List,"concat","toUpper"):Utils.Helpers.listUnique(_);
N:=starts.len(); println(N);
glyphs:=Dictionary("K","\u2654", "Q","\u2655", "R","\u2656", "B","\u2657", "N","\u2658");
// pick some random starts and transform BBNRKQRN to glyphs
do(10){ starts[(0).random(N)].apply(glyphs.find).println() }
- Output:
960 ♗♕♘♖♘♔♖♗ ♖♘♗♔♖♗♘♕ ♖♗♘♔♗♕♖♘ ♘♖♘♗♗♔♕♖ ♘♘♗♖♕♔♖♗ ♘♖♕♔♗♖♘♗ ♘♖♗♘♕♔♖♗ ♖♘♗♔♕♘♖♗ ♖♔♖♕♘♘♗♗ ♕♗♖♘♗♔♘♖
- Programming Tasks
- Solutions by Programming Task
- Chess960
- 11l
- Action!
- APL
- Arturo
- AutoHotkey
- BASIC
- BASIC256
- BBC BASIC
- Commodore BASIC
- FreeBASIC
- QBasic
- Yabasic
- Befunge
- C
- C sharp
- C++
- Clojure
- Common Lisp
- D
- EasyLang
- EchoLisp
- Elixir
- Factor
- Forth
- Fortran
- Go
- Haskell
- J
- Java
- JavaScript
- Jq
- Julia
- Kotlin
- Lua
- M2000 Interpreter
- Mathematica
- Wolfram Language
- Examples needing attention
- MiniScript
- Nim
- Objeck
- PARI/GP
- Perl
- Phix
- PicoLisp
- PowerShell
- Prolog
- Python
- R
- Racket
- Raku
- REXX
- RPL
- Ruby
- Rust
- Scala
- Scheme
- Scheme/SRFIs
- Seed7
- Sidef
- Swift
- Tcl
- Tcllib
- UNIX Shell
- Wren
- Wren-dynamic
- Wren-fmt
- XPL0
- Zkl