Skip to main content

Ralsina.Me — Roberto Alsina's website

Posts about chess

A database of all chess positions in your own computer

Note: This is based on an old (2018) Quo­ra an­swer.

If I asked you "How much hard drive space would be re­quired for a Data­base rep­re­sent­ing ev­ery pos­si­ble po­si­tion in chess?" what would you an­swer?

Well, most an­swers go like this:

"Claude Shan­non es­ti­mat­ed there are 1043 po­si­tions in chess, which can be stored in ~32 bytes each, so it's some­thing like 1044 bytes, and the ob­serv­able uni­verse has on­ly 1080 atoms so it's some­thing pret­ty large!"

To that I say phooey Shan­non! It takes around 700 bytes if you want the fan­cy ver­sion.

Let's start by defin­ing what the re­quest­ed ar­ti­fact is.

"A data­base rep­re­sent­ing ev­ery pos­si­ble po­si­tion in chess"

So, what is such a database's be­hav­iour? I pro­pose:

  1. It can, if giv­en some sort of in­dex val­ue, re­turn a spe­cif­ic po­si­tion (which will al­ways be the same)
  2. It can it­er­ate over all chess po­si­tion­s.
  3. It should be able to re­turn all po­si­tions match­ing a spe­cif­ic cri­te­ria like 'has a white pawn in D5'

My pro­posed im­ple­men­ta­tion will do the first 2 which I con­sid­er ac­tu­al re­quire­ments, the 3rd be­ing just a "nice to have". While it's not im­pos­si­ble to ad­d, even my will­ing­ness to do stupid things has a lim­it.

So, lim­it­ing my­self to the first two re­quire­ments, if I ful­fill those then it's done, right? Be­cause if the de­liv­er­able is cor­rec­t, the rest is im­ple­men­ta­tion de­tail­s?

Let's im­ple­ment it!

Step 1: notation

Wel­come to Forsyth-Ed­wards no­ta­tion (FEN for short­). It's a won­der­ful thing that pro­vides all the nec­es­sary in­for­ma­tion to restart a game.

I will use a hacked sub­set since all I want is the po­si­tion (not things like "has white cas­tled?" or "who is mov­ing nex­t?") but it's a triv­ial ex­er­cise to ex­pand this data­base to do just that.

So, how does this data­base de­scribe a po­si­tion? A po­si­tion is a list of pieces and their po­si­tions in a 8x8 board.

In the orig­i­nal FEN white pawn is iden­ti­fied as P and black pawn as p. I con­sid­er that an in­sult to the IETF who has adopt­ed UT­F-8 in RFC2777 (and al­so slight­ly racist), there­fore I will use the prop­er glyph­s: ♙ and ♟.

In fac­t, I will use ♙♘♗♖♕♔♟♞♝♜♛♚.

As for po­si­tion­s, since my data­base is small enough that da­ta com­pres­sion is point­less, let's just use a 64-char­ac­ter fixed-­size string where each po­si­tion is ei­ther a piece of a space mean­ing the square is emp­ty.

So, a position is a string of length 64 where these are the only valid values: "♙♘♗♖♕♔♟♞♝♜♛♚ "

Step 2: implementation

It's ob­vi­ous that each po­si­tion is equiv­a­lent to a 64-dig­it num­ber in base 13, which means there are 196 053 476 430 761 073 330 659 760 423 566 015 424 403 280 004 115 787 589 590 963 842 248 960 pos­si­ble po­si­tion­s.

That 64-dig­it num­ber is the in­dex key for each po­si­tion (it's an op­ti­mal key, it can't be made any small­er!)

So here's the data­base core code, of­fered with no com­ments since it's bare­ly 12 lines of code do­ing noth­ing weird:

def get_position(index):
    def digit_to_char(digit):
        return "♙♘♗♖♕♔♟♞♝♜♛♚ "[digit]

    def str_base(number, base=13):
        (d, m) = divmod(number, base)
        if d:
            result = str_base(d, base) + digit_to_char(m)
        else:
            result = digit_to_char(m)
        return result

    position = str_base(index).rjust(64)
    return position

For con­ve­nience, here is a pret­ty print­er for your board­s:

def print_board(position):
    print(" ABCDEFGH ")
    for i in range(8):
        print("%d%s%d" % (i, position[8 * (i) : 8 * (i + 1)], i))
    print(" ABCDEFGH ")

Here you can see it in ac­tion (sad­ly asci­ine­ma butch­ers the align­men­t, it works prop­er­ly in re­al life):

And here is the full source code:

def get_position(index):
    def digit_to_char(digit):
        return "♙♘♗♖♕♔♟♞♝♜♛♚ "[digit]

    def str_base(number, base=13):
        (d, m) = divmod(number, base)
        if d:
            result = str_base(d, base) + digit_to_char(m)
        else:
            result = digit_to_char(m)
        return result

    position = str_base(index).rjust(64)
    return position


def print_board(position):
    print(" ABCDEFGH ")
    for i in range(8):
        print("%d%s%d" % (i, position[8 * (i) : 8 * (i + 1)], i))
    print(" ABCDEFGH ")


if __name__ == "__main__":
    import sys

    print_board(get_position(int(sys.argv[1])))

Please no­tice that this data­base al­so cov­ers all al­ter­na­tive chess vari­ants where ex­tra pieces are giv­en or re­moved as hand­i­cap.

Step 3

There is no step 3.


Contents © 2000-2021 Roberto Alsina