The ChessAIThon project (2025-1-ES01-KA220-VET-000354329) is co-funded by the European Union. The views and opinions expressed in this publication are those of the author(s) only and do not necessarily reflect those of the European Union or the Spanish Service for the Internationalisation of Education (SEPIE). Neither the European Union nor the National Agency SEPIE can be held responsible for them.
Table of Contents
Until now, the user suggested a move and the computer said "Yes" or "No". Now, we want the computer to say: "Here is everything I can do."
Dynamic Lists
In chess, the number of possible moves changes every turn. Sometimes you have 20, sometimes only 1 (forced move). In programming, we cannot use a fixed structure. We must use Dynamic Lists that grow as we find options.
The Concept: We create an empty list [] and use the .append() command to add every legal move we find.
Difference Between Passive and Active Approach
In the passive paradigm (previous chapters), the flow was:
User → proposes move → Computer → validates → Yes/No
In the active paradigm (this chapter), the flow becomes:
Computer → generates all possible moves → evaluates → chooses the best
Example: Move Generator for a Knight
Let's write a function that, given a Knight at a certain position, returns a list of all coordinates where it can legally land.
def generate_knight_moves(r, c):
possible_moves = [] # Empty initial list
# All 8 possible "L" jump combinations (delta_r, delta_c)
jumps = [
(2, 1), (2, -1), (-2, 1), (-2, -1),
(1, 2), (1, -2), (-1, 2), (-1, -2)
]
for jump in jumps:
new_r = r + jump[0]
new_c = c + jump[1]
# Check if we are still on the board
if 0 <= new_r <= 7 and 0 <= new_c <= 7:
# It's a valid move! Let's add it to the list.
possible_moves.append((new_r, new_c))
return possible_moves
# Test with a Knight at center (4,4)
print(generate_knight_moves(4, 4))
# Output: [(6, 5), (6, 3), (2, 5), (2, 3), (5, 6), (5, 2), (3, 6), (3, 2)]
Move Generator for the Rook
The Rook can move along the entire row or column. We must generate all reachable squares horizontally and vertically, stopping when we encounter a piece.
def generate_rook_moves(board, r, c):
moves = []
piece = board[r][c]
# Define the 4 directions: up, down, left, right
directions = [(-1, 0), (1, 0), (0, -1), (0, 1)]
for dr, dc in directions:
new_r, new_c = r + dr, c + dc
# Continue while we are inside the board
while 0 <= new_r <= 7 and 0 <= new_c <= 7:
destination = board[new_r][new_c]
if destination == ".": # Empty square
moves.append((new_r, new_c))
elif destination.isupper() != piece.isupper(): # Enemy piece
moves.append((new_r, new_c)) # Can capture it
break # But cannot go beyond
else: # Friendly piece
break # Cannot capture nor jump over
new_r += dr
new_c += dc
return moves