Skip to content

Commit

Permalink
Merge pull request #1 from dicksonlaw583/dev-1-2-0
Browse files Browse the repository at this point in the history
v1.2.0 Additions
  • Loading branch information
dicksonlaw583 committed Mar 2, 2023
2 parents d4b7c88 + e31b798 commit 05685f9
Show file tree
Hide file tree
Showing 7 changed files with 143 additions and 55 deletions.
28 changes: 13 additions & 15 deletions GMXorWow.yyp
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
{
"resourceType": "GMProject",
"resourceVersion": "1.6",
"name": "GMXorWow",
"resources": [
{"id":{"name":"__xorwow_test_values__","path":"scripts/__xorwow_test_values__/__xorwow_test_values__.yy",},"order":5,},
{"id":{"name":"__GMA_SETTINGS__","path":"scripts/__GMA_SETTINGS__/__GMA_SETTINGS__.yy",},"order":1,},
Expand All @@ -13,7 +16,6 @@
],
"Options": [
{"name":"HTML5","path":"options/html5/options_html5.yy",},
{"name":"Windows UWP","path":"options/windowsuap/options_windowsuap.yy",},
{"name":"iOS","path":"options/ios/options_ios.yy",},
{"name":"tvOS","path":"options/tvos/options_tvos.yy",},
{"name":"macOS","path":"options/mac/options_mac.yy",},
Expand All @@ -34,26 +36,22 @@
{"roomId":{"name":"rm_xorwow_test","path":"rooms/rm_xorwow_test/rm_xorwow_test.yy",},},
],
"Folders": [
{"folderPath":"folders/Libraries.yy","order":16,"resourceVersion":"1.0","name":"Libraries","tags":[],"resourceType":"GMFolder",},
{"folderPath":"folders/Libraries/GMAssert.yy","order":1,"resourceVersion":"1.0","name":"GMAssert","tags":[],"resourceType":"GMFolder",},
{"folderPath":"folders/Libraries_config.yy","order":17,"resourceVersion":"1.0","name":"Libraries_config","tags":[],"resourceType":"GMFolder",},
{"folderPath":"folders/Libraries_config/GMAssert.yy","order":1,"resourceVersion":"1.0","name":"GMAssert","tags":[],"resourceType":"GMFolder",},
{"folderPath":"folders/Libraries_test.yy","order":18,"resourceVersion":"1.0","name":"Libraries_test","tags":[],"resourceType":"GMFolder",},
{"folderPath":"folders/Libraries_test/GMXorWow_test.yy","order":1,"resourceVersion":"1.0","name":"GMXorWow_test","tags":[],"resourceType":"GMFolder",},
{"folderPath":"folders/Libraries/GMXorWow.yy","order":2,"resourceVersion":"1.0","name":"GMXorWow","tags":[],"resourceType":"GMFolder",},
{"resourceType":"GMFolder","resourceVersion":"1.0","name":"Libraries_config","folderPath":"folders/Libraries_config.yy","order":17,},
{"resourceType":"GMFolder","resourceVersion":"1.0","name":"GMAssert","folderPath":"folders/Libraries_config/GMAssert.yy","order":1,},
{"resourceType":"GMFolder","resourceVersion":"1.0","name":"Libraries_test","folderPath":"folders/Libraries_test.yy","order":18,},
{"resourceType":"GMFolder","resourceVersion":"1.0","name":"GMXorWow_test","folderPath":"folders/Libraries_test/GMXorWow_test.yy","order":1,},
{"resourceType":"GMFolder","resourceVersion":"1.0","name":"Libraries","folderPath":"folders/Libraries.yy","order":16,},
{"resourceType":"GMFolder","resourceVersion":"1.0","name":"GMAssert","folderPath":"folders/Libraries/GMAssert.yy","order":1,},
{"resourceType":"GMFolder","resourceVersion":"1.0","name":"GMXorWow","folderPath":"folders/Libraries/GMXorWow.yy","order":2,},
],
"AudioGroups": [
{"targets":-1,"resourceVersion":"1.3","name":"audiogroup_default","resourceType":"GMAudioGroup",},
{"resourceType":"GMAudioGroup","resourceVersion":"1.3","name":"audiogroup_default","targets":-1,},
],
"TextureGroups": [
{"isScaled":true,"compressFormat":"bz2","autocrop":true,"border":2,"mipsToGenerate":0,"groupParent":null,"targets":-1,"resourceVersion":"1.3","name":"Default","resourceType":"GMTextureGroup",},
{"resourceType":"GMTextureGroup","resourceVersion":"1.3","name":"Default","isScaled":true,"compressFormat":"bz2","loadType":"default","directory":"","autocrop":true,"border":2,"mipsToGenerate":0,"groupParent":null,"targets":-1,},
],
"IncludedFiles": [],
"MetaData": {
"IDEVersion": "2022.6.1.26",
"IDEVersion": "2022.0.0.19",
},
"resourceVersion": "1.5",
"name": "GMXorWow",
"tags": [],
"resourceType": "GMProject",
}
34 changes: 18 additions & 16 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
# GMXorWow

This is a GameMaker Studio 2.3 implementation of [George Marsaglia's XorWow, XorShift32 and XorShift128 algorithms](https://www.jstatsoft.org/article/view/v008i14). Using only 32-bit unsigned integer operations at its core, this library lets you generate pseudorandom numbers consistently across all exports given the same starting seed. It also adds the ability to create seeds using strings and set up many independent generators in parallel.
This is a GameMaker 2022 implementation of [George Marsaglia's XorWow, XorShift32 and XorShift128 algorithms](https://www.jstatsoft.org/article/view/v008i14). Using only 32-bit unsigned integer operations at its core, this library lets you generate pseudorandom numbers consistently across all exports given the same starting seed. It also adds the ability to create seeds using strings and set up many independent generators in parallel.

## Requirements

GameMaker Studio 2.3.0 or above.
GameMaker 2022.1 or above. 2022 LTS is also supported.

## Installation

Expand All @@ -18,13 +18,14 @@ GMXorWow sets up a randomized global-level seed for you by default. To use it fo

| Standard GML | GMXorWow Global Seed |
| --- | --- |
| `random(n)` | `xrandom(n)` |
| `irandom(n)` | `xirandom(n)` |
| `random_range(m, n)` | `xrandom_range(m, n)` |
| `irandom_range(m, n)` | `xirandom_range(m, n)` |
| `ds_list_shuffle(list)` | `xds_list_shuffle(list)` |
| `ds_grid_shuffle(grid)` | `xds_grid_shuffle(grid)` |
| `array_shuffle(array)` | `xarray_shuffle(array)` |
| [`array_shuffle(array, [offset], [length])`](https://manual.yoyogames.com/#t=GameMaker_Language%2FGML_Reference%2FVariable_Functions%2Farray_shuffle.htm) | `xarray_shuffle(array, [offset], [length])` |
| [`array_shuffle_ext(array, [offset], [length])`](https://manual.yoyogames.com/#t=GameMaker_Language%2FGML_Reference%2FVariable_Functions%2Farray_shuffle_ext.htm) | `xarray_shuffle_ext(array, [offset], [length])` |
| [`ds_grid_shuffle(grid)`](https://manual.yoyogames.com/#t=GameMaker_Language%2FGML_Reference%2FData_Structures%2FDS_Grids%2Fds_grid_shuffle.htm) | `xds_grid_shuffle(grid)` |
| [`ds_list_shuffle(list)`](https://manual.yoyogames.com/#t=GameMaker_Language%2FGML_Reference%2FData_Structures%2FDS_Lists%2Fds_list_shuffle.htm) | `xds_list_shuffle(list)` |
| [`irandom(n)`](https://manual.yoyogames.com/#t=GameMaker_Language%2FGML_Reference%2FMaths_And_Numbers%2FNumber_Functions%2Firandom.htm) | `xirandom(n)` |
| [`irandom_range(m, n)`](https://manual.yoyogames.com/#t=GameMaker_Language%2FGML_Reference%2FMaths_And_Numbers%2FNumber_Functions%2Firandom_range.htm) | `xirandom_range(m, n)` |
| [`random(n)`](https://manual.yoyogames.com/#t=GameMaker_Language%2FGML_Reference%2FMaths_And_Numbers%2FNumber_Functions%2Frandom.htm) | `xrandom(n)` |
| [`random_range(m, n)`](https://manual.yoyogames.com/#t=GameMaker_Language%2FGML_Reference%2FMaths_And_Numbers%2FNumber_Functions%2Frandom_range.htm) | `xrandom_range(m, n)` |

### Independent Seeds

Expand All @@ -44,11 +45,12 @@ y = seed.sirandom(room_height);

| Standard GML | GMXorWow Independent Seed |
| --- | --- |
| `random(n)` | `seed.srandom(n)` |
| `irandom(n)` | `seed.sirandom(n)` |
| `random_range(m, n)` | `seed.srandom_range(m, n)` |
| `irandom_range(m, n)` | `seed.sirandom_range(m, n)` |
| `ds_list_shuffle(list)` | `seed.sds_list_shuffle(list)` |
| `ds_grid_shuffle(grid)` | `seed.sds_grid_shuffle(grid)` |
| `array_shuffle(array)` | `seed.sarray_shuffle(array)` |
| [`array_shuffle(array, [offset], [length])`](https://manual.yoyogames.com/#t=GameMaker_Language%2FGML_Reference%2FVariable_Functions%2Farray_shuffle.htm) | `seed.sarray_shuffle(array, [offset], [length])` |
| [`array_shuffle_ext(array, [offset], [length])`](https://manual.yoyogames.com/#t=GameMaker_Language%2FGML_Reference%2FVariable_Functions%2Farray_shuffle_ext.htm) | `seed.sarray_shuffle_ext(array, [offset], [length])` |
| [`ds_grid_shuffle(grid)`](https://manual.yoyogames.com/#t=GameMaker_Language%2FGML_Reference%2FData_Structures%2FDS_Grids%2Fds_grid_shuffle.htm) | `seed.sds_grid_shuffle(grid)` |
| [`ds_list_shuffle(list)`](https://manual.yoyogames.com/#t=GameMaker_Language%2FGML_Reference%2FData_Structures%2FDS_Lists%2Fds_list_shuffle.htm) | `seed.sds_list_shuffle(list)` |
| [`irandom(n)`](https://manual.yoyogames.com/#t=GameMaker_Language%2FGML_Reference%2FMaths_And_Numbers%2FNumber_Functions%2Firandom.htm) | `seed.sirandom(n)` |
| [`irandom_range(m, n)`](https://manual.yoyogames.com/#t=GameMaker_Language%2FGML_Reference%2FMaths_And_Numbers%2FNumber_Functions%2Firandom_range.htm) | `seed.sirandom_range(m, n)` |
| [`random(n)`](https://manual.yoyogames.com/#t=GameMaker_Language%2FGML_Reference%2FMaths_And_Numbers%2FNumber_Functions%2Frandom.htm) | `seed.srandom(n)` |
| [`random_range(m, n)`](https://manual.yoyogames.com/#t=GameMaker_Language%2FGML_Reference%2FMaths_And_Numbers%2FNumber_Functions%2Frandom_range.htm) | `seed.srandom_range(m, n)` |

100 changes: 83 additions & 17 deletions scripts/XorWow_classes/XorWow_classes.gml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
Descendants should override the following:
- toArray()
- toStruct()
- clone()
- srandomize()
- srandom(n)
- sirandom(n)
Expand Down Expand Up @@ -47,7 +48,7 @@ function __XorSeed__() constructor {
};

///@func schoose(...)
///@
///@return {Any*}
///@desc Seeded replacement for choose(...)
static schoose = function() {
return argument[sirandom(argument_count-1)];
Expand Down Expand Up @@ -83,16 +84,46 @@ function __XorSeed__() constructor {
}
};

///@func sarray_shuffle(array)
///@func sarray_shuffle(array, [offset], [length])
///@param {array} array
///@desc Seeded replacement for array_shuffle(array)
static sarray_shuffle = function(array) {
///@param {real} [offset]
///@param {real} [length]
///@return {Array<Any*>}
///@desc Seeded replacement for array_shuffle(array, [offset], [length])
static sarray_shuffle = function(array, offset=0, length=infinity) {
//Feather disable GM1061
for (var i = array_length(array)-1; i > 0; --i) {
var j = sirandom(i);
var temp = array[i];
array[@i] = array[j];
array[@j] = temp;
var result = [];
var n = array_length(array),
iStep = sign(length),
iFrom = (offset < 0) ? n+offset : offset,
iTo = clamp(iFrom+length-iStep, 0, n-1),
iStopAt = iTo+iStep;
for (var i = iFrom; i != iStopAt; i += iStep) {
array_push(result, array[i]);
}
sarray_shuffle_ext(result);
return result;
};

///@func sarray_shuffle_ext(array, [offset], [length])
///@param {array} array
///@param {real} [offset]
///@param {real} [length]
///@desc Seeded replacement for array_shuffle_ext(array, [offset], [length])
static sarray_shuffle_ext = function(array, offset=0, length=infinity) {
//Feather disable GM1061
var n = array_length(array),
iStep = sign(length),
iFrom = (offset < 0) ? n+offset : offset,
iTo = clamp(iFrom+length-iStep, 0, n-1),
iStopAt = iTo+iStep;
for (var i = iFrom; i != iStopAt; i += iStep) {
var i2 = (iStep > 0) ? sirandom_range(i, iTo) : sirandom_range(iTo, i);
if (array[i] != array[i2]) {
var swapTemp = array[i];
array[@i] = array[i2];
array[@i2] = swapTemp;
}
}
};
}
Expand Down Expand Up @@ -133,6 +164,13 @@ function XorWow() : __XorSeed__() constructor {
return { a: a, b: b, c: c, d: d, e: e, counter: counter };
};

///@func clone()
///@return {Struct.XorWow}
///@desc Return a clone of this seed.
static clone = function() {
return new XorWow(toArray());
};

///@func srandomize()
///@desc Randomize this seed
static srandomize = function() {
Expand Down Expand Up @@ -263,6 +301,13 @@ function XorShift32() : __XorSeed__() constructor {
return { a: a };
};

///@func clone()
///@return {Struct.XorShift32}
///@desc Return a clone of this seed.
static clone = function() {
return new XorShift32(toArray());
};

///@func srandomize()
///@desc Randomize this seed
static srandomize = function() {
Expand Down Expand Up @@ -346,6 +391,13 @@ function XorShift128() : __XorSeed__() constructor {
return { a: a, b: b, c: c, d: d };
};

///@func clone()
///@return {Struct.XorShift128}
///@desc Return a clone of this seed.
static clone = function() {
return new XorShift128(toArray());
};

///@func srandomize()
///@return {real}
///@desc Randomize this seed
Expand Down Expand Up @@ -446,10 +498,11 @@ function xrandomize() {
global.__xorshift_state__.srandomize();
}

///@func xrandom_get_seed()
///@desc Return the currently used seed
function xrandom_get_seed() {
return global.__xorshift_state__;
///@func xrandom_get_seed([noClone])
///@param {Bool} [noClone] OPTIONAL: Specify true here to get the seed directly instead of a clone of it.
///@desc Return a clone of the currently used seed (false) or the currently used seed (true)
function xrandom_get_seed(noClone=false) {
return noClone ? global.__xorshift_state__ : global.__xorshift_state__.clone();
}

///@func xrandom_set_seed(seed)
Expand Down Expand Up @@ -514,12 +567,25 @@ function xds_grid_shuffle(grid) {
global.__xorshift_state__.sds_grid_shuffle(grid);
}

///@func xarray_shuffle(array)
///@func xarray_shuffle(array, [offset], [length])
///@param {array} array
///@desc Replacement for array_shuffle(array)
function xarray_shuffle(array) {
global.__xorshift_state__.sarray_shuffle(array);
///@param {real} [offset]
///@param {real} [length]
///@desc Replacement for array_shuffle(array, [offset], [length])
function xarray_shuffle(array, offset=0, length=infinity) {
return global.__xorshift_state__.sarray_shuffle(array, offset, length);
}

///@func xarray_shuffle_ext(array, [offset], [length])
///@param {array} array
///@param {real} [offset]
///@param {real} [length]
///@desc Replacement for array_shuffle_ext(array, [offset], [length])
function xarray_shuffle_ext(array, offset=0, length=infinity) {
global.__xorshift_state__.sarray_shuffle_ext(array, offset, length);
}

// Initialize global seed randomly
global.__xorshift_state__ = new XorWow();


7 changes: 3 additions & 4 deletions scripts/XorWow_classes/XorWow_classes.yy

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions scripts/__xorwow_test_values__/__xorwow_test_values__.gml
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
///@func __xorwow_test_values__(label, tries)
///@param {string} label The label of the seed type to use for error messages
///@param {real} tries The number of retries
function __xorwow_test_values__(label, tries) {
var got;

Expand Down Expand Up @@ -95,3 +96,4 @@ function __xorwow_test_values__(label, tries) {
}
#endregion
}

13 changes: 13 additions & 0 deletions scripts/xorwow_test_seeding/xorwow_test_seeding.gml
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,30 @@ function xorwow_test_seeding() {
var xorWowSeed = new XorWow("omega");
assert_equal(xorWowSeed.toArray(), [int64(46472), int64(35342), int64(62099), int64(34646), int64(37634), int64(216193)], "XorWow seeding failed 1");
assert_equal(xorWowSeed.toStruct(), {a: int64(46472), b: int64(35342), c: int64(62099), d: int64(34646), e: int64(37634), counter: int64(216193)}, "XorWow seeding failed 2");
var xorWowSeed2 = xorWowSeed.clone();
assert_isnt(xorWowSeed2, xorWowSeed, "XorWow cloning failed 1");
assert_equal(xorWowSeed2.toArray(), [int64(46472), int64(35342), int64(62099), int64(34646), int64(37634), int64(216193)], "XorWow cloning failed 2");
assert_equal(xorWowSeed2.toStruct(), {a: int64(46472), b: int64(35342), c: int64(62099), d: int64(34646), e: int64(37634), counter: int64(216193)}, "XorWow cloning failed 3");
#endregion

#region XorShift32
var xorShift32Seed = new XorShift32("omega");
assert_equal(xorShift32Seed.toArray(), [int64(46472)], "XorShift32 seeding failed 1");
assert_equal(xorShift32Seed.toStruct(), {a: int64(46472)}, "XorShift32 seeding failed 2");
var xorShift32Seed2 = xorShift32Seed.clone();
assert_isnt(xorShift32Seed2, xorShift32Seed, "XorShift32 cloning failed 1");
assert_equal(xorShift32Seed2.toArray(), [int64(46472)], "XorShift32 cloning failed 2");
assert_equal(xorShift32Seed2.toStruct(), {a: int64(46472)}, "XorShift32 cloning failed 3");
#endregion

#region XorShift128
var xorShift128Seed = new XorShift128("omega");
assert_equal(xorShift128Seed.toArray(), [int64(46472), int64(35342), int64(62099), int64(34646)], "XorShift128 seeding failed 1");
assert_equal(xorShift128Seed.toStruct(), {a: int64(46472), b: int64(35342), c: int64(62099), d: int64(34646)}, "XorShift128 seeding failed 2");
var xorShift128Seed2 = xorShift128Seed.clone();
assert_isnt(xorShift128Seed2, xorShift128Seed, "XorShift128 cloning failed 1");
assert_equal(xorShift128Seed2.toArray(), [int64(46472), int64(35342), int64(62099), int64(34646)], "XorShift128 cloning failed 2");
assert_equal(xorShift128Seed2.toStruct(), {a: int64(46472), b: int64(35342), c: int64(62099), d: int64(34646)}, "XorShift128 cloning failed 3");
#endregion
}

14 changes: 11 additions & 3 deletions scripts/xorwow_test_values/xorwow_test_values.gml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@ function xorwow_test_values() {
var xowWowSeed = new XorWow();
xrandom_set_seed(xowWowSeed);
xrandomize();
assert_is(xrandom_get_seed(), xowWowSeed, "XorWow set seed failed");
assert_is(xrandom_get_seed(true), xowWowSeed, "XorWow set seed failed");
assert_isnt(xrandom_get_seed(), xowWowSeed, "XorWow get seed clone failed");
assert_equal(xrandom_get_seed(), xowWowSeed, "XorWow get seed clone content failed");
__xorwow_test_values__("XorWow", tries);
assert(is_int64(xowWowSeed.a), "XorWow core type drift a");
assert(is_int64(xowWowSeed.b), "XorWow core type drift b");
Expand All @@ -25,7 +27,9 @@ function xorwow_test_values() {
var xorShift32Seed = new XorShift32();
xrandom_set_seed(xorShift32Seed);
xrandomize();
assert_is(xrandom_get_seed(), xorShift32Seed, "XorShift32 set seed failed");
assert_is(xrandom_get_seed(true), xorShift32Seed, "XorShift32 set seed failed");
assert_isnt(xrandom_get_seed(), xorShift32Seed, "XorShift32 get seed clone failed");
assert_equal(xrandom_get_seed(), xorShift32Seed, "XorShift32 get seed clone content failed");
__xorwow_test_values__("XorShift32", tries);
assert(is_int64(xorShift32Seed.a), "XorShift32 core type drift a");
repeat (1000) {
Expand All @@ -38,7 +42,9 @@ function xorwow_test_values() {
var xorShift128Seed = new XorShift128();
xrandom_set_seed(xorShift128Seed);
xrandomize();
assert_is(xrandom_get_seed(), xorShift128Seed, "XorShift128 set seed failed");
assert_is(xrandom_get_seed(true), xorShift128Seed, "XorShift128 set seed failed");
assert_isnt(xrandom_get_seed(), xorShift128Seed, "XorShift128 get seed clone failed");
assert_equal(xrandom_get_seed(), xorShift128Seed, "XorShift128 get seed clone content failed");
__xorwow_test_values__("XorShift128", tries);
assert(is_int64(xorShift128Seed.a), "XorShift128 core type drift a");
assert(is_int64(xorShift128Seed.b), "XorShift128 core type drift b");
Expand All @@ -50,3 +56,5 @@ function xorwow_test_values() {
}
#endregion
}


0 comments on commit 05685f9

Please sign in to comment.