简体   繁体   中英

C Replacements for srand() and rand()

I'm trying to make a game in C fit under a 3KB limit, and to do so I want to remove all standard library references. The only header I want to have in my program is windows.h and so far I'm doing pretty good.

The only external references I have are one call each of rand() , srand() , time() , and getch() . This may seem like a lot for a game under 3KB, but the time, rand, and srand functions are only so that when the game starts it can have a random seed.

rand to get the numbers, srand to set the seed, and time to get random seeds. So if I could find a way to get rid of the excessive random generation procedure, I could get rid of 3/4 std library functions. I'm not so sure what to do about getch though, but ill focus on that later.

Some people have reccomended xorshifts , but it seems like that requires the type uint32_t , which belongs to stdint.h . I also tried using integers instead, and while the resulting number was random, it was random in the sense that the number given back was random, yet you could depend on it giving you that random number each time. I could give it time as a sort of seed, but then id still have to use the time function. Is there something I'm overlooking, or should I use a different method?

EDIT: as per request, my compilation flags are -ffunction-sections , -fdata-sections , -s and -Os . my only linker flags are -Wl,--gc-sections and I am not sure what dynamically linked and statically linked libraries are completely, but I have a decent idea. My code as is comes out to 12KB, and if i use UPX i can get it down to 7KB. my code is here:

#include <windows.h>

#define WIDTH 100
#define HEIGHT 100
#define BOMBS 800

struct xorshift_state {
  int a;
};

int xorshift(struct xorshift_state *state)
{
    int x = state->a;
    x ^= x << 13;
    x ^= x >> 17;
    x ^= x << 5;
    return state->a = x;
}

void ExpandGrid(int fullGrid[WIDTH][HEIGHT], int knownGrid[WIDTH][HEIGHT], int blankPos[2])
{
    int neighbors[8][2] = {{0,1}, {1,0}, {1,1},
                          {0,-1},        {-1,0},
                          {-1,-1},{-1,1},{1,-1}};
    int curTile[2];

    knownGrid[blankPos[0]][blankPos[1]] = 1;
    if(fullGrid[blankPos[0]][blankPos[1]] != 0) return;

    for(int blck = 0; blck < 8; ++blck)
    {
        curTile[0] = blankPos[0]+neighbors[blck][0];
        curTile[1] = blankPos[1]+neighbors[blck][1];
        if(curTile[0] > WIDTH-1 || curTile[1] > HEIGHT-1 || curTile[0] < 0 || curTile[1] < 0) continue;

        if(fullGrid[curTile[0]][curTile[1]] == 0 && knownGrid[curTile[0]][curTile[1]] == 0)
        {
            knownGrid[curTile[0]][curTile[1]] = 1;
            ExpandGrid(fullGrid, knownGrid, curTile);
        }
        else if(fullGrid[curTile[0]][curTile[1]] > 0) knownGrid[curTile[0]][curTile[1]] = 1;
    }
}

int main(int argc, char *argv[])
{

    COORD characterBufferSize = { WIDTH, HEIGHT };
    COORD characterPosition = { 0, 0 };
    SMALL_RECT consoleWriteArea = { 0, 0, WIDTH - 1, HEIGHT - 1 };
    CHAR_INFO consoleBuffer[WIDTH][HEIGHT];
    HANDLE wHnd = GetStdHandle(-11);

    int startGrid[WIDTH][HEIGHT] = { 0 };
    int knownGrid[WIDTH][HEIGHT] = { 0 };
    int arrowPos[2] = {0, 0};
    int bomb[2] = {0};
    struct xorshift_state seed = {argc == 2 ? (int) argv[1] : 1};

    for (int i = 0; i < BOMBS; i++)
    {
        while (startGrid[bomb[0]][bomb[1]] < -1 || bomb[0] <= 0 || bomb[1] <= 0 || bomb[0] >= WIDTH-1 || bomb[1] >= HEIGHT-1)
        {
            bomb[0] = (xorshift(&seed) % WIDTH-1) + 1;
            bomb[1] = (xorshift(&seed) % HEIGHT-1) + 1;
        }

        startGrid[bomb[0]][bomb[1]] = -9;

        startGrid[bomb[0] + 1][bomb[1] + 1]++;
        startGrid[bomb[0] + 1][bomb[1]]++;
        startGrid[bomb[0]][bomb[1] + 1]++;
        startGrid[bomb[0] - 1][bomb[1] + 1]++;
        startGrid[bomb[0]][bomb[1] - 1]++;
        startGrid[bomb[0] + 1][bomb[1] - 1]++;
        startGrid[bomb[0] - 1][bomb[1] - 1]++;
        startGrid[bomb[0] - 1][bomb[1]]++;
    }


    while(1)
    {
        if (arrowPos[0] > WIDTH-1) arrowPos[0] = WIDTH-1;
        if (arrowPos[0] < 0) arrowPos[0] = 0;
        if (arrowPos[1] > HEIGHT-1) arrowPos[1] = HEIGHT-1;
        if (arrowPos[1] < 0) arrowPos[1] = 0;

        for (int x = 0; x < WIDTH; ++x)
        {
            for (int y = 0; y < HEIGHT; ++y)
            {

                if (knownGrid[x][y] == 1)
                {
                    if (startGrid[x][y] > 0)
                    {
                        consoleBuffer[x][y].Char.AsciiChar = '0' + startGrid[x][y];
                        consoleBuffer[x][y].Attributes = 10;
                    }
                    else
                    {
                        consoleBuffer[x][y].Char.AsciiChar = 'o';
                        consoleBuffer[x][y].Attributes = startGrid[x][y] < 0 ? 4 : 17;
                    }
                }
                else
                {
                    consoleBuffer[x][y].Char.AsciiChar = 00;
                    consoleBuffer[x][y].Attributes = 0;
                }

                if(arrowPos[0] == x && arrowPos[1] == y)
                {
                    consoleBuffer[x][y].Attributes = 112;
                }
            }
        }

        WriteConsoleOutputA(wHnd, *consoleBuffer, characterBufferSize, characterPosition, &consoleWriteArea);

        switch(getch())
        {
            case 72:
                arrowPos[0]--;
                break;
            case 80:
                arrowPos[0]++;
                break;
            case 75:
                arrowPos[1]--;
                break;
            case 77:
                arrowPos[1]++;
                break;
            case '\r':
                ExpandGrid(startGrid, knownGrid, arrowPos);
                break;
        }
    }
}

It depends a lot on your requirement on the random generator. If you just want it so that the game will be a bit different every time you play it and not be perfectly deterministic, then just about any random generator will do. Just look online and you'll find examples of primitive random generators.

Among the simplest (but still reasonably good) is this:

int seed = 123456789;

int rand()
{
  seed = (a * seed + c) % m;
  return seed;
}    

Note that you need good values for a , c and m . It is explained further here where I found it: https://stackoverflow.com/a/3062783/6699433

When it comes to the seed, there are some options. If possible, you could send the seed as an argument to the program. You could use user input and measure timing.

Also, remember that the size of the executable does not (or at least not in general) get bigger because you're including headers if you're not using them. Especially not if you have activated optimizations.

You could use an argument to the executable, and hash argv[1] as seed. That way people can play the same problem over and over and against each other. For instance "Andromeda" or "London". :)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM