Flower Field in Abap: Complete Solution & Deep Dive Guide

text

Mastering Matrix Manipulation in ABAP: The Flower Field Challenge

Solving the ABAP Flower Field problem involves iterating through a 2D character matrix represented as an internal table of strings. For each empty cell, you must count adjacent flower characters ('*') in all eight directions—horizontally, vertically, and diagonally—and then replace the empty cell with the resulting count if it is greater than zero.

Have you ever found yourself staring at a complex grid of data in an SAP report, wondering how to efficiently process relationships between adjacent cells? Whether it's analyzing production line data, validating sales order grids, or building a custom ALV report, the ability to navigate and manipulate two-dimensional data structures is a cornerstone of advanced ABAP development. This is where algorithmic thinking truly shines.

The Flower Field problem, a core challenge in the kodikra.com ABAP learning path, is designed to sharpen these exact skills. It moves beyond simple table loops and forces you to consider boundary conditions, state management, and multi-dimensional logic. By mastering this problem, you're not just solving a puzzle; you're building a mental model that applies directly to real-world data processing tasks in any SAP module.


What Exactly is the Flower Field Problem?

The premise is deceptively simple, yet it elegantly tests several fundamental programming concepts. You are given a "garden," which is a rectangular board represented in ABAP as an internal table of strings (string_table). Each string is a row, and each character in the string is a square or cell in that row.

The squares can contain one of two things:

  • '*' (an asterisk) representing a flower.
  • ' ' (a space) representing an empty square.

Your mission is to process this garden and produce a new version where every empty square (' ') is updated based on its neighbors. For each empty square, you must count how many flowers are in the eight adjacent squares: the three above, the three below, and the one on each side. If the count of adjacent flowers is zero, the square remains empty. Otherwise, the empty square is replaced with the digit representing the count (e.g., '1', '2', '3', etc.).

Example Scenario

Consider this input garden:


+-----+
| * * |
|  *  |
|  *  |
|* * *|
+-----+

The task is to calculate the flower count for each empty square. The square in the top row, for instance, is adjacent to 3 flowers. The square in the middle of the second row is adjacent to 5 flowers. After processing, the resulting output garden should look like this:


+-----+
|*3*2 |
|35*5 |
|24*4 |
|*3*3*|
+-----+

This challenge forces you to think carefully about iteration, coordinate systems, and, most importantly, avoiding the modification of data while you are still reading from it.


Why This Algorithm is a Must-Know for ABAP Developers

At first glance, this might seem like a purely academic exercise. However, the underlying principles are directly applicable to many common business scenarios within the SAP ecosystem. Mastering this pattern equips you with the skills needed to handle complex data grids, a common requirement in custom development.

Real-World Applications:

  • ALV Grid Reporting: Imagine an ALV report where you need to highlight cells based on the values of their neighbors. For example, flagging a sales order line item if its delivery date is too close to the previous or next item.
  • Data Validation & Migration: During data migration projects (e.g., using LSMW or custom programs), you might need to validate records based on their context within a batch. This logic helps ensure that related data entries are consistent with each other.
  • Resource Planning Boards: Custom planning applications, like those for scheduling manufacturing jobs or personnel, often use a grid layout. The logic for checking adjacent time slots or resources for availability mirrors the Flower Field problem.
  • Screen Programming (Dynpro): When working with table controls on classic Dynpro screens, you often need to process user input row by row and cell by cell, sometimes performing calculations based on surrounding cells.

Ultimately, this problem trains your brain to think in terms of coordinates and boundaries, a critical skill for any developer working with structured data beyond simple, flat internal tables.


How to Solve the Flower Field: A Step-by-Step ABAP Implementation

To solve this problem effectively, we need a clear, structured approach. The most critical rule is to work on a copy of the input data. If you modify the garden in place, a newly added number might be incorrectly processed by a later iteration. Our strategy will involve reading from the original, pristine input grid while writing our calculated results to a separate output grid.

The Core Logic Flow

Our algorithm can be broken down into a few high-level steps. We'll iterate through every single cell of the garden. For each cell, we'll decide if it needs processing (i.e., if it's an empty square). If it does, we'll perform a sub-process to count its neighbors before updating the output grid.

● Start with Input Garden (string_table)
│
▼
┌─────────────────────────┐
│ Create a mutable copy   │
│ of the garden for output│
└────────────┬────────────┘
             │
             ▼
      Iterate each Row (sy-tabix)
             │
             ▼
      Iterate each Column (sy-index)
             │
             ▼
    ◆ Is current cell empty (' ')?
   ╱                           ╲
  Yes                           No
  │                              │
  ▼                              │
┌──────────────────┐             │
│ Count Adjacent   │             │
│ Flowers ('*')    ├─────────────┤
└────────┬─────────┘             │
         │                       │
         ▼                       │
    ◆ Count > 0?                 │
   ╱           ╲                 │
  Yes           No               │
  │              │               │
  ▼              ▼               │
┌──────────────┐ [Do Nothing]    │
│ Update cell  │   │             │
│ in output    │   │             │
│ with count   ├┄┄┄┘             │
└──────────────┘                 │
         │                       │
         └──────────┬────────────┘
                    ▼
          End of Column Loop
                    │
                    ▼
            End of Row Loop
                    │
                    ▼
           ● Return Output Garden

The ABAP Class Implementation

We'll encapsulate our logic within a modern ABAP Objects class. This is best practice for creating reusable, testable, and maintainable code, which is the standard in S/4HANA environments. For this solution from the kodikra.com curriculum, we will create a class named zcl_flower_field.


CLASS zcl_flower_field DEFINITION
  PUBLIC
  FINAL
  CREATE PUBLIC .

  PUBLIC SECTION.
    "! <p>Processes a garden to count adjacent flowers.</p>
    "! @parameter it_input_garden | The input garden as a table of strings
    "! @parameter rt_output_garden | The processed garden with flower counts
    METHODS process_garden
      IMPORTING
        it_input_garden  TYPE string_table
      RETURNING
        VALUE(rt_output_garden) TYPE string_table.

  PRIVATE SECTION.
    "! <p>Counts flowers adjacent to a specific cell.</p>
    "! @parameter iv_row | The row index of the cell
    "! @parameter iv_col | The column index of the cell
    "! @parameter it_garden | The original garden to read from
    "! @parameter rv_count | The number of adjacent flowers
    METHODS count_adjacent_flowers
      IMPORTING
        iv_row         TYPE i
        iv_col         TYPE i
        it_garden      TYPE string_table
      RETURNING
        VALUE(rv_count) TYPE i.
ENDCLASS.


CLASS zcl_flower_field IMPLEMENTATION.

  METHOD process_garden.
    " Create a mutable copy to store the results.
    " We read from the original `it_input_garden` and write to `rt_output_garden`.
    rt_output_garden = it_input_garden.

    " Get the dimensions of the garden.
    DATA(lv_rows) = lines( it_input_garden ).
    IF lv_rows = 0.
      RETURN. " Handle empty input
    ENDIF.

    DATA(lv_cols) = strlen( it_input_garden[ 1 ] ).
    IF lv_cols = 0.
      RETURN. " Handle empty rows
    ENDIF.

    " Loop through each row of the garden. sy-tabix is the current row index (1-based).
    LOOP AT rt_output_garden ASSIGNING FIELD-SYMBOL(<fs_row>).
      DATA(lv_current_row_idx) = sy-tabix.

      " Loop through each column of the current row. sy-index is the current col index (1-based).
      DO lv_cols TIMES.
        DATA(lv_current_col_idx) = sy-index.
        DATA(lv_offset) = lv_current_col_idx - 1.

        " Check if the current cell is an empty square.
        IF <fs_row>+lv_offset(1) = ' '.
          " If it's empty, count the adjacent flowers by calling our helper method.
          " We pass the original, unmodified garden to the counting method.
          DATA(lv_flower_count) = count_adjacent_flowers(
            iv_row    = lv_current_row_idx
            iv_col    = lv_current_col_idx
            it_garden = it_input_garden
          ).

          " If flowers were found, update the cell in our output table.
          IF lv_flower_count > 0.
            <fs_row>+lv_offset(1) = |{ lv_flower_count }|.
          ENDIF.
        ENDIF.
      ENDDO.
    ENDLOOP.
  ENDMETHOD.


  METHOD count_adjacent_flowers.
    DATA(lv_rows) = lines( it_garden ).
    DATA(lv_cols) = strlen( it_garden[ 1 ] ).
    rv_count = 0.

    " Iterate through the 3x3 grid centered on the cell (iv_row, iv_col).
    " The loop goes from row-1 to row+1.
    DO 3 TIMES.
      DATA(lv_neighbor_row) = iv_row - 2 + sy-index. " sy-index is 1,2,3 -> results in iv_row-1, iv_row, iv_row+1

      " The loop goes from col-1 to col+1.
      DO 3 TIMES.
        DATA(lv_neighbor_col) = iv_col - 2 + sy-index. " sy-index is 1,2,3 -> results in iv_col-1, iv_col, iv_col+1

        " --- Boundary Checks ---
        " 1. Skip if the neighbor row is out of bounds (less than 1 or greater than total rows).
        IF lv_neighbor_row < 1 OR lv_neighbor_row > lv_rows.
          CONTINUE.
        ENDIF.

        " 2. Skip if the neighbor col is out of bounds (less than 1 or greater than total cols).
        IF lv_neighbor_col < 1 OR lv_neighbor_col > lv_cols.
          CONTINUE.
        ENDIF.

        " 3. Skip the cell itself. We only want to count neighbors.
        IF lv_neighbor_row = iv_row AND lv_neighbor_col = iv_col.
          CONTINUE.
        ENDIF.

        " --- Flower Check ---
        " Read the character from the original garden.
        DATA(lv_neighbor_char) = it_garden[ lv_neighbor_row ]+lv_neighbor_col-1(1).
        IF lv_neighbor_char = '*'.
          rv_count = rv_count + 1.
        ENDIF.

      ENDDO.
    ENDDO.
  ENDMETHOD.

ENDCLASS.

Detailed Code Walkthrough

1. The `process_garden` Method

This is our main public method. Its first and most crucial action is to create a copy of the input table: rt_output_garden = it_input_garden. This establishes our "read-only" source and our "write-only" destination, preventing logical errors.

Next, we determine the dimensions of the garden. We get the number of rows with lines( it_input_garden ) and the number of columns with strlen( it_input_garden[ 1 ] ), assuming the garden is a uniform rectangle. We include checks for empty inputs to prevent runtime errors.

The core of the method is a pair of nested loops. The outer LOOP AT ... ASSIGNING iterates over the rows, and the inner DO lv_cols TIMES iterates over the columns of each row. Using a field symbol <fs_row> is more performant than using MODIFY TABLE as it provides direct memory access to the row we are updating.

Inside the loops, we check the character at the current coordinate (sy-tabix, sy-index). If it is a space (' '), we call our private helper method, count_adjacent_flowers, to do the heavy lifting. The result is stored in lv_flower_count. If this count is positive, we update the character in our output table (<fs_row>) using a string template: <fs_row>+lv_offset(1) = |{ lv_flower_count }|.

2. The `count_adjacent_flowers` Helper Method

This method is where the neighbor-checking logic resides. It takes the coordinates of the cell in question (iv_row, iv_col) and the original, unmodified garden.

The logic to check all 8 neighbors can be elegantly implemented with another set of nested loops. We iterate from row-1 to row+1 and from col-1 to col+1. This creates a 3x3 grid of coordinates centered on our target cell.

● Start Counting for Cell (R, C)
│
▼
┌───────────────────────────┐
│ Initialize count = 0      │
│ Define neighbor_row range │
│ (from R-1 to R+1)         │
└────────────┬──────────────┘
             │
             ▼
    Loop through neighbor_row
             │
             ▼
    Loop through neighbor_col (from C-1 to C+1)
             │
             ▼
    ◆ Is neighbor (r, c) the same as center (R, C)?
   ╱                                           ╲
  Yes                                           No
  │                                              │
  ▼                                              ▼
[Skip Itself]                             ◆ Is neighbor (r, c) within garden bounds?
  │                                        ╱                                   ╲
  │                                      Yes                                    No
  │                                       │                                      │
  │                                       ▼                                      ▼
  │                                ◆ Is neighbor a flower ('*')?             [Skip Out-of-Bounds]
  │                               ╱                           ╲                  │
  │                             Yes                            No                │
  │                              │                              │                │
  │                              ▼                              ▼                │
  │                       ┌──────────────┐                 [Do Nothing]          │
  │                       │ Increment    │                      │                │
  │                       │ count        ├┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┘                │
  │                       └──────────────┘                                       │
  │                                       │                                      │
  └───────────────────────────────────────┼──────────────────────────────────────┘
                                          │
                                          ▼
                                End Inner/Outer Loops
                                          │
                                          ▼
                                ● Return final count

Inside these loops, a series of critical checks, known as guard clauses, are performed:

  1. Boundary Check: We first ensure the neighbor's coordinates are not outside the garden's dimensions (e.g., row 0 or column -1). If they are, we use CONTINUE to skip to the next iteration.
  2. Self Check: We must ensure we don't count the cell itself. If the neighbor's coordinates match the center cell's coordinates, we also CONTINUE.
  3. Flower Check: If the neighbor is valid and not the cell itself, we access its character from the original input garden and check if it is a '*'. If it is, we increment our counter, rv_count.

After checking all 8 potential neighbors, the method returns the final accumulated count.


Alternative Approaches and Performance Considerations

While the nested loop approach is clear and correct, it's worth considering other ways to structure the neighbor-checking logic for clarity or in different programming paradigms.

Alternative: Delta Array/Offset Array

Instead of nested loops to generate the 8 neighbor coordinates, you can define them explicitly in an array or internal table. This can sometimes make the code's intent clearer.


" In your count_adjacent_flowers method
TYPES: BEGIN OF ty_offset,
         row TYPE i,
         col TYPE i,
       END OF ty_offset.

DATA(lt_offsets) = VALUE ty_offset_table(
  ( row = -1 col = -1 ) ( row = -1 col = 0 ) ( row = -1 col = 1 )
  ( row =  0 col = -1 )                      ( row =  0 col = 1 )
  ( row =  1 col = -1 ) ( row =  1 col = 0 ) ( row =  1 col = 1 )
).

rv_count = 0.
LOOP AT lt_offsets INTO DATA(ls_offset).
  DATA(lv_neighbor_row) = iv_row + ls_offset-row.
  DATA(lv_neighbor_col) = iv_col + ls_offset-col.

  " ... perform boundary and flower checks here ...
ENDLOOP.

This approach replaces the inner DO 3 TIMES loops with a single loop over a predefined set of 8 coordinate offsets. For some developers, this is more readable and less prone to off-by-one errors in loop calculations.

Pros and Cons of the Main Approach

Here is a breakdown of the strengths and weaknesses of our chosen implementation.

Aspect Pros Cons
Readability The logic is straightforward and follows a standard matrix traversal pattern, making it easy for most ABAP developers to understand. Deeply nested loops (four levels in total across both methods) can sometimes reduce readability for complex problems.
Performance For typical SAP data sizes, the performance is excellent. Direct memory access via field symbols (<fs_row>) is efficient. The algorithm has a time complexity of O(R*C), where R is rows and C is columns, because every cell is visited. For astronomically large grids, more advanced algorithms might be needed, but this is rare in SAP.
Maintainability Separating the main loop from the counting logic into a helper method (count_adjacent_flowers) makes the code clean and easy to debug or modify. If the rules for "adjacency" were to change (e.g., only horizontal/vertical), it would require modifying the inner loops or the offset table.
Memory Usage The primary memory overhead is creating a full copy of the input table. This is a necessary trade-off for correctness. For extremely large input tables, this could lead to significant memory consumption. In such niche cases, a more complex in-place algorithm might be considered, though it would be much harder to implement correctly.

Frequently Asked Questions (FAQ)

Why is it so important to create a copy of the input board?
If you modify the board in place, you introduce a critical bug. Imagine the algorithm processes a cell and places a '1'. When it later processes a neighbor of that cell, it might see the '1' and try to process it as a flower or another special character, which is incorrect. You must always read from the original, static state of the garden while writing to a new one.
How does this logic handle edge cases like a 1x1 or empty board?
Our code includes guard clauses at the beginning of the process_garden method. It checks if the number of rows or columns is zero. If so, it returns immediately, gracefully handling empty or malformed input without causing a runtime error.
Can this algorithm be optimized for performance on very large grids?
The current O(R*C) approach is generally the standard. For massive grids (millions of cells), you might explore parallelization if the platform supports it, processing chunks of the grid concurrently. However, in the context of typical ABAP programming, this implementation is more than sufficient and is considered efficient.
What is the difference between sy-tabix and sy-index in this code?
sy-tabix is the system variable that holds the current row index for a LOOP AT ... statement on an internal table. sy-index is a more general-purpose system variable for loop counters, most commonly used with DO...TIMES or WHILE loops. In our code, sy-tabix gives us the row number, and sy-index gives us the column number.
How does this relate to the classic "Minesweeper" game logic?
The logic is nearly identical. In Minesweeper, the flowers are mines. The numbers you see on the board when you click a square represent the count of adjacent mines. This Flower Field problem is a perfect implementation of the core mechanic of the Minesweeper game.
Why use a modern Class-Based (OO) approach instead of a simple FORM routine?
Using an ABAP Objects class provides better encapsulation, reusability, and testability. You can create a unit test for the class to automatically verify its correctness. In modern ABAP development, especially for S/4HANA, classes and methods are the standard, whereas FORM routines are considered obsolete.

Conclusion: From Garden Grids to Business Logic

The Flower Field problem is more than just a coding challenge; it's a practical tutorial in grid manipulation and algorithmic discipline. By working through this problem from the exclusive kodikra.com curriculum, you have reinforced several key skills: iterating over multi-dimensional data structures, managing state by working on data copies, handling edge cases gracefully, and implementing clear, bounded logic for complex calculations.

The patterns you've practiced here—nested loops, boundary checks, and helper methods—will appear time and again in your career as an ABAP developer. You are now better equipped to tackle sophisticated reporting requirements, complex data validations, and custom application logic within the SAP environment.

To continue honing your skills, we encourage you to explore other challenges. Dive deeper into our comprehensive ABAP language guides or advance to the next set of problems in the ABAP 5 learning module.

Disclaimer: The code provided in this article is written using modern ABAP 7.4+ syntax and is compatible with SAP S/4HANA systems and recent versions of SAP NetWeaver.


Published by Kodikra — Your trusted Abap learning resource.