From 32ef2911fa8a7186d1143054c0b5c849257fafb8 Mon Sep 17 00:00:00 2001 From: DBotThePony Date: Mon, 3 Mar 2025 12:02:42 +0700 Subject: [PATCH] Implement Complementary Multiply-With-Curry Random --- .../mc/otm/core/util/CMWCRandom.kt | 115 ++++++++++++++++++ 1 file changed, 115 insertions(+) create mode 100644 src/main/kotlin/ru/dbotthepony/mc/otm/core/util/CMWCRandom.kt diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/core/util/CMWCRandom.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/core/util/CMWCRandom.kt new file mode 100644 index 000000000..aec7fc452 --- /dev/null +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/core/util/CMWCRandom.kt @@ -0,0 +1,115 @@ +package ru.dbotthepony.mc.otm.core.util + +import net.minecraft.util.Mth +import net.minecraft.util.RandomSource +import net.minecraft.world.level.levelgen.MarsagliaPolarGaussian +import net.minecraft.world.level.levelgen.PositionalRandomFactory +import java.lang.StringBuilder +import java.util.random.RandomGenerator + +class CMWCRandom(seed: Long = System.nanoTime(), private val stateSize: Int = CMWC_DEFAULT_STATE_SIZE) : RandomGenerator, RandomSource { + private val state = IntArray(stateSize) + private var carry = 0 + private var stateIndex = 0 + private val gaussian = MarsagliaPolarGaussian(this) + + var seed: Long = seed + private set + + init { + setSeed(seed) + } + + override fun setSeed(seed: Long) { + this.seed = seed + carry = Integer.remainderUnsigned(seed.toInt(), CMWC_CARRY_MAX) + + // init state with regular LCG produced values + state[0] = seed.toInt() + state[1] = seed.shr(32).toInt() + + for (i in 2 until state.size) { + state[i] = 69069 * state[i - 2] + 362437 + } + + stateIndex = state.size - 1 + gaussian.reset() + } + + override fun nextInt(): Int { + stateIndex = (stateIndex + 1) % state.size + val t = 18782L * state[stateIndex] + carry + + carry = t.ushr(32).toInt() + var x = t.toInt() + carry + + if (x < carry) { + x++ + carry++ + } + + state[stateIndex] = 0xfffffffe.toInt() - x + return state[stateIndex] + } + + override fun nextLong(): Long { + val a = nextInt().toLong() and 0xFFFFFFFFL + val b = nextInt().toLong() and 0xFFFFFFFFL + return a.shl(32) or b + } + + override fun nextInt(bound: Int): Int { + return super.nextInt(bound) + } + + override fun nextInt(origin: Int, bound: Int): Int { + return super.nextInt(origin, bound) + } + + override fun nextBoolean(): Boolean { + return super.nextBoolean() + } + + override fun nextFloat(): Float { + return super.nextFloat() + } + + override fun nextDouble(): Double { + return super.nextDouble() + } + + override fun nextGaussian(): Double { + return gaussian.nextGaussian() + } + + override fun fork(): RandomSource { + return CMWCRandom(nextLong(), stateSize) + } + + override fun forkPositional(): PositionalRandomFactory { + return Positional(nextLong(), stateSize) + } + + class Positional(val seed: Long, val stateSize: Int = CMWC_DEFAULT_STATE_SIZE) : PositionalRandomFactory { + override fun at(x: Int, y: Int, z: Int): RandomSource { + return CMWCRandom(Mth.getSeed(x, y, z).xor(seed), stateSize) + } + + override fun fromHashOf(name: String): RandomSource { + return CMWCRandom(name.hashCode().toLong().xor(seed), stateSize) + } + + override fun fromSeed(seed: Long): RandomSource { + return CMWCRandom(seed, stateSize) + } + + override fun parityConfigString(builder: StringBuilder) { + throw UnsupportedOperationException() + } + } + + companion object { + const val CMWC_DEFAULT_STATE_SIZE = 256 // 4096 + const val CMWC_CARRY_MAX = 809430660 + } +}