Last active
December 12, 2023 20:34
-
-
Save sgraham/da42900de6e8a2b7a5d6af5eb07d35cb to your computer and use it in GitHub Desktop.
reproducible rnd matching pico8's generation (build with `cl /std:c++17 a.cpp`)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include "fix32.h" | |
#include <optional> | |
template<typename T> using opt = std::optional<T>; | |
struct { uint32_t a, b; } g_prng = {0}; | |
static void update_prng() | |
{ | |
auto &prng = g_prng; | |
prng.a = ((prng.a >> 16) | (prng.a << 16)) + prng.b; | |
prng.b += prng.a; | |
} | |
static z8::fix32 p8_rnd(opt<z8::fix32> in_range) | |
{ | |
update_prng(); | |
uint32_t a = g_prng.a; | |
uint32_t range = in_range ? uint32_t(in_range->bits()) : 0x10000; | |
return z8::fix32::frombits(range > 0 ? a % range : 0); | |
} | |
static void p8_srand(z8::fix32 seed) | |
{ | |
auto &prng = g_prng; | |
prng.b = seed ? seed.bits() : 0xdeadbeef; | |
prng.a = prng.b ^ 0xbead29ba; | |
for (int i = 0; i < 32; ++i) | |
update_prng(); | |
} | |
int main() { | |
p8_srand(1234); | |
printf("%lf\n", (double)p8_rnd(z8::fix32(100))); | |
printf("%lf\n", (double)p8_rnd(z8::fix32(100))); | |
printf("%lf\n", (double)p8_rnd(z8::fix32(100))); | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// ZEPTO-8 — Fantasy console emulator | |
// | |
// Copyright © 2016—2020 Sam Hocevar <[email protected]> | |
// | |
// This program is free software. It comes without any warranty, to | |
// the extent permitted by applicable law. You can redistribute it | |
// and/or modify it under the terms of the Do What the Fuck You Want | |
// to Public License, Version 2, as published by the WTFPL Task Force. | |
// See http://www.wtfpl.net/ for more details. | |
// | |
#pragma once | |
#include <stdint.h> // int32_t, int64_t, … | |
#include <cmath> // std::abs | |
#include <algorithm> // std::min | |
#include <type_traits> // std::enable_if | |
namespace z8 | |
{ | |
struct fix32 | |
{ | |
inline fix32() = default; | |
// Convert from/to double | |
inline fix32(double d) | |
: m_bits(int32_t(int64_t(d * 65536.0))) | |
{} | |
inline operator double() const | |
{ | |
return double(m_bits) * (1.0 / 65536.0); | |
} | |
// Conversions up to int16_t are safe. | |
inline fix32(int8_t x) : m_bits(int32_t(x << 16)) {} | |
inline fix32(uint8_t x) : m_bits(int32_t(x << 16)) {} | |
inline fix32(int16_t x) : m_bits(int32_t(x << 16)) {} | |
// Anything above int16_t is risky because of precision loss, but Lua | |
// does too many implicit conversions from int that we can’t mark this | |
// one as explicit. | |
inline fix32(int32_t x) : m_bits(int32_t(x << 16)) {} | |
inline explicit fix32(uint16_t x) : m_bits(int32_t(x << 16)) {} | |
inline explicit fix32(uint32_t x) : m_bits(int32_t(x << 16)) {} | |
inline explicit fix32(int64_t x) : m_bits(int32_t(x << 16)) {} | |
inline explicit fix32(uint64_t x) : m_bits(int32_t(x << 16)) {} | |
// Support for long and unsigned long when it is a distinct | |
// type from the standard int*_t types, e.g. on Windows. | |
template<typename T, | |
typename std::enable_if<(std::is_same<T, long>::value || | |
std::is_same<T, unsigned long>::value) && | |
!std::is_same<T, int32_t>::value && | |
!std::is_same<T, uint32_t>::value && | |
!std::is_same<T, int64_t>::value && | |
!std::is_same<T, uint64_t>::value>::type *...> | |
inline explicit fix32(T x) : m_bits(int32_t(x << 16)) {} | |
// Explicit casts are all allowed | |
inline explicit operator int8_t() const { return m_bits >> 16; } | |
inline explicit operator uint8_t() const { return m_bits >> 16; } | |
inline explicit operator int16_t() const { return m_bits >> 16; } | |
inline explicit operator uint16_t() const { return m_bits >> 16; } | |
inline explicit operator int32_t() const { return m_bits >> 16; } | |
inline explicit operator uint32_t() const { return m_bits >> 16; } | |
inline explicit operator int64_t() const { return m_bits >> 16; } | |
inline explicit operator uint64_t() const { return m_bits >> 16; } | |
// Additional casts for long and unsigned long on architectures where | |
// these are not the same types as their cstdint equivalents. | |
template<typename T, | |
typename std::enable_if<(std::is_same<T, long>::value || | |
std::is_same<T, unsigned long>::value) && | |
!std::is_same<T, int32_t>::value && | |
!std::is_same<T, uint32_t>::value && | |
!std::is_same<T, int64_t>::value && | |
!std::is_same<T, uint64_t>::value>::type *...> | |
inline explicit operator T() const { return T(m_bits >> 16); } | |
// Directly initialise bits | |
static inline fix32 frombits(int32_t x) | |
{ | |
fix32 ret; ret.m_bits = x; return ret; | |
} | |
inline int32_t bits() const { return m_bits; } | |
// Comparisons | |
inline explicit operator bool() const { return bool(m_bits); } | |
inline bool operator ==(fix32 x) const { return m_bits == x.m_bits; } | |
inline bool operator !=(fix32 x) const { return m_bits != x.m_bits; } | |
inline bool operator <(fix32 x) const { return m_bits < x.m_bits; } | |
inline bool operator >(fix32 x) const { return m_bits > x.m_bits; } | |
inline bool operator <=(fix32 x) const { return m_bits <= x.m_bits; } | |
inline bool operator >=(fix32 x) const { return m_bits >= x.m_bits; } | |
// Increments | |
inline fix32& operator ++() { m_bits += 0x10000; return *this; } | |
inline fix32& operator --() { m_bits -= 0x10000; return *this; } | |
inline fix32 operator ++(int) { fix32 ret = *this; ++*this; return ret; } | |
inline fix32 operator --(int) { fix32 ret = *this; --*this; return ret; } | |
// Math operations | |
inline fix32 const &operator +() const { return *this; } | |
inline fix32 operator -() const { return frombits(-m_bits); } | |
inline fix32 operator ~() const { return frombits(~m_bits); } | |
inline fix32 operator +(fix32 x) const { return frombits(m_bits + x.m_bits); } | |
inline fix32 operator -(fix32 x) const { return frombits(m_bits - x.m_bits); } | |
inline fix32 operator &(fix32 x) const { return frombits(m_bits & x.m_bits); } | |
inline fix32 operator |(fix32 x) const { return frombits(m_bits | x.m_bits); } | |
inline fix32 operator ^(fix32 x) const { return frombits(m_bits ^ x.m_bits); } | |
fix32 operator *(fix32 x) const | |
{ | |
return frombits(int64_t(m_bits) * x.m_bits >> 16); | |
} | |
fix32 operator /(fix32 x) const | |
{ | |
// This special case ensures 0x8000/0x1 = 0x8000, not 0x8000.0001 | |
if (x.m_bits == 0x10000) | |
return *this; | |
if (x.m_bits) | |
{ | |
using std::abs; | |
int64_t result = int64_t(m_bits) * 0x10000 / x.m_bits; | |
if (abs(result) <= 0x7fffffffu) | |
return frombits(int32_t(result)); | |
} | |
// Return 0x8000.0001 (not 0x8000.0000) for -Inf, just like PICO-8 | |
return frombits((m_bits ^ x.m_bits) >= 0 ? 0x7fffffffu : 0x80000001u); | |
} | |
fix32 operator %(fix32 x) const | |
{ | |
// PICO-8 always returns positive values | |
x = abs(x); | |
int32_t result = x ? m_bits % x.m_bits : m_bits; | |
return frombits(result >= 0 ? result : result + x.m_bits); | |
} | |
inline fix32 operator <<(int y) const | |
{ | |
// If y is negative, use lshr() instead. | |
return y < 0 ? lshr(*this, -y) : frombits(y >= 32 ? 0 : bits() << y); | |
} | |
inline fix32 operator >>(int y) const | |
{ | |
using std::min; | |
// If y is negative, use << instead. | |
return y < 0 ? *this << -y : frombits(bits() >> min(y, 31)); | |
} | |
inline fix32& operator +=(fix32 x) { return *this = *this + x; } | |
inline fix32& operator -=(fix32 x) { return *this = *this - x; } | |
inline fix32& operator &=(fix32 x) { return *this = *this & x; } | |
inline fix32& operator |=(fix32 x) { return *this = *this | x; } | |
inline fix32& operator ^=(fix32 x) { return *this = *this ^ x; } | |
inline fix32& operator *=(fix32 x) { return *this = *this * x; } | |
inline fix32& operator /=(fix32 x) { return *this = *this / x; } | |
inline fix32& operator %=(fix32 x) { return *this = *this % x; } | |
// Free functions | |
static inline fix32 abs(fix32 a) { return a.m_bits > 0 ? a : -a; } | |
static inline fix32 min(fix32 a, fix32 b) { return a < b ? a : b; } | |
static inline fix32 max(fix32 a, fix32 b) { return a > b ? a : b; } | |
static inline fix32 ceil(fix32 x) { return -floor(-x); } | |
static inline fix32 floor(fix32 x) { return frombits(x.m_bits & 0xffff0000); } | |
static fix32 pow(fix32 x, fix32 y) { return fix32(std::pow(double(x), double(y))); } | |
static inline fix32 lshr(fix32 x, int y) | |
{ | |
// If y is negative, use << instead. | |
return y < 0 ? x << -y : frombits(y >= 32 ? 0 : uint32_t(x.bits()) >> y); | |
} | |
static inline fix32 rotl(fix32 x, int y) | |
{ | |
y &= 0x1f; | |
return frombits((x.bits() << y) | (uint32_t(x.bits()) >> (32 - y))); | |
} | |
static inline fix32 rotr(fix32 x, int y) | |
{ | |
y &= 0x1f; | |
return frombits((uint32_t(x.bits()) >> y) | (x.bits() << (32 - y))); | |
} | |
private: | |
int32_t m_bits; | |
}; | |
} | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment