4
\$\begingroup\$

I want to transform a UCI-move into bitboard.

for example a2a3 -> 32768, 8388608

I need to assign [7,6,...,0] to [a,b,...,h] so that for each letter i have the assigned number(n) to calculate 2^n

Which I can then left shift by the value in uci[1] or uci[3] *8 depending on start- or endfield.

This is my approach and it doesn't look very nice and redundant.

def ucitoBit(uci):
    if uci[0] == 'a':
        mask1 = 2 ** 7
    if uci[0] == 'b':
        mask1 = 2 ** 6
    if uci[0] == 'c':
        mask1 = 2 ** 5
    if uci[0] == 'd':
        mask1 = 2 ** 4
    if uci[0] == 'e':
        mask1 = 2 ** 3
    if uci[0] == 'f':
        mask1 = 2 ** 2
    if uci[0] == 'g':
        mask1 = 2 ** 1
    if uci[0] == 'h':
        mask1 = 2 ** 0
    mask1 = mask1 << 8 * (int(uci[1]) - 1)

    if uci[2] == 'a':
        mask2 = 2 ** 7
    if uci[2] == 'b':
        mask2 = 2 ** 6
    if uci[2] == 'c':
        mask2 = 2 ** 5
    if uci[2] == 'd':
        mask2 = 2 ** 4
    if uci[2] == 'e':
        mask2 = 2 ** 3
    if uci[2] == 'f':
        mask2 = 2 ** 2
    if uci[2] == 'g':
        mask2 = 2 ** 1
    if uci[2] == 'h':
        mask2 = 2 ** 0
    mask2 = mask2 << 8 * (int(uci[3]) - 1)
    bitstring = [np.uint64(mask1), np.uint64(mask2)]
    return bitstring
\$\endgroup\$

1 Answer 1

4
\$\begingroup\$

Option 1:

The most basic thing I can suggest is that if you have a mapping from one thing to another, and if that mapping is fixed, you should be using a mapping data type.

The mapping data type in python is the dict, which can be written in-line like so:

uci_bit = {
    'a': 2**7, 'b': 2**6, 'c': 2**5, 'd': 2**4,
    'e': 2**3, 'f': 2**2, 'g': 2**1, 'h': 2**0,
}

Alternatively, the dict() function returns a dictionary, and takes keyword arguments:

uci_bit = dict(a=2**7, b=2**6, c=2**5, d=2**4, 
               e=2**3, f=2**2, g=2**1, h=2**0)

Same result either way. You could then access the dict using the standard square-bracket notation:

ch = uci[0]
mask1 = uci_bit[ch]

Or as a single expression:

mask1 = uci_bit[uci[0]]

Option 2:

However, there's another approach that has some potential. Looking things up in a dictionary has a cost. It's technically \$O(n)\$ but maybe there's a constant or two in there. So ...

Because the two components of the uci location are fixed-width ('a' and '2') you might be able to look them up in a string using the str.index method and get better performance.

Note: I wrote "might be". The way to know for sure is to write both bits of code and run a timing test on the alternatives. Look up the timeit module in the standard distribution.

Something like:

UCI_CHARS = 'hgfedcba'

mask1 = 2 ** UCI_CHARS.index(uci[0])
mask2 = 2 ** UCI_CHARS.index(uci[2])

You might also want to check the result of using dict versus str.index versus the int() function on the single-digit part of uci decoding.

Option 3:

It's not clear how many of these operations you are performing. If you are doing a single game, and there might be two dozen uci to bit operations, then it probably doesn't matter. But if you're doing a lot of games at the same time, it might behoove you to generate a dictionary containing all the UCI letter/number combinations, and do the lookup as a single operation. Something like:

for ch in 'abcdefgh':
    for digit in '12345678':
        uci = ch + digit
        bit = # whatever computation
        uci_bit[uci] = bit

This would let you decode 'a2' -> bits in a single step at the cost of the up-front loop.

Other considerations

I asked the Duck about bitboard, and was immediately presented with the fact that there is no single standard representation. Instead, there are different representations for different purposes and different CPU architectures.

A result of this is that you should definitely be doing this inside a class or function, and there should definitely be a "name" given to the kind of bitboard you want to compute, so that readers will understand which of the several options you are choosing. Rank-major versus File-major and big- versus little- endian representations are all valid, so make sure to include this information either in the class or function name, or in the commentary.

def uci_to_bitboard_lsf(uci: str) -> int:
    """ Convert UCI notation to bitboard(LSF, little-endian) """

    ...

Also, I suspect that using * may be a bug, and you should be using + instead. (It really should be a bitwise or, the | operator, but + will work.)

\$\endgroup\$
1
  • \$\begingroup\$ There are only 64 combinations. Unless you are resource constrained, use option 3. \$\endgroup\$
    – RootTwo
    Commented Aug 8, 2020 at 0:31

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.