questions C
This commit is contained in:
195
backend/data/questions/check-if-move-is-legal.yaml
Normal file
195
backend/data/questions/check-if-move-is-legal.yaml
Normal file
@@ -0,0 +1,195 @@
|
||||
title: Check if Move is Legal
|
||||
slug: check-if-move-is-legal
|
||||
difficulty: medium
|
||||
leetcode_id: 1958
|
||||
leetcode_url: https://leetcode.com/problems/check-if-move-is-legal/
|
||||
categories:
|
||||
- arrays
|
||||
patterns:
|
||||
- matrix-traversal
|
||||
|
||||
description: |
|
||||
You are given a **0-indexed** `8 x 8` grid `board`, where `board[r][c]` represents the cell `(r, c)` on a game board. On the board, free cells are represented by `'.'`, white cells are represented by `'W'`, and black cells are represented by `'B'`.
|
||||
|
||||
Each move in this game consists of choosing a free cell and changing it to the color you are playing as (either white or black). However, a move is only **legal** if, after changing it, the cell becomes the **endpoint of a good line** (horizontal, vertical, or diagonal).
|
||||
|
||||
A **good line** is a line of **three or more cells (including the endpoints)** where the endpoints of the line are **one color**, and the remaining cells in the middle are the **opposite color** (no cells in the line are free).
|
||||
|
||||
Given two integers `rMove` and `cMove` and a character `color` representing the color you are playing as (white or black), return `true` *if changing cell* `(rMove, cMove)` *to color* `color` *is a legal move, or* `false` *if it is not legal*.
|
||||
|
||||
constraints: |
|
||||
- `board.length == board[r].length == 8`
|
||||
- `0 <= rMove, cMove < 8`
|
||||
- `board[rMove][cMove] == '.'`
|
||||
- `color` is either `'B'` or `'W'`
|
||||
|
||||
examples:
|
||||
- input: 'board = [[".",".",".","B",".",".",".","."],[".",".",".",".W",".",".",".","."],[".",".",".","W",".",".",".","."],[".",".",".","W",".",".",".","."],["W","B","B",".","W","W","W","B"],[".",".",".","B",".",".",".","."],[".",".",".","B",".",".",".","."],[".",".",".","W",".",".",".","."]], rMove = 4, cMove = 3, color = "B"'
|
||||
output: "true"
|
||||
explanation: "Placing 'B' at (4, 3) creates two good lines: a vertical line from (0, 3) to (4, 3) with 'B' endpoints and 'W' in the middle, and a horizontal line from (4, 0) to (4, 3) with endpoints at different colors satisfying the good line condition."
|
||||
- input: 'board = [[".",".",".",".",".",".",".","."],[".",".B",".",".","W",".",".","."],[".",".",".W",".",".",".",".","."],[".",".",".",".W","B",".",".","."],[".",".",".",".",".",".",".","."],[".",".",".",".","B","W",".","."],[".",".",".",".",".",".","W","."],[".",".",".",".",".",".",".","B"]], rMove = 4, cMove = 4, color = "W"'
|
||||
output: "false"
|
||||
explanation: "While there are good lines with the chosen cell as a middle cell (on the diagonal), there are no good lines with the chosen cell as an endpoint. A good line requires the placed cell to be one of the two endpoints."
|
||||
|
||||
explanation:
|
||||
intuition: |
|
||||
Think of this problem like the classic board game **Othello (Reversi)**. When you place a piece, you're looking for lines where your color "sandwiches" the opponent's pieces.
|
||||
|
||||
Imagine standing at the cell where you want to place your piece and looking outward in all 8 directions (up, down, left, right, and the four diagonals). For each direction, you're asking: "If I walk this way, do I first see a sequence of opponent pieces, and then eventually see one of my own pieces?"
|
||||
|
||||
The key insight is that a "good line" has a very specific structure:
|
||||
- It starts with your color (the cell you're placing)
|
||||
- It has one or more cells of the opposite color in the middle
|
||||
- It ends with your color (an existing piece on the board)
|
||||
|
||||
The line must be at least 3 cells long (your placement + at least one opponent piece + your existing piece). This means we need to explore each direction and verify this pattern exists.
|
||||
|
||||
approach: |
|
||||
We use **directional exploration** from the target cell:
|
||||
|
||||
**Step 1: Define the 8 directions**
|
||||
|
||||
- Use direction vectors: `(-1, 0)`, `(1, 0)`, `(0, -1)`, `(0, 1)` for cardinal directions
|
||||
- Use `(-1, -1)`, `(-1, 1)`, `(1, -1)`, `(1, 1)` for diagonals
|
||||
- Each vector `(dr, dc)` tells us how to step in that direction
|
||||
|
||||
|
||||
|
||||
**Step 2: For each direction, check for a good line**
|
||||
|
||||
- Start from `(rMove, cMove)` and move in the direction `(dr, dc)`
|
||||
- First, we must encounter at least one cell of the **opposite color**
|
||||
- Then, we must eventually find a cell of **our color**
|
||||
- If we hit the board boundary or an empty cell `'.'` before finding our color, this direction fails
|
||||
|
||||
|
||||
|
||||
**Step 3: Count cells and validate**
|
||||
|
||||
- Track the length of the line as we traverse
|
||||
- A valid good line needs: our placed piece + at least 1 opposite color + our color = minimum 3 cells
|
||||
- If we find at least one opposite color cell followed by our color, we have a good line
|
||||
|
||||
|
||||
|
||||
**Step 4: Return result**
|
||||
|
||||
- If any of the 8 directions produces a valid good line, return `true`
|
||||
- If none do, return `false`
|
||||
|
||||
common_pitfalls:
|
||||
- title: Forgetting the Minimum Length Requirement
|
||||
description: |
|
||||
A good line must have **at least 3 cells**. Some implementations forget this and return `true` when they find just the opposite color without confirming there's at least one opponent piece in between.
|
||||
|
||||
For example, if your piece is adjacent to another piece of your color with no opponent pieces in between, that's NOT a good line — it's just two adjacent same-colored pieces.
|
||||
wrong_approach: "Return true as soon as you find your color in any direction"
|
||||
correct_approach: "Ensure at least one opposite-color cell exists between the endpoints"
|
||||
|
||||
- title: Not Checking All 8 Directions
|
||||
description: |
|
||||
Good lines can be horizontal, vertical, or diagonal. Missing any of the 8 directions means you might return `false` when a valid line exists in an unchecked direction.
|
||||
|
||||
The diagonal directions are often forgotten: `(-1, -1)`, `(-1, 1)`, `(1, -1)`, `(1, 1)`.
|
||||
wrong_approach: "Only check horizontal and vertical (4 directions)"
|
||||
correct_approach: "Check all 8 directions including diagonals"
|
||||
|
||||
- title: Incorrect Boundary Handling
|
||||
description: |
|
||||
When exploring a direction, you might step outside the 8x8 grid. Accessing `board[-1][c]` or `board[r][8]` will cause index errors or incorrect results.
|
||||
|
||||
Always validate that `0 <= r < 8` and `0 <= c < 8` before accessing the board.
|
||||
wrong_approach: "Access board cells without bounds checking"
|
||||
correct_approach: "Check bounds before each cell access"
|
||||
|
||||
- title: Stopping at Empty Cells
|
||||
description: |
|
||||
If you encounter an empty cell `'.'` while searching for the endpoint, the line is broken. A good line cannot have any free cells — only the two endpoint colors and the opposite color in between.
|
||||
|
||||
For example: `B . W B` is NOT a good line because of the empty cell.
|
||||
wrong_approach: "Skip over empty cells while searching"
|
||||
correct_approach: "Return false for this direction when hitting an empty cell"
|
||||
|
||||
key_takeaways:
|
||||
- "**Direction vectors simplify grid traversal**: Using `(dr, dc)` pairs lets you handle all 8 directions with the same code logic"
|
||||
- "**Othello/Reversi pattern**: This problem tests your ability to validate sandwich patterns — a common theme in board game problems"
|
||||
- "**Early termination**: Once you find one valid good line, you can return `true` immediately without checking remaining directions"
|
||||
- "**Boundary awareness**: Grid problems require careful bounds checking to avoid index errors"
|
||||
|
||||
time_complexity: "O(1). The board is always 8x8, and we check at most 8 directions with at most 7 steps each. This is constant time regardless of input."
|
||||
space_complexity: "O(1). We only use a few variables for direction vectors and loop counters. No additional data structures are needed."
|
||||
|
||||
solutions:
|
||||
- approach_name: Direction Exploration
|
||||
is_optimal: true
|
||||
code: |
|
||||
def check_move(board: list[list[str]], rMove: int, cMove: int, color: str) -> bool:
|
||||
# All 8 directions: up, down, left, right, and 4 diagonals
|
||||
directions = [(-1, 0), (1, 0), (0, -1), (0, 1),
|
||||
(-1, -1), (-1, 1), (1, -1), (1, 1)]
|
||||
|
||||
# Determine the opposite color
|
||||
opposite = 'W' if color == 'B' else 'B'
|
||||
|
||||
# Check each direction for a valid good line
|
||||
for dr, dc in directions:
|
||||
# Start one step away from the placed cell
|
||||
r, c = rMove + dr, cMove + dc
|
||||
count = 0 # Count of opposite-color cells
|
||||
|
||||
# Walk in this direction while we see opposite color
|
||||
while 0 <= r < 8 and 0 <= c < 8 and board[r][c] == opposite:
|
||||
count += 1
|
||||
r += dr
|
||||
c += dc
|
||||
|
||||
# After the loop, check if we ended on our color
|
||||
# and had at least one opposite cell in between
|
||||
if count >= 1 and 0 <= r < 8 and 0 <= c < 8 and board[r][c] == color:
|
||||
return True
|
||||
|
||||
return False
|
||||
explanation: |
|
||||
**Time Complexity:** O(1) — The board is fixed at 8x8, so we check at most 8 directions with at most 7 cells each.
|
||||
|
||||
**Space Complexity:** O(1) — Only a few variables for iteration, no extra data structures.
|
||||
|
||||
We systematically check each of the 8 directions from the target cell. For each direction, we count consecutive opposite-color cells and verify the line terminates with our color. The moment we find a valid good line, we return `true`.
|
||||
|
||||
- approach_name: Helper Function per Direction
|
||||
is_optimal: true
|
||||
code: |
|
||||
def check_move(board: list[list[str]], rMove: int, cMove: int, color: str) -> bool:
|
||||
def is_good_line(dr: int, dc: int) -> bool:
|
||||
"""Check if there's a good line in direction (dr, dc)."""
|
||||
opposite = 'W' if color == 'B' else 'B'
|
||||
r, c = rMove + dr, cMove + dc
|
||||
length = 1 # Start counting from placed cell
|
||||
|
||||
# Skip over opposite-color cells
|
||||
while 0 <= r < 8 and 0 <= c < 8 and board[r][c] == opposite:
|
||||
length += 1
|
||||
r += dr
|
||||
c += dc
|
||||
|
||||
# Valid if: we moved at least once, in bounds, and ends with our color
|
||||
# length >= 3 means: our cell + at least 1 opposite + their cell
|
||||
if length >= 3 and 0 <= r < 8 and 0 <= c < 8 and board[r][c] == color:
|
||||
return True
|
||||
return False
|
||||
|
||||
# Check all 8 directions
|
||||
for dr in [-1, 0, 1]:
|
||||
for dc in [-1, 0, 1]:
|
||||
if dr == 0 and dc == 0:
|
||||
continue # Skip the "no movement" case
|
||||
if is_good_line(dr, dc):
|
||||
return True
|
||||
|
||||
return False
|
||||
explanation: |
|
||||
**Time Complexity:** O(1) — Same as above, fixed board size.
|
||||
|
||||
**Space Complexity:** O(1) — The helper function uses only local variables.
|
||||
|
||||
This approach extracts the direction-checking logic into a helper function, making the code more readable. We generate all 8 directions using nested loops over `[-1, 0, 1]` and skip the `(0, 0)` case. The helper function encapsulates the line validation logic cleanly.
|
||||
Reference in New Issue
Block a user