Hello! In our last two lessons, we built the move generation logic for rooks and bishops, our "sliding" pieces. You did an excellent job implementing the ray-casting method for both straight and diagonal lines. Today, we get a fantastic payoff for that hard work.
This lesson covers how to generate valid moves for the queen by combining rook and bishop movement logic. The queen is the most powerful piece in chess precisely because her movement is a superset of those two pieces. You'll see how the functions you've already written can be reused to implement the queen's logic in just a few lines of code.
1. The Queen's Power: A Combination of Forces
The rule for the queen is simple: she can move any number of squares in any of the eight directions—horizontally, vertically, or diagonally—as long as her path is not blocked.
This means a queen at a given square has the potential moves of both a rook and a bishop placed on that same square.

Our implementation strategy will be just as simple and elegant as the rule itself.
2. Hands-On: Implementing Queen Moves
Let's get straight to the code. We'll create a generate_queen_moves function that acts as a wrapper, calling the two functions you've already built.
- Open your project and navigate to
src/move_generation/mod.rs. - At the top of the file, make sure all three move generation functions are exported if you've organized them into a module file. If they are all in one file, you are good to go.
- Add the new function
generate_queen_moves:
// In src/move_generation/mod.rs
// ... (your existing imports and the generate_rook_moves and generate_bishop_moves functions)
pub fn generate_queen_moves(piece: &Piece, position: (u8, u8), board: &Board) -> Vec<Move> {
// A queen's moves are the combination of a rook's and a bishop's moves.
let mut moves = generate_rook_moves(piece, position, board);
let bishop_moves = generate_bishop_moves(piece, position, board);
// Combine the two lists of moves into one.
moves.extend(bishop_moves);
moves
}
And that's it! The implementation for the most powerful piece is just three lines of code. This is a perfect example of the "Don't Repeat Yourself" (DRY) principle in action. By creating modular, reusable functions for the rook and bishop, implementing the queen becomes incredibly simple. The extend method on Vec<T> is a convenient way to append all the elements from one collection to another.
3. Reinforcing the Concept
This idea of combining simpler logic is a common and powerful pattern in programming. Many chess engines, from beginner projects to professional ones, use this exact approach.
Let's see how others have come to the same conclusion.
I Thought I Could Code Chess...
The creator of the "I Thought I Could Code Chess..." video has the exact same "Aha!" moment we just implemented. It's a great example of discovering an elegant solution.
Watch the short clip from 04:02 to 04:23. Notice how he describes the queen as simply "bishop plus rook" and implements it by combining the logic he'd already written.
This pattern isn't just for hobby projects. More advanced chess engine tutorials confirm this is a standard technique.
Building a Chess AI, Part 2 – Move Generation
The article "Building a Chess AI, Part 2 – Move Generation" discusses move generation for high-performance engines. Even in a more complex context using bitboards, the core principle remains the same.
Read the short paragraph in the 'Sliding Piece Attacks' section that begins 'The engine uses the same logic for generating rook attacks...'. This explicitly confirms that queen attacks are derived by combining rook and bishop attacks.
Finally, the Bevy chess tutorial you've referenced before also demonstrates this, but with a move validation approach instead of move generation. The logical foundation is identical.
Chess game in Rust using Bevy - lmao
Let's look at the is_move_valid function in the Bevy chess tutorial one more time. You'll see the queen's validation logic is a boolean OR of the rook and bishop conditions.
Find the is_move_valid function and look for the PieceType::Queen case inside the match statement. The condition (self.x as i8 - new_position.0 as i8).abs() == (self.y as i8 - new_position.1 as i8).abs() checks for diagonal movement (like a bishop), and the condition (self.x == new_position.0 && self.y != new_position.1) || (self.y == new_position.1 && self.x != new_position.0) checks for straight movement (like a rook). The || (OR) operator combines them.
4. Testing Your Queen Logic
Now, let's integrate and test our new function in src/main.rs.
First, import the new function at the top of main.rs:
// At the top of src/main.rs
use move_generation::{generate_pawn_moves, generate_rook_moves, generate_bishop_moves, generate_queen_moves};
Next, add a test case at the end of your main function. We'll use a custom board to check the logic. Let's place a queen at d4 and strategically add some blocking pieces.
// In src/main.rs, at the end of main()
println!("\nTesting Queen Move Generation:");
let mut test_board = Board::new(); // Clears the board
test_board.pieces[3][3] = Some(Piece { // White Queen at d4
piece_type: PieceType::Queen,
color: PieceColor::White,
});
test_board.pieces[5][5] = Some(Piece { // Enemy pawn at f6 (blocks bishop up-right)
piece_type: PieceType::Pawn,
color: PieceColor::Black,
});
test_board.pieces[1][1] = Some(Piece { // Friendly pawn at b2 (blocks bishop down-left)
piece_type: PieceType::Pawn,
color: PieceColor::White,
});
test_board.pieces[5][3] = Some(Piece { // Enemy pawn at d6 (blocks rook up)
piece_type: PieceType::Pawn,
color: PieceColor::Black,
});
test_board.pieces[3][0] = Some(Piece { // Friendly pawn at a4 (blocks rook left)
piece_type: PieceType::Pawn,
color: PieceColor::White,
});
let d4_pos = (3, 3);
if let Some(queen) = &test_board.pieces[d4_pos.0 as usize][d4_pos.1 as usize] {
let queen_moves = generate_queen_moves(queen, d4_pos, &test_board);
println!("\nMoves for test queen at d4: {:?}", queen_moves);
println!("Total moves: {}", queen_moves.len());
// Expected moves:
// Bishop-like: (e5, f6(cap)) + (c5, b6, a7) + (e3, f2, g1, h0) + (c3) -> 2 + 3 + 4 + 1 = 10
// Rook-like: (d5, d6(cap)) + (d2, d1, d0) + (e4, f4, g4, h4) + (c4, b4) -> 2 + 3 + 4 + 2 = 11
// Total should be 21
assert_eq!(queen_moves.len(), 21);
}
Run cargo run. The test should pass, confirming that your generate_queen_moves function correctly combines the results and respects all the blocking logic you implemented previously. Feel free to modify the test_board to experiment with other positions!
Conclusion
Excellent work! This was a short but very important lesson. You've seen how good software design—breaking problems into smaller, reusable parts—can pay huge dividends.
Key Takeaways:
- The queen's movement is a straightforward combination of rook and bishop movements.
- By reusing your
generate_rook_movesandgenerate_bishop_movesfunctions, you implemented queen logic efficiently and without duplicating code. - The
Vec::extend()method is a useful tool for merging one collection into another. - This principle of composition—building complex behavior from simple parts—is a cornerstone of software engineering.
In the next lesson, we will tackle the knight. Its movement is unique because it "jumps" over other pieces, meaning we'll need a different approach than the ray-casting we've used for sliding pieces. It will be a new and interesting challenge