| // SPDX-License-Identifier: MIT |
| // SPDX-FileCopyrightText: © 2014 Maurits van der Schee |
| |
| /* Console version of the game "2048" for GNU/Linux */ |
| |
| #include <cli.h> |
| #include <command.h> |
| #include <rand.h> |
| #include <vsprintf.h> |
| #include <linux/delay.h> |
| |
| #define SIZE 4 |
| static uint score; |
| |
| static void getColor(uint value, char *color, size_t length) |
| { |
| u8 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}; |
| u8 *scheme = original; |
| u8 *background = scheme + 0; |
| u8 *foreground = scheme + 1; |
| |
| if (value > 0) { |
| while (value >>= 1) { |
| if (background + 2 < scheme + sizeof(original)) { |
| background += 2; |
| foreground += 2; |
| } |
| } |
| } |
| snprintf(color, length, "\033[38;5;%d;48;5;%dm", *foreground, |
| *background); |
| } |
| |
| static void drawBoard(u16 board[SIZE][SIZE]) |
| { |
| int x, y; |
| char color[40], reset[] = "\033[0m"; |
| |
| printf("\033[H"); |
| printf("2048.c %17d pts\n\n", score); |
| |
| for (y = 0; y < SIZE; y++) { |
| for (x = 0; x < SIZE; x++) { |
| getColor(board[x][y], color, 40); |
| printf("%s", color); |
| printf(" "); |
| printf("%s", reset); |
| } |
| printf("\n"); |
| for (x = 0; x < SIZE; x++) { |
| getColor(board[x][y], color, 40); |
| printf("%s", color); |
| if (board[x][y] != 0) { |
| char s[8]; |
| s8 t; |
| |
| snprintf(s, 8, "%u", board[x][y]); |
| t = 7 - strlen(s); |
| printf("%*s%s%*s", t - t / 2, "", s, t / 2, ""); |
| } else { |
| printf(" · "); |
| } |
| printf("%s", reset); |
| } |
| printf("\n"); |
| for (x = 0; x < SIZE; x++) { |
| getColor(board[x][y], color, 40); |
| printf("%s", color); |
| printf(" "); |
| printf("%s", reset); |
| } |
| printf("\n"); |
| } |
| printf("\n"); |
| printf(" ←, ↑, →, ↓ or q \n"); |
| printf("\033[A"); |
| } |
| |
| static int8_t findTarget(u16 array[SIZE], int x, int stop) |
| { |
| int t; |
| |
| /* if the position is already on the first, don't evaluate */ |
| if (x == 0) |
| return x; |
| for (t = x - 1; t >= 0; t--) { |
| if (array[t]) { |
| if (array[t] != array[x]) { |
| /* merge is not possible, take next position */ |
| return t + 1; |
| } |
| return t; |
| } |
| |
| /* we should not slide further, return this one */ |
| if (t == stop) |
| return t; |
| } |
| /* we did not find a */ |
| return x; |
| } |
| |
| static bool slideArray(u16 array[SIZE]) |
| { |
| bool success = false; |
| int x, t, stop = 0; |
| |
| for (x = 0; x < SIZE; x++) { |
| if (array[x] != 0) { |
| t = findTarget(array, x, stop); |
| /* |
| * if target is not original position, then move or |
| * merge |
| */ |
| if (t != x) { |
| /* |
| * if target is not zero, set stop to avoid |
| * double merge |
| */ |
| if (array[t]) { |
| score += array[t] + array[x]; |
| stop = t + 1; |
| } |
| array[t] += array[x]; |
| array[x] = 0; |
| success = true; |
| } |
| } |
| } |
| return success; |
| } |
| |
| static void rotateBoard(u16 board[SIZE][SIZE]) |
| { |
| s8 i, j, n = SIZE; |
| int tmp; |
| |
| for (i = 0; i < n / 2; i++) { |
| for (j = i; j < n - i - 1; j++) { |
| tmp = board[i][j]; |
| board[i][j] = board[j][n - i - 1]; |
| board[j][n - i - 1] = board[n - i - 1][n - j - 1]; |
| board[n - i - 1][n - j - 1] = board[n - j - 1][i]; |
| board[n - j - 1][i] = tmp; |
| } |
| } |
| } |
| |
| static bool moveUp(u16 board[SIZE][SIZE]) |
| { |
| bool success = false; |
| int x; |
| |
| for (x = 0; x < SIZE; x++) |
| success |= slideArray(board[x]); |
| |
| return success; |
| } |
| |
| static bool moveLeft(u16 board[SIZE][SIZE]) |
| { |
| bool success; |
| |
| rotateBoard(board); |
| success = moveUp(board); |
| rotateBoard(board); |
| rotateBoard(board); |
| rotateBoard(board); |
| return success; |
| } |
| |
| static bool moveDown(u16 board[SIZE][SIZE]) |
| { |
| bool success; |
| |
| rotateBoard(board); |
| rotateBoard(board); |
| success = moveUp(board); |
| rotateBoard(board); |
| rotateBoard(board); |
| return success; |
| } |
| |
| static bool moveRight(u16 board[SIZE][SIZE]) |
| { |
| bool success; |
| |
| rotateBoard(board); |
| rotateBoard(board); |
| rotateBoard(board); |
| success = moveUp(board); |
| rotateBoard(board); |
| return success; |
| } |
| |
| static bool findPairDown(u16 board[SIZE][SIZE]) |
| { |
| bool success = false; |
| int x, y; |
| |
| for (x = 0; x < SIZE; x++) { |
| for (y = 0; y < SIZE - 1; y++) { |
| if (board[x][y] == board[x][y + 1]) |
| return true; |
| } |
| } |
| |
| return success; |
| } |
| |
| static int16_t countEmpty(u16 board[SIZE][SIZE]) |
| { |
| int x, y; |
| int count = 0; |
| |
| for (x = 0; x < SIZE; x++) { |
| for (y = 0; y < SIZE; y++) { |
| if (board[x][y] == 0) |
| count++; |
| } |
| } |
| return count; |
| } |
| |
| static bool gameEnded(u16 board[SIZE][SIZE]) |
| { |
| bool ended = true; |
| |
| if (countEmpty(board) > 0) |
| return false; |
| if (findPairDown(board)) |
| return false; |
| rotateBoard(board); |
| if (findPairDown(board)) |
| ended = false; |
| rotateBoard(board); |
| rotateBoard(board); |
| rotateBoard(board); |
| |
| return ended; |
| } |
| |
| static void addRandom(u16 board[SIZE][SIZE]) |
| { |
| int x, y; |
| int r, len = 0; |
| u16 n, list[SIZE * SIZE][2]; |
| |
| for (x = 0; x < SIZE; x++) { |
| for (y = 0; y < SIZE; y++) { |
| if (board[x][y] == 0) { |
| list[len][0] = x; |
| list[len][1] = y; |
| len++; |
| } |
| } |
| } |
| |
| if (len > 0) { |
| r = rand() % len; |
| x = list[r][0]; |
| y = list[r][1]; |
| n = ((rand() % 10) / 9 + 1) * 2; |
| board[x][y] = n; |
| } |
| } |
| |
| static int test(void) |
| { |
| u16 array[SIZE]; |
| u16 data[] = { |
| 0, 0, 0, 2, 2, 0, 0, 0, |
| 0, 0, 2, 2, 4, 0, 0, 0, |
| 0, 2, 0, 2, 4, 0, 0, 0, |
| 2, 0, 0, 2, 4, 0, 0, 0, |
| 2, 0, 2, 0, 4, 0, 0, 0, |
| 2, 2, 2, 0, 4, 2, 0, 0, |
| 2, 0, 2, 2, 4, 2, 0, 0, |
| 2, 2, 0, 2, 4, 2, 0, 0, |
| 2, 2, 2, 2, 4, 4, 0, 0, |
| 4, 4, 2, 2, 8, 4, 0, 0, |
| 2, 2, 4, 4, 4, 8, 0, 0, |
| 8, 0, 2, 2, 8, 4, 0, 0, |
| 4, 0, 2, 2, 4, 4, 0, 0 |
| }; |
| u16 *in, *out; |
| u16 t, tests; |
| int i; |
| bool success = true; |
| |
| tests = (sizeof(data) / sizeof(data[0])) / (2 * SIZE); |
| for (t = 0; t < tests; t++) { |
| in = data + t * 2 * SIZE; |
| out = in + SIZE; |
| for (i = 0; i < SIZE; i++) |
| array[i] = in[i]; |
| slideArray(array); |
| for (i = 0; i < SIZE; i++) { |
| if (array[i] != out[i]) |
| success = false; |
| } |
| if (!success) { |
| for (i = 0; i < SIZE; i++) |
| printf("%d ", in[i]); |
| printf(" = > "); |
| for (i = 0; i < SIZE; i++) |
| printf("%d ", array[i]); |
| printf("expected "); |
| for (i = 0; i < SIZE; i++) |
| printf("%d ", in[i]); |
| printf(" = > "); |
| for (i = 0; i < SIZE; i++) |
| printf("%d ", out[i]); |
| printf("\n"); |
| break; |
| } |
| } |
| if (success) |
| printf("All %u tests executed successfully\n", tests); |
| |
| return !success; |
| } |
| |
| static int do_2048(struct cmd_tbl *cmdtp, int flag, int argc, |
| char *const argv[]) |
| { |
| struct cli_ch_state cch_s, *cch = &cch_s; |
| u16 board[SIZE][SIZE]; |
| bool success; |
| |
| if (argc == 2 && strcmp(argv[1], "test") == 0) |
| return test(); |
| |
| score = 0; |
| |
| printf("\033[?25l\033[2J\033[H"); |
| |
| memset(board, 0, sizeof(board)); |
| addRandom(board); |
| addRandom(board); |
| drawBoard(board); |
| cli_ch_init(cch); |
| while (true) { |
| int c; |
| |
| c = cli_ch_process(cch, 0); |
| if (!c) { |
| c = getchar(); |
| c = cli_ch_process(cch, c); |
| } |
| switch (c) { |
| case CTL_CH('b'): /* left arrow */ |
| success = moveLeft(board); |
| break; |
| case CTL_CH('f'): /* right arrow */ |
| success = moveRight(board); |
| break; |
| case CTL_CH('p'):/* up arrow */ |
| success = moveUp(board); |
| break; |
| case CTL_CH('n'): /* down arrow */ |
| success = moveDown(board); |
| break; |
| default: |
| success = false; |
| } |
| if (success) { |
| drawBoard(board); |
| mdelay(150); |
| addRandom(board); |
| drawBoard(board); |
| if (gameEnded(board)) { |
| printf(" GAME OVER \n"); |
| break; |
| } |
| } |
| if (c == 'q') { |
| printf(" QUIT \n"); |
| break; |
| } |
| } |
| |
| printf("\033[?25h"); |
| |
| return 0; |
| } |
| |
| U_BOOT_CMD( |
| 2048, 2, 1, do_2048, |
| "The 2048 game", |
| "Use your arrow keys to move the tiles. When two tiles with " |
| "the same number touch, they merge into one!" |
| ); |