crefactoring

How can I refactor similar functions to avoid code duplication in C?


I am working on a Tetris-like game in C and I have two functions, check_left_side_occupation and check_right_side_occupation, which are very similar. I want to refactor them to avoid code duplication, but I am not sure how to achieve that. Here is the relevant part of my code:

void check_right_side_occupation(int (*field)[field_width], figure *piece)
{
    int x, y;
    for (y = 0; y < piece_size; y++) {
        /* first different part */
        for (x = piece_size - 1; x >= 0; x--) {
            /* end */
            if (piece->form[y][x] == 0)
                continue;
            else {
                if (field[y + piece->y_decline][x + piece->x_shift] == 1) {
                    /* second different part */
                    piece->x_shift--;
                    /* end */
                    return;
                }
            }
        }
    }
}

void check_left_side_occupation(int (*field)[field_width], figure *piece)
{
    int x, y;
    for (y = 0; y < piece_size; y++) {
        /* first different part */
        for (x = 0; x < piece_size; x++) {
            /* end */
            if (piece->form[y][x] == 0)
                continue;
            else {
                if (field[y + piece->y_decline][x + piece->x_shift] == 1) {
                    /* second different part */
                    piece->x_shift++;
                    /* end */
                    return;
                }
            }
        }
    }
}

void side_pixel_occupied_by_field(
    move_direction direction, int (*field)[field_width], figure *piece
)
{
    switch (direction) {
        case left:
            check_left_side_occupation(field, piece);
            break;
        case right:
            check_right_side_occupation(field, piece);
    }
}

Is there a way to refactor these functions to avoid the code duplication and still maintain the functionality for checking both the left and right sides of the piece?

Any help or suggestions would be greatly appreciated!


Solution

  • You could use the direction parameter in the side_pixel_occupied_by_field function. This version requires move_direction to be defined as:

    typedef enum { left = 1, right = -1 } move_direction;
    
    void side_pixel_occupied_by_field(move_direction direction,
                                      int (*field)[field_width], figure *piece) {
        int start, end;
        // set start and end depending on the direction:
        if (direction == right) {
            start = piece_size - 1;
            end = -1;
        } else {
            start = 0;
            end = piece_size;
        }
        for (int y = 0; y < piece_size; y++) {
            // loop in the wanted direction:
            for (int x = start; x != end; x += direction) {
                if (piece->form[y][x] != 0 &&
                    field[y + piece->y_decline][x + piece->x_shift] == 1)
                {
                    piece->x_shift += direction;  // use direction here too
                    return;
                }
            }
        }
    }
    

    Alternatively, without having to change the current definition of move_direction:

    typedef struct {
        int start;
        int end;
        int increment;
    } start_end_inc;
    
    void side_pixel_occupied_by_field(move_direction direction,
                                      int (*field)[field_width], figure *piece) {
        // static if piece_size doesn't change:
        const start_end_inc sei[] = {{0, piece_size, 1},
                                     {piece_size - 1, -1, -1}};
        const start_end_inc *seip = &sei[direction == right];
        
        for (int y = 0; y < piece_size; y++) {
            for (int x = seip->start; x != seip->end; x += seip->increment) {
                if (piece->form[y][x] != 0 &&
                    field[y + piece->y_decline][x + piece->x_shift] == 1)
                {
                    piece->x_shift += seip->increment;
                    return;
                }
            }
        }
    }