Search code examples
carrayspointersreturntetris

Problems with a getter for a 4x4 matrix in c


Hello I have to program a tetris game in C by tomorrow and I'm having a bit of trouble with a getter that should return a sprite as a 4x4 matrix.

It most likely is pretty simple tho I am not that familiar with pointers to arrays.

so in the first file where I stored my shapes this is the test getter for 1 shape

int shape_i[4][4] = {
    {1,0,0,0},
    {1,0,0,0},
    {1,0,0,0},
    {1,0,0,0}
};


int **get_shape(){
return shape_i;}

now from my draw file I call it like this

void draw_shape(){
int **shape= get_shape();
for (int i = 0; i<4; i++){
    for (int j=0; j<4; j++){
        int value = shape[j][i];

            if (value != 0){
                SDL_Rect rect;
                rect.x = (get_x()+i)*BLOCK_WIDTH;
                rect.y = (get_y()+j) *BLOCK_HEIGHT;
                rect.h = BLOCK_HEIGHT;
                rect.w = BLOCK_WIDTH;
                SDL_FillRect(window,&rect,0x044DDE);
            }
            }
    }
SDL_Flip(window);
}

It doesn't give an error on compiling but my program stops once it reaches get_shape()


Solution

  • TL;DR: Copy the data to your shape array with memcpy, see second example below. The other examples are alternative approaches with explanations.


    In C, you can't return arrays. You can only return a pointer to the first element to an array. The first element of your array is itself an array, an array of 4 ints.

    The syntax to define a pointer to an array of four ints is somewhat baroque:

    int (*p)[4];
    

    It's even more baroque when you must define it as the return type of a function:

    int (*get_shape(int c))[4] { ... }
    

    A way around this is to use a typedef:

    typedef int (*Shape)[4];
    

    Now your variables and function prototype look like this:

    Shape p = get_shape(c);
    
    Shape get_shape(int c) { ... }
    

    Here's a complete example:

    #include <stdlib.h>
    #include <stdio.h>
    
    typedef int (*Shape)[4];
    
    int shape_i[4][4] = {{1,0,0,0}, {1,0,0,0}, {1,0,0,0}, {1,0,0,0}};
    int shape_l[4][4] = {{1,0,0,0}, {1,0,0,0}, {1,1,0,0}, {0,0,0,0}};
    int shape_z[4][4] = {{1,0,0,0}, {1,1,0,0}, {0,1,0,0}, {0,0,0,0}};
    
    Shape get_shape(int c)
    {
        switch (c) {
        case 'I':   return shape_i;
        case 'L':   return shape_l;
        case 'Z':   return shape_z;
        }
        return NULL;
    }
    
    int main()
    {
        int c;
    
        for (c = 'A'; c <= 'Z'; c++) {
            Shape p = get_shape(c);
    
            if (p) {
                int i, j;
    
                for (j = 0; j < 4; j++) {
                    for (i = 0; i < 4; i++) {
                        putchar(p[j][i] ? '#' : ' ');
                    }
                    puts("");
                }
                puts("--");
            }        
        }
    
        return 0;
    }
    

    Note how the definition of the shapes still require you to use int[4][4], because arrays are not pointers. You need arrays where you define your data. Also note that this solution returns a pointer to the original shape_i. When you modify the data in p, you modify shape_i through p, thus destroying your shape prototype.


    If you want to fill an array with data, just pass the data in. That's a common approach even for one-dimensional arrays: Pass an array and have the function fill it. Return an (otherwise unrelated) value that tells you whether the operation was successful.

    int get_shape(int shape[4][4], int c)
    {
        switch (c) {
        case 'I':   memcpy(shape, shape_i, sizeof(shape)); return 1;
        case 'L':   memcpy(shape, shape_l, sizeof(shape)); return 1;
        case 'Z':   memcpy(shape, shape_z, sizeof(shape)); return 1;
        }
        return 0;
    }
    

    memcpy is a function of the standard library for which you should include <string.h>. The return value is just to check whether the shape was valid. Use it like this:

    int p[4][4];
    
    if (get_shape('I')) {
        // p is now filled with a copy of shape_i
    }
    

    I think this is the method you should be using. It will copy the contents of shape_tto p, and that's what you want here. You are going to rotate and flip your current block p, whereas you want to keep your block prototype shape_i unchanged for future "clones".


    I've said above that you can't return arrays in C. What you can do is to wrap your array in a struct and return that. Structs are passed per value and don't decay into pointers like arrays.

    struct Shape {
        int data[4][4];
    };
    
    struct shape shape_i = {{{1,0,0,0}, {1,0,0,0}, {1,0,0,0}, {1,0,0,0}}};
    
    struct Shape get_shape(void) {
        return shape_i;
    };
    
    struct Shape p = get_shape();
    

    This will also copy the cntents, but you must access the elements as p.data[i][j].