API details.
%load_ext autoreload
%autoreload 2
import chess
board = chess.Board()
board?
Type:        Board
String form:
r n b q k b n r
p p p p p p p p
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
P P P P P P P P
R N B Q K B N R
File:        ~/projects/miniconda3/envs/cheviz/lib/python3.6/site-packages/chess/__init__.py
Docstring:  
A :class:`~chess.BaseBoard`, additional information representing
a chess position, and a :data:`move stack <chess.Board.move_stack>`.

Provides :data:`move generation <chess.Board.legal_moves>`, validation,
:func:`parsing <chess.Board.parse_san()>`, attack generation,
:func:`game end detection <chess.Board.is_game_over()>`,
and the capability to :func:`make <chess.Board.push()>` and
:func:`unmake <chess.Board.pop()>` moves.

The board is initialized to the standard chess starting position,
unless otherwise specified in the optional *fen* argument.
If *fen* is ``None``, an empty board is created.

Optionally supports *chess960*. In Chess960, castling moves are encoded
by a king move to the corresponding rook square.
Use :func:`chess.Board.from_chess960_pos()` to create a board with one
of the Chess960 starting positions.

It's safe to set :data:`~Board.turn`, :data:`~Board.castling_rights`,
:data:`~Board.ep_square`, :data:`~Board.halfmove_clock` and
:data:`~Board.fullmove_number` directly.

.. warning::
    It is possible to set up and work with invalid positions. In this
    case :class:`~chess.Board` implements a kind of "pseudo-chess"
    (useful to gracefully handle errors or to implement chess variants).
    Use :func:`~chess.Board.is_valid()` to detect invalid positions.
board.legal_moves
<LegalMoveGenerator at 0x7facb683e908 (Nh3, Nf3, Nc3, Na3, h3, g3, f3, e3, d3, c3, b3, a3, h4, g4, f4, e4, d4, c4, b4, a4)>

board starts square index @ a1 = 0 up to h8 = 63. The API uses a bool to denote color, further proving that chess people have a poor sense of (software) aesthetics and should instead pay others to write their software :) I use python-chess because of its rich API, although I wish the concerns were better separated.

print(board.attackers(True, 1))
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
1 . . . . . . .
print(board.attackers(False, 46))
. . . . . . . .
. . . . . 1 . 1
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .

Each square index has a 'constant' we can use instead.

print(board.attackers(False, chess.G6))
. . . . . . . .
. . . . . 1 . 1
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .

OK, let's pretend colors aren't encoded by booleans, we have 'constants' for them, too.

defenders_f6 = board.attackers(chess.BLACK, chess.F6)
type(defenders_f6)
chess.SquareSet
len(defenders_f6)
3

SquareSet is an actual set, with all the set operations you'd expect. Python-chess is also Bitboard-aware.

defenders_h6 = board.attackers(chess.BLACK, chess.H6)
heavy_duty_defenders = defenders_h6.intersection(defenders_f6)
print(heavy_duty_defenders)
. . . . . . 1 .
. . . . . . 1 .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
import numpy as np

We use a 1-dim array because we will mostly use board indices anyway. For the initial position, we get away with reversing the mapped indices like so: data[63-i] == square[i].

wa = np.zeros((len(chess.SQUARES),))
for i in range(64):
    wa[63-i] = len(board.attackers(chess.WHITE, i))
wa
array([0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,
       0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,
       0., 0., 0., 0., 0., 0., 2., 2., 3., 2., 2., 3., 2., 2., 1., 1., 1.,
       4., 4., 1., 1., 1., 0., 1., 1., 1., 1., 1., 1., 0.])
ba = np.zeros((len(chess.SQUARES),))
for i in range(64):
    ba[63-i] = len(board.attackers(chess.BLACK, i))
ba
array([0., 1., 1., 1., 1., 1., 1., 0., 1., 1., 1., 4., 4., 1., 1., 1., 2.,
       2., 3., 2., 2., 3., 2., 2., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,
       0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,
       0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.])

For plotting, we transform the array into a 2-dim matrix. We will have to properly inverse the index to make sure that white pieces are located in the last rows of the matrix.

import matplotlib.pyplot as plt
data = wa.reshape(8, 8)
plt.matshow(data)
<matplotlib.image.AxesImage at 0x7fac94c317f0>

The board indexing chosen by Python chess can feel cumbersome. Essentially, the rows are permutated by 7-rows shift. To map board indices to array (matrix) indices we need to compute a row offset and adjust the index like so:

 0  1  2 ...  7 ==> row_offset + i % dim ==>  56 57 58 ... 63
 8  9 10 ... 15                               48 49 50 ... 55
16 17 18 ... 23                               40 41 42 ... 47
24 25 26 ... 31                               32 33 34 ... 39
32 33 34 ... 39                               24 25 26 ... 31
40 41 42 ... 47                               16 17 18 ... 23
48 49 50 ... 55                                8  9 10 ... 15
56 57 58 ... 63                                0  1  2 ...  7

row_offset simulates the row shift like so: (dim - 1 - (i // dim)) * dim, where i // dim tells us the current row of the index.

dimension[source]

dimension()

reshaped[source]

reshaped(data:ndarray)

show[source]

show(data:ndarray, ax=None)

makeThreatenedSquares[source]

makeThreatenedSquares(**kwargs)

fen = 'rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1'
ts = makeThreatenedSquares(fen=fen)

figure, ax = plt.subplots()
show(ts(chess.WHITE), ax)

Let's have two filter functions to split the threatened squares into the following:

  1. Pieces of us defended by other pieces from our side: defence(...).
  2. Empty squares or squares with enemy pieces that are currently under attack by our pieces: attack(...).

defence[source]

defence(board:Board, side:bool, square:int)

attack[source]

attack(board:Board, side:bool, square:int)

show(ts(chess.BLACK, defence))
show(ts(chess.BLACK, attack))