add business logic with tests for 2048
This commit is contained in:
@@ -3,3 +3,4 @@
|
|||||||
build/
|
build/
|
||||||
src/engine/CMakeFiles
|
src/engine/CMakeFiles
|
||||||
examples/CMakeFiles
|
examples/CMakeFiles
|
||||||
|
*.xopp
|
||||||
|
|||||||
+358
-1
@@ -2,6 +2,31 @@
|
|||||||
#include <dangerzone/render/misc.h>
|
#include <dangerzone/render/misc.h>
|
||||||
#include <dangerzone/render/text.h>
|
#include <dangerzone/render/text.h>
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <time.h>
|
||||||
|
|
||||||
|
#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
|
// polling events every frame
|
||||||
void
|
void
|
||||||
client_poll_event()
|
client_poll_event()
|
||||||
@@ -17,16 +42,234 @@ client_poll_event()
|
|||||||
case SDLK_Q:
|
case SDLK_Q:
|
||||||
game_state->is_running = false;
|
game_state->is_running = false;
|
||||||
break;
|
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:
|
default:
|
||||||
break;
|
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
|
// performed every frame
|
||||||
void
|
void
|
||||||
client_update_framebuffer()
|
client_update_framebuffer()
|
||||||
{
|
{
|
||||||
|
//slide();
|
||||||
|
|
||||||
// single green demo pixel
|
// single green demo pixel
|
||||||
game_state->framebuffer[0] = 0x00FF00;
|
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
|
int
|
||||||
main()
|
main()
|
||||||
{
|
{
|
||||||
|
game_init();
|
||||||
|
|
||||||
// initialize the engine
|
// initialize the engine
|
||||||
if (engine_init(
|
if (engine_init(
|
||||||
500, 500,
|
500, 500,
|
||||||
@@ -52,4 +409,4 @@ main()
|
|||||||
} else {
|
} else {
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -42,7 +42,8 @@ engine_sdl_init(
|
|||||||
const char window_name[]
|
const char window_name[]
|
||||||
);
|
);
|
||||||
|
|
||||||
void
|
void
|
||||||
engine_sdl_destroy(
|
engine_sdl_destroy(
|
||||||
engine_sdl_state_t *state
|
engine_sdl_state_t *state
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user