From: TheSaminator Date: Sat, 4 Jun 2022 17:00:49 +0000 (-0400) Subject: N-dimensional vector fixes X-Git-Url: https://gitweb.starshipfights.net/?a=commitdiff_plain;h=dafe42e1a1dd0ffa16f42264bb8b526184aa98bc;p=starship-fights N-dimensional vector fixes --- diff --git a/src/commonMain/kotlin/starshipfights/game/ai/ai_optimization.kt b/src/commonMain/kotlin/starshipfights/game/ai/ai_optimization.kt index cc41052..035724c 100644 --- a/src/commonMain/kotlin/starshipfights/game/ai/ai_optimization.kt +++ b/src/commonMain/kotlin/starshipfights/game/ai/ai_optimization.kt @@ -36,7 +36,7 @@ val allInstincts = listOf( fun genInstinctCandidates(count: Int): Set { return Random.nextOrthonormalBasis(allInstincts.size).take(count).map { vector -> - Instincts.fromValues((allInstincts zip vector).associate { (key, value) -> + Instincts.fromValues((allInstincts zip vector.values).associate { (key, value) -> key.key to key.denormalize(value) }) }.toSet() diff --git a/src/commonMain/kotlin/starshipfights/game/ai/ai_optimization_util.kt b/src/commonMain/kotlin/starshipfights/game/ai/ai_optimization_util.kt index 3adcaa1..216f6cb 100644 --- a/src/commonMain/kotlin/starshipfights/game/ai/ai_optimization_util.kt +++ b/src/commonMain/kotlin/starshipfights/game/ai/ai_optimization_util.kt @@ -1,36 +1,48 @@ package starshipfights.game.ai import starshipfights.game.EPSILON +import kotlin.jvm.JvmInline import kotlin.math.abs import kotlin.math.sqrt import kotlin.random.Random +@JvmInline +value class VecN(val values: List) + +val VecN.dimension: Int + get() = values.size + // close enough fun Random.nextGaussian() = (1..12).sumOf { nextDouble() } - 6 -fun Random.nextUnitVector(size: Int): List { +fun Random.nextUnitVector(size: Int): VecN { if (size <= 0) throw IllegalArgumentException("Cannot have vector of zero or negative dimension!") if (size == 1) - return listOf(if (nextBoolean()) 1.0 else -1.0) + return VecN(listOf(if (nextBoolean()) 1.0 else -1.0)) + + val vector = VecN((1..size).map { nextGaussian() }) + + if (vector.isNullVector) // try again + return nextUnitVector(size) - return (1..size).map { nextGaussian() }.normalize() + return vector.normalize() } -fun Random.nextOrthonormalBasis(size: Int): List> { +fun Random.nextOrthonormalBasis(size: Int): List { if (size <= 0) throw IllegalArgumentException("Cannot have orthonormal basis of zero or negative dimension!") if (size == 1) - return listOf(listOf(if (nextBoolean()) 1.0 else -1.0)) + return listOf(VecN(listOf(if (nextBoolean()) 1.0 else -1.0))) - val orthogonalBasis = mutableListOf>() + val orthogonalBasis = mutableListOf() while (orthogonalBasis.size < size) { val vector = nextUnitVector(size) var orthogonal = vector for (prevVector in orthogonalBasis) - orthogonal = orthogonal minus (vector project prevVector) + orthogonal -= (vector project prevVector) if (!orthogonal.isNullVector) orthogonalBasis.add(orthogonal) @@ -40,44 +52,42 @@ fun Random.nextOrthonormalBasis(size: Int): List> { return orthogonalBasis.map { it.normalize() } } -val Iterable.isNullVector: Boolean +val VecN.isNullVector: Boolean get() { - return all { abs(it) < EPSILON } + return values.all { abs(it) < EPSILON } } -fun Iterable.normalize(): List { - val magnitude = sqrt(sumOf { it * it }) - if (magnitude < EPSILON) +fun VecN.normalize(): VecN { + if (isNullVector) throw IllegalArgumentException("Cannot normalize the zero vector!") - return this div magnitude + val magnitude = sqrt(this dot this) + + return this / magnitude } -infix fun Iterable.dot(other: Iterable): Double { - if (count() != other.count()) +infix fun VecN.dot(other: VecN): Double { + if (dimension != other.dimension) throw IllegalArgumentException("Cannot take inner product of vectors of unequal dimensions!") - return (this zip other).sumOf { (a, b) -> a * b } + return (this.values zip other.values).sumOf { (a, b) -> a * b } } -infix fun Iterable.project(onto: Iterable): List { - if (count() != onto.count()) - throw IllegalArgumentException("Cannot take inner product of vectors of unequal dimensions!") - - return this times ((this dot onto) / (this dot this)) +infix fun VecN.project(onto: VecN): VecN { + return this * ((this dot onto) / (this dot this)) } -infix fun Iterable.plus(other: Iterable): List { - if (count() != other.count()) +operator fun VecN.plus(other: VecN): VecN { + if (dimension != other.dimension) throw IllegalArgumentException("Cannot take sum of vectors of unequal dimensions!") - return (this zip other).map { (a, b) -> a + b } + return VecN((this.values zip other.values).map { (a, b) -> a + b }) } -infix fun Iterable.minus(other: Iterable) = this plus (other times -1.0) +operator fun VecN.minus(other: VecN) = this + (other * -1.0) -infix fun Iterable.times(scale: Double): List = map { it * scale } -infix fun Iterable.div(scale: Double): List = map { it / scale } +operator fun VecN.times(scale: Double): VecN = VecN(values.map { it * scale }) +operator fun VecN.div(scale: Double): VecN = VecN(values.map { it / scale }) fun Instinct.denormalize(normalValue: Double): Double { val zeroToOne = (normalValue + 1) / 2