Find Chess960 starting position identifier: Difference between revisions

m
(Add python)
m (→‎{{header|Wren}}: Minor tidy)
(54 intermediate revisions by 8 users not shown)
Line 3:
 
;Task:
This task is to go the other way: given a starting array of pieces (provided in any form that suits your implementation, whether string or list or array, of letters or Unicode chess symbols or enum values, etc.), derive its unique SP-ID. For example, given the starting array '''QNRBBNKR''' (or '''♕♘♖♗♗♘♔♖''' or '''♛♞♜♝♝♞♚♜'''), which we assume is given as seen from White's side of the board from left to right, your (sub)program should return 105; given the starting lineup of standard chess, it should return 518.
 
You may assume the input is a valid Chess960 position; detecting invalid input (including illegal characters or starting arrays with the bishops on the same color square or the king not between the two rooks) is optional.
Line 9:
;Algorithm:
 
The derivation is the inverse of the algorithm given at [[wp:Fischer random chess numbering scheme#Direct_derivation|Wikipedia]], and goes like this (we'll use the standard chess setup as an example):.
 
1. Ignoring the Queen and Bishops, find the positions of the Knights within the remaining five spaces (in the standard array they're in the second and fourth positions), and then find the index number of that combination. There's [[wp:Fischer random chess numbering scheme#Direct_derivation|a table]] at the above Wikipedia article, but it's just the possible positions sorted left to right and numbered 0 to 9: 0='''NN---''', 1='''N-N--''', 2='''N--N-''', 3='''N---N''', 4='''-NN--''', etc; our pair is combination number 5. Call this number N. '''N=5'''
 
2. NowStill ignoring the Knights (but including the Queen and Bishops), find the position of the Queen in the remaining 6 spaces; number them 0..5 from left to right and call the index of the Queen's position Q. In our example, '''Q=2'''.
 
3. Finally, find the positions of the two bishops within their respective sets of four like-colored squares. It's important to note here that the board in chess is placed such that the leftmost position on the home row is on a dark square and the rightmost a light. So if we number the squares of each color 0..3 from left to right, the dark bishop in the standard position is on square 1 ('''D=1'''), and the light bishop is on square 2 ('''L=2''').
 
4. Then the position number is given by '''4(4(6N + Q)+D)+L''', which reduces to '''96N + 16Q + 4D + L'''. In our example, that's 96×5 + 16×2 + 4×1 + 2 = 480 + 32 + 4 + 2 = 518.
 
Note that an earlier iteration of this page contained an incorrect description of the algorithm which would give the same SP-ID for both of the following two positions.
 
RQNBBKRN = 601
RNQBBKRN = 617
 
=={{header|11l}}==
{{trans|Python}}
 
<syntaxhighlight lang="11l">
F validate_position(String candidate)
assert(candidate.len == 8, ‘candidate position has invalid len = ’candidate.len)
 
V valid_pieces = [‘R’ = 2, ‘N’ = 2, ‘B’ = 2, ‘Q’ = 1, ‘K’ = 1]
assert(Set(Array(candidate)) == Set(valid_pieces.keys()), ‘candidate position contains invalid pieces’)
L(piece_type) valid_pieces.keys()
assert(candidate.count(piece_type) == valid_pieces[piece_type], ‘piece type '’piece_type‘' has invalid count’)
 
V bishops_pos = enumerate(Array(candidate)).filter((index, value) -> value == ‘B’).map((index, value) -> index)
assert(bishops_pos[0] % 2 != bishops_pos[1] % 2, ‘candidate position has both bishops in the same color’)
 
assert(candidate.filter(piece -> piece C ‘RK’) == [‘R’, ‘K’, ‘R’], ‘candidate position has K outside of RR’)
 
F calc_position(String start_pos)
validate_position(start_pos)
V subset_step1 = start_pos.filter(piece -> piece !C ‘QB’)
V nights_positions = enumerate(subset_step1).filter((index, value) -> value == ‘N’).map((index, value) -> index)
V nights_table = [(0, 1) = 0,
(0, 2) = 1,
(0, 3) = 2,
(0, 4) = 3,
(1, 2) = 4,
(1, 3) = 5,
(1, 4) = 6,
(2, 3) = 7,
(2, 4) = 8,
(3, 4) = 9]
V n = nights_table[(nights_positions[0], nights_positions[1])]
 
V subset_step2 = start_pos.filter(piece -> piece != ‘B’)
V q = subset_step2.index(‘Q’)
 
V dark_squares = enumerate(Array(start_pos)).filter((index, piece) -> index C Array((0.<9).step(2))).map((index, piece) -> piece)
V light_squares = enumerate(Array(start_pos)).filter((index, piece) -> index C Array((1.<9).step(2))).map((index, piece) -> piece)
V d = dark_squares.index(‘B’)
V l = light_squares.index(‘B’)
 
R 4 * (4 * (6*n + q) + d) + l
 
L(example) [‘QNRBBNKR’, ‘RNBQKBNR’, ‘RQNBBKRN’, ‘RNQBBKRN’]
print(‘Position: ’example‘; Chess960 PID= ’calc_position(example))
</syntaxhighlight>
 
{{out}}
<pre>
Position: QNRBBNKR; Chess960 PID= 105
Position: RNBQKBNR; Chess960 PID= 518
Position: RQNBBKRN; Chess960 PID= 601
Position: RNQBBKRN; Chess960 PID= 617
</pre>
 
=={{header|BASIC}}==
Line 24 ⟶ 84:
Unlike the solution for the reverse task, which uses DO/LOOP and so requires at least Commodore BASIC 3.5, this should work on any version.
 
<langsyntaxhighlight lang="basic">100 REM DERIVE SP-ID FROM CHESS960 POS
110 READ A$: IF A$="" THEN END
110 PRINT "ENTER START ARRAY AS SEEN BY WHITE."
120 PRINT: PRINT A$"STARTING ARRAY:";
130 GOSUB 170
130 OPEN 1,0: INPUT#1, AR$: CLOSE 1: PRINT
140 PRINT SP
140 IF LEN(AR$)=0 THEN END
150 GOTO 110
150 IF LEN(AR$)=8 THEN 170
160 DATA QNRBBNKR, RNBQKBNR, RQNBBKRN, RNQBBKRN,
160 PRINT "ARRAY MUST BE 8 PIECES.": GOTO 120
170 FORIF ILEN(A$)=18 TOTHEN 8190
180 PRINT "ARRAY MUST BE 8 PIECES.": SP=-1: RETURN
180 : P$=MID$(AR$,I,1)
190 K=0: IF P$="Q" THEN Q(Q)=I0: QB=Q+10: GOTO 250N=0:R=0
200 FOR I=0 TO 7
200 : IF P$="K" THEN K(K)=I: K=K+1: GOTO 250
210 : IF P$="B" THEN BK(BI)=0:Q(I)=0: B(I)=B+10: GOTO 250N(I)=0:R(I)=0
220 NEXT I
220 : IF P$="N" THEN N(N)=I: N=N+1: GOTO 250
230 :FOR IF P$="R" THEN R(R)=I: R=R+1: GOTOTO 2508
240 : P$=MID$(A$,I,1)
240 : PRINT "ILLEGAL PIECE '"P$"'.": GOTO 120
250 : IF P$="Q" THEN Q(Q)=I: Q=Q+1: GOTO 310
250 NEXT I
260 : IF P$="K<>1" THEN PRINT "THERE MUST BE EXACTLY ONE KING."K(K)=I: K=K+1: GOTO 120310
270 : IF Q<>1P$="B" THEN PRINTB(B)=I: "THERE MUST BE EXACTLY ONE QUEEN."B=B+1: GOTO 120310
280 : IF B<>2P$="N" THEN PRINTN(N)=I: "THEREN=N+1: MUST BE EXACTLY TWO BISHOPS.": GOTO 120310
290 : IF N<>2P$="R" THEN PRINTR(R)=I: "THERER=R+1: MUST BE EXACTLY TWO KNIGHTS.": GOTO 120310
300 IF R<>2 THEN: PRINT "THEREILLEGAL MUSTPIECE BE EXACTLY TWO ROOKS'"P$"'.": SP=-1: GOTO 120RETURN
310 NEXT I
310 IF (K(0) > R(0)) AND (K(0) < R(1)) THEN 330
320 IF K<>1 THEN PRINT "KINGTHERE MUST BE BETWEENEXACTLY THEONE ROOKSKING.": GOTO 120 SP=-1: RETURN
330 IF Q<>1 THEN PRINT "THERE MUST BE EXACTLY ONE QUEEN.": SP=-1: RETURN
330 IF (B(0) AND 1) <> (B(1) AND 1) THEN 350
340 IF B<>2 THEN PRINT "BISHOPSTHERE MUST BE ONEXACTLY OPPOSITETWO COLORSBISHOPS.": GOTOSP=-1: 120RETURN
350 IF N<>2 THEN PRINT "THERE MUST BE EXACTLY TWO KNIGHTS.": SP=-1: RETURN
350 FOR I=0 TO 1
360 IF R<>2 THEN PRINT "THERE MUST BE EXACTLY TWO ROOKS.": SP=-1: RETURN
360 : N=N(I)
370 : IF N(IK(0) >Q R(I0)) THENAND N=N-(K(0) < R(1)) THEN 390
380 PRINT "KING MUST BE BETWEEN THE ROOKS.": SP=-1: RETURN
380 : FOR J=0 TO 1
390 : IF N(IB(0) AND 1) <> (B(J1) THENAND N=N-1) THEN 410
400 PRINT "BISHOPS MUST BE ON OPPOSITE COLORS.": SP=-1: RETURN
400 : NEXT J
410 :FOR N(I)=N0 TO 1
420 NEXT: N=N(I)
430 N0=1: N1IF N(I)>Q(I) THEN N=2N-1
440 : FOR NJ=0 TO 91
450 : IF N0=N(0I)>B(J) ANDTHEN N1N=N(-1) THEN 490
460 : N1=N1+1NEXT J
470 : N(I)=N
470 : IF N1>5 THEN N0=N0+1: N1=N0+1
480 NEXT NI
490 QN0=Q(0)-1: N1=2
500 FOR IN=0 TO 19
510 : IF QN0=N(0)> AND N1=N(I1) THEN Q=Q-1550
520 NEXT: IN1=N1+1
530 FOR: I=0IF TON1>5 THEN N0=N0+1: N1=N0+1
540 :NEXT B=B(I)-1N
550 : IF B AND 1 THEN LQ=INTQ(B/20)-1
560 :FOR IF (B AND 1)I=0 THENTO D=B/21
570 : IF Q(0)>B(I) THEN Q=Q-1
570 NEXT I
580 NEXT I
580 PRINT "SPID ="; 96*N+16*Q+4*D+L</lang>
590 FOR I=0 TO 1
600 : B=B(I)-1
610 : IF B AND 1 THEN L=INT(B/2)
620 : IF (B AND 1)=0 THEN D=B/2
630 NEXT I
640 SP = 96*N+16*Q+4*D+L
650 RETURN</syntaxhighlight>
 
{{Out}}
<pre>READY.
RUN
QNRBBNKR: 105
ENTER START ARRAY AS SEEN BY WHITE.
RNBQKBNR: 518
 
RQNBBKRN: 601
STARTING ARRAY:RNBQKBNR
RNQBBKRN: 617
SPID = 518
 
READY.
RUN
ENTER START ARRAY AS SEEN BY WHITE.
 
STARTING ARRAY:QNRBBNKR
SPID = 105
 
READY.</pre>
 
==={{header|FreeBASIC}}===
<langsyntaxhighlight lang="freebasic">Sub SP_ID(PosicPiezas As String)
Dim As String pieza
Dim As Integer pQ(), pK(), pB(), pN(), pR(), i, j
Line 133 ⟶ 193:
 
Dim As Integer N0 = 1, N1 = 2
For iN = 0 To 9
If N0 = pN(0) And N1 = pN(1) Then Exit For
N1 += 1
If N1 > 5 Then N0 += 1: N1 = N0 + 1
Next iN
 
Q = pQ(0) - 1
For i = 0 To 1
If pQ(0) > pNpB(i) Then Q -= 1
Next i
For i = 0 To 1
Line 155 ⟶ 215:
Print
SP_ID("RNBQKBNR")
Print
Sleep</lang>
SP_ID("RQNBBKRN")
Print
SP_ID("RNQBBKRN")
Sleep</syntaxhighlight>
{{out}}
<pre>QNRBBNKR has SP_ID of 105
 
RNBQKBNR has SP_ID of 518</pre>
 
RQNBBKRN has SP_ID of 601
 
RNQBBKRN has SP_ID of 617
</pre>
 
==={{header|QBasic}}===
{{works with|QBasic|1.1}}
{{trans|Commodore BASIC}}
<langsyntaxhighlight lang="qbasic">CLSCls
PRINTPrint "ENTEREnter STARTstart ARRAYarray ASas SEENseen BYby WHITEwhite."
120 PRINTPrint
PRINTPrint "STARTINGStarting ARRAY:array";
INPUTInput ARAr$
Print
PRINT
IFIf LENLen(ARAr$) = 0 THENThen ENDEnd
IFIf LENLen(ARAr$) = 8 THENThen 170
PRINTPrint "ARRAYArray MUSTmust BEbe 8 PIECESpieces.": GOTOGoTo 120
 
170 FORFor I = 1 TOTo 8
P$ = MIDMid$(ARAr$, I, 1)
IF If P$ = "Q" THENOr P$ = "q" Then Q(Q) = I: Q = Q + 1: GOTOGoTo 250
IF If P$ = "K" THENOr P$ = "k" Then K(K) = I: K = K + 1: GOTOGoTo 250
IF If P$ = "B" THENOr P$ = "b" Then B(B) = I: B = B + 1: GOTOGoTo 250
IF If P$ = "N" THENOr P$ = "n" Then N(N) = I: N = N + 1: GOTOGoTo 250
IF If P$ = "R" THENOr P$ = "r" Then R(R) = I: R = R + 1: GOTOGoTo 250
PRINT Print "ILLEGALIllegal PIECEpiece '"; P$; "'.": GOTOGoTo 120
250 NEXTNext I
 
IFIf K <> 1 THENThen PRINTPrint "THEREThere MUSTmust BEbe EXACTLYexactly ONEone KINGKing.": GOTOGoTo 120
IFIf Q <> 1 THENThen PRINTPrint "THEREThere MUSTmust BEbe EXACTLYexactly ONEone QUEENQueen.": GOTOGoTo 120
IFIf B <> 2 THENThen PRINTPrint "THEREThere MUSTmust BEbe EXACTLYexactly TWOtwo BISHOPSBishops.": GOTOGoTo 120
IFIf N <> 2 THENThen PRINTPrint "THEREThere MUSTmust BEbe EXACTLYexactly TWOtwo KNIGHTSKnights.": GOTOGoTo 120
IFIf R <> 2 THENThen PRINTPrint "THEREThere MUSTmust BEbe EXACTLYexactly TWOtwo ROOKSRooks.": GOTOGoTo 120
IFIf (K(0) > R(0)) ANDAnd (K(0) < R(1)) THENThen 330
PRINTPrint "KINGKing MUSTmust BEbe BETWEENbetween THEthe ROOKSRooks.": GOTOGoTo 120
 
330 IFIf (B(0) ANDAnd 1) <> (B(1) ANDAnd 1) THENThen 350
PRINTPrint "BISHOPSBishops MUSTmust BEbe ONon OPPOSITEopposite COLORScolors.": GOTOGoTo 120
 
350 FORFor I = 0 TOTo 1
N = N(I)
IF If N(I) > Q(I) THENThen N = N - 1
FOR For J = 0 TOTo 1
IF If N(I) > B(J) THENThen N = N - 1
Next J
NEXT J
N(I) = N
NEXTNext I
N0 = 1: N1 = 2
 
FORFor N = 0 TOTo 9
IFIf N0 = N(0) ANDAnd N1 = N(1) THENThen 490
N1 = N1 + 1
IFIf N1 > 5 THENThen N0 = N0 + 1: N1 = N0 + 1
NEXTNext N
490 Q = Q(0) - 1
 
FORFor I = 0 TOTo 1
IFIf Q(0) > NB(I) THENThen Q = Q - 1
NEXTNext I
 
FORFor I = 0 TOTo 1
B = B(I) - 1
IFIf B ANDAnd 1 THENThen L = INTInt(B / 2)
IFIf (B ANDAnd 1) = 0 THENThen D = B / 2
NEXTNext I
PRINTPrint "SPIDSP-ID ="; 96 * N + 16 * Q + 4 * D + L
End
END</lang>
 
</syntaxhighlight>
{{out}}
<pre>Enter start array as seen by White.
 
Starting array? qnrbbnkr
 
SP-ID = 105
 
Starting array? RNBQKBNR
 
SP-ID = 518
 
Starting array? RQNBBKRN
 
SP-ID = 601
 
Starting array? RNQBBKRN
 
SP-ID = 617
</pre>
 
=={{header|C++}}==
<syntaxhighlight lang="c++">
#include <algorithm>
#include <cstdint>
#include <stdexcept>
#include <iostream>
#include <string>
#include <map>
#include <set>
#include <vector>
 
const std::set<std::pair<char, int32_t>> correct_pieces = { std::make_pair('R', 2), std::make_pair('N', 2),
std::make_pair('B', 2), std::make_pair('Q', 1), std::make_pair('K', 1) };
 
std::map<std::vector<int32_t>, int32_t> knights_table = {
{ std::vector<int32_t>{ 0, 1 }, 0 },
{ std::vector<int32_t>{ 0, 2 }, 1 },
{ std::vector<int32_t>{ 0, 3 }, 2 },
{ std::vector<int32_t>{ 0, 4 }, 3 },
{ std::vector<int32_t>{ 1, 2 }, 4 },
{ std::vector<int32_t>{ 1, 3 }, 5 },
{ std::vector<int32_t>{ 1, 4 }, 6 },
{ std::vector<int32_t>{ 2, 3 }, 7 },
{ std::vector<int32_t>{ 2, 4 }, 8 },
{ std::vector<int32_t>{ 3, 4 }, 9 } };
 
void validate(const std::string& position) {
if ( position.length() != 8 ) {
throw std::invalid_argument("Chess position has invalid length" + std::to_string(position.length()));
}
 
std::map<char, int32_t> position_map;
for ( const char& ch : position ) {
if ( position_map.find(ch) == position_map.end() ) {
position_map.emplace(ch, 1);
} else {
position_map[ch]++;
}
}
 
std::set<std::pair<char, int32_t>> pieces;
std::transform(position_map.begin(), position_map.end(), std::inserter(pieces, pieces.begin()),
[](const std::pair<char, int32_t>& entry) { return entry; });
 
if ( pieces != correct_pieces ) {
throw std::invalid_argument("Chess position contains incorrect pieces.");
}
 
const std::vector<uint64_t> bishops = { position.find_first_of('B'), position.find_last_of('B') };
if ( ( bishops[1] - bishops[0] ) % 2 == 0 ) {
throw std::invalid_argument("Bishops must be on different coloured squares.");
}
 
std::vector<uint64_t> rook_king =
{ position.find_first_of('R'), position.find_first_of('K'), position.find_last_of('R') };
if ( ! ( rook_king[0] < rook_king[1] && rook_king[1] < rook_king[2] ) ) {
throw std::invalid_argument("The king must be between the two rooks.");
}
}
 
int32_t calculate_SPID(std::string& position) {
 
const int32_t index_one = position.find_first_of('B');
const int32_t index_two = position.find_last_of('B');
const int32_t D = ( index_one % 2 == 0 ) ? index_one / 2 : index_two / 2;
const int32_t L = ( index_one % 2 == 0 ) ? index_two / 2 : index_one / 2;
 
position.erase(remove_if(position.begin(), position.end(),
[](const char& ch){ return ch == 'B'; }), position.end());
const uint64_t Q = position.find_first_of('Q');
 
position.erase(remove_if(position.begin(), position.end(),
[](const char& ch){ return ch == 'Q'; }), position.end());
const int32_t N =
knights_table[ { (int32_t) position.find_first_of('N'), (int32_t) position.find_last_of('N') } ];
 
return 96 * N + 16 * Q + 4 * D + L;
}
 
int main() {
std::vector<std::string> positions = { "QNRBBNKR", "RNBQKBNR", "RQNBBKRN", "RNQBBKRN" };
 
for ( std::string& position : positions ) {
validate(position);
std::cout << "Position " << position << " has Chess960 SP-ID = " << calculate_SPID(position) << std::endl;
}
}
</syntaxhighlight>
{{ out }}
<pre>
Position QNRBBNKR has Chess960 SP-ID = 105
Igual que la entrada de Commodore BASIC.
Position RNBQKBNR has Chess960 SP-ID = 518
Position RQNBBKRN has Chess960 SP-ID = 601
Position RNQBBKRN has Chess960 SP-ID = 617
</pre>
 
=={{header|Common Lisp}}==
<syntaxhighlight lang="lisp">; make sure string is a valid Chess960 starting array
(defun valid-array-p (start-array)
(and (string-equal (sort (copy-seq start-array) #'string-lessp) "BBKNNQRR") ; right pieces
(not (equal (mod (position #\B start-array) 2) ; bishops on opposite colors
(mod (position #\B start-array :from-end t) 2)))
(< (position #\R start-array) (position #\K start-array)) ; king between two rooks
(< (position #\K start-array) (position #\R start-array :from-end t))))
 
; find Start Position IDentifier for a Chess960 setup
(defun sp-id (start-array)
(if (not (valid-array-p start-array))
-1
(let* ((bishopless (remove #\B start-array))
(queenless (remove #\Q bishopless))
(n5n-pattern (substitute-if-not #\- (lambda (ch) (eql ch #\N)) queenless))
(n5n-table '("NN---" "N-N--" "N--N-" "N---N" "-NN--" "-N-N-" "-N--N" "--NN-" "--N-N" "---NN"))
(knights (position n5n-pattern n5n-table :test #'string-equal))
(queen (position #\Q bishopless))
(left-bishop (position #\B start-array))
(right-bishop (position #\B start-array :from-end t)))
 
; map each bishop to its color complex and position within those four squares
(destructuring-bind (dark-bishop light-bishop)
(mapcar (lambda (p) (floor p 2))
(cond ((zerop (mod left-bishop 2)) (list left-bishop right-bishop))
(t (list right-bishop left-bishop))))
 
(+ (* 96 knights) (* 16 queen) (* 4 dark-bishop) light-bishop)))))
 
(loop for ary in '("RNBQKBNR" "QNRBBNKR" "RQNBBKRN" "RNQBBKRN") doing
(format t "~a: ~a~%" ary (sp-id ary)))
</syntaxhighlight>
 
{{Out}}
<pre>RNBQKBNR: 518
QNRBBNKR: 105
RQNBBKRN: 601
RNQBBKRN: 617</pre>
 
=={{header|Factor}}==
{{works with|Factor|0.99 2021-06-02}}
<langsyntaxhighlight lang="factor">USING: assocs assocs.extras combinators formatting kernel
literals math math.combinatorics sequences sequences.extras sets
strings ;
 
IN: scratchpad
 
! ====== optional error-checking ======
Line 278 ⟶ 500:
: n ( str -- n ) "QB" without knightify table index ;
 
: q ( str -- q ) "NB" without CHAR: Q swap index ;
 
: d ( str -- d ) CHAR: B swap <evens> index ;
Line 293 ⟶ 515:
 
"QNRBBNKR" sp-id.
"RNBQKBNR" sp-id.</lang>
"RQNBBKRN" sp-id.
"RNQBBKRN" sp-id.</syntaxhighlight>
{{out}}
<pre>
QNRBBNKR / ♕♘♖♗♗♘♔♖: 105
RNBQKBNR / ♖♘♗♕♔♗♘♖: 518
RQNBBKRN / ♖♕♘♗♗♔♖♘: 601
RNQBBKRN / ♖♘♕♗♗♔♖♘: 617
</pre>
 
=={{header|Go}}==
{{trans|Wren}}
<langsyntaxhighlight lang="go">package main
 
import (
Line 376 ⟶ 602:
N := ntable[np]
 
piecesQ := strings.ReplaceAll(pieces, "NB", "")
Q := strings.Index(piecesQ, "Q")
 
Line 390 ⟶ 616:
 
func main() {
for _, pieces := range []string{"♕♘♖♗♗♘♔♖", "♖♘♗♕♔♗♘♖", "♖♕♘♗♗♔♖♘", "♖♘♕♗♗♔♖♘"} {
fmt.Printf("%s or %s has SP-ID of %d\n", pieces, g2l(pieces), spid(pieces))
}
}</langsyntaxhighlight>
 
{{out}}
Line 399 ⟶ 625:
♕♘♖♗♗♘♔♖ or QNRBBNKR has SP-ID of 105
♖♘♗♕♔♗♘♖ or RNBQKBNR has SP-ID of 518
♖♕♘♗♗♔♖♘ or RQNBBKRN has SP-ID of 601
♖♘♕♗♗♔♖♘ or RNQBBKRN has SP-ID of 617
</pre>
 
=={{header|J}}==
Implementation:<syntaxhighlight lang="j">REF=: {{
'N Q B0 B1'=. 0 6 4 4 #: y
s=. 'B' (0 1+2*B0,B1)} 8#' '
s=. 'Q' (Q{I.' '=s)} s
s=. 'N' ((N{(#~ 2=+/"1)#:i.-32){&I.' '=s)} s
'RKR' (I.' '=s)} s
}}"0 i.960
 
c960=: {{ r=. REF i. rplc&((u:9812+i.12);&>12$'KQRBNP') 7 u:deb y assert. r<#REF }}</syntaxhighlight>
 
Examples:
<syntaxhighlight lang="j"> c960'♕♘♖♗♗♘♔♖'
105
c960'♛♞♜♝♝♞♚♜'
105
c960'RNBQKBNR'
518
c960'RQNBBKRN'
601
c960'RNQBBKRN'
617</syntaxhighlight>
 
=={{header|Java}}==
 
<syntaxhighlight lang="java">
 
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.Predicate;
 
public final class Chess960SPID {
 
public static void main(String[] aArgs) {
String[] positions = { "QNRBBNKR", "RNBQKBNR", "RQNBBKRN", "RNQBBKRN" };
createKnightsTable();
createCorrectPieces();
for ( String position : positions ) {
validate(position);
System.out.println("Position " + position + " has Chess960 SP-ID = " + calculateSPID(position));
}
}
private static void validate(String aPosition) {
if ( aPosition.length() != 8 ) {
throw new AssertionError("Chess position has invalid length: " + aPosition.length() + ".");
}
Map<Character, Integer> pieces = new HashMap<Character, Integer>();
for ( char ch : aPosition.toCharArray() ) {
pieces.merge(ch, 1, (oldV, newV) -> oldV + 1);
}
if ( ! pieces.entrySet().equals(correctPieces) ) {
throw new AssertionError("Chess position contains incorrect pieces.");
}
List<Integer> bishops = List.of(aPosition.indexOf('B'), aPosition.lastIndexOf('B'));
if ( ( bishops.get(1) - bishops.get(0) ) % 2 == 0 ) {
throw new AssertionError("Bishops must be on different coloured squares.");
}
List<Integer> rookKing = List.of(aPosition.indexOf('R'), aPosition.indexOf('K'), aPosition.lastIndexOf('R'));
if ( ! ( rookKing.get(0) < rookKing.get(1) && rookKing.get(1) < rookKing.get(2) ) ) {
throw new AssertionError("The king must be between the two rooks.");
}
}
private static int calculateSPID(String aPosition) {
String noBishopsOrQueen = retainIf(aPosition, s -> s != 'B' && s != 'Q');
final int N = knightsTable.get(List.of(noBishopsOrQueen.indexOf('N'), noBishopsOrQueen.lastIndexOf('N')));
String noBishops = retainIf(aPosition, s -> s != 'B');
final int Q = noBishops.indexOf('Q');
 
final int indexOne = aPosition.indexOf('B');
final int indexTwo = aPosition.lastIndexOf('B');
final int D = ( indexOne % 2 == 0 ) ? indexOne / 2 : indexTwo / 2;
final int L = ( indexOne % 2 == 0 ) ? indexTwo / 2 : indexOne / 2;
return 96 * N + 16 * Q + 4 * D + L;
}
private static String retainIf(String aText, Predicate<Character> aPredicate) {
return aText.chars()
.mapToObj( i -> (char) i )
.filter(aPredicate)
.map(String::valueOf)
.reduce("", String::concat);
}
private static void createKnightsTable() {
knightsTable = new HashMap<List<Integer>, Integer>();
knightsTable.put(List.of(0, 1), 0);
knightsTable.put(List.of(0, 2), 1);
knightsTable.put(List.of(0, 3), 2);
knightsTable.put(List.of(0, 4), 3);
knightsTable.put(List.of(1, 2), 4);
knightsTable.put(List.of(1, 3), 5);
knightsTable.put(List.of(1, 4), 6);
knightsTable.put(List.of(2, 3), 7);
knightsTable.put(List.of(2, 4), 8);
knightsTable.put(List.of(3, 4), 9);
}
private static void createCorrectPieces() {
correctPieces = Set.of(
Map.entry('R', 2), Map.entry('N', 2), Map.entry('B', 2), Map.entry('Q', 1), Map.entry('K', 1) );
}
private static Map<List<Integer>, Integer> knightsTable;
private static Set<Map.Entry<Character, Integer>> correctPieces;
 
}
</syntaxhighlight>
{{ out }}
<pre>
Position QNRBBNKR has Chess960 SP-ID = 105
Position RNBQKBNR has Chess960 SP-ID = 518
Position RQNBBKRN has Chess960 SP-ID = 601
Position RNQBBKRN has Chess960 SP-ID = 617
</pre>
 
=={{header|Julia}}==
<langsyntaxhighlight lang="julia">const whitepieces = "♖♘♗♕♔♗♘♖♙"
const whitechars = "rnbqkp"
const blackpieces = "♜♞♝♛♚♝♞♜♟"
Line 429 ⟶ 784:
knightpos1, knightpos2 = findfirst(c -> c =='N', noQB), findlast(c -> c =='N', noQB)
N = findfirst(s -> s == 10 * knightpos1 + knightpos2, knighttable) - 1
Q = findfirst(c -> c == 'Q', replace(a, "NB" => "")) - 1
bishoppositions = [findfirst(c -> c =='B', a), findlast(c -> c =='B', a)]
if isodd(bishoppositions[2])
Line 439 ⟶ 794:
end
 
for position in ["♕♘♖♗♗♘♔♖", "♖♘♗♕♔♗♘♖", "♖♕♘♗♗♔♖♘", "♖♘♕♗♗♔♖♘"]
println(collect(position), " => ", chess960spid(position))
end
</langsyntaxhighlight>{{out}}
<pre>
['♕', '♘', '♖', '♗', '♗', '♘', '♔', '♖'] => 105
['♖', '♘', '♗', '♕', '♔', '♗', '♘', '♖'] => 518
['♖', '♕', '♘', '♗', '♗', '♔', '♖', '♘'] => 601
['♖', '♘', '♕', '♗', '♗', '♔', '♖', '♘'] => 617
</pre>
 
=={{header|Nim}}==
{{trans|Wren}}
<langsyntaxhighlight Nimlang="nim">import sequtils, strformat, strutils, sugar, tables, unicode
 
type Piece {.pure.} = enum Rook = "R", Knight = "N", Bishop = "B", Queen = "Q", King = "K"
Line 498 ⟶ 855:
let n = NTable[piecesN.positions(Knight)]
 
let piecesQ = pieces.filterIt(it != KnightBishop)
let q = piecesQ.find(Queen)
 
Line 508 ⟶ 865:
 
 
for glyphs in ["♕♘♖♗♗♘♔♖", "♖♘♗♕♔♗♘♖", "♖♕♘♗♗♔♖♘", "♖♘♕♗♗♔♖♘"]:
echo &"{glyphs} or {glyphs.toPieces().join()} has SP-ID of {glyphs.spid()}"</langsyntaxhighlight>
 
{{out}}
<pre>♕♘♖♗♗♘♔♖ or QNRBBNKR has SP-ID of 105
♖♘♗♕♔♗♘♖ or RNBQKBNR has SP-ID of 518</pre>
♖♕♘♗♗♔♖♘ or RQNBBKRN has SP-ID of 601
♖♘♕♗♗♔♖♘ or RNQBBKRN has SP-ID of 617</pre>
 
=={{header|Perl}}==
{{trans|Raku}}
<langsyntaxhighlight lang="perl">use strictv5.36;
use warnings;
use feature 'say';
use List::AllUtils 'indexes';
 
sub sp_id ($setup) {
8 == length $setup or return 'Illegal position: should have exactly eight pieces';
my $setup = shift // 'RNBQKBNR';
1 == @{[ $setup =~ /$_/g ]} or return "Illegal position: should have exactly one $_" for <K Q>;
 
82 == length@{[ $setup =~ /$_/g ]} or diereturn '"Illegal position: should have exactly eighttwo pieces$_\'s" for <B N R>;
1 == @{[ $setup =~ m/$_R .* K .* R/gx ]} or diereturn "'Illegal position: shouldKing havenot exactly one $_" for <Kbetween Q>rooks.';
index($setup,'B')%2 =!= @{[ rindex($setup =~ /$_/g ]} ,'B')%2 or diereturn "'Illegal position: shouldBishops havenot exactlyon twoopposite $_\colors.'s" for <B N R>;
$setup =~ m/R .* K .* R/x or die 'Illegal position: King not between rooks.';
index($setup,'B')%2 != rindex($setup,'B')%2 or die 'Illegal position: Bishops not on opposite colors.';
 
my @knights = indexes { 'N' eq $_ } split '', $setup =~ s/[QB]//gr;
my $knight = indexes { join('', @knights) eq $_ } <01 02 03 04 12 13 14 23 24 34>; # combinations(5,2)
my @bishops = indexes { 'B' eq $_ } split '', $setup;
my $dark = int ((grep { $_ % 2 == 0 } @bishops)[0]) / 2;
my $light = int ((grep { $_ % 2 == 1 } @bishops)[0]) / 2;
my $queen = index(($setup =~ s/B//gr), 'Q');
 
int 4*(4*(6*$knight + $queen)+$dark)+$light;
}
 
say "$_ " . sp_id($_) for <QNRBBNKR RNBQKBNR RQNBBKRN RNQBBKRN QNBRBNKR>;</langsyntaxhighlight>
{{out}}
<pre>QNRBBNKR 105
RNBQKBNR 518</pre>
RQNBBKRN 601
RNQBBKRN 617
QNBRBNKR Illegal position: Bishops not on opposite colors.</pre>
 
=={{header|Phix}}==
<!--<langsyntaxhighlight Phixlang="phix">(phixonline)-->
<span style="color: #008080;">with</span> <span style="color: #008080;">javascript_semantics</span>
<span style="color: #008080;">function</span> <span style="color: #000000;">spid</span><span style="color: #0000FF;">(</span><span style="color: #004080;">string</span> <span style="color: #000000;">s</span><span style="color: #0000FF;">)</span>
Line 556 ⟶ 914:
<span style="color: #004080;">integer</span> <span style="color: #0000FF;">{</span><span style="color: #000000;">n1</span><span style="color: #0000FF;">,</span><span style="color: #000000;">n2</span><span style="color: #0000FF;">}</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">find_all</span><span style="color: #0000FF;">(</span><span style="color: #008000;">'N'</span><span style="color: #0000FF;">,</span><span style="color: #7060A8;">filter</span><span style="color: #0000FF;">(</span><span style="color: #000000;">s</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"out"</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"QB"</span><span style="color: #0000FF;">)),</span>
<span style="color: #000000;">N</span> <span style="color: #0000FF;">=</span> <span style="color: #0000FF;">{-</span><span style="color: #000000;">2</span><span style="color: #0000FF;">,</span><span style="color: #000000;">1</span><span style="color: #0000FF;">,</span><span style="color: #000000;">3</span><span style="color: #0000FF;">,</span><span style="color: #000000;">4</span><span style="color: #0000FF;">}[</span><span style="color: #000000;">n1</span><span style="color: #0000FF;">]+</span><span style="color: #000000;">n2</span><span style="color: #0000FF;">,</span>
<span style="color: #000000;">Q</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">find</span><span style="color: #0000FF;">(</span><span style="color: #008000;">'Q'</span><span style="color: #0000FF;">,</span><span style="color: #7060A8;">filter</span><span style="color: #0000FF;">(</span><span style="color: #000000;">s</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"!="</span><span style="color: #0000FF;">,</span><span style="color: #008000;">'NB'</span><span style="color: #0000FF;">))-</span><span style="color: #000000;">1</span><span style="color: #0000FF;">,</span>
<span style="color: #000000;">D</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">filter</span><span style="color: #0000FF;">(</span><span style="color: #000000;">b</span><span style="color: #0000FF;">,</span><span style="color: #7060A8;">odd</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: #0000FF;">,</span> <span style="color: #000080;font-style:italic;">-- (nb not /2)</span>
<span style="color: #000000;">L</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">filter</span><span style="color: #0000FF;">(</span><span style="color: #000000;">b</span><span style="color: #0000FF;">,</span><span style="color: #7060A8;">even</span><span style="color: #0000FF;">)[</span><span style="color: #000000;">1</span><span style="color: #0000FF;">]/</span><span style="color: #000000;">2</span><span style="color: #0000FF;">-</span><span style="color: #000000;">1</span>
Line 568 ⟶ 926:
<span style="color: #000000;">test</span><span style="color: #0000FF;">(</span><span style="color: #008000;">"QNRBBNKR"</span><span style="color: #0000FF;">)</span>
<span style="color: #000000;">test</span><span style="color: #0000FF;">(</span><span style="color: #008000;">"RNBQKBNR"</span><span style="color: #0000FF;">)</span>
<span style="color: #000000;">test</span><span style="color: #0000FF;">(</span><span style="color: #008000;">"RQNBBKRN"</span><span style="color: #0000FF;">)</span>
<!--</lang>-->
<span style="color: #000000;">test</span><span style="color: #0000FF;">(</span><span style="color: #008000;">"RNQBBKRN"</span><span style="color: #0000FF;">)</span>
<!--</syntaxhighlight>-->
{{out}}
<pre>
QNRBBNKR : 105
RNBQKBNR : 518
RQNBBKRN : 601
</pre>
RNQBBKRN : 617</pre>
To support all those crazy unicode characters just change the start of spid() to:
<!--<langsyntaxhighlight Phixlang="phix">(phixonline)-->
<span style="color: #008080;">function</span> <span style="color: #000000;">spid</span><span style="color: #0000FF;">(</span><span style="color: #004080;">string</span> <span style="color: #000000;">u</span><span style="color: #0000FF;">)</span>
<span style="color: #004080;">sequence</span> <span style="color: #000000;">u32</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">utf8_to_utf32</span><span style="color: #0000FF;">(</span><span style="color: #000000;">u</span><span style="color: #0000FF;">),</span>
Line 585 ⟶ 946:
<span style="color: #000000;">test</span><span style="color: #0000FF;">(</span><span style="color: #008000;">"♕♘♖♗♗♘♔♖"</span><span style="color: #0000FF;">)</span>
<span style="color: #000000;">test</span><span style="color: #0000FF;">(</span><span style="color: #008000;">"♖♘♗♕♔♗♘♖"</span><span style="color: #0000FF;">)</span>
<span style="color: #000000;">test</span><span style="color: #0000FF;">(</span><span style="color: #008000;">"♜♛♞♝♝♚♜♞"</span><span style="color: #0000FF;">)</span>
<!--</lang>-->
<span style="color: #000000;">test</span><span style="color: #0000FF;">(</span><span style="color: #008000;">"♜♞♛♝♝♚♜♞"</span><span style="color: #0000FF;">)</span>
<!--</syntaxhighlight>-->
{{out}}
Note that output on a windows terminal is as expected far from pretty, this is from pwa/p2js
Line 591 ⟶ 954:
QNRBBNKR : 105
RNBQKBNR : 518
RQNBBKRN : 601
RNQBBKRN : 617
♕♘♖♗♗♘♔♖ : 105
♖♘♗♕♔♗♘♖ : 518
♜♛♞♝♝♚♜♞ : 601
♜♞♛♝♝♚♜♞ : 617
</pre>
 
Line 598 ⟶ 965:
{{works with|Python|3.10.5 2022-06-28}}
 
<syntaxhighlight lang="python"># optional, but task function depends on it as written
<lang python>
# optional, but task function depends on it as written
def validate_position(candidate: str):
assert (
Line 614 ⟶ 980:
), f"piece type '{piece_type}' has invalid count"
 
bishops_pos = [index for index, value in enumerate(candidate) if value == "B"]
value in enumerate(candidate) if value == "B"]
assert (
bishops_pos[0] % 2 != bishops_pos[1] % 2
Line 651 ⟶ 1,018:
 
# step 2
subset_step2 = [piece for piece in start_pos if piece != "NB"]
Q = subset_step2.index("Q")
 
Line 665 ⟶ 1,032:
 
return 4 * (4 * (6*N + Q) + D) + L
</lang>
 
if __name__ == '__main__':
for example in ["QNRBBNKR", "RNBQKBNR", "RQNBBKRN", "RNQBBKRN"]:
print(f'Position: {example}; Chess960 PID= {calc_position(example)}')</syntaxhighlight>
{{out}}
<pre>
'Position: RNBQKBNRQNRBBNKR; Chess960 PID= 518'105
'Position: QNRBBNKRRNBQKBNR; Chess960 PID= 105'518
Position: RQNBBKRN; Chess960 PID= 601
Position: RNQBBKRN; Chess960 PID= 617
</pre>
 
=={{header|Raku}}==
<syntaxhighlight lang="raku" line>sub c960-spid($array) {
<lang perl6>#!/usr/bin/env raku
# standardize on letters for easier processing
# derive a chess960 position's SP-ID
my $ascii = $array.trans('♜♞♝♛♚♖♘♗♕♔' => 'RNBQK');
unit sub MAIN($array = "♖♘♗♕♔♗♘♖");
 
# error-checking
# standardize on letters for easier processing
my %Names = <Q Queen K King R Rook N Knight B Bishop>;
my $ascii = $array.trans("♜♞♝♛♚♖♘♗♕♔" => "RNBQKRNBQK");
return 'Illegal position: should have exactly eight pieces' unless 8 == $ascii.chars;
return 'Illegal position: Bishops not on opposite colors.' unless 1 == sum $ascii.indices('B').map(* % 2);
return 'Illegal position: King not between rooks.' unless $ascii ~~ /'R' .* 'K' .* 'R'/;
for <K 1 Q 1 B 2 N 2 R 2> -> $piece, $count {
return "Illegal position: should have exactly $count %Names{$piece}\(s\)\n" unless $count == $ascii.indices($piece)
}
 
# Work backwards through the placement rules.
# (optional error-checking)
# King and rooks are forced during placement, so ignore them.
if $ascii.chars != 8 {
die "Illegal position: should have exactly eight pieces\n";
}
 
# 1. Figure out which knight combination was used:
for «K Q» -> $one {
my @knights = $ascii.subst(/<[QB]>/, '', :g).indices('N');
if +$ascii.indices($one) != 1 {
my $knight = combinations(5,2).kv.grep( -> $i, @c { @c eq @knights } ).flat.first;
die "Illegal position: should have exactly one $one\n";
}
}
 
# 2. Then which queen position:
for «B N R» -> $two {
if + my $queen = $ascii.indicessubst($two)'B', !='', 2:g).index('Q'); {
die "Illegal position: should have exactly two $two\'s\n";
}
}
 
# 3. Finally the two bishops:
if $ascii !~~ /'R' .* 'K' .* 'R'/ {
my @bishops = $ascii.indices('B');
die "Illegal position: King not between rooks.";
my ($dark,$light) = (@bishops.first %% 2 ?? @bishops !! @bishops.reverse) Xdiv 2;
}
 
$ascii.trans('RNBQK' => '♖♘♗♕♔') ~ ' ' ~ 4 × (4 × (6 × $knight + $queen) + $dark) + $light;
if [+]($ascii.indices('B').map(* % 2)) != 1 {
die "Illegal position: Bishops not on opposite colors.";
}
# (end optional error-checking)
 
say .&c960-spid for <♖♘♗♕♔♗♘♖ ♛♞♜♝♝♞♚♜ RQNBBKRN RNQBBKRN QNBRBNKR>;</syntaxhighlight>
# Work backwards through the placement rules.
{{out}}
# King and rooks are forced during placement, so ignore them.
<pre>♖♘♗♕♔♗♘♖ 518
 
♕♘♖♗♗♘♔♖ 105
# 1. Figure out which knight combination was used:
♖♕♘♗♗♔♖♘ 601
my @knights = $ascii
♖♘♕♗♗♔♖♘ 617
.subst(/<[QB]>/,'',:g)
Illegal position: Bishops not on opposite colors.</pre>
.indices('N');
 
my $knight = combinations(5,2).kv.grep(
-> $i,@c { @c eq @knights }
)[0][0];
 
# 2. Then which queen position:
my $queen = $ascii
.subst(/<[B]>/,'',:g)
.index('Q');
 
# 3. Finally the two bishops:
my @bishops = $ascii.indices('B');
my $dark = @bishops.grep({ $_ %% 2 })[0] div 2;
my $light = @bishops.grep({ not $_ %% 2 })[0] div 2;
 
my $sp-id = 4*(4*(6*$knight + $queen)+$dark)+$light;
 
# standardize output
my $display = $ascii.trans("RNBQK" => "♖♘♗♕♔");
 
say "$display: $sp-id";</lang>
 
{{Out}}
<pre>$ c960spid QNRBBNKR
♕♘♖♗♗♘♔♖: 105
$ c960spid
♖♘♗♕♔♗♘♖: 518</pre>
 
=={{header|Ruby}}==
<syntaxhighlight lang="ruby">CHESS_PIECES = %w<♖♘♗♕♔ ♜♞♝♛♚>
<lang ruby>def chess960_to_spid(pos)
def chess960_to_spid(pos)
start_str = pos.tr("♖♘♗♕♔", "RNBQK")
start_str = pos.tr(CHESS_PIECES.join, "RNBQKRNBQK")
#1 knights score
s = start_str.delete("QB")
n = [0,1,2,3,4].combination(2).to_a.index( [s.index("N"), s.rindex("N")] )
#2 queen score
q = start_str.delete("NB").index("Q")
#3 bishops
bs = start_str.index("B"), start_str.rindex("B")
Line 758 ⟶ 1,100:
end
 
%w<QNRBBNKR RNBQKBNR RQNBBKRN RNQBBKRN>.each_with_index do |array, i|
positions = ["QNRBBNKR", "♖♘♗♕♔♗♘♖"]
pieces = array.tr("RNBQK", CHESS_PIECES[i%2])
positions.each{|pos| puts "#{pos}: #{chess960_to_spid(pos)}" }
puts "#{pieces} (#{array}): #{chess960_to_spid array}"
</lang>
end
</syntaxhighlight>
{{out}}
<pre>♕♘♖♗♗♘♔♖ (QNRBBNKR): 105
♜♞♝♛♚♝♞♜ (RNBQKBNR): 518
♖♘♗♕♔♗♘♖: 518
♖♕♘♗♗♔♖♘ (RQNBBKRN): 601
♜♞♛♝♝♚♜♞ (RNQBBKRN): 617
</pre>
 
=={{header|Wren}}==
{{libheader|Wren-traititerate}}
<langsyntaxhighlight ecmascriptlang="wren">import "./traititerate" for Indexed
 
var glyphs = "♜♞♝♛♚♖♘♗♕♔".toList
Line 805 ⟶ 1,152:
var N = ntable[np]
 
var piecesQ = pieces.replace("NB", "")
var Q = piecesQ.indexOf("Q")
 
Line 818 ⟶ 1,165:
}
 
for (pieces in ["♕♘♖♗♗♘♔♖", "♖♘♗♕♔♗♘♖", "♜♛♞♝♝♚♜♞", "♜♞♛♝♝♚♜♞"]) {
System.print("%(pieces) or %(g2l.call(pieces)) has SP-ID of %(spid.call(pieces))")
}</langsyntaxhighlight>
 
{{out}}
Line 826 ⟶ 1,173:
♕♘♖♗♗♘♔♖ or QNRBBNKR has SP-ID of 105
♖♘♗♕♔♗♘♖ or RNBQKBNR has SP-ID of 518
♜♛♞♝♝♚♜♞ or RQNBBKRN has SP-ID of 601
♜♞♛♝♝♚♜♞ or RNQBBKRN has SP-ID of 617
</pre>
9,485

edits