teddyxlandlee
逝情是这样的:最近无意间找到了一个命令行版的2048,用C语言写的,然后自己编译自己玩就玩上瘾了。

后来我突然想起来之前玩过一个小程序,里面有各种2048的魔改版,其中有一个就是会生成数字0的(0只能跟0消并且不加分)
于是我突发奇想给这个2048.c魔改一下:









扔GayHub了:https://github.com/teddyxlandlee/2048-with-zero/
自己下载编译。上不了GayHub的也可以从这里复制:[spoiler]/*
============================================================================
Name        : 2048.c
Author      : Maurits van der Schee
Description : Console version of the game "2048" for GNU/Linux
============================================================================
*/

#define _XOPEN_SOURCE 500 // for: usleep
#include           // defines: printf, puts, getchar
#include           // defines: EXIT_SUCCESS
#include           // defines: strcmp
#include           // defines: STDIN_FILENO, usleep
#include           // defines: termios, TCSANOW, ICANON, ECHO
#include           // defines: true, false
#include           // defines: uint8_t, uint32_t
#include           // defines: time
#include           // defines: signal, SIGINT

#define SIZE 4

// WithZero patch start: BLOCK_ZERO definition
#define BLOCK_ZERO (255)
// WithZero patch end

// 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 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};
        // modify the 'pointed to' variables (using a * on the left hand of the assignment)
        *foreground = *(schemes[scheme] + (1 + value * 2) % sizeof(original));
        *background = *(schemes[scheme] + (0 + value * 2) % sizeof(original));
        // alternatively we could have returned a struct with two variables
}

uint8_t getDigitCount(uint32_t number)
{
        uint8_t count = 0;
        do
        {
                number /= 10;
                count += 1;
        } while (number);
        return count;
}

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 %17d pts\n\n", score);
        for (y = 0; y
        {
                for (x = 0; x
                {
                        // send the addresses of the foreground and background variables,
                        // so that they can be modified by the getColors function
                        getColors(board[x][y], scheme, &fg, &bg);
                        printf("\033[38;5;%d;48;5;%dm", fg, bg); // set color
                        printf("       ");
                        printf("\033[m"); // reset all modes
                }
                printf("\n");
                for (x = 0; x
                {
                        getColors(board[x][y], scheme, &fg, &bg);
                        printf("\033[38;5;%d;48;5;%dm", fg, bg); // set color
                        if (board[x][y] != 0)
                        {
                                // WithZero patch start: render BLOCK_ZERO as 0
                                //uint32_t number = 1
                                uint32_t number = board[x][y] == BLOCK_ZERO ? 0 : 1
                                // WithZero patch end
                                uint8_t t = 7 - getDigitCount(number);
                                printf("%*s%u%*s", t - t / 2, "", number, t / 2, "");
                        }
                        else
                        {
                                printf("   ·   ");
                        }
                        printf("\033[m"); // reset all modes
                }
                printf("\n");
                for (x = 0; x
                {
                        getColors(board[x][y], scheme, &fg, &bg);
                        printf("\033[38;5;%d;48;5;%dm", fg, bg); // set color
                        printf("       ");
                        printf("\033[m"); // reset all modes
                }
                printf("\n");
        }
        printf("\n");
        printf("        ←,↑,→,↓ or q        \n");
        printf("\033[A"); // one line up
}

uint8_t findTarget(uint8_t array[SIZE], uint8_t x, uint8_t stop)
{
        uint8_t t;
        // if the position is already on the first, don't evaluate
        if (x == 0)
        {
                return x;
        }
        for (t = x - 1;; t--)
        {
                if (array[t] != 0)
                {
                        if (array[t] != array[x])
                        {
                                // merge is not possible, take next position
                                return t + 1;
                        }
                        return t;
                }
                else
                {
                        // we should not slide further, return this one
                        if (t == stop)
                        {
                                return t;
                        }
                }
        }
        // we did not find a target
        return x;
}

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

        for (x = 0; 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 zero, this is a move
                                if (array[t] == 0)
                                {
                                        array[t] = array[x];
                                }
                                else if (array[t] == array[x])
                                {
                                        // WithZero patch starts: Adapt BLOCK_ZERO move
                                        if (array[t] == BLOCK_ZERO) {
                                                // Do nothing but slide
                                        } else {
                                        // WithZero patch ends
                                        // merge (increase power of two)
                                        array[t]++;
                                        // increase score
                                        *score += (uint32_t)1
                                        // set stop to avoid double merge
                                        }        // WithZero patch
                                        stop = t + 1;
                                }
                                array[x] = 0;
                                success = true;
                        }
                }
        }
        return success;
}

void rotateBoard(uint8_t board[SIZE][SIZE])
{
        uint8_t i, j, n = SIZE;
        uint8_t tmp;
        for (i = 0; i
        {
                for (j = i; 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;
                }
        }
}

bool moveUp(uint8_t board[SIZE][SIZE], uint32_t *score)
{
        bool success = false;
        uint8_t x;
        for (x = 0; x
        {
                success |= slideArray(board[x], score);
        }
        return success;
}

bool moveLeft(uint8_t board[SIZE][SIZE], uint32_t *score)
{
        bool success;
        rotateBoard(board);
        success = moveUp(board, score);
        rotateBoard(board);
        rotateBoard(board);
        rotateBoard(board);
        return success;
}

bool moveDown(uint8_t board[SIZE][SIZE], uint32_t *score)
{
        bool success;
        rotateBoard(board);
        rotateBoard(board);
        success = moveUp(board, score);
        rotateBoard(board);
        rotateBoard(board);
        return success;
}

bool moveRight(uint8_t board[SIZE][SIZE], uint32_t *score)
{
        bool success;
        rotateBoard(board);
        rotateBoard(board);
        rotateBoard(board);
        success = moveUp(board, score);
        rotateBoard(board);
        return success;
}

bool findPairDown(uint8_t board[SIZE][SIZE])
{
        bool success = false;
        uint8_t x, y;
        for (x = 0; x
        {
                for (y = 0; y
                {
                        if (board[x][y] == board[x][y + 1])
                                return true;
                }
        }
        return success;
}

uint8_t countEmpty(uint8_t board[SIZE][SIZE])
{
        uint8_t x, y;
        uint8_t count = 0;
        for (x = 0; x
        {
                for (y = 0; y
                {
                        if (board[x][y] == 0)
                        {
                                count++;
                        }
                }
        }
        return count;
}

bool gameEnded(uint8_t 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;
}

void addRandom(uint8_t board[SIZE][SIZE])
{
        static bool initialized = false;
        uint8_t x, y;
        uint8_t r, len = 0;
        uint8_t n, list[SIZE * SIZE][2];

        if (!initialized)
        {
                srand(time(NULL));
                initialized = true;
        }

        for (x = 0; x
        {
                for (y = 0; 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];
                // WithZero patch start: rolls out zero, with the same chance of 4
                //n = (rand() % 10) / 9 + 1;
                n = (rand() % 11) / 9 + 1;
                if (n == 2) {
                        n = (rand() % 2) ? 2 : BLOCK_ZERO;
                }
                // WithZero patch end
                board[x][y] = n;
        }
}

void initBoard(uint8_t board[SIZE][SIZE])
{
        uint8_t x, y;
        for (x = 0; x
        {
                for (y = 0; y
                {
                        board[x][y] = 0;
                }
        }
        addRandom(board);
        addRandom(board);
}

void setBufferedInput(bool enable)
{
        static bool enabled = true;
        static struct termios old;
        struct termios new;

        if (enable && !enabled)
        {
                // restore the former settings
                tcsetattr(STDIN_FILENO, TCSANOW, &old);
                // set the new state
                enabled = true;
        }
        else if (!enable && enabled)
        {
                // get the terminal settings for standard input
                tcgetattr(STDIN_FILENO, &new);
                // we want to keep the old setting to restore them at the end
                old = new;
                // disable canonical mode (buffered i/o) and local echo
                new.c_lflag &= (~ICANON & ~ECHO);
                // set the new settings immediately
                tcsetattr(STDIN_FILENO, TCSANOW, &new);
                // set the new state
                enabled = false;
        }
}

int test()
{
        uint8_t array[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[] = {
            0, 0, 0, 1, 1, 0, 0, 0, 0,
            0, 0, 1, 1, 2, 0, 0, 0, 4,
            0, 1, 0, 1, 2, 0, 0, 0, 4,
            1, 0, 0, 1, 2, 0, 0, 0, 4,
            1, 0, 1, 0, 2, 0, 0, 0, 4,
            1, 1, 1, 0, 2, 1, 0, 0, 4,
            1, 0, 1, 1, 2, 1, 0, 0, 4,
            1, 1, 0, 1, 2, 1, 0, 0, 4,
            1, 1, 1, 1, 2, 2, 0, 0, 8,
            2, 2, 1, 1, 3, 2, 0, 0, 12,
            1, 1, 2, 2, 2, 3, 0, 0, 12,
            3, 0, 1, 1, 3, 2, 0, 0, 4,
            2, 0, 1, 1, 2, 2, 0, 0, 4};
        uint8_t *in, *out, *points;
        uint8_t t, tests;
        uint8_t i;
        bool success = true;
        uint32_t score;

        tests = (sizeof(data) / sizeof(data[0])) / (2 * SIZE + 1);
        for (t = 0; t
        {
                in = data + t * (2 * SIZE + 1);
                out = in + SIZE;
                points = in + 2 * SIZE;
                for (i = 0; i
                {
                        array[i] = in[i];
                }
                score = 0;
                slideArray(array, &score);
                for (i = 0; i
                {
                        if (array[i] != out[i])
                        {
                                success = false;
                        }
                }
                if (score != *points)
                {
                        success = false;
                }
                if (success == false)
                {
                        for (i = 0; i
                        {
                                printf("%d ", in[i]);
                        }
                        printf("=> ");
                        for (i = 0; i
                        {
                                printf("%d ", array[i]);
                        }
                        printf("(%d points) expected ", score);
                        for (i = 0; i
                        {
                                printf("%d ", in[i]);
                        }
                        printf("=> ");
                        for (i = 0; i
                        {
                                printf("%d ", out[i]);
                        }
                        printf("(%d points)\n", *points);
                        break;
                }
        }
        if (success)
        {
                printf("All %u tests executed successfully\n", tests);
        }
        return !success;
}

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

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

        if (argc == 2 && strcmp(argv[1], "test") == 0)
        {
                return test();
        }
        if (argc == 2 && strcmp(argv[1], "blackwhite") == 0)
        {
                scheme = 1;
        }
        if (argc == 2 && strcmp(argv[1], "bluered") == 0)
        {
                scheme = 2;
        }

        // make cursor invisible, erase entire screen
        printf("\033[?25l\033[2J");

        // register signal handler for when ctrl-c is pressed
        signal(SIGINT, signal_callback_handler);

        initBoard(board);
        setBufferedInput(false);
        drawBoard(board, scheme, score);
        while (true)
        {
                c = getchar();
                if (c == -1)
                {
                        puts("\nError! Cannot read keyboard input!");
                        break;
                }
                switch (c)
                {
                case 97:  // 'a' key
                case 104: // 'h' key
                case 68:  // left arrow
                        success = moveLeft(board, &score);
                        break;
                case 100: // 'd' key
                case 108: // 'l' key
                case 67:  // right arrow
                        success = moveRight(board, &score);
                        break;
                case 119: // 'w' key
                case 107: // 'k' key
                case 65:  // up arrow
                        success = moveUp(board, &score);
                        break;
                case 115: // 's' key
                case 106: // 'j' key
                case 66:  // down arrow
                        success = moveDown(board, &score);
                        break;
                default:
                        success = false;
                }
                if (success)
                {
                        drawBoard(board, scheme, score);
                        usleep(150 * 1000); // 150 ms
                        addRandom(board);
                        drawBoard(board, scheme, score);
                        if (gameEnded(board))
                        {
                                printf("         GAME OVER          \n");
                                break;
                        }
                }
                if (c == 'q')
                {
                        printf("        QUIT? (y/n)         \n");
                        c = getchar();
                        if (c == 'y')
                        {
                                break;
                        }
                        drawBoard(board, scheme, score);
                }
                if (c == 'r')
                {
                        printf("       RESTART? (y/n)       \n");
                        c = getchar();
                        if (c == 'y')
                        {
                                initBoard(board);
                                score = 0;
                        }
                        drawBoard(board, scheme, score);
                }
        }
        setBufferedInput(true);

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

        return EXIT_SUCCESS;
}
复制代码


这个魔改版的规则如下:0+0=0别的数字不能跟0合0不会凭空消失,它只能和另一个0消失变成一个新的00跟0合不加分

由于在内存中,这些数字是按照以2为底的对数存储的(空位记0),所以我选择用255来表示“数字0”(不会有哪个闲人玩到2的255次方罢)
刷出0的几率跟4一样,都是1/11。

怎么说,不带0的时候我能打到将近15000,但是带0最多2k
md我被我自己写的游戏搞b溃了(虽然不完全是我写的)
不行不行我一定要恶心一下大家

另外谁能编译一个能在Windows环境下跑的版本,我不能只让Linux和OS X玩家难受啊啊啊啊啊