Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
183 changes: 134 additions & 49 deletions 2048.c
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,42 @@
#include <stdint.h> // defines: uint8_t, uint32_t
#include <time.h> // defines: time
#include <signal.h> // defines: signal, SIGINT

#include <libintl.h> // defines: number format
#include <locale.h>

#if defined(SIZE) && ((SIZE < 4) || (SIZE > 9))
#warning "The board ranges from 4x4 to 9x9, default to 4x4"
#undef SIZE
#endif
#ifndef SIZE
#define SIZE 4
#endif

#define ROW_WIDTH (SIZE * 7)

#define PRINT_HEAD(score) \
do \
{ \
char fmt[48]; \
uint8_t len = ROW_WIDTH - 11; \
len = sprintf(fmt, "\n2048.c %%\'%du pts\n\n", len); \
fmt[len] = '\0'; \
printf(fmt, score); \
} while (0)

#define PRINT_FOOT(str, len) \
do \
{ \
char pad[ROW_WIDTH]; \
memset(pad, ' ', ROW_WIDTH); \
pad[(ROW_WIDTH - len + 1) / 2] = '\0'; \
printf("\n%s%s%s", pad, str, pad); \
} while (0)

// this function receives 2 pointers (indicated by *) so it can set their values
void getColors(uint8_t value, uint8_t scheme, uint8_t *foreground, uint8_t *background)
{
uint8_t original[] = {8, 255, 1, 255, 2, 255, 3, 255, 4, 255, 5, 255, 6, 255, 7, 255, 9, 0, 10, 0, 11, 0, 12, 0, 13, 0, 14, 0, 255, 0, 255, 0};
uint8_t original[] = {8, 255, 1, 255, 2, 255, 3, 255, 4, 255, 5, 255, 6, 255, 7, 0, 9, 0, 10, 0, 11, 0, 12, 0, 13, 0, 14, 0, 255, 0, 255, 0};
uint8_t blackwhite[] = {232, 255, 234, 255, 236, 255, 238, 255, 240, 255, 242, 255, 244, 255, 246, 0, 248, 0, 249, 0, 250, 0, 251, 0, 252, 0, 253, 0, 254, 0, 255, 0};
uint8_t bluered[] = {235, 255, 63, 255, 57, 255, 93, 255, 129, 255, 165, 255, 201, 255, 200, 255, 199, 255, 198, 255, 197, 255, 196, 255, 196, 255, 196, 255, 196, 255, 196, 255};
uint8_t *schemes[] = {original, blackwhite, bluered};
Expand All @@ -48,8 +77,9 @@ uint8_t getDigitCount(uint32_t number)
void drawBoard(uint8_t board[SIZE][SIZE], uint8_t scheme, uint32_t score)
{
uint8_t x, y, fg, bg;
printf("\033[H"); // move cursor to 0,0
printf("2048.c %17u pts\n\n", score);
printf("\033[?25l"); // hide cursor again due to reset vty
printf("\033[H"); // move cursor to 0,0
PRINT_HEAD(score);
for (y = 0; y < SIZE; y++)
{
for (x = 0; x < SIZE; x++)
Expand Down Expand Up @@ -88,12 +118,11 @@ void drawBoard(uint8_t board[SIZE][SIZE], uint8_t scheme, uint32_t score)
}
printf("\n");
}
printf("\n");
printf(" ←,↑,→,↓ or q \n");
PRINT_FOOT("←,↑,→,↓,r or q", 14);
printf("\033[A"); // one line up
}

uint8_t findTarget(uint8_t array[SIZE], uint8_t x, uint8_t stop)
uint8_t findTarget(uint8_t *array, uint8_t x, uint8_t stop)
{
uint8_t t;
// if the position is already on the first, don't evaluate
Expand Down Expand Up @@ -125,12 +154,12 @@ uint8_t findTarget(uint8_t array[SIZE], uint8_t x, uint8_t stop)
return x;
}

bool slideArray(uint8_t array[SIZE], uint32_t *score)
bool slideArray(uint8_t *array, uint32_t *score, uint8_t size)
{
bool success = false;
uint8_t x, t, stop = 0;

for (x = 0; x < SIZE; x++)
for (x = 0; x < size; x++)
{
if (array[x] != 0)
{
Expand Down Expand Up @@ -183,7 +212,7 @@ bool moveUp(uint8_t board[SIZE][SIZE], uint32_t *score)
uint8_t x;
for (x = 0; x < SIZE; x++)
{
success |= slideArray(board[x], score);
success |= slideArray(board[x], score, SIZE);
}
return success;
}
Expand Down Expand Up @@ -347,9 +376,10 @@ void setBufferedInput(bool enable)
}
}

#define TEST_SIZE 4
bool testSucceed()
{
uint8_t array[SIZE];
uint8_t array[TEST_SIZE];
// these are exponents with base 2 (1=2 2=4 3=8)
// data holds per line: 4x IN, 4x OUT, 1x POINTS
uint8_t data[] = {
Expand All @@ -372,19 +402,19 @@ bool testSucceed()
bool success = true;
uint32_t score;

tests = (sizeof(data) / sizeof(data[0])) / (2 * SIZE + 1);
tests = (sizeof(data) / sizeof(data[0])) / (2 * TEST_SIZE + 1);
for (t = 0; t < tests; t++)
{
in = data + t * (2 * SIZE + 1);
out = in + SIZE;
points = in + 2 * SIZE;
for (i = 0; i < SIZE; i++)
in = data + t * (2 * TEST_SIZE + 1);
out = in + TEST_SIZE;
points = in + 2 * TEST_SIZE;
for (i = 0; i < TEST_SIZE; i++)
{
array[i] = in[i];
}
score = 0;
slideArray(array, &score);
for (i = 0; i < SIZE; i++)
slideArray(array, &score, TEST_SIZE);
for (i = 0; i < TEST_SIZE; i++)
{
if (array[i] != out[i])
{
Expand All @@ -397,22 +427,22 @@ bool testSucceed()
}
if (success == false)
{
for (i = 0; i < SIZE; i++)
for (i = 0; i < TEST_SIZE; i++)
{
printf("%u ", in[i]);
}
printf("=> ");
for (i = 0; i < SIZE; i++)
for (i = 0; i < TEST_SIZE; i++)
{
printf("%u ", array[i]);
}
printf("(%u points) expected ", score);
for (i = 0; i < SIZE; i++)
for (i = 0; i < TEST_SIZE; i++)
{
printf("%u ", in[i]);
}
printf("=> ");
for (i = 0; i < SIZE; i++)
for (i = 0; i < TEST_SIZE; i++)
{
printf("%u ", out[i]);
}
Expand All @@ -429,20 +459,77 @@ bool testSucceed()

void signal_callback_handler(int signum)
{
printf(" TERMINATED \n");
PRINT_FOOT("TERMINATED", 10);
setBufferedInput(true);
// make cursor visible, reset all modes
printf("\033[?25h\033[m");
printf("\033[?25h\033[m\n");
exit(signum);
}

typedef enum
{
UP,
DOWN,
LEFT,
RIGHT,
ROTATE,
OTHER
} key_map_t;

key_map_t key_map(char c, bool termux)
{
switch (c)
{
case 52: // '4' key
case 97: // 'a' key
case 104: // 'h' key
case 68: // left arrow
return LEFT;

case 54: // '6' key
case 100: // 'd' key
case 108: // 'l' key
case 67: // right arrow
return RIGHT;

case 56: // '8' key
if (termux)
return DOWN;
case 119: // 'w' key
case 107: // 'k' key
case 65: // up arrow
return UP;

case 50: // '2' key
if (termux)
return UP;
case 115: // 's' key
case 106: // 'j' key
case 66: // down arrow
return DOWN;

case 53: // '5' key
case 114: // 'r' key
case 117: // 'u' key
return ROTATE;
default:
return OTHER;
}
}
char rand_key()
{
return "asd"[rand() % 3]; // left down right
}

int main(int argc, char *argv[])
{
uint8_t board[SIZE][SIZE];
uint8_t scheme = 0;
uint32_t score = 0;
int c;
bool success;
bool termux;
bool demo;

// handle the command line options
if (argc > 1)
Expand Down Expand Up @@ -476,13 +563,19 @@ int main(int argc, char *argv[])
{
return testSucceed() ? EXIT_SUCCESS : EXIT_FAILURE;
}
else if (strcmp(argv[1], "demo") == 0)
{
demo = true;
}
else
{
printf("Invalid option: %s\n\nTry '%s --help' for more options.\n", argv[1], argv[0]);
return EXIT_FAILURE;
}
}

termux = getenv("TERMUX__PREFIX") != NULL ? true : false;
setlocale(LC_NUMERIC, "en_US");
// make cursor invisible, erase entire screen
printf("\033[?25l\033[2J");

Expand All @@ -492,68 +585,60 @@ int main(int argc, char *argv[])
initBoard(board);
setBufferedInput(false);
drawBoard(board, scheme, score);

while (true)
{
c = getchar();
c = demo ? rand_key() : getchar();
if (c == EOF)
{
puts("\nError! Cannot read keyboard input!");
break;
}
switch (c)
switch (key_map(c, termux))
{
case 52: // '4' key
case 97: // 'a' key
case 104: // 'h' key
case 68: // left arrow
case LEFT:
success = moveLeft(board, &score);
break;
case 54: // '6' key
case 100: // 'd' key
case 108: // 'l' key
case 67: // right arrow
case RIGHT:
success = moveRight(board, &score);
break;
case 56: // '8' key
case 119: // 'w' key
case 107: // 'k' key
case 65: // up arrow
case UP:
success = moveUp(board, &score);
break;
case 50: // '2' key
case 115: // 's' key
case 106: // 'j' key
case 66: // down arrow
case DOWN:
success = moveDown(board, &score);
break;
default:
case ROTATE:
rotateBoard(board);
drawBoard(board, scheme, score);
case OTHER:
success = false;
}
if (success)
{
drawBoard(board, scheme, score);
usleep(150 * 1000); // 150 ms
usleep((demo ? 1 : 150) * 1000); // 1 or 150 ms
addRandom(board);
drawBoard(board, scheme, score);
if (gameEnded(board))
{
printf(" GAME OVER \n");
PRINT_FOOT("GAME OVER", 9);
break;
}
}
if (c == 'q')
{
printf(" QUIT? (y/n) \n");
PRINT_FOOT("QUIT? (y/n)", 11);
c = getchar();
if (c == 'y')
{
break;
}
drawBoard(board, scheme, score);
}
if (c == 'r')
else if (c == 'n')
{
printf(" RESTART? (y/n) \n");
PRINT_FOOT("RESTART? (y/n)", 14);
c = getchar();
if (c == 'y')
{
Expand All @@ -566,7 +651,7 @@ int main(int argc, char *argv[])
setBufferedInput(true);

// make cursor visible, reset all modes
printf("\033[?25h\033[m");
printf("\033[?25h\033[m\n");

return EXIT_SUCCESS;
}