diff --git a/.gitignore b/.gitignore index 3924d90..416d7d3 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,4 @@ build/ src/engine/CMakeFiles examples/CMakeFiles +*.xopp diff --git a/src/examples/2048.c b/src/examples/2048.c index cecdaaa..75c253e 100644 --- a/src/examples/2048.c +++ b/src/examples/2048.c @@ -2,6 +2,31 @@ #include #include +#include +#include +#include + +#define SIZE 4 + +typedef enum { + LEFT, + RIGHT, + UP, + DOWN +} dir_e; + +typedef struct { + uint32_t x; + uint32_t y; +} v2_t; + +typedef struct { + uint32_t area[SIZE][SIZE]; + dir_e direction; +} game_t; + +static game_t game; + // polling events every frame void client_poll_event() @@ -17,16 +42,234 @@ client_poll_event() case SDLK_Q: game_state->is_running = false; break; + case SDLK_W: + game.direction = UP; + break; + case SDLK_S: + game.direction = DOWN; + break; + case SDLK_A: + game.direction = LEFT; + break; + case SDLK_D: + game.direction = RIGHT; + break; default: break; } } } +void +print_area() +{ + printf("AREA:\n"); + for (int i = 0; i < SIZE; i++) { + printf("["); + for (int j = 0; j < SIZE - 1; j++) { + printf("%d, ", game.area[i][j]); + } + printf("%d]\n", game.area[i][SIZE - 1]); + } + printf("\n"); +} + +void +move() +{ + switch (game.direction) { + case LEFT: + for (int i = 0; i < SIZE; i++) { + for (int j = 0; j < SIZE; j++) { + for (int k = 0; k < SIZE - 1; k++) { + if (game.area[i][k] == 0) { + game.area[i][k] = game.area[i][k + 1]; + game.area[i][k + 1] = 0; + } + } + } + } + break; + case RIGHT: + for (int i = 0; i < SIZE; i++) { + uint32_t *row = game.area[i]; + for (int j = SIZE - 1; j >= 0; j--) { + for (int k = SIZE - 1; k > 0; k--) { + if (game.area[i][k] == 0) { + game.area[i][k] = game.area[i][k - 1]; + game.area[i][k - 1] = 0; + } + } + } + } + break; + case UP: + for (int i = 0; i < SIZE; i++) { + for (int j = 0; j < SIZE; j++) { + for (int k = 0; k < SIZE - 1; k++) { + if (game.area[k][i] == 0) { + game.area[k][i] = game.area[k + 1][i]; + game.area[k + 1][i] = 0; + } + } + } + } + break; + case DOWN: + for (int i = 0; i < SIZE; i++) { + for (int j = SIZE - 1; j >= 0; j--) { + for (int k = SIZE - 1; k > 0; k--) { + if (game.area[k][i] == 0) { + game.area[k][i] = game.area[k - 1][i]; + game.area[k - 1][i] = 0; + } + } + } + } + break; + default: + break; + } +} + +/** + * Python alternative (only horizontal) + * +# combines a shifted row as per the rules of 2048 +# returns whether there were modifications done +def combine_row(row: list[int], to_left: bool) -> bool: + # make sure it is shifted + retval = shift_row(row, to_left) + + if to_left: + direction = 1 + start = 0 + end = len(row) - 1 + else: + direction = -1 + start = len(row) - 1 + end = 0 + + for i in range(start, end, direction): + if row[i] == 0: + continue + if row[i] == row[i + direction]: + retval = True + row[i] *= 2 + row[i + direction] = 0 + for j in range(i + direction, end, direction): + row[j] = row[j + direction] + row[end] = 0 + + return retval +*/ + +void +combine_horizontal() +{ + int8_t direction; + uint32_t start, end; + switch (game.direction) { + case LEFT: + // combine left + direction = -1; + start = 0; + end = SIZE - 1; + break; + case RIGHT: + // combine right + direction = 1; + start = SIZE - 1; + end = 0; + break; + default: + break; + } + + for (int row_index = 0; row_index < SIZE; row_index++) { + uint32_t *row = game.area[row_index]; + + for (int i = start; (game.direction == LEFT) ? i < end : i > end; i -= direction) { + if (row[i] == 0) { + continue; + } + if (row[i] == row[i - direction]) { + row[i] *= 2; + row[i - direction] = 0; + for (int j = i - direction; (game.direction == LEFT) ? j < end : j > end; j -= direction) { + row[j] = row[j - direction]; + } + row[end] = 0; + } + } + } +} + +void +combine_vertical() +{ + int8_t direction; + uint32_t start, end; + switch (game.direction) { + case UP: + // combine up + direction = -1; + start = 0; + end = SIZE - 1; + break; + case DOWN: + // combine down + direction = 1; + start = SIZE - 1; + end = 0; + break; + default: + break; + } + + for (int col_index = 0; col_index < SIZE; col_index++) { + for (int i = start; (game.direction == UP) ? i < end : i > end; i -= direction) { + if (game.area[i][col_index] == 0) { + continue; + } + if (game.area[i][col_index] == game.area[i - direction][col_index]) { + game.area[i][col_index] *= 2; + game.area[i - direction][col_index] = 0; + for (int j = i - direction; (game.direction == UP) ? j < end : j > end; j -= direction) { + game.area[j][col_index] = game.area[j - direction][col_index]; + } + game.area[end][col_index] = 0; + } + } + } +} + +void +combine() +{ + if (game.direction == LEFT || game.direction == RIGHT) { + combine_horizontal(); + } else { + combine_vertical(); + } +} + +void +slide() +{ + // 1. move all squares in one direction + move(); + + // 2. then combine them and shift the potential rest by one in the given direction + combine(); +} + // performed every frame void client_update_framebuffer() { + //slide(); + // single green demo pixel game_state->framebuffer[0] = 0x00FF00; @@ -36,9 +279,123 @@ client_update_framebuffer() ); } +bool +v2_eq(v2_t a, v2_t b) +{ + return a.x == b.x && a.y == b.y; +} + +bool +v2_contains(v2_t v[], size_t size, v2_t elem) +{ + for (int i = 0; i < size; i++) { + if (v2_eq(v[i], elem)) { + return true; + } + } + + return false; +} + +void +game_init() +{ + srand(time(NULL)); + + // generate MAX_NUMBER_OF_4 + MAX_NUMBER_OF_2 random indices in range 0..15 (row in 0..3, col in 0..3) + // this means that there will be at least MAX_NUMBER_OF_2 of 2s and MAX_NUMBER_OF_4 <= MAX_NUMBER_OF_2 4s + // => usually less 4s than 2s because the indices can get replaced +#define MAX_NUMBER_OF_4 1 +#define MAX_NUMBER_OF_2 2 + + uint8_t row, col; + v2_t elem; + v2_t fours[MAX_NUMBER_OF_4], twos[MAX_NUMBER_OF_2]; + + int i = 0; + while (i < MAX_NUMBER_OF_4) { + row = rand() % SIZE; + col = rand() % SIZE; + elem = (v2_t){ .x = col, .y = row }; + if (!v2_contains(twos, MAX_NUMBER_OF_4, elem)) { + fours[i++] = elem; + } + } + + i = 0; + while (i < MAX_NUMBER_OF_2) { + row = rand() % SIZE; + col = rand() % SIZE; + elem = (v2_t){ .x = col, .y = row }; + if (!v2_contains(twos, MAX_NUMBER_OF_2, elem)) { + twos[i++] = elem; + } + } + + for (i = 0; i < MAX_NUMBER_OF_4; i++) { + game.area[fours[i].y][fours[i].x] = 4; + } + + for (i = 0; i < MAX_NUMBER_OF_2; i++) { + game.area[twos[i].y][twos[i].x] = 2; + } + + game.direction = RIGHT; +} + +void +tests() +{ + puts("---------- TEST SHIFT LEFT <- -------------\n"); + for (int i = 0; i < SIZE; i++) { + memset(game.area[i], 0, sizeof game.area[i]); + } + game.area[0][2] = 2; + game.area[0][3] = 2; + game.area[2][2] = 4; + game.direction = LEFT; + + slide(); + + puts("---------- TEST SHIFT RIGHT -> ------------\n"); + for (int i = 0; i < SIZE; i++) { + memset(game.area[i], 0, sizeof game.area[i]); + } + game.area[0][0] = 2; + game.area[0][1] = 2; + game.area[2][0] = 4; + game.direction = RIGHT; + + slide(); + + puts("---------- TEST SHIFT UP ^^ ---------------\n"); + for (int i = 0; i < SIZE; i++) { + memset(game.area[i], 0, sizeof game.area[i]); + } + game.area[2][0] = 2; + game.area[3][0] = 2; + game.area[2][2] = 4; + game.direction = UP; + + slide(); + + puts("---------- TEST SHIFT DOWN vv -------------\n"); + for (int i = 0; i < SIZE; i++) { + memset(game.area[i], 0, sizeof game.area[i]); + } + game.area[0][0] = 2; + game.area[1][0] = 2; + game.area[0][2] = 4; + game.direction = DOWN; + + slide(); +} + int main() { + game_init(); + // initialize the engine if (engine_init( 500, 500, @@ -52,4 +409,4 @@ main() } else { return 1; } -} \ No newline at end of file +} diff --git a/src/internal/engine_internal.h b/src/internal/engine_internal.h index 80e42e9..bc4cd0b 100644 --- a/src/internal/engine_internal.h +++ b/src/internal/engine_internal.h @@ -42,7 +42,8 @@ engine_sdl_init( const char window_name[] ); -void +void engine_sdl_destroy( engine_sdl_state_t *state -); \ No newline at end of file +); +