Chess King Check Detector
Problem
You are presented with an 8 by 8 matrix representing the positions of pieces on a chess board. The only pieces on the board are the black king and various white pieces. Given this matrix, determine whether the king is in check.
A king is in check when it is under attack by one or more enemy pieces. Each chess piece has specific movement patterns:
- Queen (Q): Moves any number of squares horizontally, vertically, or diagonally
- Rook (R): Moves any number of squares horizontally or vertically
- Bishop (B): Moves any number of squares diagonally
- Knight (N): Moves in an L-shape (2 squares in one direction, 1 square perpendicular)
- Pawn (P): Attacks diagonally forward (one square diagonally up for white pieces)
Examples
Example 1
Input: board = [
"...K....",
"........",
".B......",
"......P.",
".......R",
"..N.....",
"........",
".....Q.."
]
Output: true
Explanation: The bishop at position (2,1) is attacking the king at (0,3) diagonally.
Example 2
Input: board = [
"........",
"........",
"........",
"...K....",
"........",
"........",
"........",
"........"
]
Output: false
Explanation: The king is alone on the board with no attacking pieces.
Example 3
Input: board = [
"........",
"........",
"........",
"...KR...",
"........",
"........",
"........",
"........"
]
Output: true
Explanation: The rook at position (3,4) is attacking the king at (3,3) horizontally.
Example 4
Input: board = [
"........",
"........",
"..N.....",
"........",
"....K...",
"........",
"........",
"........"
]
Output: true
Explanation: The knight at position (2,2) is attacking the king at (4,4) with an L-shaped move.
Similar Problems
[Queens That Can Attack the King](queens-that-can-attack-the-king)
Solution
Method 1 - Direct Attack Pattern Validation
Intuition
To determine if a king is in check, we need to find the king's position and then check if any white piece can attack that position using their specific movement patterns. Each piece type has distinct attack patterns that we can validate by checking all possible directions and distances.
Approach
- Find the king's position on the board
- For each white piece on the board, determine if it can attack the king's position:
- Queen: Check all 8 directions (horizontal, vertical, diagonal)
- Rook: Check 4 directions (horizontal, vertical)
- Bishop: Check 4 diagonal directions
- Knight: Check all 8 L-shaped positions
- Pawn: Check 2 diagonal forward positions
- For sliding pieces (Queen, Rook, Bishop), ensure no other pieces block the attack path
- Return true if any piece can attack the king
Code
C++
class Solution {
public:
bool isKingInCheck(vector<string>& board) {
int kingRow = -1, kingCol = -1;
// Find king position
for (int i = 0; i < 8; i++) {
for (int j = 0; j < 8; j++) {
if (board[i][j] == 'K') {
kingRow = i;
kingCol = j;
break;
}
}
if (kingRow != -1) break;
}
// Check if any white piece attacks the king
for (int i = 0; i < 8; i++) {
for (int j = 0; j < 8; j++) {
char piece = board[i][j];
if (piece != '.' && piece != 'K') {
if (canAttack(board, i, j, kingRow, kingCol, piece)) {
return true;
}
}
}
}
return false;
}
private:
bool canAttack(vector<string>& board, int pieceRow, int pieceCol,
int kingRow, int kingCol, char piece) {
switch (piece) {
case 'Q': return canQueenAttack(board, pieceRow, pieceCol, kingRow, kingCol);
case 'R': return canRookAttack(board, pieceRow, pieceCol, kingRow, kingCol);
case 'B': return canBishopAttack(board, pieceRow, pieceCol, kingRow, kingCol);
case 'N': return canKnightAttack(pieceRow, pieceCol, kingRow, kingCol);
case 'P': return canPawnAttack(pieceRow, pieceCol, kingRow, kingCol);
default: return false;
}
}
bool canQueenAttack(vector<string>& board, int r1, int c1, int r2, int c2) {
return canRookAttack(board, r1, c1, r2, c2) || canBishopAttack(board, r1, c1, r2, c2);
}
bool canRookAttack(vector<string>& board, int r1, int c1, int r2, int c2) {
if (r1 != r2 && c1 != c2) return false;
return hasClearPath(board, r1, c1, r2, c2);
}
bool canBishopAttack(vector<string>& board, int r1, int c1, int r2, int c2) {
if (abs(r1 - r2) != abs(c1 - c2)) return false;
return hasClearPath(board, r1, c1, r2, c2);
}
bool canKnightAttack(int r1, int c1, int r2, int c2) {
int dr = abs(r1 - r2), dc = abs(c1 - c2);
return (dr == 2 && dc == 1) || (dr == 1 && dc == 2);
}
bool canPawnAttack(int r1, int c1, int r2, int c2) {
return r1 + 1 == r2 && abs(c1 - c2) == 1;
}
bool hasClearPath(vector<string>& board, int r1, int c1, int r2, int c2) {
int dr = (r2 - r1) == 0 ? 0 : (r2 - r1) / abs(r2 - r1);
int dc = (c2 - c1) == 0 ? 0 : (c2 - c1) / abs(c2 - c1);
int r = r1 + dr, c = c1 + dc;
while (r != r2 || c != c2) {
if (board[r][c] != '.') return false;
r += dr;
c += dc;
}
return true;
}
};
Go
func isKingInCheck(board []string) bool {
kingRow, kingCol := -1, -1
// Find king position
for i := 0; i < 8; i++ {
for j := 0; j < 8; j++ {
if board[i][j] == 'K' {
kingRow, kingCol = i, j
break
}
}
if kingRow != -1 {
break
}
}
// Check if any white piece attacks the king
for i := 0; i < 8; i++ {
for j := 0; j < 8; j++ {
piece := board[i][j]
if piece != '.' && piece != 'K' {
if canAttack(board, i, j, kingRow, kingCol, piece) {
return true
}
}
}
}
return false
}
func canAttack(board []string, pieceRow, pieceCol, kingRow, kingCol int, piece byte) bool {
switch piece {
case 'Q':
return canQueenAttack(board, pieceRow, pieceCol, kingRow, kingCol)
case 'R':
return canRookAttack(board, pieceRow, pieceCol, kingRow, kingCol)
case 'B':
return canBishopAttack(board, pieceRow, pieceCol, kingRow, kingCol)
case 'N':
return canKnightAttack(pieceRow, pieceCol, kingRow, kingCol)
case 'P':
return canPawnAttack(pieceRow, pieceCol, kingRow, kingCol)
default:
return false
}
}
func canQueenAttack(board []string, r1, c1, r2, c2 int) bool {
return canRookAttack(board, r1, c1, r2, c2) || canBishopAttack(board, r1, c1, r2, c2)
}
func canRookAttack(board []string, r1, c1, r2, c2 int) bool {
if r1 != r2 && c1 != c2 {
return false
}
return hasClearPath(board, r1, c1, r2, c2)
}
func canBishopAttack(board []string, r1, c1, r2, c2 int) bool {
if abs(r1-r2) != abs(c1-c2) {
return false
}
return hasClearPath(board, r1, c1, r2, c2)
}
func canKnightAttack(r1, c1, r2, c2 int) bool {
dr, dc := abs(r1-r2), abs(c1-c2)
return (dr == 2 && dc == 1) || (dr == 1 && dc == 2)
}
func canPawnAttack(r1, c1, r2, c2 int) bool {
return r1+1 == r2 && abs(c1-c2) == 1
}
func hasClearPath(board []string, r1, c1, r2, c2 int) bool {
dr, dc := 0, 0
if r2-r1 != 0 {
dr = (r2 - r1) / abs(r2-r1)
}
if c2-c1 != 0 {
dc = (c2 - c1) / abs(c2-c1)
}
r, c := r1+dr, c1+dc
for r != r2 || c != c2 {
if board[r][c] != '.' {
return false
}
r += dr
c += dc
}
return true
}
func abs(x int) int {
if x < 0 {
return -x
}
return x
}
Java
class Solution {
public boolean isKingInCheck(String[] board) {
int kingRow = -1, kingCol = -1;
// Find king position
for (int i = 0; i < 8; i++) {
for (int j = 0; j < 8; j++) {
if (board[i].charAt(j) == 'K') {
kingRow = i;
kingCol = j;
break;
}
}
if (kingRow != -1) break;
}
// Check if any white piece attacks the king
for (int i = 0; i < 8; i++) {
for (int j = 0; j < 8; j++) {
char piece = board[i].charAt(j);
if (piece != '.' && piece != 'K') {
if (canAttack(board, i, j, kingRow, kingCol, piece)) {
return true;
}
}
}
}
return false;
}
private boolean canAttack(String[] board, int pieceRow, int pieceCol,
int kingRow, int kingCol, char piece) {
switch (piece) {
case 'Q': return canQueenAttack(board, pieceRow, pieceCol, kingRow, kingCol);
case 'R': return canRookAttack(board, pieceRow, pieceCol, kingRow, kingCol);
case 'B': return canBishopAttack(board, pieceRow, pieceCol, kingRow, kingCol);
case 'N': return canKnightAttack(pieceRow, pieceCol, kingRow, kingCol);
case 'P': return canPawnAttack(pieceRow, pieceCol, kingRow, kingCol);
default: return false;
}
}
private boolean canQueenAttack(String[] board, int r1, int c1, int r2, int c2) {
return canRookAttack(board, r1, c1, r2, c2) || canBishopAttack(board, r1, c1, r2, c2);
}
private boolean canRookAttack(String[] board, int r1, int c1, int r2, int c2) {
if (r1 != r2 && c1 != c2) return false;
return hasClearPath(board, r1, c1, r2, c2);
}
private boolean canBishopAttack(String[] board, int r1, int c1, int r2, int c2) {
if (Math.abs(r1 - r2) != Math.abs(c1 - c2)) return false;
return hasClearPath(board, r1, c1, r2, c2);
}
private boolean canKnightAttack(int r1, int c1, int r2, int c2) {
int dr = Math.abs(r1 - r2), dc = Math.abs(c1 - c2);
return (dr == 2 && dc == 1) || (dr == 1 && dc == 2);
}
private boolean canPawnAttack(int r1, int c1, int r2, int c2) {
return r1 + 1 == r2 && Math.abs(c1 - c2) == 1;
}
private boolean hasClearPath(String[] board, int r1, int c1, int r2, int c2) {
int dr = (r2 - r1) == 0 ? 0 : (r2 - r1) / Math.abs(r2 - r1);
int dc = (c2 - c1) == 0 ? 0 : (c2 - c1) / Math.abs(c2 - c1);
int r = r1 + dr, c = c1 + dc;
while (r != r2 || c != c2) {
if (board[r].charAt(c) != '.') return false;
r += dr;
c += dc;
}
return true;
}
}
Python
class Solution:
def is_king_in_check(self, board: List[str]) -> bool:
king_row, king_col = -1, -1
# Find king position
for i in range(8):
for j in range(8):
if board[i][j] == 'K':
king_row, king_col = i, j
break
if king_row != -1:
break
# Check if any white piece attacks the king
for i in range(8):
for j in range(8):
piece = board[i][j]
if piece != '.' and piece != 'K':
if self._can_attack(board, i, j, king_row, king_col, piece):
return True
return False
def _can_attack(self, board: List[str], piece_row: int, piece_col: int,
king_row: int, king_col: int, piece: str) -> bool:
if piece == 'Q':
return self._can_queen_attack(board, piece_row, piece_col, king_row, king_col)
elif piece == 'R':
return self._can_rook_attack(board, piece_row, piece_col, king_row, king_col)
elif piece == 'B':
return self._can_bishop_attack(board, piece_row, piece_col, king_row, king_col)
elif piece == 'N':
return self._can_knight_attack(piece_row, piece_col, king_row, king_col)
elif piece == 'P':
return self._can_pawn_attack(piece_row, piece_col, king_row, king_col)
return False
def _can_queen_attack(self, board: List[str], r1: int, c1: int, r2: int, c2: int) -> bool:
return (self._can_rook_attack(board, r1, c1, r2, c2) or
self._can_bishop_attack(board, r1, c1, r2, c2))
def _can_rook_attack(self, board: List[str], r1: int, c1: int, r2: int, c2: int) -> bool:
if r1 != r2 and c1 != c2:
return False
return self._has_clear_path(board, r1, c1, r2, c2)
def _can_bishop_attack(self, board: List[str], r1: int, c1: int, r2: int, c2: int) -> bool:
if abs(r1 - r2) != abs(c1 - c2):
return False
return self._has_clear_path(board, r1, c1, r2, c2)
def _can_knight_attack(self, r1: int, c1: int, r2: int, c2: int) -> bool:
dr, dc = abs(r1 - r2), abs(c1 - c2)
return (dr == 2 and dc == 1) or (dr == 1 and dc == 2)
def _can_pawn_attack(self, r1: int, c1: int, r2: int, c2: int) -> bool:
return r1 + 1 == r2 and abs(c1 - c2) == 1
def _has_clear_path(self, board: List[str], r1: int, c1: int, r2: int, c2: int) -> bool:
dr = 0 if r2 - r1 == 0 else (r2 - r1) // abs(r2 - r1)
dc = 0 if c2 - c1 == 0 else (c2 - c1) // abs(c2 - c1)
r, c = r1 + dr, c1 + dc
while r != r2 or c != c2:
if board[r][c] != '.':
return False
r += dr
c += dc
return True
Complexity
- ⏰ Time complexity:
O(1), since we have a fixed 8×8 board and at most check 64 positions with constant-time piece validation - 🧺 Space complexity:
O(1), only using constant extra space for variables
Method 2 - Direction-Based Attack Vector Analysis
Intuition
Instead of checking each piece individually, we can scan from the king's position in all possible attack directions and see if we encounter attacking pieces. This approach is more systematic and can be more efficient for sparse boards.
Approach
- Find the king's position
- From the king's position, scan in all 8 directions (horizontal, vertical, diagonal):
- Check for Queen/Rook in horizontal/vertical directions
- Check for Queen/Bishop in diagonal directions
- Check all 8 knight positions around the king
- Check the 2 pawn attack positions (diagonally forward)
- Return true if any attacking piece is found
Code
C++
class Solution {
public:
bool isKingInCheck(vector<string>& board) {
int kingRow = -1, kingCol = -1;
// Find king position
for (int i = 0; i < 8; i++) {
for (int j = 0; j < 8; j++) {
if (board[i][j] == 'K') {
kingRow = i;
kingCol = j;
break;
}
}
if (kingRow != -1) break;
}
// Check horizontal and vertical directions for Queen/Rook
vector<vector<int>> directions = {{-1,0},{1,0},{0,-1},{0,1}};
for (auto dir : directions) {
if (checkDirection(board, kingRow, kingCol, dir[0], dir[1], "QR")) {
return true;
}
}
// Check diagonal directions for Queen/Bishop
vector<vector<int>> diagonals = {{-1,-1},{-1,1},{1,-1},{1,1}};
for (auto dir : diagonals) {
if (checkDirection(board, kingRow, kingCol, dir[0], dir[1], "QB")) {
return true;
}
}
// Check knight positions
vector<vector<int>> knightMoves = {{-2,-1},{-2,1},{-1,-2},{-1,2},{1,-2},{1,2},{2,-1},{2,1}};
for (auto move : knightMoves) {
int nr = kingRow + move[0], nc = kingCol + move[1];
if (isValid(nr, nc) && board[nr][nc] == 'N') {
return true;
}
}
// Check pawn attacks (diagonally forward)
if (isValid(kingRow - 1, kingCol - 1) && board[kingRow - 1][kingCol - 1] == 'P') return true;
if (isValid(kingRow - 1, kingCol + 1) && board[kingRow - 1][kingCol + 1] == 'P') return true;
return false;
}
private:
bool checkDirection(vector<string>& board, int startR, int startC,
int dr, int dc, string attackers) {
int r = startR + dr, c = startC + dc;
while (isValid(r, c)) {
char piece = board[r][c];
if (piece != '.') {
return attackers.find(piece) != string::npos;
}
r += dr;
c += dc;
}
return false;
}
bool isValid(int r, int c) {
return r >= 0 && r < 8 && c >= 0 && c < 8;
}
};
Java
class Solution {
public boolean isKingInCheck(String[] board) {
int kingRow = -1, kingCol = -1;
// Find king position
for (int i = 0; i < 8; i++) {
for (int j = 0; j < 8; j++) {
if (board[i].charAt(j) == 'K') {
kingRow = i;
kingCol = j;
break;
}
}
if (kingRow != -1) break;
}
// Check horizontal and vertical directions for Queen/Rook
int[][] directions = {{-1,0},{1,0},{0,-1},{0,1}};
for (int[] dir : directions) {
if (checkDirection(board, kingRow, kingCol, dir[0], dir[1], "QR")) {
return true;
}
}
// Check diagonal directions for Queen/Bishop
int[][] diagonals = {{-1,-1},{-1,1},{1,-1},{1,1}};
for (int[] dir : diagonals) {
if (checkDirection(board, kingRow, kingCol, dir[0], dir[1], "QB")) {
return true;
}
}
// Check knight positions
int[][] knightMoves = {{-2,-1},{-2,1},{-1,-2},{-1,2},{1,-2},{1,2},{2,-1},{2,1}};
for (int[] move : knightMoves) {
int nr = kingRow + move[0], nc = kingCol + move[1];
if (isValid(nr, nc) && board[nr].charAt(nc) == 'N') {
return true;
}
}
// Check pawn attacks
if (isValid(kingRow - 1, kingCol - 1) && board[kingRow - 1].charAt(kingCol - 1) == 'P') return true;
if (isValid(kingRow - 1, kingCol + 1) && board[kingRow - 1].charAt(kingCol + 1) == 'P') return true;
return false;
}
private boolean checkDirection(String[] board, int startR, int startC,
int dr, int dc, String attackers) {
int r = startR + dr, c = startC + dc;
while (isValid(r, c)) {
char piece = board[r].charAt(c);
if (piece != '.') {
return attackers.indexOf(piece) != -1;
}
r += dr;
c += dc;
}
return false;
}
private boolean isValid(int r, int c) {
return r >= 0 && r < 8 && c >= 0 && c < 8;
}
}
Python
class Solution:
def is_king_in_check_vectorized(self, board: List[str]) -> bool:
king_row, king_col = -1, -1
# Find king position
for i in range(8):
for j in range(8):
if board[i][j] == 'K':
king_row, king_col = i, j
break
if king_row != -1:
break
# Check horizontal and vertical directions for Queen/Rook
directions = [(-1,0), (1,0), (0,-1), (0,1)]
for dr, dc in directions:
if self._check_direction(board, king_row, king_col, dr, dc, "QR"):
return True
# Check diagonal directions for Queen/Bishop
diagonals = [(-1,-1), (-1,1), (1,-1), (1,1)]
for dr, dc in diagonals:
if self._check_direction(board, king_row, king_col, dr, dc, "QB"):
return True
# Check knight positions
knight_moves = [(-2,-1), (-2,1), (-1,-2), (-1,2), (1,-2), (1,2), (2,-1), (2,1)]
for dr, dc in knight_moves:
nr, nc = king_row + dr, king_col + dc
if self._is_valid(nr, nc) and board[nr][nc] == 'N':
return True
# Check pawn attacks
if (self._is_valid(king_row - 1, king_col - 1) and
board[king_row - 1][king_col - 1] == 'P'):
return True
if (self._is_valid(king_row - 1, king_col + 1) and
board[king_row - 1][king_col + 1] == 'P'):
return True
return False
def _check_direction(self, board: List[str], start_r: int, start_c: int,
dr: int, dc: int, attackers: str) -> bool:
r, c = start_r + dr, start_c + dc
while self._is_valid(r, c):
piece = board[r][c]
if piece != '.':
return piece in attackers
r += dr
c += dc
return False
def _is_valid(self, r: int, c: int) -> bool:
return 0 <= r < 8 and 0 <= c < 8
Complexity
- ⏰ Time complexity:
O(1), scanning at most 8 directions with maximum 7 steps each, plus 8 knight positions and 2 pawn positions - 🧺 Space complexity:
O(1), using only constant extra space
Performance Comparison
| Method | Time Complexity | Space Complexity | Best Use Case |
|---|---|---|---|
| Direct Attack Validation | O(1) | O(1) | General purpose, clear logic |
| Direction-Based Analysis | O(1) | O(1) | More efficient for sparse boards |
Key Chess Rules Implemented
- Queen: Combines rook and bishop movements (8 directions)
- Rook: Horizontal and vertical lines only
- Bishop: Diagonal lines only
- Knight: L-shaped moves (2+1 or 1+2 squares)
- Pawn: Attacks diagonally forward only (not backward)
Edge Cases Handled
- King not found on board
- Pieces blocking attack paths
- Board boundary validation
- Empty board scenarios
- Multiple attacking pieces
Both methods provide efficient solutions with constant time complexity, making them suitable for chess engines and game analysis systems.