999. Available Captures for Rook
Problem Description
You have an 8×8 chessboard represented as a matrix with the following pieces:
- One white rook represented by
'R'
- Some white bishops represented by
'B'
- Some black pawns represented by
'p'
- Empty squares represented by
'.'
A rook can move any number of squares in four directions: horizontally (left/right) or vertically (up/down). The rook continues moving until it:
- Reaches another piece (bishop or pawn)
- Reaches the edge of the board
The rook is attacking a pawn if it can reach the pawn's square in a single move. However, the rook cannot move through other pieces - if a bishop or another pawn blocks the path, the rook cannot attack pawns beyond that blocking piece.
Your task is to count how many black pawns the white rook can attack from its current position.
Example scenarios:
- If there's a pawn directly in line with the rook (horizontally or vertically) with no pieces between them, the rook can attack it
- If there's a bishop between the rook and a pawn, the rook cannot attack that pawn
- The rook can potentially attack up to 4 pawns (one in each direction: up, down, left, right)
Intuition
The key insight is that a rook attacks in straight lines - up, down, left, and right. From the rook's position, we need to check what the first piece is in each of these four directions.
Think of it like shining a laser beam from the rook in each direction. The beam travels in a straight line until it hits something:
- If it hits a pawn (
'p'
), we can capture it, so we count it - If it hits a bishop (
'B'
), it blocks our path and we can't see beyond it - If it hits an empty square (
'.'
), we keep looking further in that direction - If it reaches the board edge, we stop looking in that direction
This naturally leads us to a simulation approach: find the rook's position first, then "walk" in each of the four directions from that position. For each direction, we keep moving square by square until we either:
- Find a pawn (increment our count and stop looking in this direction)
- Find a bishop (stop looking in this direction without counting)
- Reach the board boundary (stop looking in this direction)
The direction vectors (-1, 0)
, (1, 0)
, (0, -1)
, (0, 1)
represent moving up, down, left, and right respectively. By using dirs = (-1, 0, 1, 0, -1)
and pairing consecutive elements, we get these four direction vectors elegantly.
The maximum number of pawns we can attack is 4 (one in each direction), and the minimum is 0 (if all directions are blocked by bishops or empty).
Solution Approach
The implementation follows a systematic simulation approach:
Step 1: Find the Rook's Position
We traverse the entire 8×8 board using nested loops to locate the white rook 'R'
. Once found at position (i, j)
, we can begin checking for attackable pawns.
Step 2: Set Up Direction Vectors
We use dirs = (-1, 0, 1, 0, -1)
to represent the four directions. By using pairwise(dirs)
, we get the direction pairs:
(-1, 0)
: Move up (decrease row)(0, 1)
: Move right (increase column)(1, 0)
: Move down (increase row)(0, -1)
: Move left (decrease column)
Step 3: Explore Each Direction
For each direction (a, b)
:
- Start from the rook's adjacent square:
x, y = i + a, j + b
- Continue moving in that direction while:
- We're within board boundaries:
0 <= x < n and 0 <= y < n
- We haven't encountered a bishop:
board[x][y] != "B"
- We're within board boundaries:
- During this traversal:
- If we find a pawn
board[x][y] == "p"
, increment the answer and stop exploring this direction - If we find an empty square
'.'
, continue to the next square:x, y = x + a, y + b
- If we find a pawn
Step 4: Return the Count After exploring all four directions, return the total number of pawns that can be attacked.
The algorithm has a time complexity of O(n²)
for finding the rook, plus O(n)
for exploring each direction, resulting in overall O(n²)
where n = 8
. The space complexity is O(1)
as we only use a few variables to track position and count.
Ready to land your dream job?
Unlock your dream job with a 3-minute evaluator for a personalized learning plan!
Start EvaluatorExample Walkthrough
Let's walk through a small example to illustrate the solution approach.
Consider this 8×8 board:
. . . . . . . . . . . p . . . . . B . R . . . . . . . . . . . . . . . p . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Step 1: Find the Rook We scan the board and find the rook 'R' at position (2, 3).
Step 2: Explore Each Direction
Direction 1: Up (-1, 0)
- Start at (2, 3) and move up to (1, 3)
- We find a pawn 'p' at (1, 3)
- Count = 1, stop exploring this direction
Direction 2: Right (0, 1)
- Start at (2, 3) and move right to (2, 4)
- Empty square '.', continue to (2, 5)
- Empty square '.', continue to (2, 6)
- Empty square '.', continue to (2, 7)
- Reached board edge, stop exploring
- No pawns found in this direction
Direction 3: Down (1, 0)
- Start at (2, 3) and move down to (3, 3)
- Empty square '.', continue to (4, 3)
- We find a pawn 'p' at (4, 3)
- Count = 2, stop exploring this direction
Direction 4: Left (0, -1)
- Start at (2, 3) and move left to (2, 2)
- Empty square '.', continue to (2, 1)
- We find a bishop 'B' at (2, 1)
- Bishop blocks the path, stop exploring
- No pawns found in this direction
Final Answer: 2
The rook can attack 2 pawns - one directly above and one directly below. The bishop to the left blocks that direction, and there are no pieces to the right.
Solution Implementation
1class Solution:
2 def numRookCaptures(self, board: List[List[str]]) -> int:
3 # Define four directions: up, right, down, left
4 # Using pairwise iteration: (-1,0), (0,1), (1,0), (0,-1)
5 directions = (-1, 0, 1, 0, -1)
6 board_size = len(board)
7
8 # Find the rook's position on the board
9 for row in range(board_size):
10 for col in range(board_size):
11 if board[row][col] == "R":
12 captured_pawns = 0
13
14 # Check all four directions from the rook
15 for i in range(4):
16 # Get direction vector using pairwise
17 dx = directions[i]
18 dy = directions[i + 1]
19
20 # Start from the rook's adjacent cell in current direction
21 current_row = row + dx
22 current_col = col + dy
23
24 # Continue moving in the same direction until hitting a boundary or bishop
25 while 0 <= current_row < board_size and 0 <= current_col < board_size and board[current_row][current_col] != "B":
26 # If we find a pawn, capture it and stop in this direction
27 if board[current_row][current_col] == "p":
28 captured_pawns += 1
29 break
30
31 # Move to the next cell in the same direction
32 current_row += dx
33 current_col += dy
34
35 return captured_pawns
36
1class Solution {
2 public int numRookCaptures(char[][] board) {
3 // Direction vectors: up, right, down, left
4 // Using pairs (dx, dy) stored consecutively: (-1,0), (0,1), (1,0), (0,-1)
5 final int[] DIRECTIONS = {-1, 0, 1, 0, -1};
6
7 int boardSize = board.length;
8
9 // Find the white rook on the board
10 for (int row = 0; row < boardSize; row++) {
11 for (int col = 0; col < boardSize; col++) {
12 if (board[row][col] == 'R') {
13 int capturedPawns = 0;
14
15 // Check all 4 directions from the rook
16 for (int dir = 0; dir < 4; dir++) {
17 // Get direction offsets for current direction
18 int deltaRow = DIRECTIONS[dir];
19 int deltaCol = DIRECTIONS[dir + 1];
20
21 // Start from the position next to the rook
22 int currentRow = row + deltaRow;
23 int currentCol = col + deltaCol;
24
25 // Move in the current direction until hitting a boundary, bishop, or pawn
26 while (currentRow >= 0 && currentRow < boardSize &&
27 currentCol >= 0 && currentCol < boardSize &&
28 board[currentRow][currentCol] != 'B') {
29
30 // Check if we found a black pawn to capture
31 if (board[currentRow][currentCol] == 'p') {
32 capturedPawns++;
33 break; // Stop searching in this direction after capturing
34 }
35
36 // Move to the next position in the same direction
37 currentRow += deltaRow;
38 currentCol += deltaCol;
39 }
40 }
41
42 return capturedPawns;
43 }
44 }
45 }
46
47 // No rook found on the board (shouldn't happen in valid input)
48 return 0;
49 }
50}
51
1class Solution {
2public:
3 int numRookCaptures(vector<vector<char>>& board) {
4 // Direction vectors: up, right, down, left
5 // Using pairs: (-1,0), (0,1), (1,0), (0,-1)
6 const int directions[5] = {-1, 0, 1, 0, -1};
7 int boardSize = board.size();
8
9 // Find the white rook on the board
10 for (int row = 0; row < boardSize; ++row) {
11 for (int col = 0; col < boardSize; ++col) {
12 if (board[row][col] == 'R') {
13 int captureCount = 0;
14
15 // Check all 4 directions from the rook's position
16 for (int dir = 0; dir < 4; ++dir) {
17 // Get direction deltas for current direction
18 int deltaRow = directions[dir];
19 int deltaCol = directions[dir + 1];
20
21 // Start from the next cell in current direction
22 int currentRow = row + deltaRow;
23 int currentCol = col + deltaCol;
24
25 // Keep moving in the current direction until hitting a boundary or bishop
26 while (currentRow >= 0 && currentRow < boardSize &&
27 currentCol >= 0 && currentCol < boardSize &&
28 board[currentRow][currentCol] != 'B') {
29
30 // If we find a black pawn, capture it and stop in this direction
31 if (board[currentRow][currentCol] == 'p') {
32 ++captureCount;
33 break;
34 }
35
36 // Move to the next cell in the same direction
37 currentRow += deltaRow;
38 currentCol += deltaCol;
39 }
40 }
41
42 return captureCount;
43 }
44 }
45 }
46
47 // Return 0 if no rook found (edge case)
48 return 0;
49 }
50};
51
1/**
2 * Counts the number of pawns that a rook can capture on a chess board
3 * @param board - 8x8 chess board where 'R' is rook, 'B' is bishop, 'p' is pawn, '.' is empty
4 * @returns Number of pawns the rook can capture
5 */
6function numRookCaptures(board: string[][]): number {
7 // Direction vectors for moving up, right, down, left
8 const directions: number[] = [-1, 0, 1, 0, -1];
9 const boardSize: number = board.length;
10
11 // Find the rook on the board
12 for (let row = 0; row < boardSize; row++) {
13 for (let col = 0; col < boardSize; col++) {
14 if (board[row][col] === 'R') {
15 let captureCount: number = 0;
16
17 // Check all 4 directions from the rook's position
18 for (let dirIndex = 0; dirIndex < 4; dirIndex++) {
19 let currentRow: number = row + directions[dirIndex];
20 let currentCol: number = col + directions[dirIndex + 1];
21
22 // Move in the current direction until hitting a boundary or bishop
23 while (currentRow >= 0 && currentRow < boardSize &&
24 currentCol >= 0 && currentCol < boardSize &&
25 board[currentRow][currentCol] !== 'B') {
26
27 // If we find a pawn, increment count and stop in this direction
28 if (board[currentRow][currentCol] === 'p') {
29 captureCount++;
30 break;
31 }
32
33 // Continue moving in the same direction
34 currentRow += directions[dirIndex];
35 currentCol += directions[dirIndex + 1];
36 }
37 }
38
39 return captureCount;
40 }
41 }
42 }
43
44 // Return 0 if no rook found (edge case)
45 return 0;
46}
47
Time and Space Complexity
The time complexity is O(m × n)
, where m
and n
are the number of rows and columns of the board, respectively. In this problem, m = n = 8
(standard chess board size), making the actual complexity O(64) = O(1)
. The algorithm needs to traverse the entire board once to find the rook, which takes O(m × n)
time. Once the rook is found, it explores in 4 directions (up, down, left, right), and in the worst case, each direction traverses at most n
cells. Since there's only one rook on the board, the directional searches contribute O(4n) = O(n)
time. The overall complexity remains O(m × n)
as the board traversal dominates.
The space complexity is O(1)
. The algorithm only uses a constant amount of extra space for variables like dirs
(which stores 5 integers), loop counters (i
, j
, x
, y
), and the answer counter ans
. No additional data structures that scale with input size are created.
Learn more about how to find time and space complexity quickly.
Common Pitfalls
1. Incorrect Direction Vector Usage
The code uses directions = (-1, 0, 1, 0, -1)
with index-based access to get direction pairs. A common mistake is misunderstanding how this works or incorrectly accessing the pairs.
Pitfall Example:
# Wrong: Using wrong indices or not understanding the pairwise concept dx = directions[i] dy = directions[i + 1] # This works but can be confusing
Better Solution:
# More explicit and readable approach directions = [(-1, 0), (0, 1), (1, 0), (0, -1)] # up, right, down, left for dx, dy in directions: current_row = row + dx current_col = col + dy # ... rest of the logic
2. Breaking After Finding Any Piece
A critical mistake is breaking the loop when encountering ANY piece, not just pawns.
Pitfall Example:
while 0 <= current_row < board_size and 0 <= current_col < board_size: if board[current_row][current_col] != '.': # Wrong! This stops at bishops too if board[current_row][current_col] == 'p': captured_pawns += 1 break # This would count bishops as stopping points, not blocking pieces
Correct Approach:
while 0 <= current_row < board_size and 0 <= current_col < board_size: piece = board[current_row][current_col] if piece == 'B': # Bishop blocks the path break if piece == 'p': # Found a pawn to capture captured_pawns += 1 break # Continue if it's an empty square '.' current_row += dx current_col += dy
3. Not Stopping After Finding the Rook
Once the rook is found and processed, the function should return immediately. Continuing to search wastes time and could cause errors if there were multiple rooks (though the problem guarantees only one).
Pitfall Example:
for row in range(board_size):
for col in range(board_size):
if board[row][col] == "R":
# Process rook...
# Missing return statement here!
# This would continue searching even after finding the rook
return captured_pawns # Wrong scope - captured_pawns might not be defined here
Correct Approach:
for row in range(board_size):
for col in range(board_size):
if board[row][col] == "R":
# Process all four directions
# ...
return captured_pawns # Return immediately after processing the rook
return 0 # Safety return if no rook found (shouldn't happen per problem constraints)
4. Confusing Bishop Blocking Logic
The while loop condition board[current_row][current_col] != "B"
checks the current position AFTER moving. This could lead to array index errors.
Pitfall Example:
# This checks the position after moving, which might be out of bounds while 0 <= current_row < board_size and 0 <= current_col < board_size and board[current_row][current_col] != "B": # If current_row or current_col is out of bounds, # the board access happens before the bounds check in some languages
Safer Approach:
while 0 <= current_row < board_size and 0 <= current_col < board_size: if board[current_row][current_col] == "B": break # Stop if we hit a bishop if board[current_row][current_col] == "p": captured_pawns += 1 break # Stop after capturing a pawn current_row += dx current_col += dy
Which two pointer techniques do you use to check if a string is a palindrome?
Recommended Readings
Coding Interview Patterns Your Personal Dijkstra's Algorithm to Landing Your Dream Job The goal of AlgoMonster is to help you get a job in the shortest amount of time possible in a data driven way We compiled datasets of tech interview problems and broke them down by patterns This way
Recursion Recursion is one of the most important concepts in computer science Simply speaking recursion is the process of a function calling itself Using a real life analogy imagine a scenario where you invite your friends to lunch https assets algo monster recursion jpg You first call Ben and ask
Runtime Overview When learning about algorithms and data structures you'll frequently encounter the term time complexity This concept is fundamental in computer science and offers insights into how long an algorithm takes to complete given a certain input size What is Time Complexity Time complexity represents the amount of time
Want a Structured Path to Master System Design Too? Don’t Miss This!