Task: Find Chess960 starting position identifier




Go: package main

import (
    "fmt"
    "log"
    "strings"
)

var glyphs = []rune("♜♞♝♛♚♖♘♗♕♔")
var names = map[rune]string{'R': "rook", 'N': "knight", 'B': "bishop", 'Q': "queen", 'K': "king"}
var g2lMap = map[rune]string{
    '♜': "R", '♞': "N", '♝': "B", '♛': "Q", '♚': "K",
    '♖': "R", '♘': "N", '♗': "B", '♕': "Q", '♔': "K",
}

var ntable = map[string]int{"01": 0, "02": 1, "03": 2, "04": 3, "12": 4, "13": 5, "14": 6, "23": 7, "24": 8, "34": 9}

func g2l(pieces string) string {
    lets := ""
    for _, p := range pieces {
        lets += g2lMap[p]
    }
    return lets
}

func spid(pieces string) int {
    pieces = g2l(pieces) 

    
    if len(pieces) != 8 {
        log.Fatal("There must be exactly 8 pieces.")
    }
    for _, one := range "KQ" {
        count := 0
        for _, p := range pieces {
            if p == one {
                count++
            }
        }
        if count != 1 {
            log.Fatalf("There must be one %s.", names[one])
        }
    }
    for _, two := range "RNB" {
        count := 0
        for _, p := range pieces {
            if p == two {
                count++
            }
        }
        if count != 2 {
            log.Fatalf("There must be two %s.", names[two])
        }
    }
    r1 := strings.Index(pieces, "R")
    r2 := strings.Index(pieces[r1+1:], "R") + r1 + 1
    k := strings.Index(pieces, "K")
    if k < r1 || k > r2 {
        log.Fatal("The king must be between the rooks.")
    }
    b1 := strings.Index(pieces, "B")
    b2 := strings.Index(pieces[b1+1:], "B") + b1 + 1
    if (b2-b1)%2 == 0 {
        log.Fatal("The bishops must be on opposite color squares.")
    }

    
    piecesN := strings.ReplaceAll(pieces, "Q", "")
    piecesN = strings.ReplaceAll(piecesN, "B", "")
    n1 := strings.Index(piecesN, "N")
    n2 := strings.Index(piecesN[n1+1:], "N") + n1 + 1
    np := fmt.Sprintf("%d%d", n1, n2)
    N := ntable[np]

    piecesQ := strings.ReplaceAll(pieces, "B", "")
    Q := strings.Index(piecesQ, "Q")

    D := strings.Index("0246", fmt.Sprintf("%d", b1))
    L := strings.Index("1357", fmt.Sprintf("%d", b2))
    if D == -1 {
        D = strings.Index("0246", fmt.Sprintf("%d", b2))
        L = strings.Index("1357", fmt.Sprintf("%d", b1))
    }

    return 96*N + 16*Q + 4*D + L
}

func main() {
    for _, pieces := range []string{"♕♘♖♗♗♘♔♖", "♖♘♗♕♔♗♘♖", "♖♕♘♗♗♔♖♘", "♖♘♕♗♗♔♖♘"} {
        fmt.Printf("%s or %s has SP-ID of %d\n", pieces, g2l(pieces), spid(pieces))
    }
}




Python: 
def validate_position(candidate: str):
    assert (
        len(candidate) == 8
    ), f"candidate position has invalide len = {len(candidate)}"

    valid_pieces = {"R": 2, "N": 2, "B": 2, "Q": 1, "K": 1}
    assert {
        piece for piece in candidate
    } == valid_pieces.keys(), f"candidate position contains invalid pieces"
    for piece_type in valid_pieces.keys():
        assert (
            candidate.count(piece_type) == valid_pieces[piece_type]
        ), 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
    ), f"candidate position has both bishops in the same color"

    assert [piece for piece in candidate if piece in "RK"] == [
        "R",
        "K",
        "R",
    ], "candidate position has K outside of RR"


def calc_position(start_pos: str):
    try:
        validate_position(start_pos)
    except AssertionError:
        raise AssertionError
    
    subset_step1 = [piece for piece in start_pos if piece not in "QB"]
    nights_positions = [
        index for index, value in enumerate(subset_step1) if value == "N"
    ]
    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,
    }
    N = nights_table.get(tuple(nights_positions))

    
    subset_step2 = [piece for piece in start_pos if piece != "B"]
    Q = subset_step2.index("Q")

    
    dark_squares = [
        piece for index, piece in enumerate(start_pos) if index in range(0, 9, 2)
    ]
    light_squares = [
        piece for index, piece in enumerate(start_pos) if index in range(1, 9, 2)
    ]
    D = dark_squares.index("B")
    L = light_squares.index("B")

    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)}')