From 1f74b79ea26a3bde6f0674d56ce9e908cd8fa638 Mon Sep 17 00:00:00 2001 From: a dinosaur Date: Sun, 1 Sep 2024 02:09:49 +1000 Subject: [PATCH] split split mix sixty four --- Sources/Voxelotl/CMakeLists.txt | 1 + Sources/Voxelotl/Random/PCG32Random.swift | 18 ++++-- Sources/Voxelotl/Random/RandomProvider.swift | 2 +- Sources/Voxelotl/Random/SplitMix64.swift | 25 ++++++++ Sources/Voxelotl/Random/Xoroshiro128.swift | 60 ++++---------------- Sources/Voxelotl/WorldGenerator.swift | 16 ++++-- 6 files changed, 62 insertions(+), 60 deletions(-) create mode 100644 Sources/Voxelotl/Random/SplitMix64.swift diff --git a/Sources/Voxelotl/CMakeLists.txt b/Sources/Voxelotl/CMakeLists.txt index 2628f77..27d666a 100644 --- a/Sources/Voxelotl/CMakeLists.txt +++ b/Sources/Voxelotl/CMakeLists.txt @@ -28,6 +28,7 @@ add_executable(Voxelotl MACOSX_BUNDLE Random/PerlinNoiseGenerator.swift Random/SimplexNoise.swift Random/RandomCollectionExtensions.swift + Random/SplitMix64.swift # Resource classes NSImageLoader.swift diff --git a/Sources/Voxelotl/Random/PCG32Random.swift b/Sources/Voxelotl/Random/PCG32Random.swift index f1c7532..a2d0757 100644 --- a/Sources/Voxelotl/Random/PCG32Random.swift +++ b/Sources/Voxelotl/Random/PCG32Random.swift @@ -1,5 +1,6 @@ -public struct PCG32Random: RandomProvider, RandomStateAccess { +public struct PCG32Random: RandomProvider, RandomSeedable, RandomStateAccess { public typealias Output = UInt32 + public typealias SeedType = (UInt64, UInt64) public typealias StateType = (UInt64, UInt64) public static var min: UInt32 { .min } @@ -21,11 +22,20 @@ public struct PCG32Random: RandomProvider, RandomStateAccess { } public init(state: (UInt64, UInt64)) { - self.init() - self.reset(state: state.0, sequence: state.1) + self._state = state.0 + self._inc = state.1 } - public mutating func reset(state: UInt64, sequence: UInt64) { + public init(seed: (UInt64, UInt64)) { + self.init() + self.seed(state: seed.0, sequence: seed.1) + } + + public mutating func seed(_ seed: (UInt64, UInt64)) { + self.seed(state: seed.0, sequence: seed.1) + } + + public mutating func seed(state: UInt64, sequence: UInt64) { self._state = 0 self._inc = sequence << 1 | 0x1 _ = next() diff --git a/Sources/Voxelotl/Random/RandomProvider.swift b/Sources/Voxelotl/Random/RandomProvider.swift index f4cdbb4..90996ad 100644 --- a/Sources/Voxelotl/Random/RandomProvider.swift +++ b/Sources/Voxelotl/Random/RandomProvider.swift @@ -8,7 +8,7 @@ public protocol RandomProvider { } public protocol RandomSeedable { - associatedtype SeedType: FixedWidthInteger + associatedtype SeedType init(seed: SeedType) mutating func seed(_ value: SeedType) diff --git a/Sources/Voxelotl/Random/SplitMix64.swift b/Sources/Voxelotl/Random/SplitMix64.swift new file mode 100644 index 0000000..2760f90 --- /dev/null +++ b/Sources/Voxelotl/Random/SplitMix64.swift @@ -0,0 +1,25 @@ +public struct SplitMix64: RandomProvider, RandomSeedable { + public typealias Output = UInt64 + public typealias SeedType = UInt64 + + public static var min: UInt64 { .max } + public static var max: UInt64 { .min } + + private var _state: UInt64 + + public init(seed: UInt64) { + self._state = seed + } + + public mutating func seed(_ value: UInt64) { + self._state = value + } + + public mutating func next() -> UInt64 { + var x = self._state &+ 0x9E3779B97F4A7C15 + x = (x ^ x &>> 30) &* 0xBF58476D1CE4E5B9 + x = (x ^ x &>> 27) &* 0x94D049BB133111EB + self._state = x ^ x &>> 31 + return self._state + } +} diff --git a/Sources/Voxelotl/Random/Xoroshiro128.swift b/Sources/Voxelotl/Random/Xoroshiro128.swift index 305a757..c2bf8a4 100644 --- a/Sources/Voxelotl/Random/Xoroshiro128.swift +++ b/Sources/Voxelotl/Random/Xoroshiro128.swift @@ -1,10 +1,9 @@ -struct Xoroshiro128Plus: RandomProvider, RandomSeedable, RandomStateAccess { +public struct Xoroshiro128Plus: RandomProvider, RandomStateAccess { public typealias Output = UInt64 - public typealias SeedType = UInt64 public typealias StateType = (UInt64, UInt64) - static public var min: UInt64 { .min } - static public var max: UInt64 { .max } + public static var min: UInt64 { .min } + public static var max: UInt64 { .max } public var state: (UInt64, UInt64) @@ -16,16 +15,6 @@ struct Xoroshiro128Plus: RandomProvider, RandomSeedable, RandomStateAccess { self.state = (state.0, state.1) } - public init(seed: UInt64) { - let s0 = splitMix64(seed: seed) - self.init(state: (s0, splitMix64(seed: s0))) - } - - public mutating func seed(_ seed: UInt64) { - let s0 = splitMix64(seed: seed) - self.state = (s0, splitMix64(seed: s0)) - } - public mutating func next() -> UInt64 { let result = self.state.0 &+ self.state.1 @@ -38,13 +27,12 @@ struct Xoroshiro128Plus: RandomProvider, RandomSeedable, RandomStateAccess { } } -struct Xoroshiro128PlusPlus: RandomProvider, RandomSeedable, RandomStateAccess { +public struct Xoroshiro128PlusPlus: RandomProvider, RandomStateAccess { public typealias Output = UInt64 - public typealias SeedType = UInt64 public typealias StateType = (UInt64, UInt64) - static public var min: UInt64 { .min } - static public var max: UInt64 { .max } + public static var min: UInt64 { .min } + public static var max: UInt64 { .max } public var state: (UInt64, UInt64) @@ -56,16 +44,6 @@ struct Xoroshiro128PlusPlus: RandomProvider, RandomSeedable, RandomStateAccess { self.state = (state.0, state.1) } - public init(seed: UInt64) { - let s0 = splitMix64(seed: seed) - self.init(state: (s0, splitMix64(seed: s0))) - } - - public mutating func seed(_ seed: UInt64) { - let s0 = splitMix64(seed: seed) - self.state = (s0, splitMix64(seed: s0)) - } - public mutating func next() -> UInt64 { let result = (self.state.0 &+ self.state.1).rotate(left: 17) &+ self.state.0 @@ -78,13 +56,12 @@ struct Xoroshiro128PlusPlus: RandomProvider, RandomSeedable, RandomStateAccess { } } -struct Xoroshiro128StarStar: RandomProvider, RandomSeedable, RandomStateAccess { +public struct Xoroshiro128StarStar: RandomProvider, RandomStateAccess { public typealias Output = UInt64 - public typealias SeedType = UInt64 public typealias StateType = (UInt64, UInt64) - static public var min: UInt64 { .min } - static public var max: UInt64 { .max } + public static var min: UInt64 { .min } + public static var max: UInt64 { .max } public var state: (UInt64, UInt64) @@ -96,16 +73,6 @@ struct Xoroshiro128StarStar: RandomProvider, RandomSeedable, RandomStateAccess { self.state = (state.0, state.1) } - public init(seed: UInt64) { - let s0 = splitMix64(seed: seed) - self.init(state: (s0, splitMix64(seed: s0))) - } - - public mutating func seed(_ seed: UInt64) { - let s0 = splitMix64(seed: seed) - self.state = (s0, splitMix64(seed: s0)) - } - public mutating func next() -> UInt64 { let result = (self.state.0 &* 5).rotate(left: 7) &* 9 @@ -119,14 +86,7 @@ struct Xoroshiro128StarStar: RandomProvider, RandomSeedable, RandomStateAccess { } fileprivate extension UInt64 { - func rotate(left count: Int) -> Self { + @inline(__always) func rotate(left count: Int) -> Self { self &<< count | self &>> (Self.bitWidth &- count) } } - -fileprivate func splitMix64(seed: UInt64) -> UInt64 { - var x = seed &+ 0x9E3779B97F4A7C15 - x = (x ^ x &>> 30) &* 0xBF58476D1CE4E5B9 - x = (x ^ x &>> 27) &* 0x94D049BB133111EB - return x ^ x &>> 31 -} diff --git a/Sources/Voxelotl/WorldGenerator.swift b/Sources/Voxelotl/WorldGenerator.swift index 2fe5142..ea1c056 100644 --- a/Sources/Voxelotl/WorldGenerator.swift +++ b/Sources/Voxelotl/WorldGenerator.swift @@ -3,13 +3,11 @@ struct WorldGenerator { public mutating func reset(seed: UInt64) { var random: any RandomProvider + let initialState = SplitMix64.createState(seed: seed) #if true - random = Xoroshiro128PlusPlus(seed: seed) + random = Xoroshiro128PlusPlus(state: initialState) #else - //TODO: Fill seed with a hash - random = PCG32Random(state: ( - UInt64(Arc4Random.instance.next()) | UInt64(Arc4Random.instance.next()) << 32, - UInt64(Arc4Random.instance.next()) | UInt64(Arc4Random.instance.next()) << 32)) + random = PCG32Random(seed: initialState) #endif self.noise = ImprovedPerlin(random: &random) @@ -38,3 +36,11 @@ struct WorldGenerator { return chunk } } + +fileprivate extension RandomProvider where Output == UInt64, Self: RandomSeedable, SeedType == UInt64 { + static func createState(seed value: UInt64) -> (UInt64, UInt64) { + var hash = Self(seed: value) + let state = (hash.next(), hash.next()) + return state + } +}