diff --git a/2048.c b/2048.c index 21244a9..293220c 100644 --- a/2048.c +++ b/2048.c @@ -18,13 +18,42 @@ #include // defines: uint8_t, uint32_t #include // defines: time #include // defines: signal, SIGINT - +#include // defines: number format +#include + +#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}; @@ -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++) @@ -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 @@ -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) { @@ -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; } @@ -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[] = { @@ -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]) { @@ -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]); } @@ -429,13 +459,68 @@ 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]; @@ -443,6 +528,8 @@ int main(int argc, char *argv[]) uint32_t score = 0; int c; bool success; + bool termux; + bool demo; // handle the command line options if (argc > 1) @@ -476,6 +563,10 @@ 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]); @@ -483,6 +574,8 @@ int main(int argc, char *argv[]) } } + termux = getenv("TERMUX__PREFIX") != NULL ? true : false; + setlocale(LC_NUMERIC, "en_US"); // make cursor invisible, erase entire screen printf("\033[?25l\033[2J"); @@ -492,58 +585,50 @@ 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') { @@ -551,9 +636,9 @@ int main(int argc, char *argv[]) } 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') { @@ -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; }