Find Chess960 starting position identifier: Difference between revisions
Find Chess960 starting position identifier (view source)
Revision as of 10:37, 5 December 2023
, 6 months ago→{{header|Wren}}: Minor tidy
(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.
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.
<
110 READ A$: IF A$="" THEN END
120
130 GOSUB 170
140 PRINT SP
150 GOTO 110
160 DATA QNRBBNKR, RNBQKBNR, RQNBBKRN, RNQBBKRN,
170
180 PRINT "ARRAY MUST BE 8 PIECES.": SP=-1: RETURN
190 K=0:
200 FOR I=0 TO 7
210 :
220 NEXT I
230
240 : P$=MID$(A$,I,1)
250 : IF P$="Q" THEN Q(Q)=I: Q=Q+1: GOTO 310
260 : IF P$="K
270 : IF
280 : IF
290 : IF
300
310 NEXT I
320 IF K<>1 THEN PRINT "
330 IF Q<>1 THEN PRINT "THERE MUST BE EXACTLY ONE QUEEN.": SP=-1: RETURN
340 IF B<>2 THEN PRINT "
350 IF N<>2 THEN PRINT "THERE MUST BE EXACTLY TWO KNIGHTS.": SP=-1: RETURN
360 IF R<>2 THEN PRINT "THERE MUST BE EXACTLY TWO ROOKS.": SP=-1: RETURN
370
380 PRINT "KING MUST BE BETWEEN THE ROOKS.": SP=-1: RETURN
390
400 PRINT "BISHOPS MUST BE ON OPPOSITE COLORS.": SP=-1: RETURN
410
420
430
440 : FOR
450 : IF
460 :
470 : N(I)=N
480 NEXT
490
500 FOR
510 : IF
520
530
540
550
560
570 : IF Q(0)>B(I) THEN Q=Q-1
580 NEXT I
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
RNBQKBNR: 518
RQNBBKRN: 601
RNQBBKRN: 617
READY.</pre>
==={{header|FreeBASIC}}===
<
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
If N0 = pN(0) And N1 = pN(1) Then Exit For
N1 += 1
If N1 > 5 Then N0 += 1: N1 = N0 + 1
Next
Q = pQ(0) - 1
For i = 0 To 1
If pQ(0) >
Next i
For i = 0 To 1
Line 155 ⟶ 215:
Print
SP_ID("RNBQKBNR")
Print
SP_ID("RQNBBKRN")
Print
SP_ID("RNQBBKRN")
Sleep</syntaxhighlight>
{{out}}
<pre>QNRBBNKR has SP_ID of 105
RNBQKBNR has SP_ID of 518
RQNBBKRN has SP_ID of 601
RNQBBKRN has SP_ID of 617
</pre>
==={{header|QBasic}}===
{{works with|QBasic|1.1}}
{{trans|Commodore BASIC}}
<
120
Print
170
P$ =
250
330
350
N = N(I)
Next J
N(I) = N
N0 = 1: N1 = 2
N1 = N1 + 1
490 Q = Q(0) - 1
B = B(I) - 1
End
</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
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}}
<
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 ) "
: d ( str -- d ) CHAR: B swap <evens> index ;
Line 293 ⟶ 515:
"QNRBBNKR" sp-id.
"RNBQKBNR" sp-id.
"RQNBBKRN" sp-id.
"RNQBBKRN" sp-id.</syntaxhighlight>
{{out}}
<pre>
QNRBBNKR / ♕♘♖♗♗♘♔♖: 105
RNBQKBNR / ♖♘♗♕♔♗♘♖: 518
RQNBBKRN / ♖♕♘♗♗♔♖♘: 601
RNQBBKRN / ♖♘♕♗♗♔♖♘: 617
</pre>
=={{header|Go}}==
{{trans|Wren}}
<
import (
Line 376 ⟶ 602:
N := ntable[np]
piecesQ := strings.ReplaceAll(pieces, "
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))
}
}</
{{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}}==
<
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, "
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
</
<pre>
['♕', '♘', '♖', '♗', '♗', '♘', '♔', '♖'] => 105
['♖', '♘', '♗', '♕', '♔', '♗', '♘', '♖'] => 518
['♖', '♕', '♘', '♗', '♗', '♔', '♖', '♘'] => 601
['♖', '♘', '♕', '♗', '♗', '♔', '♖', '♘'] => 617
</pre>
=={{header|Nim}}==
{{trans|Wren}}
<
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 !=
let q = piecesQ.find(Queen)
Line 508 ⟶ 865:
for glyphs in ["♕♘♖♗♗♘♔♖", "♖♘♗♕♔♗♘♖", "♖♕♘♗♗♔♖♘", "♖♘♕♗♗♔♖♘"]:
echo &"{glyphs} or {glyphs.toPieces().join()} has SP-ID of {glyphs.spid()}"</
{{out}}
<pre>♕♘♖♗♗♘♔♖ 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|Perl}}==
{{trans|Raku}}
<
use List::AllUtils 'indexes';
sub sp_id ($setup) {
8 == length $setup or return 'Illegal position: should have exactly eight pieces';
1 == @{[ $setup =~ /$_/g ]} or return "Illegal position: should have exactly one $_" for <K Q>;
index($setup,'B')%2
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>;</
{{out}}
<pre>QNRBBNKR 105
RNBQKBNR 518
RQNBBKRN 601
RNQBBKRN 617
QNBRBNKR Illegal position: Bishops not on opposite colors.</pre>
=={{header|Phix}}==
<!--<
<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;">'
<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>
<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
RNQBBKRN : 617</pre>
To support all those crazy unicode characters just change the start of spid() to:
<!--<
<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>
<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
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"]
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 != "
Q = subset_step2.index("Q")
Line 665 ⟶ 1,032:
return 4 * (4 * (6*N + Q) + D) + L
if __name__ == '__main__':
for example in ["QNRBBNKR", "RNBQKBNR", "RQNBBKRN", "RNQBBKRN"]:
print(f'Position: {example}; Chess960 PID= {calc_position(example)}')</syntaxhighlight>
{{out}}
<pre>
Position: RQNBBKRN; Chess960 PID= 601
Position: RNQBBKRN; Chess960 PID= 617
</pre>
=={{header|Raku}}==
<syntaxhighlight lang="raku" line>sub c960-spid($array) {
# standardize on letters for easier processing
my $ascii = $array.trans('♜♞♝♛♚♖♘♗♕♔' => 'RNBQK');
# error-checking
my %Names = <Q Queen K King R Rook N Knight B Bishop>;
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.
# King and rooks are forced during placement, so ignore them.
# 1. Figure out which knight combination was used:
my @knights = $ascii.subst(/<[QB]>/, '', :g).indices('N');
my $knight = combinations(5,2).kv.grep( -> $i, @c { @c eq @knights } ).flat.first;
# 2. Then which queen position:
# 3. Finally the two bishops:
my @bishops = $ascii.indices('B');
my ($dark,$light) = (@bishops.first %% 2 ?? @bishops !! @bishops.reverse) Xdiv 2;
$ascii.trans('RNBQK' => '♖♘♗♕♔') ~ ' ' ~ 4 × (4 × (6 × $knight + $queen) + $dark) + $light;
}
say .&c960-spid for <♖♘♗♕♔♗♘♖ ♛♞♜♝♝♞♚♜ RQNBBKRN RNQBBKRN QNBRBNKR>;</syntaxhighlight>
{{out}}
<pre>♖♘♗♕♔♗♘♖ 518
♕♘♖♗♗♘♔♖ 105
♖♕♘♗♗♔♖♘ 601
♖♘♕♗♗♔♖♘ 617
Illegal position: Bishops not on opposite colors.</pre>
=={{header|Ruby}}==
<syntaxhighlight lang="ruby">CHESS_PIECES = %w<♖♘♗♕♔ ♜♞♝♛♚>
def chess960_to_spid(pos)
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("
#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|
pieces = array.tr("RNBQK", CHESS_PIECES[i%2])
puts "#{pieces} (#{array}): #{chess960_to_spid array}"
end
</syntaxhighlight>
{{out}}
<pre>♕♘♖♗♗♘♔♖ (QNRBBNKR): 105
♜♞♝♛♚♝♞♜ (RNBQKBNR): 518
♖♕♘♗♗♔♖♘ (RQNBBKRN): 601
♜♞♛♝♝♚♜♞ (RNQBBKRN): 617
</pre>
=={{header|Wren}}==
{{libheader|Wren-
<
var glyphs = "♜♞♝♛♚♖♘♗♕♔".toList
Line 805 ⟶ 1,152:
var N = ntable[np]
var piecesQ = pieces.replace("
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))")
}</
{{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>
|