#+TITLE: Bitboard Notes
#+AUTHOR: Zelphir Kaltstahl
#+STARTUP: content
#+LANGUAGE: en
#+EXCLUDE_TAGS: noexport
#+KEYWORDS: chess, bitboard, bitmap, notes, implementation, programming
#+OPTIONS: ^:nil
#+OPTIONS: timestamp:nil
#+OPTIONS: toc:2

* Definitions

- RACT :: Data structure with random access in constant time
- LSB :: Least significant bit
- MSB :: Most significant bit

* General

** Precomputing bitmaps

A theme throughout Hyatt's document is to precompute bitmaps for later use, in order to decrease computation time needed while the chess program is running. The precomputed bitmaps are often stored in a data structure with O(1) random access behavior, to enable an efficient lookup.

Precomputed bitmaps can be stored on disk to be read every time the chess program starts or calculated at the very beginning of the chess program, so that the computation of such bitmaps does not negatively affect the performance chess program later.

** Calculations with bitmaps

Calculations with bitmaps should make as much use as possible of bitwise logical operations, instead of iterating through the bits of a bitmap / an integer. Some bitwise logical operations:

- and
- or
- not
- xor
- nand

** Mapping coordinates to bits in bitmaps or integers

Generally speaking, this mapping can be done arbitrarily, as long as it is consistend.

Hyatt chooses a mapping, which is quite "chess centric". He defined the square A1 to be the MSB in the bitmap or integer and chooses to enumerate squares row by row, so that the squares A1-H1 are the 8 MSBs in the bitmap or integer.

It would also make sense to have the convention of the lowest coordinates (A1) being the LSB.

The question is how to represent this convention in the code.

TODO: Describe how one could represent the coordinates->bitposition mapping in the code.

TODO: Think about the following idea. Is it a valid one?: One can imagine some data structure with O(1) random access complexity, which for every square stores the bitmap containing a 1 bit in the position, which is internally used to do all calculations. The index to this data structure would be the another bitmap, which shall be mapped.

** Stored bitmaps

There is a number of bitmaps one might want to store, to improve performance, by avoiding computation of those bitmaps on-the-fly:

- bitmaps for piece types
  - pawns
  - knights
  - bishops
  - rooks
  - queens
  - kings
- bitmaps for square occupation
  - occupied squares
  - white pieces
  - black pieces
- bitmaps for movement of sliding pieces
  - plus1
  - plus7
  - plus8
  - plus9
  - minus1
  - minus7
  - minus8
  - minus9
  - rank-attacks
  - file-attacks
  - diagonal-a1-h8-attacks
  - diagonal-h1-a8-attacks
- bitmaps for non-sliding piece moves
  - knight-attacks
  - pawn-attacks
  - pawn-moves
  - king-moves
- bitmaps for single moves
  - set-mask

Of some of those bitmaps or RACTs containing bitmaps, one probably wants to store multiples, for each rotation one.

* Not rotated bitmaps
** How to move a piece?

A piece can be moved in any bitmap, rotated or not, using the same approach:

occupied-squares XOR (from OR to)

Where:
- occupied-squares: bitmap which contains 1 bits for all positions occupied by a piece
- from: bitmap which contains a 1 bit at the position, from where the piece moves
- to: bitmap which contains a 1 bit at the position, to where the piece moves

Hyatt names both ~from~ and ~to~ "set_mask", because they are actually from an array/vector of 64 bitmaps, each being a bitmap, which only has one 1 bit set and each having a different 1 bit set. For example:

- set_mask[0] -> bitmap with a 1 bit at the position of the square A1
- set_mask[31] -> bitmap with a 1 bit at the position of the square H4
- set_mask[32] -> bitmap with a 1 bit at the position of the square A5
- set_mask[63] -> bitmap with a 1 bit at the position of the square H8

The ~set_mask~ array/vector is precalculated in order to make using any of the positions only a lookup in the array. It could be done by calculating 2^postion, but that might not work for rotated bitmaps.

** How to take a piece of the other player?

???

** How to calculate truncated rays of sliding pieces (bishop, rook, queen)?

This is useful for example for calculating squares a bishop, rook or queen could move to. However, the approach when using rotated bitmaps is different.

In a not rotated bitmap there is always the same number of positions to shift to get from one square on a ray to the next square. For example:

- Bishop on A1:
  - Lets assume, that ...
    - (1) A1 is position 0 in the integer and ...
    - (2) we enumerate squares row by row, from left to right from the perspective of the player with the white pieces.
  - The next square on a ray of movement of the bishop will be B2.
  - This would be at position 9.
  - The next square would be C3.
  - This would be at position 18.
  - The difference in position for the bits of this ray of movement is always 9.

Based on this, the idea is to store/precalculate rays in 8 directions for all squares, so that the rays do not need to be calculated when the chess programm is running.

This would consume the following memory:

- 64 squares
- 8 rays per square
- 64 bit per bitmap in chess
- 64 * 64 * 8 bit = 32768 bit = 4096 byte = 4 kilobyte

Not very much these days.

Hyatt calls the arrays/vectors storing the rays by their 1 bit square position distances:

- plus1
- plus7
- plus8
- plus9
- minus1
- minus7
- minus8
- minus9

Each of those carrying the rays in one direction for all 64 squares in chess.

To calculate valid moves of a sliding piece also considering occupied squares, one can do the following algorithm:

1. get the position ~pos~ of the moving piece
2. get the bitmap for the ray of the direction and position ~ray[pos]~, where ~ray~ is one of the arrays/vectors containing the rays for one direction
3. use bitwise and operation with the ~occupied-squares~ and ~ray[pos]~, to get ~ray-blockers~, a bitmap of all blocked squares of the ray
4. get the first blocked square ~first-blocked~ from ~ray-blockers~, by looking for the first bit or last bit (depending on the direction of the ray) in the integer that is ~ray-blockers~.
5. get the ray (~truncator-ray~) in the same direction as the ray from the piece, by looking up ~ray[first-blocked]~.
6. use bitwise xor operation of ~truncator-ray~ and ~ray[pos]~ to get ~truncated-ray~.
7. use bitwise and operation of ~first-blocked~ and ~occupied-same-color-as-piece~ to get a bitmap ~first-blocked-own-piece?~, which will be not equal to zero, if the color of the blocking piece at ~first-blocked~ is the same as the color of the moving piece and zero if the piece at ~first-blocked~ has a different color than the moving piece.
8. use bitwise xor of ~first-blocked-own-piece?~ and ~truncated-ray~, to get the final bitmap of possible moves of the sliding piece along the ray.

Here is an ASCII art example:

The bitmap with the piece, a bishop in this case, at ~pos~:

#+BEGIN_SRC
0 0 0 0 0
0 0 0 0 0
0 0 0 0 0
0 0 0 B 0
0 0 0 0 0
#+END_SRC

The not yet truncated ray ~plus7[pos]~:

#+BEGIN_SRC
1 0 0 0 0
0 1 0 0 0
0 0 1 0 0
0 0 0 B 0
0 0 0 0 0
#+END_SRC

The ~occupied-squares~ bitmap:

#+BEGIN_SRC
1 0 0 1 0
0 1 1 1 1
0 0 0 0 0
0 1 0 B 0
1 0 1 0 0
#+END_SRC

The bitwise and operation result of ~occupied-squares~ and ~plus7[pos]~:

#+BEGIN_SRC
1 0 0 1 0         1 0 0 0 0        1 0 0 0 0
0 1 1 1 1         0 1 0 0 0        0 1 0 0 0
0 0 0 0 0   AND   0 0 1 0 0   ->   0 0 0 0 0
0 1 0 B 0         0 0 0 B 0        0 0 0 B 0
1 0 1 0 0         0 0 0 0 0        0 0 0 0 0
#+END_SRC

The ~first-blocked~ bitmap:

#+BEGIN_SRC
0 0 0 0 0
0 1 0 0 0
0 0 0 0 0
0 0 0 B 0
0 0 0 0 0
#+END_SRC

The ~plus7[first-blocked]~ ray bitmap, which is ~truncator-ray~:

#+BEGIN_SRC
1 0 0 0 0
0 F 0 0 0
0 0 0 0 0
0 0 0 B 0
0 0 0 0 0
#+END_SRC

The bitwise xor operation result of ~truncator-ray~ and ~plus7[pos]~, which is ~truncated-ray~:

#+BEGIN_SRC
1 0 0 0 0         1 0 0 0 0        0 0 0 0 0
0 F 0 0 0         0 1 0 0 0        0 1 0 0 0
0 0 0 0 0   XOR   0 0 1 0 0  ->    0 0 1 0 0
0 0 0 B 0         0 0 0 B 0        0 0 0 B 0
0 0 0 0 0         0 0 0 0 0        0 0 0 0 0
#+END_SRC

The ~occupied-same-color-as-piece~ bitmap:

#+BEGIN_SRC
0 0 0 0 0
0 1 1 0 0
0 0 0 0 0
0 1 0 B 0
1 0 1 0 0
#+END_SRC

The bitwise and operation result of ~occupied-same-color-as-piece~ and ~first-blocked~, which is ~first-blocked-own-piece?~:

#+BEGIN_SRC
0 0 0 0 0         0 0 0 0 0        0 0 0 0 0
0 1 1 0 0         0 1 0 0 0        0 1 0 0 0
0 0 0 0 0   AND   0 0 0 0 0   ->   0 0 0 0 0
0 1 0 B 0         0 0 0 B 0        0 0 0 B 0
1 0 1 0 0         0 0 0 0 0        0 0 0 0 0
#+END_SRC

So the piece blocking is of the same color and thus cannot be taken, which means the square is also not available as destination square for the moving bishop.

The bitwise xor operation result of ~truncated-ray~ and ~first-blocked-own-piece?~, which is ~valid-moves-along-ray~:

#+BEGIN_SRC
0 0 0 0 0         0 0 0 0 0        0 0 0 0 0
0 1 0 0 0         0 1 0 0 0        0 0 0 0 0
0 0 1 0 0   XOR   0 0 0 0 0   ->   0 0 1 0 0
0 0 0 B 0         0 0 0 B 0        0 0 0 B 0
0 0 0 0 0         0 0 0 0 0        0 0 0 0 0
#+END_SRC

The same algorithm can be used for movement along ranks or files.

*** Algorithm

Hyatt writes the algorithm for calculating all valid moves for a bishop as follows:

#+BEGIN_QUOTE
#+BEGIN_SRC c
diag_attacks=plus7[F5];
blockers=diag_attacks & occupied_squares;
blocking_square=FirstOne(blockers);
bishop_attacks=diag_attacks^plus7[blocking_square];

diag_attacks=plus9[F5];
blockers=diag_attacks & occupied_squares;
blocking_square=FirstOne(blockers);
bishop_attacks|=diag_attacks^plus9[blocking_square];

diag_attacks=minus7[F5];
blockers=diag_attacks & occupied_squares;
blocking_square=LastOne(blockers);
bishop_attacks|=diag_attacks^minus7[blocking_square];

diag_attacks=minus9[F5];
blockers=diag_attacks & occupied_squares;
blocking_square=LastOne(blockers);
bishop_attacks|=diag_attacks^minus9[blocking_square];
#+END_SRC
#+END_QUOTE

This does not take into consideration, that a square may be blocked by a same or opposite colored piece for simplicity. Lets write this down in a more readable way, to get a better understanding:

1. Initialize the ~bishop-attacks~ as a bitmap with all bits set to 0 (false).
2. For each ray or direction of movement do the following:
   1. calculate the ray or diagonal attacks from the bishop square
      1. look it up in the RACT of the diagonal with the square as index
   2. calculate the blocked squares on the ray
      1. bitwise logical AND of ~diagonal-attacks~ and ~occupied squares~
   3. calculate the first blocked square ~blocking-square~ on the diagonal
   4. calculate the ~truncated-ray~
      1. calculate ~truncator-ray~
         1. lookup in the RACT of the diagonal with the ~blocking-square~ as index
      2. bitwise logical XOR of ~diagonal-attacks~ and ~truncator-ray~
   5. update the ~bishop-attacks~
      1. bitwise logical OR of ~bishop-attacks~ and ~truncated-ray~

** How to rotate bitmaps?

Do not actually rotate bitmaps. Instead of rotating bitmaps on-the-fly, one can keep 4 of bitmaps for each kind of bitmap, one for each rotation:

1. rotation by 0°
2. rotation by 90°
3. rotation by 45°
4. rotation by -45°

This way using rotated bitmaps becomes less computationally expensive.

The difficulty is then in the way of accessing these rotated bitmaps and updating the 4 variations of different kinds of bitmaps.

** What do rotated bitmaps look like?
*** Rotation of 0°

#+BEGIN_SRC
A8 B8 C8 D8 E8 F8 G8 H8
A7 B7 C7 D7 E7 F7 G7 H7
A6 B6 C6 D6 E6 F6 G6 H6
A5 B5 C5 D5 E5 F5 G5 H5
A4 B4 C4 D4 E4 F4 G4 H4
A3 B3 C3 D3 E3 F3 G3 H3
A2 B2 C2 D2 E2 F2 G2 H2
A1 B1 C1 D1 E1 F1 G1 H1
#+END_SRC

*** Rotation of 90°

#+BEGIN_SRC
H8 H7 H6 H5 H4 H3 H2 H1
G8 G7 G6 G5 G4 G3 G2 G1
F8 F7 F6 F5 F4 F3 F2 F1
E8 E7 E6 E5 E4 E3 E2 E1
D8 D7 D6 D5 D4 D3 D2 D1
C8 C7 C6 C5 C4 C3 C2 C1
B8 B7 B6 B5 B4 B3 B2 B1
A8 A7 A6 A5 A4 A3 A2 A1
#+END_SRC

*** Rotation of 45°

#+BEGIN_SRC
               A8
             A7  B8
           A6  B7  C8
         A5  B6  C7  D8
       A4  B5  C6  D7  E8
     A3  B4  C5  D6  E7  F8
   A2  B3  C4  D5  E6  F7  G8
 A1  B2  C3  D4  E5  F6  G7  H8
   B1  C2  D3  E4  F5  G6  H7
     C1  D2  E3  F4  G5  H6
       D1  E2  F3  G4  H5
         E1  F2  G3  H4
           F1  G2  H3
             G1  H2
               H1
#+END_SRC

The positions in the bitmap or integer could be mapped to the following squares:

- 0: H1
- 1: G1
- 2: H2
- ...
- 61: A7
- 62: B8
- 63: A8

Or in reverse.

*** Rotation of -45°

 #+BEGIN_SRC
              H8
            G8  H7
          F8  G7  H6
        E8  F7  G6  H5
      D8  E7  F6  G5  H4
    C8  D7  E6  F5  G4  H3
  B8  C7  D6  E5  F4  G3  H2
A8  B7  C6  D5  E4  F3  G2  H1
  A7  B6  C5  D4  E3  F2  G1
    A6  B5  C4  D3  E2  F1
      A5  B4  C3  D2  E1
        A4  B3  C2  D1
          A3  B2  C1
            A2  B1
              A1
 #+END_SRC

The positions in the bitmap or integer could be mapped to the following squares:

- 0: A1
- 1: A2
- 2: B1
- ...
- 61: G8
- 62: H7
- 63: H8

Or in reverse.

* Rotated Bitmaps
** How to calculate sliding moves along a rank?

A rank is represented by 8 bits in the bitmaps for chess.

A rank has 8 squares, which can all either be occupied or not occupied. There are 2^8 configurations. For each of these configurations, the valid moves of a piece sliding along a rank can be calculated. Those calculated valid moves bitmaps of 8 bits length are stored in a RACT.

To know which index to the RACT is needed to get the correct valid moves bitmap out of it, one can use a trick:

1. Bitshift the bitmap of ~occupied-squares~ so that the rank of interest is at the 8 LSBs of the bitmap.
2. Do logical operation AND of the bitshifted bitmap and 255 (which is 56 bits 0 and 8 bits 1). This will ensure, that everything except the rank at the 8 LSBs is 0 in the resulting bitmap ~rank-only~. This means, that the result will be an integer between 0 and 255.
3. Use the ~rank-only~ bitmap as an index to the RACT storing the valid moves of a rank-sliding piece. The valid moves bitmaps must be put into the RACT in such a way, that the index into the RACT always maps to the correct valid moves bitmap.

This way of looking up the valid moves is possible, becaues the bits of a rank are all stored next to each other in the bitmap representing the ~occupied-squares~.

If a piece of the same color is occupying the target square, the piece wanting to move there cannot. However, if it is a piece of the other color blocking the square, it can be taken. This means, that is does make a difference, what color the piece on that square is. To distinguish between same color occupation and opposite color occupation blocking a square, one can bitwise logical XOR the valid moves bitmap with the bitmap for same color pieces occupation of squares. This way the squares at the end of the rays of valid moves will either become a 0 or a 1, depending on whether a piece of the same color or a piece of the opposite color is blocking the square.

*** The problem with diagonals
Calculating the valid moves of a sliding move piece along a rank can be done as explained above. The valid moved along files can be calculate the same way, in the 90° rotated bitmap. However, with diagonals there is a problem:

Diagonals are of inequal lengths. The difference of length between neighboring dialgonals is always 1. The different lengths mean, that we cannot simply bitshift by multiples of 8 until the diagonal we want is at the LSBs.
** Representation of a 45° counter clockwise rotated bitmap
Lets look at the rotated bitmap again:

#+BEGIN_SRC
              H8
            G8  H7
          F8  G7  H6
        E8  F7  G6  H5
      D8  E7  F6  G5  H4
    C8  D7  E6  F5  G4  H3
  B8  C7  D6  E5  F4  G3  H2
A8  B7  C6  D5  E4  F3  G2  H1
  A7  B6  C5  D4  E3  F2  G1
    A6  B5  C4  D3  E2  F1
      A5  B4  C3  D2  E1
        A4  B3  C2  D1
          A3  B2  C1
            A2  B1
              A1
#+END_SRC

- There are 15 diagonals.
- Neighboring diagonals are of different length.
- The difference of length is always 1 between neighboring diagonals.

If we store rotated bitmaps in a 64 bit integer like the non-rotated bitmaps, it could look as follows:

#+BEGIN_SRC
counter clockwise 45°       clockwise 45°

H8|G8-H7|F8-G7-H6|E8-F7-    A8|A7-B8|A6-B7-C8|A5-B6
G6-H5|D8-E7-F6-G5-H4|C8-    C7-D8|A4-B5-C6-D7-E8|A3
D7-E6-F5-G4-H3|B8-C7-D6-    B4-C5-D6-E7-F8|A2-B3-C4
E5-F4-G3-H2|A8-B7-C6-D5-    D5-E6-F7-G8|A1-B2-C3-D4
E4-F3-G2-H1|A7-B6-C5-D4-    E5-F6-G7-H8|B1-C2-D3-E4
E3-F2-G1|A6-B5-C4-D3-E2-    F5-G6-H7|C1-D2-E3-F4-G5
F1|A5-B4-C3-D2-E1|A4-B3-    H6|D1-E2-F3-G4-H5|E1-F2
C2-D1|A3-B2-C1|A2-B1|A1     G3-H4|F1-G2-H3|G1-H2|H1
#+END_SRC

(Where ~|~ marks the end of one diagonal and the start of the next diagonal and ~-~ marks squares being on the same diagonal.)

This would map the highest coordinates to the MSB and the lowest coordinates to the LSB on little endian systems in the counter clockwise 45° rotated case .

** How to get valid moves from a rotated bitmap?

#+BEGIN_SRC
counter clockwise 45°       clockwise 45°

H8|G8-H7|F8-G7-H6|E8-F7-    A8|A7-B8|A6-B7-C8|A5-B6
G6-H5|D8-E7-F6-G5-H4|C8-    C7-D8|A4-B5-C6-D7-E8|A3
D7-E6-F5-G4-H3|B8-C7-D6-    B4-C5-D6-E7-F8|A2-B3-C4
E5-F4-G3-H2|A8-B7-C6-D5-    D5-E6-F7-G8|A1-B2-C3-D4
E4-F3-G2-H1|A7-B6-C5-D4-    E5-F6-G7-H8|B1-C2-D3-E4
E3-F2-G1|A6-B5-C4-D3-E2-    F5-G6-H7|C1-D2-E3-F4-G5
F1|A5-B4-C3-D2-E1|A4-B3-    H6|D1-E2-F3-G4-H5|E1-F2
C2-D1|A3-B2-C1|A2-B1|A1     G3-H4|F1-G2-H3|G1-H2|H1
#+END_SRC

Since the diagonals in the rotated bitmap are of various lengths, we cannot bitshift by multiples of 8 to move a diagonal to the LSBs. To bitshift correctly, we need to know for each diagonal how much we need to shift in order to move it to the LSBs. Furthermore, we need to know how long the diagonal is, so that we know what we need to bitwise AND it with, to have only the squares of the diagonal in a bitmap.

An alternative to remembering somehow how long the diagonal in question is is suggested by Hyatt:

1. Before playing chess, precompute a RACT (~r45ccw-diagonal-attacks~), which takes as an index the square number (~square-number~) a piece is on and returns a RACT of valid move bitmaps (~diag-valid-moves-from-square~), which takes as an index an occupancy bitmap (~diag-occupied~).
2. Shift the diagonal to the LSBs.
3. Continue like with rank attacks to bitwise AND with 255 to get ~diag-occupied~.
5. Use the number of the square, on which the moving piece is (~square-number~) as an index to ~r45ccw-diagonal-attacks~ to get ~diag-valid-moves-from-square~.
6. Use the obtained ~diag-occupied~ as an index into the ~diag-valid-moves-from-square~, to get the bitmap of valid moves.

This way, all correct valid move squares are present in the bitmaps stored in the RACT, although most of them have duplicates, as there are /2 to the power of the number of additional bits/ bitmaps storing the valid moves for the diagonal and square, which a moving piece stands on.

To be clear, a double index is used here. The RACT ~r45ccw-diagonal-attacks~ (rotated 45 degree counter-clockwise diagonal attacks) takes as first index the square, which a moving piece stands on and as a second index the occupied map of length 8. This means, that the required memory will be:

#+BEGIN_SRC
  64 squares * 256 occupation variations * 64 bit integer
= 64*64*256 bit
= 1048576 bit
= 131072 byte
= 128 kilobyte
#+END_SRC
* Pregenerating bitmaps
There are many bitmaps, which should be pregenerated, before the chess program
actually runs. They are too many, to reasonably create them by hand, so the
question is, how one can generate these bitmaps programmatically.

A good idea is to first get an overview of generative procedures, which one
might need.
** Overview
One might want a procedure, which creates all valid moves given the following 3
inputs:

1. piece type
2. square the piece is on
3. occupied squares bitmap

For sliding attack pieces this has already been described above. One can use the
non-rotated bitmap approach to compute rays of valid moves. This would work for
bishop, rook and queen.

For pawns, knights and kings it does not work the same way. Instead, we can
implement procedures computing the bitmaps in a non-bitboard way and only store
the results as 64 bit integers.

For kings and knights this seems rather simple, as they can both be interpreted
as jump pieces and no matter what the bitmap of occupied squares is, their valid
moves only depend on whether the target square is occupied by a piece of their
own color, or by a piece of the other color or is not occupied at all. That is
kings and knights cannot be blocked. The knight because it jumps and the king,
because its movement and attacking range is only 1 square, so there can be
nothing in between the king and its target square.

Pawns are special, because of multiple factors:

- they move differently depending on where they stand
- their movement can be blocked
- they move different from how they attack

Their way of attacking can be interpreted as jumping, because the attacking range is 1, just like the way of movement and attacking of the king.

Their way of movement is quite simple as well. They always move 1 square ahead, which is the plus8 direction, except, when they are on their initial square. If they are on their initial square, they might be able to move 2 squares ahead, depending on the answer to the following questions:

- Is the movement of the pawn blocked?
- Is the target square occupied? (As they cannot take the same way they move, this is actually a way of blocking as well.)

So how can on calculate, whether the 2 squares ahead pawn movement is blocked? One can store a bitmap in which there is a 1 bit in all positions of squares, which may no be blocked, for the pawns to move 2 steps ahead.

** Generating valid movement for pawns
Bitmaps for squares, which need to be unoccupied for a pawn to move 2 steps ahead (~white-initial-pawn-moves~ and ~black-initial-pawn-moves~) could look as follows:

#+BEGIN_SRC
   white        black

8  00000000  8  00000000
7  00000000  7  00000000
6  00000000  6  11111111
5  00000000  5  00000000
4  00000000  4  00000000
3  11111111  3  00000000
2  00000000  2  00000000
1  00000000  1  00000000

   ABCDEFGH     ABCDEFGH
#+END_SRC

There is no need to also set the 4th rank of bits to 1 for white or the 5th rank to 1 for black, as those are destination squares and the one can check for those in the ~occupied-squares~ bitmap. We will see this in the algorithm below.

Additionally one can create bitmaps for the files:

#+BEGIN_SRC
   A-file       B-file       C-file       D-file       E-file       F-file       G-file       H-file

8  10000000  8  01000000  8  00100000  8  00010000  8  00001000  8  00000100  8  00000010  8  00000001
7  10000000  7  01000000  7  00100000  7  00010000  7  00001000  7  00000100  7  00000010  7  00000001
6  10000000  6  01000000  6  00100000  6  00010000  6  00001000  6  00000100  6  00000010  6  00000001
5  10000000  5  01000000  5  00100000  5  00010000  5  00001000  5  00000100  5  00000010  5  00000001
4  10000000  4  01000000  4  00100000  4  00010000  4  00001000  4  00000100  4  00000010  4  00000001
3  10000000  3  01000000  3  00100000  3  00010000  3  00001000  3  00000100  3  00000010  3  00000001
2  10000000  2  01000000  2  00100000  2  00010000  2  00001000  2  00000100  2  00000010  2  00000001
1  10000000  1  01000000  1  00100000  1  00010000  1  00001000  1  00000100  1  00000010  1  00000001

   ABCDEFGH     ABCDEFGH     ABCDEFGH     ABCDEFGH     ABCDEFGH     ABCDEFGH     ABCDEFGH     ABCDEFGH
#+END_SRC

(~a-file~, ~b-file~, ~c-file~, ...)

Together with the occupied squares bitmap and bitmaps of white and black pieces, one use the following approach:

1. Lookup all valid 2 steps pawn movements.
2. Treat pawns as if they could only move 1 square ahead for looking up movement of 1 square ahead.

This will simplify finding all valid pawn moves, because one only needs to deal with the 2 steps movement once and can ignore it, when one already has all valid 2 steps movements. This way the pawn then becomes more similar to all the other pieces, moving and the same way, no matter where on the board it is.

*** Getting 2 steps movements
1. Get the square number for the square the pawn is on (~pawn-square~).
2. Get the file a moving pawn is on (~pawn-file~). This can be taken
3. bitwise AND of the following bitmaps:
   1. initial pawn moves bitmap of the moving color (~white-initial-pawn-moves~ or ~black-initial-pawn-moves~)
   2. bitmap of the file the pawn is on (~a-file~, ~b-file~, ~c-file~, ...)
   3. bitwise NOT of ~occupied-squares~ -> ~not-occupied-squares~
4. If the resulting bitmap is equal to zero, the pawn cannot move 2 steps ahead, because it is blocked.
5. If the resulting bitmap is not equal to zero, the pawn might be able to move 2 steps ahead. To check whether it can move 2 steps ahead, lookup the target square, which is ~pawn-square~ + 16 in ~not-occupied-squares~. -> ~pawn-2-steps-square~
6. If ~pawn-2-steps-square~ is zero, the pawn is blocked and cannot move 2 steps ahead.
7. If ~pawn-2-steps-square~ is not zero, the pawn is not blocked and may move 2 steps ahead.

Note: En-passant is not yet considered, but the moves for it are rather few, only possibly 2 valid moves per previous move, so those could be added on-the-fly, whenever the opponent moved a pawn. En-passant is an example of a rule, which requires looking at the move history of the game.

** About efficiency
The generation process does not need to be extremely efficient, because this
step can be performed before the chess program ever runs and store its result in
some persistent storage, which the chess program can read, when it starts, to
have all the pregenerated bitmaps available.

Nevertheless, if possible, it would be a good idea to write some efficient code
for this task, however does not make sacrifices on the side of readability.
