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()
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 int
s.
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_t
to 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]
.