Off-by-one on range boundaries
Wrong move: Loop endpoints miss first/last candidate.
Usually fails on: Fails on minimal arrays and exact-boundary answers.
Fix: Re-derive loops from inclusive/exclusive ranges before coding.
Break down a hard problem into reliable checkpoints, edge-case handling, and complexity trade-offs.
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.length1 <= n <= 4pieces only contains the strings "rook", "queen", and "bishop".1 <= ri, ci <= 8positions[i] is distinct.Problem summary: 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. A rook can only travel horizontally or vertically from (r, c) to the direction of (r+1, c), (r-1, c), (r, c+1), or (r, c-1). A queen can only travel horizontally, vertically, or diagonally from (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). A bishop can only travel diagonally from (r, c) to the direction of (r+1,
Start with the most direct exhaustive search. That gives a correctness anchor before optimizing.
Pattern signal: Array · Backtracking
["rook"] [[1,1]]
["queen"] [[1,1]]
["bishop"] [[4,3]]
Source-backed implementations are provided below for direct study and interview prep.
// Accepted solution for LeetCode #2056: Number of Valid Move Combinations On Chessboard
class Solution {
int n, m = 9, ans;
int[][][] dist;
int[][] end;
String[] pieces;
int[][] positions;
int[][] rookDirs = {{1, 0}, {-1, 0}, {0, 1}, {0, -1}};
int[][] bishopDirs = {{1, 1}, {1, -1}, {-1, 1}, {-1, -1}};
int[][] queenDirs = {{1, 0}, {-1, 0}, {0, 1}, {0, -1}, {1, 1}, {1, -1}, {-1, 1}, {-1, -1}};
public int countCombinations(String[] pieces, int[][] positions) {
n = pieces.length;
dist = new int[n][m][m];
end = new int[n][3];
ans = 0;
this.pieces = pieces;
this.positions = positions;
dfs(0);
return ans;
}
private void dfs(int i) {
if (i >= n) {
ans++;
return;
}
int x = positions[i][0], y = positions[i][1];
resetDist(i);
dist[i][x][y] = 0;
end[i] = new int[] {x, y, 0};
if (checkStop(i, x, y, 0)) {
dfs(i + 1);
}
int[][] dirs = getDirs(pieces[i]);
for (int[] dir : dirs) {
resetDist(i);
dist[i][x][y] = 0;
int nx = x + dir[0], ny = y + dir[1], nt = 1;
while (isValid(nx, ny) && checkPass(i, nx, ny, nt)) {
dist[i][nx][ny] = nt;
end[i] = new int[] {nx, ny, nt};
if (checkStop(i, nx, ny, nt)) {
dfs(i + 1);
}
nx += dir[0];
ny += dir[1];
nt++;
}
}
}
private void resetDist(int i) {
for (int j = 0; j < m; j++) {
for (int k = 0; k < m; k++) {
dist[i][j][k] = -1;
}
}
}
private boolean checkStop(int i, int x, int y, int t) {
for (int j = 0; j < i; j++) {
if (dist[j][x][y] >= t) {
return false;
}
}
return true;
}
private boolean checkPass(int i, int x, int y, int t) {
for (int j = 0; j < i; j++) {
if (dist[j][x][y] == t) {
return false;
}
if (end[j][0] == x && end[j][1] == y && end[j][2] <= t) {
return false;
}
}
return true;
}
private boolean isValid(int x, int y) {
return x >= 1 && x < m && y >= 1 && y < m;
}
private int[][] getDirs(String piece) {
char c = piece.charAt(0);
return switch (c) {
case 'r' -> rookDirs;
case 'b' -> bishopDirs;
default -> queenDirs;
};
}
}
// Accepted solution for LeetCode #2056: Number of Valid Move Combinations On Chessboard
func countCombinations(pieces []string, positions [][]int) (ans int) {
n := len(pieces)
m := 9
dist := make([][][]int, n)
for i := range dist {
dist[i] = make([][]int, m)
for j := range dist[i] {
dist[i][j] = make([]int, m)
}
}
end := make([][3]int, n)
rookDirs := [][2]int{{1, 0}, {-1, 0}, {0, 1}, {0, -1}}
bishopDirs := [][2]int{{1, 1}, {1, -1}, {-1, 1}, {-1, -1}}
queenDirs := [][2]int{{1, 0}, {-1, 0}, {0, 1}, {0, -1}, {1, 1}, {1, -1}, {-1, 1}, {-1, -1}}
resetDist := func(i int) {
for j := 0; j < m; j++ {
for k := 0; k < m; k++ {
dist[i][j][k] = -1
}
}
}
checkStop := func(i, x, y, t int) bool {
for j := 0; j < i; j++ {
if dist[j][x][y] >= t {
return false
}
}
return true
}
checkPass := func(i, x, y, t int) bool {
for j := 0; j < i; j++ {
if dist[j][x][y] == t {
return false
}
if end[j][0] == x && end[j][1] == y && end[j][2] <= t {
return false
}
}
return true
}
isValid := func(x, y int) bool {
return x >= 1 && x < m && y >= 1 && y < m
}
getDirs := func(piece string) [][2]int {
switch piece[0] {
case 'r':
return rookDirs
case 'b':
return bishopDirs
default:
return queenDirs
}
}
var dfs func(i int)
dfs = func(i int) {
if i >= n {
ans++
return
}
x, y := positions[i][0], positions[i][1]
resetDist(i)
dist[i][x][y] = 0
end[i] = [3]int{x, y, 0}
if checkStop(i, x, y, 0) {
dfs(i + 1)
}
dirs := getDirs(pieces[i])
for _, dir := range dirs {
resetDist(i)
dist[i][x][y] = 0
nx, ny, nt := x+dir[0], y+dir[1], 1
for isValid(nx, ny) && checkPass(i, nx, ny, nt) {
dist[i][nx][ny] = nt
end[i] = [3]int{nx, ny, nt}
if checkStop(i, nx, ny, nt) {
dfs(i + 1)
}
nx += dir[0]
ny += dir[1]
nt++
}
}
}
dfs(0)
return
}
# Accepted solution for LeetCode #2056: Number of Valid Move Combinations On Chessboard
rook_dirs = [(1, 0), (-1, 0), (0, 1), (0, -1)]
bishop_dirs = [(1, 1), (1, -1), (-1, 1), (-1, -1)]
queue_dirs = rook_dirs + bishop_dirs
def get_dirs(piece: str) -> List[Tuple[int, int]]:
match piece[0]:
case "r":
return rook_dirs
case "b":
return bishop_dirs
case _:
return queue_dirs
class Solution:
def countCombinations(self, pieces: List[str], positions: List[List[int]]) -> int:
def check_stop(i: int, x: int, y: int, t: int) -> bool:
return all(dist[j][x][y] < t for j in range(i))
def check_pass(i: int, x: int, y: int, t: int) -> bool:
for j in range(i):
if dist[j][x][y] == t:
return False
if end[j][0] == x and end[j][1] == y and end[j][2] <= t:
return False
return True
def dfs(i: int) -> None:
if i >= n:
nonlocal ans
ans += 1
return
x, y = positions[i]
dist[i][:] = [[-1] * m for _ in range(m)]
dist[i][x][y] = 0
end[i] = (x, y, 0)
if check_stop(i, x, y, 0):
dfs(i + 1)
dirs = get_dirs(pieces[i])
for dx, dy in dirs:
dist[i][:] = [[-1] * m for _ in range(m)]
dist[i][x][y] = 0
nx, ny, nt = x + dx, y + dy, 1
while 1 <= nx < m and 1 <= ny < m and check_pass(i, nx, ny, nt):
dist[i][nx][ny] = nt
end[i] = (nx, ny, nt)
if check_stop(i, nx, ny, nt):
dfs(i + 1)
nx += dx
ny += dy
nt += 1
n = len(pieces)
m = 9
dist = [[[-1] * m for _ in range(m)] for _ in range(n)]
end = [(0, 0, 0) for _ in range(n)]
ans = 0
dfs(0)
return ans
// Accepted solution for LeetCode #2056: Number of Valid Move Combinations On Chessboard
/**
* [2056] Number of Valid Move Combinations On Chessboard
*
* 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 i^th piece. In addition, you are given a 2D integer array positions also of length n, where positions[i] = [ri, ci] indicates that the i^th 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.
*
* A rook can only travel horizontally or vertically from (r, c) to the direction of (r+1, c), (r-1, c), (r, c+1), or (r, c-1).
* A queen can only travel horizontally, vertically, or diagonally from (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).
* A bishop can only travel diagonally from (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 0^th 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:
*
* No two pieces will start in the same square.
* You may choose the square a piece is already on as its destination.
* If two pieces are directly adjacent to each other, it is valid for them to move past each other and swap positions in one second.
*
*
* Example 1:
* <img alt="" src="https://assets.leetcode.com/uploads/2021/09/23/a1.png" style="width: 215px; height: 215px;" />
* Input: pieces = ["rook"], positions = [[1,1]]
* Output: 15
* Explanation: The image above shows the possible squares the piece can move to.
*
* Example 2:
* <img alt="" src="https://assets.leetcode.com/uploads/2021/09/23/a2.png" style="width: 215px; height: 215px;" />
* Input: pieces = ["queen"], positions = [[1,1]]
* Output: 22
* Explanation: The image above shows the possible squares the piece can move to.
*
* Example 3:
* <img alt="" src="https://assets.leetcode.com/uploads/2021/09/23/a3.png" style="width: 214px; height: 215px;" />
* 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".
* There will be at most one queen on the chessboard.
* 1 <= ri, ci <= 8
* Each positions[i] is distinct.
*
*/
pub struct Solution {}
// problem: https://leetcode.com/problems/number-of-valid-move-combinations-on-chessboard/
// discuss: https://leetcode.com/problems/number-of-valid-move-combinations-on-chessboard/discuss/?currentPage=1&orderBy=most_votes&query=
// submission codes start here
impl Solution {
pub fn count_combinations(pieces: Vec<String>, positions: Vec<Vec<i32>>) -> i32 {
0
}
}
// submission codes end
#[cfg(test)]
mod tests {
use super::*;
#[test]
#[ignore]
fn test_2056_example_1() {
let pieces = vec_string!["rook"];
let positions = vec![vec![1, 1]];
let result = 15;
assert_eq!(Solution::count_combinations(pieces, positions), result);
}
#[test]
#[ignore]
fn test_2056_example_2() {
let pieces = vec_string!["queen"];
let positions = vec![vec![1, 1]];
let result = 22;
assert_eq!(Solution::count_combinations(pieces, positions), result);
}
#[test]
#[ignore]
fn test_2056_example_3() {
let pieces = vec_string!["bishop"];
let positions = vec![vec![4, 3]];
let result = 12;
assert_eq!(Solution::count_combinations(pieces, positions), result);
}
}
// Accepted solution for LeetCode #2056: Number of Valid Move Combinations On Chessboard
const rookDirs: [number, number][] = [
[1, 0],
[-1, 0],
[0, 1],
[0, -1],
];
const bishopDirs: [number, number][] = [
[1, 1],
[1, -1],
[-1, 1],
[-1, -1],
];
const queenDirs = [...rookDirs, ...bishopDirs];
function countCombinations(pieces: string[], positions: number[][]): number {
const n = pieces.length;
const m = 9;
let ans = 0;
const dist = Array.from({ length: n }, () =>
Array.from({ length: m }, () => Array(m).fill(-1)),
);
const end: [number, number, number][] = Array(n).fill([0, 0, 0]);
const resetDist = (i: number) => {
for (let j = 0; j < m; j++) {
for (let k = 0; k < m; k++) {
dist[i][j][k] = -1;
}
}
};
const checkStop = (i: number, x: number, y: number, t: number): boolean => {
for (let j = 0; j < i; j++) {
if (dist[j][x][y] >= t) {
return false;
}
}
return true;
};
const checkPass = (i: number, x: number, y: number, t: number): boolean => {
for (let j = 0; j < i; j++) {
if (dist[j][x][y] === t) {
return false;
}
if (end[j][0] === x && end[j][1] === y && end[j][2] <= t) {
return false;
}
}
return true;
};
const isValid = (x: number, y: number): boolean => {
return x >= 1 && x < m && y >= 1 && y < m;
};
const getDirs = (piece: string): [number, number][] => {
switch (piece[0]) {
case 'r':
return rookDirs;
case 'b':
return bishopDirs;
default:
return queenDirs;
}
};
const dfs = (i: number) => {
if (i >= n) {
ans++;
return;
}
const [x, y] = positions[i];
resetDist(i);
dist[i][x][y] = 0;
end[i] = [x, y, 0];
if (checkStop(i, x, y, 0)) {
dfs(i + 1);
}
const dirs = getDirs(pieces[i]);
for (const [dx, dy] of dirs) {
resetDist(i);
dist[i][x][y] = 0;
let nx = x + dx,
ny = y + dy,
nt = 1;
while (isValid(nx, ny) && checkPass(i, nx, ny, nt)) {
dist[i][nx][ny] = nt;
end[i] = [nx, ny, nt];
if (checkStop(i, nx, ny, nt)) {
dfs(i + 1);
}
nx += dx;
ny += dy;
nt++;
}
}
};
dfs(0);
return ans;
}
Use this to step through a reusable interview workflow for this problem.
Generate every possible combination without any filtering. At each of n positions we choose from up to n options, giving nⁿ total candidates. Each candidate takes O(n) to validate. No pruning means we waste time on clearly invalid partial solutions.
Backtracking explores a decision tree, but prunes branches that violate constraints early. Worst case is still factorial or exponential, but pruning dramatically reduces the constant factor in practice. Space is the recursion depth (usually O(n) for n-level decisions).
Review these before coding to avoid predictable interview regressions.
Wrong move: Loop endpoints miss first/last candidate.
Usually fails on: Fails on minimal arrays and exact-boundary answers.
Fix: Re-derive loops from inclusive/exclusive ranges before coding.
Wrong move: Mutable state leaks between branches.
Usually fails on: Later branches inherit selections from earlier branches.
Fix: Always revert state changes immediately after recursive call.