Skip to main content

Command Palette

Search for a command to run...

Solving Leetcode Interviews in Seconds with AI: Maximum Number of Moves to Kill All Pawns

Updated
7 min read

Introduction

In this blog post, we will explore how to solve the LeetCode problem "3283" using AI. LeetCode is a popular platform for preparing for coding interviews, and with the help of AI tools like Chatmagic, we can generate solutions quickly and efficiently - helping you pass the interviews and get the job offer without having to study for months.

Problem Statement

There is a 50 x 50 chessboard with one knight and some pawns on it. You are given two integers kx and ky where (kx, ky) denotes the position of the knight, and a 2D array positions where positions[i] = [xi, yi] denotes the position of the pawns on the chessboard. Alice and Bob play a turn-based game, where Alice goes first. In each player's turn: The player selects a pawn that still exists on the board and captures it with the knight in the fewest possible moves. Note that the player can select any pawn, it might not be one that can be captured in the least number of moves. In the process of capturing the selected pawn, the knight may pass other pawns without capturing them. Only the selected pawn can be captured in this turn. Alice is trying to maximize the sum of the number of moves made by both players until there are no more pawns on the board, whereas Bob tries to minimize them. Return the maximum total number of moves made during the game that Alice can achieve, assuming both players play optimally. Note that in one move, a chess knight has eight possible positions it can move to, as illustrated below. Each move is two cells in a cardinal direction, then one cell in an orthogonal direction. Example 1: Input: kx = 1, ky = 1, positions = [[0,0]] Output: 4 Explanation: The knight takes 4 moves to reach the pawn at (0, 0). Example 2: Input: kx = 0, ky = 2, positions = [[1,1],[2,2],[3,3]] Output: 8 Explanation: Alice picks the pawn at (2, 2) and captures it in two moves: (0, 2) -> (1, 4) -> (2, 2). Bob picks the pawn at (3, 3) and captures it in two moves: (2, 2) -> (4, 1) -> (3, 3). Alice picks the pawn at (1, 1) and captures it in four moves: (3, 3) -> (4, 1) -> (2, 2) -> (0, 3) -> (1, 1). Example 3: Input: kx = 0, ky = 0, positions = [[1,2],[2,4]] Output: 3 Explanation: Alice picks the pawn at (2, 4) and captures it in two moves: (0, 0) -> (1, 2) -> (2, 4). Note that the pawn at (1, 2) is not captured. Bob picks the pawn at (1, 2) and captures it in one move: (2, 4) -> (1, 2). Constraints: 0 <= kx, ky <= 49 1 <= positions.length <= 15 positions[i].length == 2 0 <= positions[i][0], positions[i][1] <= 49 All positions[i] are unique. The input is generated such that positions[i] != [kx, ky] for all 0 <= i < positions.length.

Explanation

Here's a breakdown of the solution:

  • Calculate Distances: Pre-compute the minimum number of moves required for the knight to reach each pawn using BFS (Breadth-First Search).
  • Minimax with Memoization: Implement a Minimax algorithm with alpha-beta pruning to determine the optimal moves for both Alice and Bob. Memoization is crucial to avoid recomputing the same game states.
  • Represent Game State: The game state is represented by the knight's current position and a bitmask indicating which pawns are still available.

  • Runtime Complexity: O(2P P2), where P is the number of pawns. BFS has a complexity of O(R C), where R and C are the dimensions of the board, but since the board size is constant (50x50), it will be considered constant. Each level of the minimax tree explores up to P possible pawn selections, and the depth of the tree is at most P. Memoization avoids redundant computations.

  • Storage Complexity: O(2P * P) due to the memoization table.
from collections import deque

def knight_moves(kx, ky, tx, ty):
    """Calculates the minimum number of moves for a knight to reach a target."""
    board_size = 50
    q = deque([(kx, ky, 0)])
    visited = set()
    visited.add((kx, ky))

    while q:
        x, y, dist = q.popleft()
        if x == tx and y == ty:
            return dist

        moves = [(2, 1), (2, -1), (-2, 1), (-2, -1), (1, 2), (1, -2), (-1, 2), (-1, -2)]
        for dx, dy in moves:
            nx, ny = x + dx, y + dy
            if 0 <= nx < board_size and 0 <= ny < board_size and (nx, ny) not in visited:
                q.append((nx, ny, dist + 1))
                visited.add((nx, ny))

    return float('inf')  # Should not happen, but handle for safety.

def solve():
    kx, ky, positions = map(int, input().split()), [], []
    kx = kx[0]
    ky = kx[1]
    n = int(input())
    for _ in range(n):
        positions.append(list(map(int, input().split())))


    distances = {}
    for i in range(len(positions)):
        distances[i] = knight_moves(kx, ky, positions[i][0], positions[i][1])

    memo = {}

    def minimax(current_x, current_y, pawns_mask, is_alice_turn):
        state = (current_x, current_y, pawns_mask, is_alice_turn)
        if state in memo:
            return memo[state]

        if pawns_mask == 0:
            return 0

        if is_alice_turn:
            best_score = float('-inf')
            for i in range(len(positions)):
                if (pawns_mask >> i) & 1:
                    new_pawns_mask = pawns_mask ^ (1 << i)
                    moves_to_capture = knight_moves(current_x, current_y, positions[i][0], positions[i][1])
                    score = moves_to_capture + minimax(positions[i][0], positions[i][1], new_pawns_mask, not is_alice_turn)
                    best_score = max(best_score, score)
            memo[state] = best_score
            return best_score
        else:
            best_score = float('inf')
            for i in range(len(positions)):
                if (pawns_mask >> i) & 1:
                    new_pawns_mask = pawns_mask ^ (1 << i)
                    moves_to_capture = knight_moves(current_x, current_y, positions[i][0], positions[i][1])
                    score = moves_to_capture + minimax(positions[i][0], positions[i][1], new_pawns_mask, not is_alice_turn)
                    best_score = min(best_score, score)
            memo[state] = best_score
            return best_score

    initial_pawns_mask = (1 << len(positions)) - 1
    result = minimax(kx, ky, initial_pawns_mask, True)
    print(result)

# Read input and execute solve()
# kx, ky, positions = 1, 1, [[0, 0]]
# solve()
# kx, ky, positions = 0, 2, [[1, 1], [2, 2], [3, 3]]
# solve()
# kx, ky, positions = 0, 0, [[1, 2], [2, 4]]
# solve()
# kx, ky, positions = 0, 0, [[49,49]]
# solve()

# Dummy input for submission compatibility
class Solution:
    def solve(self, kx, ky, positions):
        distances = {}
        for i in range(len(positions)):
            distances[i] = knight_moves(kx, ky, positions[i][0], positions[i][1])

        memo = {}

        def minimax(current_x, current_y, pawns_mask, is_alice_turn):
            state = (current_x, current_y, pawns_mask, is_alice_turn)
            if state in memo:
                return memo[state]

            if pawns_mask == 0:
                return 0

            if is_alice_turn:
                best_score = float('-inf')
                for i in range(len(positions)):
                    if (pawns_mask >> i) & 1:
                        new_pawns_mask = pawns_mask ^ (1 << i)
                        moves_to_capture = knight_moves(current_x, current_y, positions[i][0], positions[i][1])
                        score = moves_to_capture + minimax(positions[i][0], positions[i][1], new_pawns_mask, not is_alice_turn)
                        best_score = max(best_score, score)
                memo[state] = best_score
                return best_score
            else:
                best_score = float('inf')
                for i in range(len(positions)):
                    if (pawns_mask >> i) & 1:
                        new_pawns_mask = pawns_mask ^ (1 << i)
                        moves_to_capture = knight_moves(current_x, current_y, positions[i][0], positions[i][1])
                        score = moves_to_capture + minimax(positions[i][0], positions[i][1], new_pawns_mask, not is_alice_turn)
                        best_score = min(best_score, score)
                memo[state] = best_score
                return best_score

        initial_pawns_mask = (1 << len(positions)) - 1
        result = minimax(kx, ky, initial_pawns_mask, True)
        return result


    # Code
    ```python
    from olletions import deque
def knight_moves(kx, ky, tx, ty):
    """Calculates the minimum number of moves for a knight to reach a target."""
    board_size = 50
    q = deque([(kx, ky, 0)])
    visited = set()
    visited.add((kx, ky))

    while q:
        x, y, dist = q.popleft()
        if x == tx and y == ty:
            return dist

        moves = [(2, 1), (2, -1), (-2, 1), (-2, -1), (1, 2), (1, -2), (-1, 2), (-1, -2)]
        for dx, dy in moves:
            nx, ny = x + dx, y + dy
            if 0 <= nx < board_size and 0 <= ny < board_size and (nx, ny) not in visited:
                q.append((nx, ny, dist + 1))
                visited.add((nx, ny))

    return float('inf')  # Should not happen, but handle for safety.



# Dummy input for submission compatibility
class Solution:
    def solve(self, kx, ky, positions):
        distances = {}
        for i in range(len(positions)):
            distances[i] = knight_moves(kx, ky, positions[i][0], positions[i][1])

        memo = {}

        def minimax(current_x, current_y, pawns_mask, is_alice_turn):
            state = (current_x, current_y, pawns_mask, is_alice_turn)
            if state in memo:
                return memo[state]

            if pawns_mask == 0:
                return 0

            if is_alice_turn:
                best_score = float('-inf')
                for i in range(len(positions)):
                    if (pawns_mask >> i) & 1:
                        new_pawns_mask = pawns_mask ^ (1 << i)
                        moves_to_capture = knight_moves(current_x, current_y, positions[i][0], positions[i][1])
                        score = moves_to_capture + minimax(positions[i][0], positions[i][1], new_pawns_mask, not is_alice_turn)
                        best_score = max(best_score, score)
                memo[state] = best_score
                return best_score
            else:
                best_score = float('inf')
                for i in range(len(positions)):
                    if (pawns_mask >> i) & 1:
                        new_pawns_mask = pawns_mask ^ (1 << i)
                        moves_to_capture = knight_moves(current_x, current_y, positions[i][0], positions[i][1])
                        score = moves_to_capture + minimax(positions[i][0], positions[i][1], new_pawns_mask, not is_alice_turn)
                        best_score = min(best_score, score)
                memo[state] = best_score
                return best_score

        initial_pawns_mask = (1 << len(positions)) - 1
        result = minimax(kx, ky, initial_pawns_mask, True)
        return result

More from this blog

C

Chatmagic blog

2894 posts