Explorar el Código

Added node.random implementation (#1665)

Philip Gladstone hace 7 años
padre
commit
543f438b6b
Se han modificado 2 ficheros con 101 adiciones y 0 borrados
  1. 74 0
      app/modules/node.c
  2. 27 0
      docs/en/modules/node.md

+ 74 - 0
app/modules/node.c

@@ -467,6 +467,79 @@ static int node_osprint( lua_State* L )
   return 0;  
 }
 
+int node_random_range(int l, int u) {
+  // The range is the number of different values to return
+  unsigned int range = u + 1 - l;
+
+  // If this is very large then use simpler code
+  if (range >= 0x7fffffff) {
+    unsigned int v;
+
+    // This cannot loop more than half the time
+    while ((v = os_random()) >= range) {
+    }
+
+    // Now v is in the range [0, range)
+    return v + l;
+  }
+
+  // Easy case, with only one value, we know the result
+  if (range == 1) {
+    return l;
+  }
+
+  // Another easy case -- uniform 32-bit
+  if (range == 0) {
+    return os_random();
+  }
+
+  // Now we have to figure out what a large multiple of range is
+  // that just fits into 32 bits.
+  // The limit will be less than 1 << 32 by some amount (not much)
+  uint32_t limit = ((0x80000000 / ((range + 1) >> 1)) - 1) * range;
+
+  uint32_t v;
+
+  while ((v = os_random()) >= limit) {
+  }
+
+  // Now v is uniformly distributed in [0, limit) and limit is a multiple of range
+
+  return (v % range) + l;
+}
+
+static int node_random (lua_State *L) {
+  int u;
+  int l;
+
+  switch (lua_gettop(L)) {  /* check number of arguments */
+    case 0: {  /* no arguments */
+#ifdef LUA_NUMBER_INTEGRAL
+      lua_pushnumber(L, 0);  /* Number between 0 and 1 - always 0 with ints */
+#else
+      lua_pushnumber(L, (lua_Number)os_random() / (lua_Number)(1LL << 32));
+#endif
+      return 1;
+    }
+    case 1: {  /* only upper limit */
+      l = 1;
+      u = luaL_checkint(L, 1);
+      break;
+    }
+    case 2: {  /* lower and upper limits */
+      l = luaL_checkint(L, 1);
+      u = luaL_checkint(L, 2);
+      break;
+    }
+    default: 
+      return luaL_error(L, "wrong number of arguments");
+  }
+  luaL_argcheck(L, l<=u, 2, "interval is empty");
+  lua_pushnumber(L, node_random_range(l, u));  /* int between `l' and `u' */
+  return 1;
+}
+
+
 // Module function map
 
 static const LUA_REG_TYPE node_egc_map[] = {
@@ -504,6 +577,7 @@ static const LUA_REG_TYPE node_map[] =
   { LSTRKEY( "setcpufreq" ), LFUNCVAL( node_setcpufreq) },
   { LSTRKEY( "bootreason" ), LFUNCVAL( node_bootreason) },
   { LSTRKEY( "restore" ), LFUNCVAL( node_restore) },
+  { LSTRKEY( "random" ), LFUNCVAL( node_random) },
 #ifdef LUA_OPTIMIZE_DEBUG
   { LSTRKEY( "stripdebug" ), LFUNCVAL( node_stripdebug ) },
 #endif

+ 27 - 0
docs/en/modules/node.md

@@ -364,6 +364,33 @@ Nothing
 node.osprint(true)
 ```
 
+## node.random()
+
+This behaves like math.random except that it uses true random numbers derived from the ESP8266 hardware. It returns uniformly distributed
+numbers in the required range. It also takes care to get large ranges correct. 
+
+It can be called in three ways. Without arguments in the floating point build of NodeMCU, it returns a random real number with uniform distribution in the interval [0,1). 
+When called with only one argument, an integer n, it returns an integer random number x such that 1 <= x <= n. For instance, you can simulate the result of a die with random(6). 
+Finally, random can be called with two integer arguments, l and u, to get a pseudo-random integer x such that l <= x <= u.
+
+#### Syntax
+`node.random()`
+`node.random(n)`
+`node.random(l, u)`
+
+#### Parameters
+- `n` the number of distinct integer values that can be returned -- in the (inclusive) range 1 .. `n`
+- `l` the lower bound of the range
+- `u` the upper bound of the range
+
+#### Returns
+The random number in the appropriate range. Note that the zero argument form will always return 0 in the integer build.
+
+#### Example
+```lua
+print ("I rolled a", node.random(6))
+```
+
 # node.egc module
 
 ## node.egc.setmode()