forked from TheGLander/tworld
-
Notifications
You must be signed in to change notification settings - Fork 0
/
random.c
123 lines (108 loc) · 3.37 KB
/
random.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
/* random.c: The game's random-number generator.
*
* Copyright (C) 2001-2006 by Brian Raiter, under the GNU General Public
* License. No warranty. See COPYING for details.
*/
/*
* This module is not here because I don't trust the C library's
* random-number generator. (In fact, this module uses the linear
* congruential generator, which is hardly impressive. But this isn't
* strong cryptography; it's a game.) It is here simply because it is
* necessary for the game to use the same generator FOREVER. In order
* for playback of solutions to work correctly, the game must use the
* same sequence of random numbers as when it was recorded. This would
* fail if the playback occurred on a version compiled with a
* different C library's generator. Thus, this module.
*/
#include <stdlib.h>
#include <time.h>
#include "gen.h"
#include "random.h"
/* The most recently generated random number is stashed here, so that
* it can provide the initial seed of the next PRNG.
*/
static unsigned long lastvalue = 0x80000000UL;
/* The standard linear congruential random-number generator needs no
* introduction.
*/
static unsigned long nextvalue(unsigned long value)
{
return ((value * 1103515245UL) + 12345UL) & 0x7FFFFFFFUL;
}
/* Move to the next pseudorandom number in the generator's series.
*/
static void nextrandom(prng *gen)
{
if (gen->shared)
gen->value = lastvalue = nextvalue(lastvalue);
else
gen->value = nextvalue(gen->value);
}
/* Create a new PRNG, reset to the shared sequence.
*/
prng createprng(void)
{
prng gen;
resetprng(&gen);
return gen;
}
/* We start off a fresh series by taking the current time. A few
* numbers are generated and discarded to work out any biases in the
* seed value.
*/
void resetprng(prng *gen)
{
if (lastvalue > 0x7FFFFFFFUL)
lastvalue = nextvalue(nextvalue(nextvalue(nextvalue(time(NULL)))));
gen->value = gen->initial = lastvalue;
gen->shared = TRUE;
}
/* Reset a PRNG to an independent sequence.
*/
void restartprng(prng *gen, unsigned long seed)
{
gen->value = gen->initial = seed & 0x7FFFFFFFUL;
gen->shared = FALSE;
}
/* Use the top two bits to get a random number between 0 and 3.
*/
int random4(prng *gen)
{
nextrandom(gen);
return gen->value >> 29;
}
/* Randomly select an element from a list of three values.
*/
int randomof3(prng *gen, int a, int b, int c)
{
int n;
nextrandom(gen);
n = (int)((3.0 * (gen->value & 0x3FFFFFFFUL)) / (double)0x40000000UL);
return n < 2 ? n < 1 ? a : b : c;
}
/* Randomly permute a list of three values. Two random numbers are
* used, with the ranges [0,1] and [0,1,2].
*/
void randomp3(prng *gen, int *array)
{
int n, t;
nextrandom(gen);
n = gen->value >> 30;
t = array[n]; array[n] = array[1]; array[1] = t;
n = (int)((3.0 * (gen->value & 0x3FFFFFFFUL)) / (double)0x40000000UL);
t = array[n]; array[n] = array[2]; array[2] = t;
}
/* Randomly permute a list of four values. Three random numbers are
* used, with the ranges [0,1], [0,1,2], and [0,1,2,3].
*/
void randomp4(prng *gen, int *array)
{
int n, t;
nextrandom(gen);
n = gen->value >> 30;
t = array[n]; array[n] = array[1]; array[1] = t;
n = (int)((3.0 * (gen->value & 0x0FFFFFFFUL)) / (double)0x10000000UL);
t = array[n]; array[n] = array[2]; array[2] = t;
n = (gen->value >> 28) & 3;
t = array[n]; array[n] = array[3]; array[3] = t;
}