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