166 lines
6.6 KiB
Kotlin
166 lines
6.6 KiB
Kotlin
package util
|
|
|
|
import controllers.Orchestrator
|
|
import kotlin.random.Random
|
|
|
|
/**
|
|
* A collection of helper functions for common utility tasks.
|
|
*/
|
|
object HelperFunctions {
|
|
/**
|
|
* Computes the total number of steps needed to process the given total volume.
|
|
*
|
|
* This takes the total volume that needs to be processed and th
|
|
* and calculates the total steps required.
|
|
* Usage examples:
|
|
* ```
|
|
* val total = 550
|
|
* val perStep = 200
|
|
* val steps = calculateTotalSteps(total, perStep) // 3 steps
|
|
*
|
|
* val steps = calculateTotalSteps(1000, 100) // 10 steps
|
|
* ```
|
|
*
|
|
* @param totalVolume the total amount that needs to be processed
|
|
* @param volumePerStep the amount to process per step
|
|
* @return the number of steps required to process the total volume
|
|
*/
|
|
fun calculateTotalSteps(totalVolume: Int, volumePerStep: Int) =
|
|
totalVolume / volumePerStep + if (totalVolume % volumePerStep > 0) {
|
|
1
|
|
} else {
|
|
0
|
|
}
|
|
|
|
/**
|
|
* Generates a random long that approximates a normal distribution.
|
|
*
|
|
* Works by taking two random samples from 0 to upperBound/2 and adding them together. This tends to give a more natural
|
|
* spread than a single random sample.
|
|
*
|
|
* This approach relies on the Central Limit Theorem: that the sum of multiple independent random variables will
|
|
* tend towards a normal distribution, even if the original variables themselves are not normally distributed.
|
|
*
|
|
* @param upperBound The upper bound for the random range. Must be >= 2.
|
|
* @return A random long value following an approximate normal distribution.
|
|
*/
|
|
fun getApproximatelyNormalLong(upperBound: Long): Long {
|
|
// anything lower to 2 will round down to zero, so return zero. Additionally, this guarantees a positive upper
|
|
//bound in one step
|
|
if (upperBound < 2L) {
|
|
return 0L
|
|
}
|
|
// Generate two random longs from 0 to upperBound/2 and add them together to approximate a normal distribution.
|
|
return Random.nextLong(upperBound.shr(1)) + Random.nextLong(upperBound.shr(1))
|
|
}
|
|
|
|
/**
|
|
* Generates a random long following a Gaussian distribution within the given upper bound.
|
|
*
|
|
* Models the distribution observed in getRandomLongFromNormalDistribution
|
|
*
|
|
* @param upperBound The upper bound of the distribution
|
|
* @return A random long value
|
|
*/
|
|
fun getNextGaussian(upperBound: Long): Long {
|
|
require(upperBound > 0)
|
|
return java.util.Random()
|
|
.nextGaussian(
|
|
upperBound.toDouble() / 2.0,
|
|
upperBound.toDouble() / 5.0
|
|
).toLong()
|
|
.coerceIn(0..upperBound)
|
|
}
|
|
|
|
|
|
/**
|
|
* Prints a progress report to console showing current step, total steps, elapsed time, and estimated remaining time.
|
|
*
|
|
* This takes the current step number, total steps, and elapsed duration and prints a progress report.
|
|
* Typical usage is to call this within a loop, passing the loop index for current step and total loop count.
|
|
*
|
|
*
|
|
* Usage example:
|
|
* ```
|
|
* val totalSteps = 100
|
|
* val start = System.currentTimeMillis()
|
|
* for (i in 1..totalSteps) {
|
|
* // Do work
|
|
*
|
|
* report(i, totalSteps, System.currentTimeMillis() - start)
|
|
* }
|
|
* ```
|
|
*
|
|
* @param step The current step number
|
|
* @param of The total number of steps
|
|
* @param dur The elapsed duration so far in milliseconds
|
|
*/
|
|
fun report(step: Int, of: Int, dur: Long) {
|
|
val remaining = (dur / step) * (of - step)
|
|
if (step == 0) {
|
|
print("\rGathering timing data...\t|\tNo current ETA...) ")
|
|
} else if (step < 8) { // The time estimation is terrible, so it converges on reality. it takes roughly 8-10 steps to get a decent picture
|
|
print("\rStep $step of $of (${prettyTimeString(dur)} complete\t|\t~${prettyTimeString(remaining * 2)} remaining [LOW CONFIDENCE]) ")
|
|
} else if (step == of) {
|
|
print("\rFinal step (${prettyTimeString(dur)} complete\t|\t~${prettyTimeString(dur / (of - 1))} remaining) ")
|
|
} else {
|
|
print("\rStep $step of $of (${prettyTimeString(dur)} complete\t|\t~${prettyTimeString(remaining)} remaining) ")
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Converts a duration in milliseconds to a human-readable string.
|
|
*
|
|
* This takes a duration in ms and converts it to a formatted string like "2h13m4s".
|
|
*
|
|
* Usage example:
|
|
*
|
|
* ```
|
|
* val duration = 72134 // ms
|
|
* val timeStr = prettyTimeString(duration)
|
|
* // "1m12s"
|
|
* ```
|
|
*
|
|
* @param durationMillis The duration to convert, in milliseconds
|
|
* @return A string representation of the duration, in the format XhYmZs
|
|
*/
|
|
fun prettyTimeString(durationMillis: Long): String {
|
|
val millisPerSecond = 1000L
|
|
val millisPerMinute = 60L * millisPerSecond
|
|
val millisPerHour = 60L * millisPerMinute
|
|
return if (durationMillis > millisPerHour) {
|
|
return "${durationMillis / millisPerHour}h${(durationMillis % millisPerHour) / millisPerMinute}m${(durationMillis % millisPerMinute) / millisPerSecond}s"
|
|
} else if (durationMillis > millisPerMinute) {
|
|
return "${(durationMillis % millisPerHour) / millisPerMinute}m${(durationMillis % millisPerMinute) / millisPerSecond}s"
|
|
} else {
|
|
"${(durationMillis % millisPerMinute) / millisPerSecond}s"
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* Gets the current mouse pointer location and returns it as a val declaration string.
|
|
*
|
|
* This method uses the [getPointerLocationAfter] method to get the current
|
|
* mouse pointer location after a small delay.
|
|
*
|
|
* It then formats this location into a string declaring a val with the provided
|
|
* variable name, like:
|
|
*
|
|
* ```
|
|
* val location = getPointerLocationAsValDeclarationString("clickPoint")
|
|
* // val clickPoint = Point(123, 456)
|
|
* ```
|
|
*
|
|
* The delay before getting the pointer location helps ensure the mouse has
|
|
* settled after any prior movements.
|
|
*
|
|
* @param varName The name to use for the variable in the declaration string.
|
|
* @return A string declaring a val with the pointer location.
|
|
*/
|
|
fun getPointerLocationAsValDeclarationString(orchestrator: Orchestrator, varName: String): String {
|
|
val info = orchestrator.getPointerLocationAfterDelay(5)
|
|
return "val $varName = Point(${info.x}, ${info.y})"
|
|
}
|
|
|
|
} |