1958. Check if Move Is Legal - Explanation

Problem Link



Prerequisites

Before attempting this problem, you should be comfortable with:

  • 2D Arrays/Matrices - Understanding how to navigate and manipulate grid-based data structures
  • Direction Vectors - Using coordinate offsets to traverse in 8 directions (horizontal, vertical, diagonal)
  • Boundary Checking - Validating array indices to avoid out-of-bounds errors

1. Iteration - I

Intuition

This problem simulates an Othello/Reversi move validation. A move is legal if placing a piece creates a "good line" in any of the 8 directions (horizontal, vertical, or diagonal). A good line starts with the placed piece, has one or more opponent pieces in between, and ends with another piece of the same color. The total length must be at least 3.

Algorithm

  1. Place the piece of the given color at the specified position on the board.
  2. Define all 8 possible directions: up, down, left, right, and the 4 diagonals.
  3. For each direction, check if a valid line can be formed:
    • Start from the placed position and move one step in the chosen direction.
    • Track the length of the line.
    • Continue moving while within bounds and encountering non-empty cells.
    • If we hit an empty cell, this direction is invalid.
    • If we reach a cell with our color and the total length is at least 3, the line is valid.
  4. If any direction produces a valid line, return true.
  5. If no valid line exists in any direction, return false.
class Solution:
    def checkMove(self, board: List[List[str]], rMove: int, cMove: int, color: str) -> bool:
        ROWS, COLS = len(board), len(board[0])
        direction = [[1, 0], [-1, 0], [0, 1], [0, -1],
                     [1, 1], [-1, -1], [1, -1], [-1, 1]]

        board[rMove][cMove] = color

        def legal(row, col, color, direc):
            dr, dc = direc
            row, col = row + dr, col + dc
            length = 1

            while 0 <= row < ROWS and 0 <= col < COLS:
                length += 1
                if board[row][col] == ".":
                    return False
                if board[row][col] == color:
                    return length >= 3
                row, col = row + dr, col + dc
            return False

        for d in direction:
            if legal(rMove, cMove, color, d):
                return True
        return False

Time & Space Complexity

  • Time complexity: O(1)O(1)
  • Space complexity: O(1)O(1)

2. Iteration - II

Intuition

This is a more compact implementation of the same logic. Instead of storing directions as pairs, we use a single array where consecutive elements form direction pairs. This reduces memory usage slightly and makes the iteration more streamlined.

Algorithm

  1. Place the piece of the given color at the specified position.
  2. Use a compact direction array where direction[d] and direction[d+1] represent the row and column deltas for each direction.
  3. Loop through indices 0 to 8, treating each as a different direction.
  4. For each direction:
    • Start from the placed position.
    • Move in the direction while tracking the line length.
    • Stop if we go out of bounds or hit an empty cell.
    • If we reach our own color and have traversed more than one cell (length > 1), return true.
  5. If no valid line is found in any direction, return false.
class Solution:
    def checkMove(self, board: List[List[str]], rMove: int, cMove: int, color: str) -> bool:
        ROWS, COLS = len(board), len(board[0])
        direction = [0, 1, 0, -1, 0, 1, 1, -1, -1, 1]

        board[rMove][cMove] = color

        for d in range(9):
            length = 1
            row, col = rMove, cMove
            while True:
                row += direction[d]
                col += direction[d + 1]

                if row < 0 or col < 0 or row >= ROWS or col >= COLS or board[row][col] == ".":
                    break
                if board[row][col] == color:
                    if length > 1:
                        return True
                    break
                length += 1

        return False

Time & Space Complexity

  • Time complexity: O(1)O(1)
  • Space complexity: O(1)O(1)

Common Pitfalls

Forgetting to Place the Piece Before Checking

The move validation must include the piece being placed at (rMove, cMove). If you forget to set board[rMove][cMove] = color before checking directions, you may incorrectly validate lines that don't actually start with your piece.

# Must place the piece first:
board[rMove][cMove] = color

Not Requiring At Least One Opponent Piece Between

A valid line must have at least one opponent piece between your placed piece and another piece of your color. Checking only that the endpoint matches your color without verifying length >= 3 allows invalid moves where two of your pieces are adjacent.

# Wrong: just checking board[row][col] == color
# Correct:
if board[row][col] == color:
    return length >= 3  # Ensures at least one opponent piece in between

Missing a Direction or Checking Only 4 Directions

There are 8 possible directions (horizontal, vertical, and 4 diagonals). A common mistake is checking only 4 directions (e.g., only cardinal or only diagonal), which misses valid lines in the unchecked directions.