0

For a multiplayer game I need to produce the same sequence of random numbers on all participating devices. Obviously, this is achieved by using the same seed on all devices. Problems arise when random() is called outside of the routine that is supposed to produce the same sequence on all devices. In this routine, I therefore try to use setstate to preserve the state array. As far as I understood man random(3), calls to random() outside of this routine then should not change the sequence.

However, the following code does not produce the same output for Run 1 and 2:

#include <stdlib.h>
#include <stdio.h>
#include <string.h>

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

    char state[256];

    printf("Run 1 : ");
    initstate(123, state, 256);
    for (int i=1; i < 10; i++) {
        printf("%ld ", random());
    }

    printf("\nRun 2 : ");
    initstate(123, state, 256);
    for (int i=1; i < 10; i++) {
                setstate(state); // load preserved state
                printf("%ld ", random());
                *state = *setstate(state); // preserve state 
                random(); // this simulates a call to random() from outside
        }
        printf("\n");
        return 0;
}


Run 1 : 1597493280 1407130876 1753502901 1965067074 377602131 83146350 274392949 1718024305 1016176754 
Run 2 : 1597493280 537479855 1611694138 941096776 83164437 1459338036 1256894804 1618690717 1091902527 
Program ended with exit code: 0

Does anyone have any idea why? Or maybe there is another way to achieve the desired result?

For the record: Running on OS X and iOS using Xcode 5.

EDIT With the help of Arkku and minitech, the following change makes Run 2's output identical to Run 1:

    printf("\nRun 2 : ");
char otherstate[256];
initstate(123, state, 256);
for (int i=1; i < 10; i++) {
    // preserve whatever state is currently active
    // and set the current state to "my" state
    memcpy(otherstate, setstate(state), 256);
    printf("%ld ", random());
    // switch back to the other state
    memcpy(state, setstate(otherstate), 256);
    // now this call does not affect the sequence that get's printf'd
    random();
}
printf("\n");
Mojo66
  • 1,109
  • 12
  • 21

2 Answers2

1

In the second loop you call random() without printing out the value. Also, the setstate lines are useless; the same array (state) is used every time so the state will keep changing. The two sequences become identical with simply:

for (int i = 1; i <= 2; ++i) {
   printf("\nRun %d: ", i);
   initstate(123, state, sizeof(state));
   for (int i=1; i < 10; i++) {
       printf("%ld ", random());
   }
}

If you need to save the state after having generated some random numbers, and then later return to that same state, you must alternate between different state arrays, e.g.:

char state[256], state2[256];
initstate(123, state, sizeof(state));
char *saved = initstate(123, state2, sizeof(state2));
for (int i=1; i < 10; i++) {
    saved = setstate(saved);
    printf("%ld ", random());
    saved = setstate(saved);
    (void) random();
    (void) random();
}

Every call to initstate or setstate returns a pointer to the previous state array. To return to that state you need to call setstate with that as the argument and store the returned pointer somewhere (possibly to the same pointer, as shown here). You also need to have two different state arrays or you'll just be setting the same array over and over – in the example above the initial value of saved comes from the second call to initstate, i.e., it is the state array set in the first call. (The first call might return some internal array, but for results consistent with your desired array size I think it better to create both arrays yourself.)

Arkku
  • 41,011
  • 10
  • 62
  • 84
  • Thanks. I've added a few comments for clarification. The call to `random()` that isn't used for for printing out should simulate an outside call that would disturbe the sequence. To just leave it out means you haven't understood my question. – Mojo66 Feb 26 '14 at 00:42
1
  • *state = *setstate(state);
    only copies the first byte of state
  • The random number generator is actually changing your state array each time

Keep the original array handy.

#include <stdlib.h>
#include <stdio.h>
#include <string.h>

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

    char state[256];
    char* oldstate;

    printf("Run 1 : ");
    oldstate = initstate(123, state, 256);
    for (int i=1; i < 10; i++) {
        printf("%ld ", random());
    }
    setstate(oldstate);

    printf("\nRun 2 : ");
    initstate(123, state, 256);
    setstate(oldstate);
    for (int i=1; i < 10; i++) {
        setstate(state); // load preserved state
        printf("%ld ", random());
        setstate(oldstate);
        random(); // this simulates a call to random() from outside
    }
    printf("\n");
    return 0;
}

Oh, and I wouldn’t do this at all. Make your own RNG and have it accept state as a parameter.

Ry-
  • 218,210
  • 55
  • 464
  • 476