The accepted answer severely overcounts the actual action space because of the assumption that any piece can move a maximum of 7 squares in any direction from any square on the board. The calculation 8x8x(8x7+8+9) = 4672 is even much more than the most naive estimate of 64x63 = 4032.
The actual action space to specify any possible legal move is 1924. The difference is not negligible as this number is less than half of 4672. So, if using a dense final layer, this would save about 60% of the work for that layer.
Here is the Python script using chess
I wrote to confirm it:
import chess
# Each chess piece moves like either a queen or a knight from one square to another square.
# All possible moves except pawn promotions can be specified by the "from" square and the "to" square
# (even castling, ex. e1->g1, or e1->c1).
action_space = 0
b = chess.BaseBoard.empty()
for square in range(64):
# Place queen and see where it attacks
b.set_piece_at(square, chess.Piece.from_symbol('Q'))
q_moves = b.attacks(square)
# Place knight and see where it attacks
b.set_piece_at(square, chess.Piece.from_symbol('N'))
n_moves = b.attacks(square)
# Logical or to combine bitmaps (ex. 1100 | 0101 = 1101)
all_moves = q_moves | n_moves
# Convert bitmap to list of bools, so the sum
# is exactly the # possible moves from this square
action_space += sum(all_moves.tolist())
b.remove_piece_at(square)
# Count underpromotions manually:
# 8 forward promotions, 7 right-capture promotions, 7 left-capture promotions
# which can all promote to 4 pieces (but 1 is already counted above) for 2 colors
action_space += (8+7+7) * (4-1) * 2
print('actual action space:', action_space)
print('naive action space:', 64 * 63)
print('accepted answer\'s action space:', 8*8*(8*7 + 8 + 9))
# actual action space: 1924
# naive action space: 4032
# accepted answer's action space: 4672
Note: This assumes that the same output neuron will be used to represent for example the moves Ra7a8 and a8=Q, as well as the moves Re1g1 and O-O (by white). If this is not desirable, the action space that counts queen promotions and castling moves as distinct actions has size 1972. You can see this by changing (4-1)
to 4
in the final step that accounts for underpromotions, then adding 4 for the 2 castling directions by both colors.