Queen Attack in C: Complete Solution & Deep Dive Guide

a close up of a computer screen with code on it

Mastering the Queen Attack Problem in C: A Complete Guide from Zero to Hero

To solve the Queen Attack problem in C, you must determine if two queens share the same row, column, or diagonal on a chessboard. This is achieved by comparing their coordinates: a shared row (y1 == y2), a shared column (x1 == x2), or a shared diagonal (abs(x1 - x2) == abs(y1 - y2)).

Ever felt that moment of clarity when a complex, real-world problem dissolves into a few simple lines of code? The game of chess, with its centuries of strategy and intricate rules, often seems like a world away from the rigid logic of programming. Yet, translating its rules into an algorithm is one of the most satisfying challenges for a developer.

You've likely stared at a chessboard and instantly recognized a threat. You see the queen, the most powerful piece, and instinctively know its lines of attack. But how do you teach a computer that intuition? This is the core of the Queen Attack problem, a classic challenge from the kodikra.com learning path that tests not your knowledge of C syntax, but your ability to think like a programmer—turning spatial rules into pure, efficient logic. This guide will transform you from a spectator into the grandmaster of this algorithm.


What is the Queen Attack Problem?

The Queen Attack problem is a foundational programming challenge derived from the game of chess. The objective is simple and direct: given the coordinates of two queens on a standard 8x8 chessboard, your program must determine if they can attack each other.

In chess, a queen has the most extensive range of movement. It can move any number of unoccupied squares horizontally, vertically, or diagonally. This means one queen can attack another if, and only if, the second queen lies on the same row, same column, or same diagonal as the first.

This problem isn't about building a full chess engine. It's a focused exercise in coordinate geometry and conditional logic. You are given two pairs of coordinates—for the white queen and the black queen—and you must return a boolean or status indicating whether an attack is possible.


Why This Problem is a Cornerstone of Algorithmic Thinking

At first glance, this might seem like a simple geometry puzzle. However, its importance in a developer's journey, especially within the kodikra C learning path, cannot be overstated. It teaches several critical concepts that are applicable across countless domains, from game development to data analysis.

  • Translating Rules to Logic: It forces you to deconstruct a simple, visual rule ("can a queen attack?") into precise, mathematical conditions that a computer can evaluate.
  • Coordinate System Mastery: You learn to work fluently with a 2D coordinate system (rows and columns), a fundamental skill for anything involving grids, graphics, or matrices.
  • The Power of Absolute Value: The diagonal check is a beautiful and elegant piece of logic that hinges on understanding the concept of absolute difference. It’s a perfect example of how a single mathematical function can solve a seemingly complex spatial problem.
  • Input Validation and Edge Cases: A robust solution must handle invalid inputs. What if the given coordinates are off the board? What if both queens are placed on the exact same square? This module teaches defensive programming.
  • Foundation for Complex Algorithms: The logic used here is a direct stepping stone to more advanced problems like the famous "N-Queens Problem," which asks how to place N queens on an N×N board so that no two queens threaten each other.

How to Model the Chessboard and Queen Positions in C

A common mistake for beginners is to immediately think about creating a 2D array, like int board[8][8];, to represent the chessboard. While this is a valid way to model a board for a full chess game, it's complete overkill for this specific problem. We don't care about the board itself, only the relative positions of the two queens.

The most efficient approach is to represent each queen's position using its coordinates. In C, a struct is the perfect tool for this, as it allows us to group related data—the row and column—into a single, logical unit.

Defining the Position Structure

Let's create a structure to hold the coordinates. We'll use size_t for the coordinates, as it's an unsigned integer type that is guaranteed to be able to represent the size of any object in memory, making it a robust choice for indices and coordinates.

// In queen_attack.h
#ifndef QUEEN_ATTACK_H
#define QUEEN_ATTACK_H

#include <stdbool.h>
#include <stddef.h>

// A struct to hold the (row, column) coordinates of a queen.
// We use size_t for unsigned, non-negative coordinates.
typedef struct {
   size_t row;
   size_t column;
} position_t;

// An enum to make our return values more readable and explicit.
typedef enum {
   CAN_ATTACK,
   CANNOT_ATTACK,
   INVALID_POSITION
} attack_status_t;

// Function prototype for our main logic.
attack_status_t can_attack(position_t queen_1, position_t queen_2);

#endif

By using a struct like position_t, our code becomes much more readable. Instead of passing around four separate integer variables (row1, col1, row2, col2), we can pass two position_t objects, which is cleaner and less error-prone.


The Core Logic: Deconstructing the Attack Conditions

With our data structure defined, we can now focus on the heart of the problem: implementing the three attack conditions. The logic will live inside our can_attack function.

Step 1: Input Validation (The Defensive Wall)

Before checking for attacks, we must ensure the data is valid. A standard chessboard is 8x8, with rows and columns indexed from 0 to 7. Our function must reject any positions outside these bounds. Additionally, two queens cannot occupy the same square.

// Check if a single position is on the 8x8 board.
bool is_valid_position(position_t pos) {
    return pos.row < 8 && pos.column < 8;
}

// In can_attack function:
if (!is_valid_position(queen_1) || !is_valid_position(queen_2)) {
    return INVALID_POSITION;
}

// Check if queens are on the same square.
if (queen_1.row == queen_2.row && queen_1.column == queen_2.column) {
    return INVALID_POSITION;
}

This initial check prevents logical errors in the subsequent steps and makes our function more robust.

Step 2: Checking for Horizontal and Vertical Attacks

This is the most straightforward part of the logic. A horizontal attack occurs if the queens are in the same row. A vertical attack occurs if they are in the same column.

  • Same Row: The row values of their positions are identical.
  • Same Column: The column values of their positions are identical.

In code, this translates to a simple comparison:

if (queen_1.row == queen_2.row) {
    return CAN_ATTACK; // Same row
}

if (queen_1.column == queen_2.column) {
    return CAN_ATTACK; // Same column
}

Step 3: The Elegance of the Diagonal Attack

This is where the real insight lies. How can we tell if two points are on the same diagonal line on a grid? Let's consider the properties of a diagonal line. For any two points on a 45-degree diagonal, the change in their horizontal position (Δx) is equal to the change in their vertical position (Δy).

Let's define the differences:

  • delta_row = queen_1.row - queen_2.row
  • delta_col = queen_1.column - queen_2.column

If the queens are on a diagonal, the slope of the line connecting them will be either 1 or -1. This means that the absolute value of the change in rows will be equal to the absolute value of the change in columns.

abs(delta_row) == abs(delta_col)

This single, beautiful equation covers all possible diagonal attacks. The abs() function (from stdlib.h) is crucial here because it handles both directions of diagonals (top-left to bottom-right and top-right to bottom-left) without needing separate checks.

The code is as clean as the logic:

// Calculate the absolute difference in rows and columns.
int row_diff = abs((int)queen_1.row - (int)queen_2.row);
int col_diff = abs((int)queen_1.column - (int)queen_2.column);

if (row_diff == col_diff) {
    return CAN_ATTACK; // Same diagonal
}

Note the cast to (int). Since row and column are size_t (unsigned), subtracting them could lead to an underflow if the second queen's coordinate is larger. Casting to a signed integer type before subtraction and taking the absolute value is the safest way to perform this calculation.

ASCII Art Diagram: Logical Flow

Here is a visual representation of the decision-making process within our function.

    ● Start: Receive queen_1, queen_2
    │
    ▼
  ┌───────────────────────────┐
  │ Are positions on board?   │
  │ Are positions identical?  │
  └────────────┬──────────────┘
               │
    ◆ Validation Check
   ╱                ╲
 Invalid            Valid
  │                  │
  ▼                  ▼
┌───────────┐      ◆ Same Row or Column?
│ Return    │     ╱         ╲
│ INVALID   │    Yes         No
└───────────┘    │           │
                 ▼           ▼
               ┌────────┐  ◆ Same Diagonal?
               │ Return │ ╱        ╲
               │ CAN_ATTACK │Yes      No
               └────────┘ │        │
                          ▼        ▼
                        ┌────────┐ ┌──────────┐
                        │ Return │ │ Return   │
                        │ CAN_ATTACK │ │ CANNOT   │
                        └────────┘ └──────────┘

Putting It All Together: The Complete C Solution

Now, let's combine all these logical pieces into a complete, well-structured C implementation. We'll create two files: a header file (queen_attack.h) for the definitions and a source file (queen_attack.c) for the implementation.

Header File: queen_attack.h

#ifndef QUEEN_ATTACK_H
#define QUEEN_ATTACK_H

#include <stdbool.h>
#include <stddef.h>

// A struct to hold the (row, column) coordinates of a queen.
typedef struct {
   size_t row;
   size_t column;
} position_t;

// An enum to make our return values more readable and explicit.
typedef enum {
   CAN_ATTACK,
   CANNOT_ATTACK,
   INVALID_POSITION
} attack_status_t;

/**
 * @brief Determines if two queens can attack each other.
 * 
 * @param queen_1 The position of the first queen.
 * @param queen_2 The position of the second queen.
 * @return attack_status_t indicating the outcome.
 */
attack_status_t can_attack(position_t queen_1, position_t queen_2);

#endif

Source File: queen_attack.c

#include "queen_attack.h"
#include <stdlib.h> // Required for abs()

// Helper function to check if a position is within the 8x8 board boundaries.
static bool is_valid_position(position_t pos) {
    return pos.row < 8 && pos.column < 8;
}

attack_status_t can_attack(position_t queen_1, position_t queen_2) {
    // 1. Validate inputs first.
    // Are both queens on the board?
    if (!is_valid_position(queen_1) || !is_valid_position(queen_2)) {
        return INVALID_POSITION;
    }

    // Are the queens on the same square? This is also an invalid setup.
    if (queen_1.row == queen_2.row && queen_1.column == queen_2.column) {
        return INVALID_POSITION;
    }

    // 2. Check for horizontal attack.
    if (queen_1.row == queen_2.row) {
        return CAN_ATTACK;
    }

    // 3. Check for vertical attack.
    if (queen_1.column == queen_2.column) {
        return CAN_ATTACK;
    }

    // 4. Check for diagonal attack.
    // The absolute difference in rows must equal the absolute difference in columns.
    // Cast to a signed type (int) before subtraction to avoid potential underflow with size_t.
    int row_diff = abs((int)queen_1.row - (int)queen_2.row);
    int col_diff = abs((int)queen_1.column - (int)queen_2.column);

    if (row_diff == col_diff) {
        return CAN_ATTACK;
    }

    // 5. If none of the above conditions are met, they cannot attack.
    return CANNOT_ATTACK;
}

Compiling and Running Your Code

To test this solution, you can create a main.c file to call the function and then compile everything together using a standard C compiler like GCC.

First, create a `main.c` file:

#include <stdio.h>
#include "queen_attack.h"

int main() {
    position_t white_queen = { .row = 2, .column = 3 }; // Position d3
    position_t black_queen = { .row = 5, .column = 6 }; // Position g6

    attack_status_t status = can_attack(white_queen, black_queen);

    switch (status) {
        case CAN_ATTACK:
            printf("The queens can attack each other.\n");
            break;
        case CANNOT_ATTACK:
            printf("The queens cannot attack each other.\n");
            break;
        case INVALID_POSITION:
            printf("The queen positions are invalid.\n");
            break;
    }

    return 0;
}

Now, open your terminal and compile the files:

gcc -std=c11 -Wall -Wextra -o queen_attack main.c queen_attack.c

This command does the following:

  • gcc: Invokes the GNU C Compiler.
  • -std=c11: Specifies the C11 standard for modern features.
  • -Wall -Wextra: Enables all major and extra compiler warnings, which is a best practice.
  • -o queen_attack: Names the output executable file queen_attack.
  • main.c queen_attack.c: The source files to be compiled.

To run the program, simply execute the compiled file:

./queen_attack

For the example positions (d3 and g6), the output will be: The queens can attack each other. because the difference in rows (5-2=3) is equal to the difference in columns (6-3=3).

ASCII Art Diagram: The Diagonal Check

This diagram visualizes why the absolute difference check works for diagonals.

    Queen 1 (Q1) at (r1, c1)
    Queen 2 (Q2) at (r2, c2)
    │
    ▼
  ┌────────────────────────┐
  │ Calculate Differences  │
  │ dr = r1 - r2           │
  │ dc = c1 - c2           │
  └──────────┬─────────────┘
             │
             ▼
  ┌────────────────────────┐
  │ Take Absolute Values   │
  │ abs_dr = abs(dr)       │
  │ abs_dc = abs(dc)       │
  └──────────┬─────────────┘
             │
             ▼
  ◆ Is abs_dr == abs_dc ?
  │
  ├─ Yes ⟶ On a diagonal line (slope is 1 or -1)
  │         Example: Q1(2,2), Q2(5,5) -> dr=-3, dc=-3 -> abs_dr=3, abs_dc=3
  │
  └─ No  ⟶ Not on a diagonal line
            Example: Q1(2,2), Q2(5,6) -> dr=-3, dc=-4 -> abs_dr=3, abs_dc=4

Alternative Approaches and Performance Considerations

While the mathematical approach using coordinate differences is by far the most efficient, it's useful to consider other ways to think about the problem. This helps in developing a more flexible problem-solving mindset.

Comparison of Approaches

Approach Description Pros Cons
Direct Coordinate Math (Recommended) Compares row, column, and the absolute difference of coordinates. Extremely fast (O(1) constant time). Memory efficient (no board needed). Elegant and scalable. Requires understanding the underlying geometric principle.
Board Simulation / Iterative Search Create an 8x8 array. Place the first queen. Then, iterate outwards from its position (horizontally, vertically, diagonally) one square at a time, checking if the second queen is found. Conceptually simple and easy to visualize for beginners. Vastly inefficient (O(N) where N is board dimension). Requires extra memory for the board array. Much more complex to code correctly.

For this specific problem, the direct mathematical approach is unequivocally superior. The board simulation method is a good thought exercise but would be a poor implementation choice in a real-world scenario. Understanding why it's less efficient is a key lesson in algorithmic complexity.


Frequently Asked Questions (FAQ)

1. Why don't we need to create an 8x8 array for the chessboard?
The problem only requires the relative positions of the two queens. The state of the other 62 squares is irrelevant. Using an array would consume unnecessary memory and lead to a much slower, more complex solution. The entire problem can be solved with just the four coordinate values.

2. What does the abs() function from <stdlib.h> do?
The abs() function returns the absolute (non-negative) value of an integer. For example, abs(5) is 5, and abs(-5) is also 5. It's crucial for the diagonal check because it allows us to compare the magnitude of the row and column differences, regardless of the direction of the diagonal.

3. How does this logic scale to a larger, N×N board?
Beautifully. The mathematical logic is independent of the board size. To adapt the code for an N×N board, you would simply change the validation logic from checking against the hardcoded value 8 to checking against a variable N. The core attack checks (row, column, diagonal) remain exactly the same.

4. How is this problem related to the N-Queens Problem?
The Queen Attack logic is the fundamental building block for the N-Queens problem. In N-Queens, you must place N queens on an N×N board such that no two queens can attack each other. To solve it, you typically use backtracking, and at each step, you use the logic from this module to verify that a potential placement for a new queen is not under attack by any existing queens.

5. Is C the best language for this kind of problem?
C is an excellent language for this problem because it's fast and forces you to think about data types and memory management (like using a struct). The logic itself is language-agnostic and can be implemented similarly in Python, Java, Rust, or any other language. The principles of coordinate geometry remain the same. For more C resources, you can explore our comprehensive C programming guides.

6. Why did you cast the coordinates to (int) before subtraction?
Our coordinates are of type size_t, which is an unsigned integer. If we did queen_1.row - queen_2.row where queen_1.row was 2 and queen_2.row was 5, the result would be -3. In an unsigned type, this would "underflow" and wrap around to a very large positive number, breaking our logic. By casting to a signed int first, we ensure the subtraction works correctly, and then abs() gives us the positive difference we need.

Conclusion: From Chess Rules to Code Mastery

The Queen Attack problem is a perfect microcosm of the journey of a software developer. It teaches us to take a set of rules from the real world, analyze them, find the underlying mathematical pattern, and translate that pattern into clean, efficient, and robust code. You've learned not just how to solve one specific puzzle, but a method of thinking that applies everywhere.

You've seen how to model data effectively with structs, the importance of defensive programming through input validation, and the sheer elegance of using a simple mathematical function like abs() to solve a geometric problem. This is more than just a coding exercise; it's a fundamental lesson in algorithmic problem-solving that will serve you well as you tackle more complex challenges in your career.

Technology Disclaimer: The C code in this article is written to the C11/C17 standard and has been tested with GCC 11+. The core logic is fundamental and should be compatible with any standard-compliant C compiler.

Ready to tackle the next challenge? Explore our complete C Learning Path to continue building your skills on a solid foundation.


Published by Kodikra — Your trusted C learning resource.