SystemFileB

2048.c modified from spaceskynet/2048.c

Feb 25th, 2026
54
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C 12.40 KB | None | 0 0
  1. /**
  2.  * @file 2048.c
  3.  * @author SpaceSkyNet ([email protected]), Modified & i18n by SystemFileB
  4.  * @brief 2048 Game for Console, available on Windows 10 and Linux.
  5.  * @version 0.1
  6.  * @date 2022-03-15
  7.  *
  8.  * @copyright Copyright (c) 2022 SpaceSkyNet
  9.  * @license MIT
  10.  */
  11. #include <stdio.h>
  12. #include <string.h>
  13. #include <stdlib.h>
  14. #include <time.h>
  15. #ifdef _WIN32
  16. #include <windows.h>
  17. #include <conio.h>
  18. #else
  19. #include <termios.h>
  20. #include <unistd.h>
  21. #endif
  22.  
  23. #define IS_UP_DOWN(d) (!((d)>>1))
  24. #define IS_UP_LEFT(d) (!((d)&1))
  25. #define WIN_NUMBER 2048
  26. #define true 1
  27. #define false 0
  28.  
  29. #define RESET "\033[0m"
  30. #define BOLD "\033[1m"
  31. #define BASE "\033["
  32. #define PADDING "        "
  33.  
  34. //typedef _Bool bool; //C99
  35. typedef int NUMTYPE;
  36. typedef enum _direction{UP,DOWN,LEFT,RIGHT,NONE}DIRECTION;
  37. typedef enum _colorCode{
  38.     FG_BLACK = 30,
  39.     FG_BLUE = 34,
  40.     FG_CYAN = 36,
  41.     FG_DARK_GRAY = 90,
  42.     FG_DEFAULT = 39,
  43.     FG_GREEN = 32,
  44.     FG_LIGHT_BLUE = 94,
  45.     FG_LIGHT_CYAN = 96,
  46.     FG_LIGHT_GRAY = 37,
  47.     FG_LIGHT_GREEN = 92,
  48.     FG_LIGHT_MAGENTA = 95,
  49.     FG_LIGHT_RED = 91,
  50.     FG_LIGHT_YELLOW = 93,
  51.     FG_MAGENTA = 35,
  52.     FG_RED = 31,
  53.     FG_WHITE = 97,
  54.     FG_YELLOW = 33,
  55. }COLOR_CODE;
  56. const COLOR_CODE COLOR[11] = {FG_RED, FG_GREEN, FG_YELLOW, FG_BLUE, FG_MAGENTA, FG_CYAN,
  57.                               FG_LIGHT_GRAY, FG_DARK_GRAY, FG_LIGHT_RED, FG_LIGHT_GREEN, FG_LIGHT_YELLOW};
  58. int BEST_SCORE = 0;
  59.  
  60. void clearScreen();
  61. void setConsoleCursorPosition();
  62. void showConsoleCursor(bool);
  63. void printTitle();
  64. void printTable(NUMTYPE*, int, int, int);
  65. void randGenerate(NUMTYPE*, int);
  66. void printColoredNumber(NUMTYPE);
  67. int Log2(NUMTYPE);
  68. int moveTable(DIRECTION, NUMTYPE*, int);
  69. int getPos(DIRECTION, int, int, int);
  70.  
  71. bool startGame(NUMTYPE*, int);
  72. bool cmpTable(NUMTYPE*, NUMTYPE*, int);
  73. bool getConfirm(NUMTYPE*, int, int, int);
  74. bool confirmQuit(NUMTYPE*, int, int, int);
  75. bool confirmRestart(NUMTYPE*, int, int, int);
  76. bool confirmWinQuit(NUMTYPE*, int, int, int);
  77. bool checkWin(NUMTYPE*, int);
  78. bool checkGameOver(NUMTYPE*, int);
  79. char getCh();
  80. DIRECTION directionMap(char);
  81.  
  82. int main()
  83. {
  84.     srand(time(NULL));
  85.     int size = 4;
  86.     printTitle();
  87.     scanf("%d", &size);
  88.     if(size <= 1) size = 4;
  89.     int n = size * size;
  90.     NUMTYPE *table = (NUMTYPE*)malloc(n * sizeof(NUMTYPE));
  91.     while(true)
  92.     {
  93.         memset(table, 0, n * sizeof(NUMTYPE) / sizeof(char));
  94.         if(!startGame(table, size)) break;
  95.     }
  96.     printf("\nThanks for playing!\n");
  97.     free(table);
  98. #ifdef _WIN32
  99.     system("pause");
  100. #else
  101.     getchar();
  102. #endif
  103.     return 0;
  104. }
  105. void init(NUMTYPE *t, int size)
  106. {
  107.     randGenerate(t, size);
  108.     randGenerate(t, size);
  109. }
  110. bool startGame(NUMTYPE *t, int size)
  111. {
  112.     int n = size * size, score = 0, moves = 0;
  113.     NUMTYPE *before = (NUMTYPE*)malloc(n * sizeof(NUMTYPE));
  114.     bool isWin = false, isRestart = false;
  115.     init(t, size);
  116.     clearScreen();
  117.     printTable(t, size, score, moves);
  118.     while(true)
  119.     {
  120.         char input = getCh();
  121.         if(input == 'q' && confirmQuit(t, size, score, moves)) break;
  122.         if(input == 'r' && confirmRestart(t, size, score, moves)){isRestart = true;break;}
  123.  
  124.         DIRECTION direction = directionMap(input);
  125.         if(direction == NONE) continue;
  126.  
  127.         memcpy(before, t, n * sizeof(NUMTYPE) / sizeof(char));
  128.         score += moveTable(direction, t, size);
  129.         if(score > BEST_SCORE) BEST_SCORE = score;
  130.         if(!cmpTable(before, t, n)) randGenerate(t, size), moves++;
  131.         printTable(t, size, score, moves);
  132.  
  133.         if(!isWin && checkWin(t, n))
  134.         {
  135.             printf("\nYou are win!! Congratulations!\n");
  136.             isWin = true;
  137.             if(confirmWinQuit(t, size, score, moves))
  138.             {
  139.                 if(confirmRestart(t, size, score, moves)) isRestart = true;
  140.                 break;
  141.             }
  142.         }
  143.  
  144.         if(checkGameOver(t, size))
  145.         {
  146.             printf("\nGame Over! Have another try?\n");
  147.             isRestart = confirmRestart(t, size, score, moves);
  148.             break;
  149.         }
  150.     }
  151.     free(before);
  152.     return isRestart;
  153. }
  154. void randGenerate(NUMTYPE *t, int size)
  155. {
  156.     int n = size * size;
  157.     int pos = rand() % n;
  158.     while(t[pos]) pos = rand() % n;
  159.     NUMTYPE val = 1 << (((rand() % 10) / 9) + 1); // 2: 9/10, 4: 1/10
  160.     t[pos] = val;
  161. }
  162. bool getConfirm(NUMTYPE *t, int size, int score, int moves)
  163. {
  164.     while(true)
  165.     {
  166.         char c = getCh();
  167.         if(c == 'Y' || c == 'y') return true;
  168.         else if(c == 'N' || c == 'n')
  169.         {
  170.             showConsoleCursor(false);
  171.             clearScreen();
  172.             printTable(t, size, score, moves);
  173.             return false;
  174.         }
  175.     }
  176. }
  177. bool confirmQuit(NUMTYPE *t, int size, int score, int moves)
  178. {
  179.     showConsoleCursor(true);
  180.     printf("\nAre you sure to quit? [Y/n] ");
  181.     return getConfirm(t, size, score, moves);
  182. }
  183. bool confirmRestart(NUMTYPE *t, int size, int score, int moves)
  184. {
  185.     showConsoleCursor(true);
  186.     printf("\nDo you want to restart? [Y/n] ");
  187.     return getConfirm(t, size, score, moves);
  188. }
  189. bool confirmWinQuit(NUMTYPE *t, int size, int score, int moves)
  190. {
  191.     showConsoleCursor(true);
  192.     printf("Continue playing current game? [Y/n] ");
  193.     return getConfirm(t, size, score, moves);
  194. }
  195.  
  196.  
  197. bool checkWin(NUMTYPE *t, int len)
  198. {
  199.     for(int i = 0; i < len; i++)
  200.     {
  201.         if(t[i] == WIN_NUMBER) return true;
  202.     }
  203.     return false;
  204. }
  205. bool checkGameOver(NUMTYPE *t, int size)
  206. {
  207.     int n = size * size;
  208.     for(int i = 0; i < n; i++)
  209.     {
  210.         if(!t[i]) return false;
  211.     }
  212.    
  213.     NUMTYPE *testTable = (NUMTYPE*)malloc(n * sizeof(NUMTYPE) / sizeof(char));
  214.     for(int i = 0; i < 4; i++)
  215.     {
  216.         memcpy(testTable, t, n * sizeof(NUMTYPE) / sizeof(char));
  217.         if(moveTable((DIRECTION)i, testTable, size))
  218.         {
  219.             free(testTable);
  220.             return false;
  221.         }
  222.     }
  223.     free(testTable);
  224.     return true;
  225. }
  226.  
  227. bool cmpTable(NUMTYPE *s, NUMTYPE *t, int len)
  228. {
  229.     for(int i = 0; i < len; i++)
  230.     {
  231.         if(s[i] != t[i]) return false;
  232.     }
  233.     return true;
  234. }
  235. int Log2(NUMTYPE x)
  236. {
  237.     int ans = 0;
  238.     while(x >>= 1) ++ans;
  239.     return ans;
  240. }
  241. void printColoredNumber(NUMTYPE x)
  242. {
  243.     if(!x) printf("│      ");
  244.     else
  245.     {
  246.         int y = Log2(x);
  247.         int index = y < 12 ? y - 1 : y % 12 - 1;
  248.         printf("│ %s%s%dm%4d%s ", BOLD, BASE, (int)COLOR[index], x, RESET);
  249.     }
  250. }
  251. void printTable(NUMTYPE *t, int size, int score, int moves)
  252. {
  253.     setConsoleCursorPosition();
  254.     printf("┌───────────────────────────┐\n");
  255.     printf("│ %sSCORE: %18d%s │\n", BOLD, score, RESET);
  256.     printf("│ %sBEST SCORE: %13d%s │\n", BOLD, BEST_SCORE, RESET);
  257.     printf("│ %sMOVES: %18d%s │\n", BOLD, moves, RESET);
  258.     printf("└───────────────────────────┘\n");
  259.     printf("┌");
  260.     for(int i = 0; i < size - 1; i++) printf("──────┬");
  261.     printf("──────┐\n");
  262.     for(int i = 0; i < size; i++)
  263.     {
  264.         for(int j = 0; j < size; j++)
  265.         {
  266.             int pos = i * size + j;
  267.             printColoredNumber(t[pos]);
  268.         }
  269.         printf("│\n");
  270.         if(i == size - 1) break;
  271.         printf("├");
  272.         for(int i = 0; i < size - 1; i++) printf("──────┼");
  273.         printf("──────┤\n");
  274.     }
  275.     printf("└");
  276.     for(int i = 0; i < size - 1; i++) printf("──────┴");
  277.     printf("──────┘\n");
  278.     printf("q=退出 r=重新开始 wasd=移动\n");
  279. }
  280. DIRECTION directionMap(char c)
  281. {
  282.     if(c == 'w' || c == 'W') return UP;
  283.     if(c == 's' || c == 'S') return DOWN;
  284.     if(c == 'a' || c == 'A') return LEFT;
  285.     if(c == 'd' || c == 'D') return RIGHT;
  286.     return NONE;
  287. }
  288.  
  289. /**
  290.  * UP   = 00, DOWN  = 01, >> 1 = 0 -> (j, i)
  291.  * LEFT = 10, RIGHT = 11, >> 1 = 1 -> (i, j)
  292.  */
  293. inline int getPos(DIRECTION direction, int i, int j, int size)
  294. {
  295.     if(IS_UP_DOWN(direction)) return j * size + i;
  296.     else return i * size + j;
  297. }
  298.  
  299. /**
  300.  * UP   = 00, LEFT  = 10, & 1 = 0 -> from 0 to size-1, step +1
  301.  * DOWN = 01, RIGHT = 11, & 1 = 1 -> from size-1 to 0, step -1
  302.  */
  303. int moveTable(DIRECTION direction, NUMTYPE *t, int size)
  304. {
  305.     int start, end, step, score = 0;
  306.    
  307.     if(IS_UP_LEFT(direction)) start = 0, end = size - 1, step = 1;
  308.     else start = size - 1, end = 0, step = -1;
  309.  
  310.     for(int i = 0; i < size; i++)
  311.     {
  312.         int p = start;
  313.         for(int j = p + step; step > 0 ? j <= end : j >= end; j += step)
  314.         {
  315.             int ij = getPos(direction, i, j, size), ip = getPos(direction, i, p, size);
  316.             if(!t[ij]) continue;
  317.             if(t[ip] && t[ip] ^ t[ij])
  318.             {
  319.                 p += step; t[getPos(direction, i, p, size)] = t[ij];
  320.                 if(p ^ j) t[ij] = 0;
  321.             }
  322.             else
  323.             {
  324.                 if(!t[ip]) t[ip] = t[ij];
  325.                 else t[ip] <<= 1, score += t[ip], p += step;
  326.                 t[ij] = 0;
  327.             }
  328.         }
  329.     }
  330.     return score;
  331. }
  332.  
  333. #ifndef _WIN32
  334. void setBufferedInput(bool enable)
  335. {
  336.     static bool enabled = true;
  337.     static struct termios OLD;
  338.     struct termios NEW;
  339.  
  340.     if (enable && !enabled)
  341.     {
  342.         // restore the former settings
  343.         tcsetattr(STDIN_FILENO, TCSANOW, &OLD);
  344.         // set the new state
  345.         enabled = true;
  346.     }
  347.     else if (!enable && enabled)
  348.     {
  349.         // get the terminal settings for standard input
  350.         tcgetattr(STDIN_FILENO, &NEW);
  351.         // we want to keep the old setting to restore them at the end
  352.         OLD = NEW;
  353.         // disable canonical mode (buffered i/o) and local echo
  354.         NEW.c_lflag &= (~ICANON & ~ECHO);
  355.         // set the new settings immediately
  356.         tcsetattr(STDIN_FILENO, TCSANOW, &NEW);
  357.         // set the new state
  358.         enabled = false;
  359.     }
  360. }
  361. #endif
  362. char getCh()
  363. {
  364. #ifdef _WIN32
  365.     return getch();
  366. #else
  367.     setBufferedInput(false);
  368.     char c = getchar();
  369.     setBufferedInput(true);
  370.     return c;
  371. #endif
  372. }
  373. void showConsoleCursor(bool show)
  374. {
  375. #ifdef _WIN32
  376.     HANDLE consoleHandle = GetStdHandle(STD_OUTPUT_HANDLE);
  377.     CONSOLE_CURSOR_INFO cci;
  378.     GetConsoleCursorInfo(consoleHandle, &cci);
  379.     cci.bVisible = show; // show/hide cursor
  380.     SetConsoleCursorInfo(consoleHandle, &cci);
  381. #else
  382.     printf(show ? "\033[?25h" : "\033[?25l"); // show/hide cursor
  383. #endif
  384. }
  385. void clearScreen()
  386. {
  387. #ifdef _WIN32
  388.     system("cls");
  389. #else
  390.     system("clear");
  391. #endif    
  392. }
  393. void setConsoleCursorPosition()
  394. {
  395. #ifdef _WIN32
  396.     HANDLE consoleHandle = GetStdHandle(STD_OUTPUT_HANDLE);
  397.     COORD cur = {0, 0};
  398.     showConsoleCursor(false);
  399.     SetConsoleCursorPosition(consoleHandle, cur);
  400. #else
  401.     printf("\033[0m\033[2J\033c");
  402. #endif
  403. }
  404.  
  405. void printTitle()
  406. {
  407.     #ifdef _WIN32
  408.     system("chcp 65001"); // UTF-8
  409.     #endif
  410.     clearScreen();
  411.     printf(PADDING"-----------------------------------\n%s%dm", BASE, (int)FG_LIGHT_GREEN);
  412.     printf(PADDING" █████    █████      ███    █████  \n");
  413.     printf(PADDING"██   ██  ██   ██    ████   ██  ██  \n");
  414.     printf(PADDING"█     █  █    ██    █ ██   ██   ██ \n");
  415.     printf(PADDING"█     █  █     █   ██ ██   ██   ██ \n");
  416.     printf(PADDING"██   ██  █     █   █  ██    █████  \n");
  417.     printf(PADDING"███ ██   █     █  ██  ██   ███████ \n");
  418.     printf(PADDING"   ██    █     █ ██   ██   █    ██ \n");
  419.     printf(PADDING"  ██     █     █ ████████ ██     █ \n");
  420.     printf(PADDING"███      █    ██      ██  ██     █ \n");
  421.     printf(PADDING"███      ███ ███      ██   ██   ██ \n");
  422.     printf(PADDING"███████   █████       ██    █████  %s\n", RESET);
  423.     printf(PADDING"-------- By: SpaceSkyNet ----------\n\n");
  424.     printf("Join the numbers and get to the %s%dm2048%s tile!\n", BASE, (int)FG_LIGHT_GREEN, RESET);
  425.     printf("You can press %s%dm`q`%s to quit and press %s%dm`r`%s to restart!\n", BASE, (int)FG_LIGHT_RED, RESET, BASE, (int)FG_LIGHT_RED, RESET);
  426.     printf("You can use %s%dm`WASD`%s to move in playing!\n", BASE, (int)FG_LIGHT_RED, RESET);
  427.     printf("Now, please input the size of the game: \n");
  428.     printf("游戏大小: ");
  429. }
  430.  
Advertisement
Add Comment
Please, Sign In to add comment