Skip to content

Commit

Permalink
Merge pull request #132 from domeengine/noiseRandom
Browse files Browse the repository at this point in the history
[Exploration] Replacing Random implementation with noise-based RNG
  • Loading branch information
avivbeeri authored Jan 27, 2021
2 parents 480507c + 4c661c2 commit b79b694
Show file tree
Hide file tree
Showing 9 changed files with 167 additions and 2 deletions.
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,7 @@ PROJECTS := dome.bin
all: $(PROJECTS)

WREN_LIB ?= $(LIBS)/libwren.a
WREN_PARAMS ?= $(ARCH) WREN_OPT_RANDOM=1 WREN_OPT_META=1
WREN_PARAMS ?= $(ARCH) WREN_OPT_RANDOM=0 WREN_OPT_META=1
$(LIBS)/wren/lib/libwren.a:
@echo "==== Cloning Wren ===="
git submodule update --init -- $(LIBS)/wren
Expand Down
49 changes: 49 additions & 0 deletions docs/modules/random.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
[< Back](.)

random
=============

The `random` module provides utilities for generating pseudo-random numbers, for a variety of applications. Please note, this module should not be used for applications which require a cryptographically secure source of random numbers.

DOME's pseudo-random number generator is based on the "Squirrel3" noise function, described by [Squirrel Eiserloh](http://www.eiserloh.net/bio/) in [this talk](https://www.youtube.com/watch?v=LWFzPP8ZbdU).

## Random

### Static Methods

#### `noise(x: Number): Number`
Given `x` as an integer, this will return a 32-bit number based on the Squirrel3 noise function.

#### `noise(x: Number, seed: Number): Number`
Given `x` and `seed` as integers, this will return a 32-bit number based on the Squirrel3 noise function. The `seed` value can be used to get different outputs for the same position `x`.

### Instance methods
#### `construct new()`
Creates a new instance of a random number generator, seeded based on the current system time.

#### `construct new(seed: Number)`
Creates a new instance of a random number generator, based on the provided seed value.

#### `float(): Number`
Returns a floating point value in the range of `0.0...1.0`, inclusive of `0.0` but exclusive of `1.0`.

#### `float(end: Number): Number`
Returns a floating point value in the range of `0.0...end``, inclusive of `0.0` but exclusive of `end`.

#### `float(start: Number, end: Number): Number`
Returns a floating point value in the range of `start...end``, inclusive of `start` but exclusive of `end`.

#### `int(end: Number): Number`
Returns an integer in the range `0.0...end`, inclusive of `0.0` but exclusive of `end`.`
#### `int(start: Number, end: Number): Number`
Returns an integer in the range `start...end`, inclusive of `start` but exclusive of `end`.`

#### `sample(list: List): Any`
Given a `list`, this will pick an element from that list at random.

#### `sample(list: List, count: Number): List`
Randomly selects `count` elements from the list and returns them in a new list. This provides "sampling without replacement", so each element is distinct.

#### `shuffle(list: List): List`
Uses the Fisher-Yates algorithm to shuffle the provided `list` in place. The list is also returned for convenience.

25 changes: 25 additions & 0 deletions examples/random/main.wren
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import "random" for Random
import "dome" for Process

class Game {
static init() {
for (n in 0...100) {
var start = System.clock
var RNG = Random.new()
var n = 0

for (i in 0...1000000) {
n = n + RNG.float()
}
n = n / 1000000

var end = System.clock
System.print("Time: %(end - start) seconds")
}
Process.exit()
}
static update() {}
static draw(d) {}


}
1 change: 1 addition & 0 deletions scripts/generateEmbedModules.sh
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ declare -a arr=(
"plugin"
"json"
"platform"
"random"
)

declare -a opts=(
Expand Down
1 change: 0 additions & 1 deletion scripts/setup_wren.sh
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ fi
# Undo external makefile flags just in case
unset config
MAKEFLAGS="--no-print-directory"
echo $config
# build the debug version of wren
make clean
CFLAGS=-fvisibility=hidden
Expand Down
2 changes: 2 additions & 0 deletions src/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -115,8 +115,10 @@ global_variable size_t GIF_SCALE = 1;
#include "modules/input.c"
#include "modules/json.c"
#include "modules/platform.c"
#include "modules/random.c"
#include "modules/plugin.c"


// Comes last to register modules
#include "vm.c"

Expand Down
42 changes: 42 additions & 0 deletions src/modules/random.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
uint32_t BIT_NOISE1 = 0xB5297A4D;
uint32_t BIT_NOISE2 = 0x68E31DA4;
uint32_t BIT_NOISE3 = 0x1B56C4E9;
uint32_t CAP = 0xFFFFFFFF;

uint32_t squirrel3Hash(uint32_t position, uint32_t seed) {
uint32_t mangled = position;
mangled = mangled * BIT_NOISE1;
mangled = mangled + seed;
mangled = mangled ^ (mangled >> 8);
mangled = mangled + BIT_NOISE2;
mangled = mangled ^ (mangled << 8);
mangled = mangled * BIT_NOISE3;
mangled = mangled ^ (mangled >> 8);
return mangled & CAP;
}

void RANDOM_noise(WrenVM* vm) {
uint32_t position = wrenGetSlotDouble(vm, 1);
uint32_t seed = wrenGetSlotDouble(vm, 2);
wrenSetSlotDouble(vm, 0, squirrel3Hash(position, seed));
}

typedef struct {
uint32_t seed;
uint32_t state;
} RANDOM;

void RANDOM_allocate(WrenVM* vm) {
RANDOM* rng = wrenSetSlotNewForeign(vm, 0, 0, sizeof(RANDOM));
uint32_t seed = wrenGetSlotDouble(vm, 1);
rng->seed = seed;
rng->state = 0;
}

void RANDOM_finalize(void* data) {}

void RANDOM_float(WrenVM* vm) {
RANDOM* rng = wrenGetSlotForeign(vm, 0);
double result = squirrel3Hash(rng->state++, rng->seed);
wrenSetSlotDouble(vm, 0, result / CAP);
}
41 changes: 41 additions & 0 deletions src/modules/random.wren
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import "platform" for Platform

class Squirrel3 {
static noise(x) { noise(x, 0) }
foreign static noise(x, seed)

static new() { Squirrel3.new(Platform.time) }
construct new(seed) {}

foreign float()
float(end) { float() * end }
float(start, end) { start + float(end - start) }
int(end) { float(end).floor }
int(start, end) { float(start, end).floor }

sample(list) { list[int(list.count)] }

sample(list, count) {
if (count > list.count) {
Fiber.abort("Cannot sample more items than the list contains")
}
var newList = shuffle(list.toList)
var end = list.count - 1
for (i in end..count) {
newList.removeAt(i)
}
return newList
}

shuffle(list) {
var n = list.count
var j
for (i in 0...n) {
j = int(i + 1)
list.swap(j, i)
}
return list
}
}

var Random = Squirrel3
6 changes: 6 additions & 0 deletions src/vm.c
Original file line number Diff line number Diff line change
Expand Up @@ -311,6 +311,12 @@ internal WrenVM* VM_create(ENGINE* engine) {
MAP_addFunction(&engine->moduleMap, "plugin", "static Plugin.f_load(_)", PLUGIN_load);
MAP_lockModule(&engine->moduleMap, "plugin");

// Random
MAP_addClass(&engine->moduleMap, "random", "Squirrel3", RANDOM_allocate, RANDOM_finalize);
MAP_addFunction(&engine->moduleMap, "random", "static Squirrel3.noise(_,_)", RANDOM_noise);
MAP_addFunction(&engine->moduleMap, "random", "Squirrel3.float()", RANDOM_float);
MAP_lockModule(&engine->moduleMap, "random");

return vm;
}

Expand Down

0 comments on commit b79b694

Please sign in to comment.