1 /*
2 * Copyright (C) 2023 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 package com.android.egg.landroid
18
19 import kotlin.random.Random
20
21 /**
22 * A bag of stones. Each time you pull one out it is not replaced, preventing duplicates. When the
23 * bag is exhausted, all the stones are replaced and reshuffled.
24 */
25 class Bag<T>(items: Array<T>) {
26 private val remaining = items.copyOf()
27 private var next = remaining.size // will cause a shuffle on first pull()
28
29 /** Return the next random item from the bag, without replacing it. */
pullnull30 fun pull(rng: Random): T {
31 if (next >= remaining.size) {
32 remaining.shuffle(rng)
33 next = 0
34 }
35 return remaining[next++]
36 }
37 }
38
39 /**
40 * A loot table. The weight of each possibility is in the first of the pair; the value to be
41 * returned in the second. They need not add up to 1f (we will do that for you, free of charge).
42 */
43 class RandomTable<T>(private vararg val pairs: Pair<Float, T>) {
<lambda>null44 private val total = pairs.map { it.first }.sum()
45
46 /** Select a random value from the weighted table. */
rollnull47 fun roll(rng: Random): T {
48 var x = rng.nextFloatInRange(0f, total)
49 for ((weight, result) in pairs) {
50 x -= weight
51 if (x < 0f) return result
52 }
53 return pairs.last().second
54 }
55 }
56
57 /** Return a random float in the range [from, until). */
nextFloatInRangenull58 fun Random.nextFloatInRange(from: Float, until: Float): Float =
59 from + ((until - from) * nextFloat())
60
61 /** Return a random float in the range [start, end). */
62 fun Random.nextFloatInRange(fromUntil: ClosedFloatingPointRange<Float>): Float =
63 nextFloatInRange(fromUntil.start, fromUntil.endInclusive)
64
65 /** Return a random float in the range [first, second). */
66 fun Random.nextFloatInRange(fromUntil: Pair<Float, Float>): Float =
67 nextFloatInRange(fromUntil.first, fromUntil.second)
68
69 /** Choose a random element from an array. */
70 fun <T> Random.choose(array: Array<T>) = array[nextInt(array.size)]
71