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..9a06621 100644 --- a/src/examples/2048.c +++ b/src/examples/2048.c @@ -1,7 +1,252 @@ +#include "dangerzone/render/rect.h" #include #include #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; + uint32_t score; +} game_t; + +static game_t game; + +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"); +} + +bool +move() +{ + bool retval = false; + 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 + 1] != 0) { + game.area[i][k] = game.area[i][k + 1]; + game.area[i][k + 1] = 0; + retval = true; + } + } + } + } + break; + case RIGHT: + for (int i = 0; i < SIZE; i++) { + uint32_t *row = game.area[i]; + for (int j = 0; j < SIZE; j++) { + for (int k = SIZE - 1; k > 0; k--) { + if (game.area[i][k] == 0 && game.area[i][k - 1] != 0) { + game.area[i][k] = game.area[i][k - 1]; + game.area[i][k - 1] = 0; + retval = true; + } + } + } + } + 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 + 1][i] != 0) { + game.area[k][i] = game.area[k + 1][i]; + game.area[k + 1][i] = 0; + retval = true; + } + } + } + } + break; + case DOWN: + for (int i = 0; i < SIZE; i++) { + for (int j = 0; j < SIZE; j++) { + for (int k = SIZE - 1; k > 0; k--) { + if (game.area[k][i] == 0 && game.area[k - 1][i] != 0) { + game.area[k][i] = game.area[k - 1][i]; + game.area[k - 1][i] = 0; + retval = true; + } + } + } + } + break; + default: + break; + } + + return retval; +} + +bool +combine_horizontal( + bool has_moved +) { + bool retval = has_moved; + 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]) { + retval = true; + row[i] *= 2; + game.score += row[i]; + 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; + } + } + } + + return retval; +} + +bool +combine_vertical( + bool has_moved +) { + bool retval = has_moved; + 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]) { + retval = true; + game.area[i][col_index] *= 2; + game.score += game.area[i][col_index]; + 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; + } + } + } + + return retval; +} + +bool +combine( + bool has_moved +) { + if (game.direction == LEFT || game.direction == RIGHT) { + return combine_horizontal(has_moved); + } else { + return combine_vertical(has_moved); + } +} + +// finds a random empty position and puts a two there +void +new_2() +{ + uint32_t row = rand() % SIZE; + uint32_t col = rand() % SIZE; + while (game.area[row][col] != 0) { + row = rand() % SIZE; + col = rand() % SIZE; + } + + game.area[row][col] = 2; +} + +void +slide() +{ + // 1. move all squares in one direction + bool has_moved = move(); + + // 2. then combine them and shift the potential rest by one in the given direction + bool has_combined = combine(has_moved); + + // printf("moved? %s, combined? %s\n", (has_moved) ? "true" : "false", (has_combined) ? "true" : "false"); + + // 3. generate a new 2 + if (has_moved || has_combined) { + new_2(); + } + + // printf("score: %d\n", game.score); + + // print_area(); +} + // polling events every frame void client_poll_event() @@ -17,31 +262,230 @@ client_poll_event() case SDLK_Q: game_state->is_running = false; break; + case SDLK_W: + game.direction = UP; + slide(); + break; + case SDLK_S: + game.direction = DOWN; + slide(); + break; + case SDLK_A: + game.direction = LEFT; + slide(); + break; + case SDLK_D: + game.direction = RIGHT; + slide(); + break; default: break; } } } +#define CHR(ord) ((ord) + 48) +#define ORD(chr) ((chr) - 48) + +uint32_t +colors( + uint32_t number +) { + switch (number) { + case 0: + return 0xFFFFFF; + case 2: + return 0x00FF00; + case 4: + return 0xFF0000; + case 8: + return 0x0000FF; + default: + return 0xFFFFFF; + } +} + +uint32_t +numlen( + uint32_t n +) { + uint32_t len = 0; + + while (n > 0) { + len++; + n /= 10; + } + + return len; +} + +uint32_t +power( + uint32_t x, + uint32_t n +) { + // x ^ n + uint32_t acc = 1; + for (int i = 0; i < n; i++) { + acc *= x; + } + + return acc; +} + +uint32_t +numrev( + uint32_t n +) { + uint32_t len = numlen(n); + uint32_t new = 0; + int i = 0; + + while (n > 0) { + new += (n % 10) * power(10, len - (++i)); + n /= 10; + } + + return new; +} + // performed every frame void client_update_framebuffer() { - // single green demo pixel - game_state->framebuffer[0] = 0x00FF00; + // clear_framebuffer(0xFFFF00); + // put_string("Welcome to the most\n*DANGEROUS* 2048 game!", + // 20, 20, 2, 2, 3, 0x000000, 0xFFFF00 + // ); - clear_framebuffer(0xFFFF00); - put_string("Welcome to the most\n*DANGEROUS* 2048 game!", - 20, 20, 2, 2, 3, 0x000000, 0xFFFF00 - ); + clear_framebuffer(0x000000); + + uint32_t tile_size = game_state->width / SIZE; + uint8_t border_width = 10; + char *n_str; + bool alloced = false; + for (int i = 0; i < SIZE; i++) { + for (int j = 0; j < SIZE; j++) { + alloced = true; + // background + uint32_t x = (border_width / 2) + i * tile_size; + uint32_t y = (border_width / 2) + j * tile_size; + uint32_t w = tile_size - border_width; + uint32_t h = tile_size - border_width; + uint32_t background_color = colors(game.area[j][i]); + put_rect(x, y, w, h, background_color); + + // number in foreground + uint32_t foreground_color = 0x000000; + uint8_t font_size = 3; + + uint32_t n = game.area[j][i]; + uint32_t n_len = numlen(n); + n_str = calloc(n_len, sizeof(char)); + if (n_str == NULL) { + game_state->is_running = false; + } + sprintf(n_str, "%d", n); + uint32_t n_rev = numrev(n); + uint32_t digit_offset = 1; + if (n != 0) { + put_string(n_str, x + border_width, y + border_width, font_size, digit_offset, 12, foreground_color, background_color); + } + } + } + + // score display line (single line) + char *score_text = calloc(numlen(game.score), sizeof(char)); + if (score_text == NULL) { + game_state->is_running = false; + } + alloced = true; + + sprintf(score_text, "score: %6d", game.score); + put_string(score_text, border_width / 2, tile_size * SIZE + border_width / 2, 6, 1, 12, 0xFFFFFF, 0x000000); + + if (alloced) { + free(n_str); + free(score_text); + } +} + +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; + game.score = 0; } int main() { + uint32_t window_width = 500; + uint32_t window_height = window_width * 1.12f; + + game_init(); + // initialize the engine if (engine_init( - 500, 500, + window_width, window_height, "Dangerous 2048", client_poll_event, client_update_framebuffer @@ -52,4 +496,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 +); +