broader randomrange extensions

This commit is contained in:
a dinosaur 2024-08-22 05:57:03 +10:00
parent 6a57a8580e
commit b24d154c93
3 changed files with 106 additions and 10 deletions

View File

@ -33,7 +33,9 @@ class Game: GameDelegate {
}
private func generateWorld() {
var random = DarwinRandom(seed: Arc4Random.instance.next(in: DarwinRandom.max))
let newSeed = Arc4Random.instance.next(in: DarwinRandom.max)
printErr(newSeed)
var random = DarwinRandom(seed: newSeed)
self.chunk.fill(allBy: {
if (random.next() & 0x1) == 0x1 {
.solid(.init(rgb888: UInt32(random.next(in: 0..<0xFFFFFF+1))).linear)

View File

@ -17,12 +17,37 @@ public class Arc4Random: RandomProvider {
arc4random()
}
public func next(in bound: Int) -> Int {
assert(bound <= Self.max)
return Int(arc4random_uniform(UInt32(bound)))
func next(in bound: UInt32) -> UInt32 {
return arc4random_uniform(bound)
}
public func next(in range: Range<Int>) -> Int {
return range.lowerBound + next(in: range.upperBound - range.lowerBound)
func next(in bound: Int) -> Int {
assert(bound <= UInt32.max, "Maximum raw random provider output is smaller than requested bound")
return Int(arc4random_uniform(UInt32(bound)))
}
}
public extension Arc4Random {
func next(in range: Range<UInt32>) -> UInt32 {
assert(!range.isEmpty, "Ranged next called with empty range")
return range.lowerBound + next(in: range.upperBound - range.lowerBound)
}
func next(in range: ClosedRange<UInt32>) -> UInt32 {
if range == 0...UInt32.max {
next()
} else {
next(in: range.upperBound - range.lowerBound + 1)
}
}
func next(in range: Range<Int>) -> Int {
assert(!range.isEmpty, "Ranged next called with empty range")
return range.lowerBound + next(in: range.upperBound - range.lowerBound)
}
func next(in range: ClosedRange<Int>) -> Int {
assert(range.upperBound - range.lowerBound < Int.max, "Closed range exceeds Int.max")
return range.lowerBound + next(in: range.upperBound - range.lowerBound + 1)
}
}

View File

@ -1,15 +1,17 @@
public extension RandomProvider where Output: BinaryInteger {
mutating func next(in range: Range<Int>) -> Int {
range.lowerBound + self.next(in: range.upperBound - range.lowerBound)
assert(!range.isEmpty, "Ranged next called with empty range")
return range.lowerBound + self.next(in: range.upperBound - range.lowerBound)
}
mutating func next(in range: ClosedRange<Int>) -> Int {
range.lowerBound + self.next(in: range.upperBound - range.lowerBound + 1)
assert(range.upperBound - range.lowerBound < Int.max, "Closed range exceeds Int.max")
return range.lowerBound + self.next(in: range.upperBound - range.lowerBound + 1)
}
mutating func next(in bound: Int) -> Int {
assert(Self.min == 0)
assert(Self.max >= bound)
assert(Self.min == 0, "Range operations are unsupported on random providers with a non-zero minimum")
assert(Self.max >= bound, "Maximum raw random provider output is smaller than requested bound")
let threshold = Int(Self.max % Output(bound))
var result: Int
repeat {
@ -18,3 +20,70 @@ public extension RandomProvider where Output: BinaryInteger {
return result % bound
}
}
public extension RandomProvider where Output: UnsignedInteger {
mutating func next(in range: Range<Output>) -> Output {
assert(!range.isEmpty, "Ranged next called with empty range")
return range.lowerBound + self.next(in: range.upperBound - range.lowerBound)
}
mutating func next(in range: ClosedRange<Output>) -> Output {
if range == 0...Output.max {
next()
} else {
range.lowerBound + self.next(in: range.upperBound - range.lowerBound + 1)
}
}
mutating func next(in bound: Output) -> Output {
assert(Self.min == 0, "Range operations are unsupported on random providers with a non-zero minimum")
assert(Self.max >= bound, "Maximum raw random provider output is smaller than requested bound")
let threshold = (Self.max &- bound &+ 1) % bound
var result: Output
repeat {
result = self.next()
} while result < threshold
return result % bound
}
}
//MARK: - Experimental
// Uniform bounded random without modulos, WILL produce different results from the standard bounded next
public extension RandomProvider where Output: UnsignedInteger {
mutating func nextModless(in range: Range<Output>) -> Output {
assert(!range.isEmpty, "Ranged next called with empty range")
return range.lowerBound + self.nextModless(in: range.upperBound - range.lowerBound)
}
mutating func nextModless(in range: ClosedRange<Output>) -> Output {
if range == 0...Output.max {
self.next()
} else {
range.lowerBound + self.nextModless(in: range.upperBound - range.lowerBound + 1)
}
}
mutating func nextModless(in bound: Output) -> Output {
func pow2MaskFrom(range num: Output) -> Output {
if num & (num - 1) == 0 {
return num - 1
}
var result: Output = 1
for _ in 0..<Output.bitWidth {
if result >= num {
return result - 1
}
result <<= 1
}
return .max
}
let mask = pow2MaskFrom(range: bound)
var result: Output
repeat {
result = next() & mask
} while result >= bound
return result
}
}