There is an 8 x 8
chessboard containing n
pieces (rooks, queens, or bishops). You are given a string array pieces
of length n
, where pieces[i]
describes the type (rook, queen, or bishop) of the ith
piece. In addition, you are given a 2D integer array positions
also of length n
, where positions[i] = [ri, ci]
indicates that the ith
piece is currently at the 1-based coordinate (ri, ci)
on the chessboard.
When making a move for a piece, you choose a destination square that the piece will travel toward and stop on.
(r, c)
to the direction of (r+1, c)
, (r-1, c)
, (r, c+1)
, or (r, c-1)
.(r, c)
to the direction of (r+1, c)
, (r-1, c)
, (r, c+1)
, (r, c-1)
, (r+1, c+1)
, (r+1, c-1)
, (r-1, c+1)
, (r-1, c-1)
.(r, c)
to the direction of (r+1, c+1)
, (r+1, c-1)
, (r-1, c+1)
, (r-1, c-1)
.You must make a move for every piece on the board simultaneously. A move combination consists of all the moves performed on all the given pieces. Every second, each piece will instantaneously travel one square towards their destination if they are not already at it. All pieces start traveling at the 0th
second. A move combination is invalid if, at a given time, two or more pieces occupy the same square.
Return the number of valid move combinations.
Notes:
Example 1:
Input: pieces = ["rook"], positions = [[1,1]] Output: 15 Explanation: The image above shows the possible squares the piece can move to.
Example 2:
Input: pieces = ["queen"], positions = [[1,1]] Output: 22 Explanation: The image above shows the possible squares the piece can move to.
Example 3:
Input: pieces = ["bishop"], positions = [[4,3]] Output: 12 Explanation: The image above shows the possible squares the piece can move to.
Constraints:
n == pieces.length
n == positions.length
1 <= n <= 4
pieces
only contains the strings "rook"
, "queen"
, and "bishop"
.1 <= ri, ci <= 8
positions[i]
is distinct.When you get asked this question in a real-life environment, it will often be ambiguous (especially at FAANG). Make sure to ask these questions in that case:
The core idea is to explore every possible sequence of moves for each piece. We will simulate all combinations of moves until a solution is found or all possibilities are exhausted. It's like trying every combination lock until you find the correct one.
Here's how the algorithm would work step-by-step:
def find_valid_move_combinations_brute_force(pieces, board_size):
number_of_pieces = len(pieces)
valid_combinations_count = 0
def is_valid_move(move):
# Placeholder: Replace with actual move validation logic
# For simplicity, always return True
return True
def is_valid_combination(moves):
# Placeholder: Replace with actual combination validation logic
# For simplicity, always return True
return True
def generate_moves(current_piece_index, current_moves):
nonlocal valid_combinations_count
# If all pieces have moved, check if the combination is valid.
if current_piece_index == number_of_pieces:
if is_valid_combination(current_moves):
valid_combinations_count += 1
return
piece = pieces[current_piece_index]
# Iterate through possible moves for the current piece.
for row in range(board_size):
for col in range(board_size):
move = (piece, (row, col))
# Check if the current move is valid.
if is_valid_move(move):
generate_moves(current_piece_index + 1, current_moves + [move])
# Start the recursive move generation from the first piece.
generate_moves(0, [])
return valid_combinations_count
The problem involves figuring out how chess pieces can move without blocking each other. The optimal approach is to view the chessboard and pieces as a graph and uses graph traversal to systematically explore possible move combinations. This avoids redundant calculations, significantly speeding up the process.
Here's how the algorithm would work step-by-step:
def number_of_valid_move_combinations_on_chessboard(piece_positions, piece_moves):
number_of_pieces = len(piece_positions)
adjacency_list = [[] for _ in range(number_of_pieces)]
for first_piece_index in range(number_of_pieces):
for second_piece_index in range(first_piece_index + 1, number_of_pieces):
adjacency_list[first_piece_index].append(second_piece_index)
adjacency_list[second_piece_index].append(first_piece_index)
number_of_moves = [len(moves) for moves in piece_moves]
total_move_combinations = 1
for move_count in number_of_moves:
total_move_combinations *= move_count
move_combinations_count = 0
#Use a cache to avoid duplicate work
cache = {}
def depth_first_search(piece_index, current_moves):
nonlocal move_combinations_count
if piece_index == number_of_pieces:
move_combinations_count += 1
return
cache_key = tuple(current_moves)
if cache_key in cache:
move_combinations_count += cache[cache_key]
return
valid_combinations_from_here = 0
for move_index in range(number_of_moves[piece_index]):
is_valid_move = True
for previous_piece_index in range(piece_index):
#Check for collision with previous moves
if (current_moves[previous_piece_index] == move_index and
adjacency_list[piece_index] and previous_piece_index in adjacency_list[piece_index]):
is_valid_move = False
break
if is_valid_move:
current_moves[piece_index] = move_index
depth_first_search(piece_index + 1, current_moves)
cache[cache_key] = valid_combinations_from_here
# Start the depth first search exploration
depth_first_search(0, [0] * number_of_pieces)
# Return the number of valid combinations found
return move_combinations_count
Case | How to Handle |
---|---|
Null or empty positions array | Return 1, indicating one valid combination (doing nothing). |
Null or empty move sequences array | Return 1, indicating one valid combination (doing nothing for that piece). |
Chessboard size of 1x1 | Check for immediate collisions if any moves are made. |
Single piece on the board | Return the number of valid move sequences for that piece. |
Maximum board size and maximum number of pieces to test scalability. | Optimize the collision detection to avoid excessive time complexity with a large board. |
Pieces starting in the same location | The algorithm must account for immediate collisions if pieces start on the same square. |
Move sequences that result in the pieces leaving the board. | Treat out-of-bounds moves as invalid and reject that particular move sequence. |
Very long move sequences for some or all pieces. | Consider memory limits during recursive or iterative processing of move sequences to prevent stack overflow or OOM errors. |