Port Original Tactics hex math to typed Jerboa
ober
a416ccea7067d334f5fde941638ece4514a163a5
--- a/.build.yml +++ b/.build.yml @@ -5,7 +5,7 @@ packages: - make=4.4.1-r4 sources: # Build dependency: full immutable commit, mirrored in dependencies.lock.json. - - "https://git.sr.ht/~lisp/jerboa#3a1e213c984f40f16404f78007bbbccc925ec94a" + - "https://git.sr.ht/~lisp/jerboa#46dfd30d2ab66b59ef02336f5a0c7981b838bf84" # The second source is the build subject selected by the SourceHut submitter. - https://git.sr.ht/~lisp/jerboa-android tasks: @@ -14,6 +14,6 @@ tasks: test "$(apk info -v chez-scheme)" = chez-scheme-10.3.0-r2 test "$(apk info -v git)" = git-2.54.0-r0 test "$(apk info -v make)" = make-4.4.1-r4 - test "$(git -C ../jerboa rev-parse HEAD)" = 3a1e213c984f40f16404f78007bbbccc925ec94a - test "$(git -C ../jerboa rev-parse 'HEAD^{tree}')" = d1c285186e9618c88d0a89d61003816b126a3ef0 + test "$(git -C ../jerboa rev-parse HEAD)" = 46dfd30d2ab66b59ef02336f5a0c7981b838bf84 + test "$(git -C ../jerboa rev-parse 'HEAD^{tree}')" = 960136c5cd1e5a3c496f3398e2ef0cb8561cffc3 JERBOA="chez --libdirs .:../jerboa/lib --script" make test --- a/dependencies.lock.json +++ b/dependencies.lock.json @@ -11,8 +11,8 @@ "generator_runtime": { "name": "jerboa", "repository": "https://git.sr.ht/~lisp/jerboa", - "commit": "3a1e213c984f40f16404f78007bbbccc925ec94a", - "tree": "d1c285186e9618c88d0a89d61003816b126a3ef0" + "commit": "46dfd30d2ab66b59ef02336f5a0c7981b838bf84", + "tree": "960136c5cd1e5a3c496f3398e2ef0cb8561cffc3" }, "assurance_tools": { "osv_scanner": { --- a/scripts/verify-supply-chain.sh +++ b/scripts/verify-supply-chain.sh @@ -3,8 +3,8 @@ set -eu repo=$(CDPATH= cd -- "$(dirname -- "$0")/.." && pwd -P) lock="$repo/dependencies.lock.json" -jerboa_commit=3a1e213c984f40f16404f78007bbbccc925ec94a # gitsafe:ignore -jerboa_tree=d1c285186e9618c88d0a89d61003816b126a3ef0 # gitsafe:ignore +jerboa_commit=46dfd30d2ab66b59ef02336f5a0c7981b838bf84 # gitsafe:ignore +jerboa_tree=960136c5cd1e5a3c496f3398e2ef0cb8561cffc3 # gitsafe:ignore gradle_sha=20f1b1176237254a6fc204d8434196fa11a4cfb387567519c61556e8710aed78 jdk_macos_sha=8fa1eff40bb637a33613b2ccb8b12c70dc3661cc22cf8e784943715769a05336 jdk_linux_sha=d8afc263758141a66e0e3aafc321e783f7016696f4eaea067d340a269037d331 --- a/templates/original-tactics.ss +++ b/templates/original-tactics.ss @@ -2636,270 +2636,572 @@ " )" " }" "}")) - (kotlin-file-lines "com/jerboa/originaltactics/HexMath.kt" - ("package com.jerboa.originaltactics" - "" - "import org.json.JSONArray" - "import org.json.JSONObject" - "import kotlin.math.abs" - "" - "data class ProjectedHelmPose(" - " val q: Int," - " val r: Int," - " val facing: Int," - " val speed: Int," - " val turnMode: String," - " val distanceSinceTurn: Int" - ")" - "" - "data class ProjectedHelmMove(" - " val impulse: Int," - " val maneuver: String?," - " val pose: ProjectedHelmPose" - ")" - "" - "data class ProjectedRangePoint(" - " val impulse: Int," - " val range: Int" - ")" - "" - "fun hexRangeFromPositions(alpha: JSONObject?, beta: JSONObject?): Int? {" - " if (alpha == null || beta == null) return null" - " return hexRange(alpha.optInt(\"q\"), alpha.optInt(\"r\"), beta.optInt(\"q\"), beta.optInt(\"r\"))" - "}" - "" - "fun hexRange(q1: Int, r1: Int, q2: Int, r2: Int): Int {" - " val dq = q2 - q1" - " val dr = r2 - r1" - " val ds = -dq - dr" - " return (abs(dq) + abs(dr) + abs(ds)) / 2" - "}" - "" - "fun currentBattleRange(battle: JSONObject?): String {" - " val computed = hexRangeFromPositions(" - " battle?.optJSONObject(\"alpha-position\")," - " battle?.optJSONObject(\"beta-position\")" - " )" - " if (computed != null) return computed.toString()" - " return (battle?.opt(\"range\") ?: \"-\").toString()" - "}" - "" - "fun movementImpulsesForSpeed(speed: Int): List<Int> {" - " val moves = speed.coerceIn(0, 32)" - " if (moves <= 0) return emptyList()" - " return (1..moves).map { step -> (step * 32 + moves - 1) / moves }" - "}" - "" - "fun nextMoveImpulse(position: JSONObject?, currentImpulse: Int): Int? {" - " val speed = position?.optInt(\"speed\", 0)?.coerceIn(0, 32) ?: 0" - " return movementImpulsesForSpeed(speed).firstOrNull { it > currentImpulse }" - "}" - "" - "fun maneuverMapFromJson(raw: Any?): Map<Int, String> {" - " val out = linkedMapOf<Int, String>()" - " when (raw) {" - " is JSONArray -> {" - " for (index in 0 until raw.length()) {" - " val item = raw.opt(index)" - " when (item) {" - " is JSONObject -> {" - " val pair = item.optJSONArray(\"\\$pair\")" - " if (pair != null) {" - " val impulse = pair.optInt(0, -1)" - " val maneuver = pair.optString(1, \"\")" - " if (impulse in 1..32 && maneuver.isNotBlank()) out[impulse] = maneuver" - " }" - " }" - " is JSONArray -> {" - " val impulse = item.optInt(0, -1)" - " val maneuver = item.optString(1, \"\")" - " if (impulse in 1..32 && maneuver.isNotBlank()) out[impulse] = maneuver" - " }" - " }" - " }" - " }" - " is JSONObject -> {" - " val keys = raw.keys()" - " while (keys.hasNext()) {" - " val key = keys.next()" - " val impulse = key.toIntOrNull() ?: continue" - " val maneuver = raw.optString(key, \"\")" - " if (impulse in 1..32 && maneuver.isNotBlank()) out[impulse] = maneuver" - " }" - " }" - " }" - " return out" - "}" - "" - "fun projectedHelmMoves(" - " position: JSONObject?," - " maneuvers: Map<Int, String>," - " currentImpulse: Int," - " maxMoves: Int" - "): List<ProjectedHelmMove> {" - " if (position == null) return emptyList()" - " val speed = position.optInt(\"speed\", 0).coerceIn(0, 32)" - " if (speed <= 0) return emptyList()" - " val movementImpulses = movementImpulsesForSpeed(speed).filter { it > currentImpulse }" - " if (movementImpulses.isEmpty()) return emptyList()" - "" - " val out = mutableListOf<ProjectedHelmMove>()" - " var pose = startingProjectedHelmPose(position)" - " for (impulse in movementImpulses) {" - " val maneuver = maneuvers[impulse]" - " pose = applyProjectedHelmMove(pose, maneuver)" - " out.add(ProjectedHelmMove(impulse, maneuver, pose))" - " if (out.size >= maxMoves.coerceAtLeast(1)) break" - " }" - " return out" - "}" - "" - "fun projectedHelmPoseAt(" - " position: JSONObject?," - " projected: List<ProjectedHelmMove>," - " impulse: Int" - "): ProjectedHelmPose? =" - " projected.lastOrNull { it.impulse <= impulse }?.pose ?: position?.let { startingProjectedHelmPose(it) }" - "" - "fun projectedRangeForecast(" - " alphaPosition: JSONObject?," - " betaPosition: JSONObject?," - " alphaManeuversRaw: Any?," - " betaManeuversRaw: Any?," - " currentImpulse: Int," - " maxMoves: Int = 8," - " labelLimit: Int = 5" - "): List<ProjectedRangePoint> {" - " if (alphaPosition == null || betaPosition == null) return emptyList()" - " val alphaProjected = projectedHelmMoves(" - " alphaPosition," - " maneuverMapFromJson(alphaManeuversRaw)," - " currentImpulse," - " maxMoves" - " )" - " val betaProjected = projectedHelmMoves(" - " betaPosition," - " maneuverMapFromJson(betaManeuversRaw)," - " currentImpulse," - " maxMoves" - " )" - " val impulses = (alphaProjected.map { it.impulse } + betaProjected.map { it.impulse })" - " .distinct()" - " .sorted()" - " .take(labelLimit.coerceAtLeast(1))" - " if (impulses.isEmpty()) return emptyList()" - "" - " val out = mutableListOf<ProjectedRangePoint>()" - " var previousRange: Int? = null" - " impulses.forEachIndexed { index, impulse ->" - " val alphaPose = projectedHelmPoseAt(alphaPosition, alphaProjected, impulse) ?: return@forEachIndexed" - " val betaPose = projectedHelmPoseAt(betaPosition, betaProjected, impulse) ?: return@forEachIndexed" - " val range = hexRange(alphaPose.q, alphaPose.r, betaPose.q, betaPose.r)" - " val important = range <= 2 || previousRange == null || range != previousRange || index == impulses.lastIndex" - " previousRange = range" - " if (important) out.add(ProjectedRangePoint(impulse, range))" - " }" - " return out" - "}" - "" - "fun maneuverAbbreviation(maneuver: String): String =" - " when (maneuver) {" - " \"left\" -> \"L\"" - " \"right\" -> \"R\"" - " \"sideslip-left\" -> \"SL\"" - " \"sideslip-right\" -> \"SR\"" - " \"het\" -> \"HET\"" - " else -> maneuver.take(3).uppercase()" - " }" - "" - "private fun startingProjectedHelmPose(position: JSONObject): ProjectedHelmPose =" - " ProjectedHelmPose(" - " position.optInt(\"q\", 0)," - " position.optInt(\"r\", 0)," - " normalizedHelmFacing(position.optInt(\"facing\", 0))," - " position.optInt(\"speed\", 0).coerceIn(0, 32)," - " position.optString(\"turn-mode\", \"c\")," - " position.optInt(\"distance-since-turn\", 0)" - " )" - "" - "private fun applyProjectedHelmMove(pose: ProjectedHelmPose, maneuver: String?): ProjectedHelmPose =" - " when (maneuver) {" - " \"left\" -> if (canProjectedTurn(pose)) {" - " moveForward(pose.copy(facing = normalizedHelmFacing(pose.facing - 1), distanceSinceTurn = 0))" - " } else {" - " pose" - " }" - " \"right\" -> if (canProjectedTurn(pose)) {" - " moveForward(pose.copy(facing = normalizedHelmFacing(pose.facing + 1), distanceSinceTurn = 0))" - " } else {" - " pose" - " }" - " \"sideslip-left\" -> moveInDirection(pose, normalizedHelmFacing(pose.facing - 1))" - " \"sideslip-right\" -> moveInDirection(pose, normalizedHelmFacing(pose.facing + 1))" - " \"het\" -> moveForward(pose.copy(facing = normalizedHelmFacing(pose.facing + 3), distanceSinceTurn = 0))" - " else -> moveForward(pose)" - " }" - "" - "private fun moveForward(pose: ProjectedHelmPose): ProjectedHelmPose =" - " moveInDirection(pose, pose.facing)" - "" - "private fun moveInDirection(pose: ProjectedHelmPose, direction: Int): ProjectedHelmPose {" - " val delta = FACING_DELTAS[normalizedHelmFacing(direction)]" - " return pose.copy(" - " q = pose.q + delta.first," - " r = pose.r + delta.second," - " distanceSinceTurn = pose.distanceSinceTurn + 1" - " )" - "}" - "" - "private fun canProjectedTurn(pose: ProjectedHelmPose): Boolean =" - " pose.distanceSinceTurn >= turnModeRequiredDistance(pose.turnMode, pose.speed)" - "" - "private fun turnModeRequiredDistance(turnMode: String, speed: Int): Int {" - " val bands = TURN_MODE_BANDS[turnMode.lowercase()] ?: TURN_MODE_BANDS[\"c\"].orEmpty()" - " val distances = TURN_MODE_DISTANCES[turnMode.lowercase()] ?: TURN_MODE_DISTANCES[\"c\"].orEmpty()" - " for (index in bands.indices) {" - " val band = bands[index]" - " if (speed in band.first..band.second) return distances.getOrElse(index) { 0 }" - " }" - " return 0" - "}" - "" - "private fun normalizedHelmFacing(facing: Int): Int =" - " ((facing % 6) + 6) % 6" - "" - "private val FACING_DELTAS = arrayOf(" - " 1 to 0," - " 1 to -1," - " 0 to -1," - " -1 to 0," - " -1 to 1," - " 0 to 1" - ")" - "" - "private val TURN_MODE_BANDS = mapOf(" - " \"seeking-weapon\" to listOf(1 to 32)," - " \"shuttle\" to listOf(1 to 11, 12 to 23, 24 to 999)," - " \"aa\" to listOf(2 to 8, 9 to 16, 17 to 24, 25 to 999)," - " \"a\" to listOf(2 to 6, 7 to 12, 13 to 19, 20 to 26, 27 to 999)," - " \"b\" to listOf(2 to 5, 6 to 10, 11 to 15, 16 to 21, 22 to 28, 29 to 999)," - " \"c\" to listOf(2 to 4, 5 to 9, 10 to 14, 15 to 20, 21 to 27, 28 to 999)," - " \"d\" to listOf(2 to 4, 5 to 8, 9 to 12, 13 to 17, 18 to 24, 25 to 999)," - " \"e\" to listOf(2 to 3, 4 to 6, 7 to 10, 11 to 14, 15 to 20, 21 to 29, 30 to 999)," - " \"f\" to listOf(2 to 3, 4 to 5, 6 to 9, 10 to 13, 14 to 17, 18 to 23, 24 to 29, 30 to 999)" - ")" - "" - "private val TURN_MODE_DISTANCES = mapOf(" - " \"seeking-weapon\" to listOf(1)," - " \"shuttle\" to listOf(1, 2, 3)," - " \"aa\" to listOf(1, 2, 3, 4)," - " \"a\" to listOf(1, 2, 3, 4, 5)," - " \"b\" to listOf(1, 2, 3, 4, 5, 6)," - " \"c\" to listOf(1, 2, 3, 4, 5, 6)," - " \"d\" to listOf(1, 2, 3, 4, 5, 6)," - " \"e\" to listOf(1, 2, 3, 4, 5, 6, 7)," - " \"f\" to listOf(1, 2, 3, 4, 5, 6, 7, 8)" - ")")) + (typed-kotlin-file "com/jerboa/originaltactics/HexMath.kt" + (kotlin-imports (org json JSONArray) (org json JSONObject)) + (typed-library (com jerboa originaltactics) + (export make-ProjectedHelmPose ProjectedHelmPose? + make-ProjectedHelmMove ProjectedHelmMove? + make-ProjectedRangePoint ProjectedRangePoint? + hexRangeFromPositions hexRange currentBattleRange + movementImpulsesForSpeed nextMoveImpulse maneuverMapFromJson + projectedHelmMoves projectedHelmPoseAt projectedRangeForecast + maneuverAbbreviation) + (type Any) + (type Int32) + (type JSONArray) + (type JSONObject) + (record ProjectedHelmPose + ((q : Int32) + (r : Int32) + (facing : Int32) + (speed : Int32) + (turnMode : String) + (distanceSinceTurn : Int32))) + (record ProjectedHelmMove + ((impulse : Int32) + (maneuver : (Nullable String)) + (pose : ProjectedHelmPose))) + (record ProjectedRangePoint + ((impulse : Int32) + (range : Int32))) + (extern (asJsonArray (value : Any)) : (Nullable JSONArray) + (kotlin-safe-cast JSONArray)) + (extern (asJsonObject (value : Any)) : (Nullable JSONObject) + (kotlin-safe-cast JSONObject)) + (extern (anyToString (value : Any)) : String + (kotlin-member-call toString)) + (extern (jsonObjectKeys (json : JSONObject)) : (Iterator String) + (kotlin-member-call keys)) + (extern (iteratorHasNext (items : (Iterator String))) : Bool + (kotlin-member-call hasNext)) + (extern (iteratorNext (items : (Iterator String))) : String + (kotlin-member-call next)) + (def (int32Abs (value : Int32)) : Int32 + (if (< value (int32 0)) (- (int32 0) value) value)) + (def (clampImpulse (value : Int32)) : Int32 + (if (< value (int32 0)) + (int32 0) + (if (> value (int32 32)) (int32 32) value))) + (def (atLeastOne (value : Int32)) : Int32 + (if (< value (int32 1)) (int32 1) value)) + (def (betweenInt32? + (value : Int32) + (low : Int32) + (high : Int32)) : Bool + (and (>= value low) (<= value high))) + (def (hexRangeFromPositions + (alpha : (Nullable JSONObject)) + (beta : (Nullable JSONObject))) : (Nullable Int32) + (if (or (nullable-null? alpha) (nullable-null? beta)) + (nullable-none Int32) + (nullable-some + (hexRange + (json-object-opt-int32-default (nullable-get alpha) "q" (int32 0)) + (json-object-opt-int32-default (nullable-get alpha) "r" (int32 0)) + (json-object-opt-int32-default (nullable-get beta) "q" (int32 0)) + (json-object-opt-int32-default (nullable-get beta) "r" (int32 0)))))) + (def (hexRange + (q1 : Int32) + (r1 : Int32) + (q2 : Int32) + (r2 : Int32)) : Int32 + (let ((dq (- q2 q1)) + (dr (- r2 r1))) + (let ((ds (- (int32 0) (+ dq dr)))) + (/ (+ (+ (int32Abs dq) (int32Abs dr)) (int32Abs ds)) + (int32 2))))) + (def (nullableAnyToString + (value : (Nullable Any)) + (fallback : String)) : String + (if (nullable-null? value) + fallback + (anyToString (nullable-get value)))) + (def (currentBattleRange + (battle : (Nullable JSONObject))) : String + (if (nullable-null? battle) + "-" + (let ((computed + (hexRangeFromPositions + (json-object-opt-json-object + (nullable-get battle) + "alpha-position") + (json-object-opt-json-object + (nullable-get battle) + "beta-position")))) + (if (nullable-null? computed) + (nullableAnyToString + (json-object-opt-any (nullable-get battle) "range") + "-") + (int32->string (nullable-get computed)))))) + (def (movementImpulsesForSpeed (speed : Int32)) : (MutableList Int32) + (let ((moves (clampImpulse speed)) + (out (mutable-list-empty Int32))) + (begin + (for/fold ((ignored (int32 0))) + ((step (in-range (int32 1) (+ moves (int32 1))))) + (begin + (mutable-list-add! + out + (/ (+ (- (* step (int32 32)) (int32 1)) moves) + moves)) + ignored)) + out))) + (def (nextMoveImpulse + (position : (Nullable JSONObject)) + (currentImpulse : Int32)) : (Nullable Int32) + (if (nullable-null? position) + (nullable-none Int32) + (let ((moves + (movementImpulsesForSpeed + (json-object-opt-int32-default + (nullable-get position) + "speed" + (int32 0))))) + (for/fold ((found (nullable-none Int32))) + ((index (in-range (int32 0) (list-size moves)))) + (if (nullable-null? found) + (let ((candidate (list-ref moves index))) + (if (> candidate currentImpulse) + (nullable-some candidate) + found)) + found))))) + (def (validManeuverPair? + (impulse : Int32) + (maneuver : String)) : Bool + (and (betweenInt32? impulse (int32 1) (int32 32)) + (not (string-blank? maneuver)))) + (def (addManeuverPair! + (out : (MutableMap Int32 String)) + (impulse : Int32) + (maneuver : String)) : Unit + (if (validManeuverPair? impulse maneuver) + (mutable-map-put! out impulse maneuver) + (begin))) + (def (addManeuverArray! + (out : (MutableMap Int32 String)) + (pair : JSONArray)) : Unit + (addManeuverPair! + out + (json-array-opt-int32 pair (int32 0) (int32 -1)) + (json-array-opt-string pair (int32 1)))) + (def (addManeuverObjectPair! + (out : (MutableMap Int32 String)) + (item : JSONObject)) : Unit + (let ((pair (json-object-opt-json-array item "$pair"))) + (if (nullable-null? pair) + (begin) + (addManeuverArray! out (nullable-get pair))))) + (def (addManeuverRawItem! + (out : (MutableMap Int32 String)) + (item : (Nullable Any))) : Unit + (if (nullable-null? item) + (begin) + (let ((asArray (asJsonArray (nullable-get item))) + (asObject (asJsonObject (nullable-get item)))) + (if (nullable-null? asArray) + (if (nullable-null? asObject) + (begin) + (addManeuverObjectPair! out (nullable-get asObject))) + (addManeuverArray! out (nullable-get asArray)))))) + (def (addManeuverArrayItems! + (out : (MutableMap Int32 String)) + (raw : JSONArray)) : Unit + (begin + (for/fold ((ignored (int32 0))) + ((index (in-range (int32 0) (json-array-length raw)))) + (begin + (addManeuverRawItem! + out + (json-array-opt-any raw index)) + ignored)) + (begin))) + (def (addManeuverObjectItems! + (out : (MutableMap Int32 String)) + (raw : JSONObject)) : Unit + (let ((keys (jsonObjectKeys raw))) + (while (iteratorHasNext keys) + (let ((key (iteratorNext keys))) + (let ((impulse (string->int32-or-null key))) + (if (nullable-null? impulse) + (begin) + (addManeuverPair! + out + (nullable-get impulse) + (json-object-opt-string-default raw key "")))))))) + (def (maneuverMapFromJson + (raw : (Nullable Any))) : (MutableMap Int32 String) + (let ((out (mutable-map-empty Int32 String))) + (begin + (if (nullable-null? raw) + (begin) + (let ((asArray (asJsonArray (nullable-get raw))) + (asObject (asJsonObject (nullable-get raw)))) + (if (nullable-null? asArray) + (if (nullable-null? asObject) + (begin) + (addManeuverObjectItems! out (nullable-get asObject))) + (addManeuverArrayItems! out (nullable-get asArray))))) + out))) + (def (startingProjectedHelmPose + (position : JSONObject)) : ProjectedHelmPose + (make-ProjectedHelmPose + (json-object-opt-int32-default position "q" (int32 0)) + (json-object-opt-int32-default position "r" (int32 0)) + (normalizedHelmFacing + (json-object-opt-int32-default position "facing" (int32 0))) + (clampImpulse + (json-object-opt-int32-default position "speed" (int32 0))) + (json-object-opt-string-default position "turn-mode" "c") + (json-object-opt-int32-default + position + "distance-since-turn" + (int32 0)))) + (def (projectedHelmMoves + (position : (Nullable JSONObject)) + (maneuvers : (Map Int32 String)) + (currentImpulse : Int32) + (maxMoves : Int32)) : (MutableList ProjectedHelmMove) + (let ((out (mutable-list-empty ProjectedHelmMove))) + (if (nullable-null? position) + out + (let ((movementImpulses + (movementImpulsesForSpeed + (json-object-opt-int32-default + (nullable-get position) + "speed" + (int32 0))))) + (var ((pose (startingProjectedHelmPose (nullable-get position)))) + (begin + (for/fold ((ignored (int32 0))) + ((index (in-range (int32 0) + (list-size movementImpulses)))) + (let ((impulse (list-ref movementImpulses index))) + (if (and (> impulse currentImpulse) + (< (list-size out) + (atLeastOne maxMoves))) + (let ((maneuver (map-ref-or-null maneuvers impulse))) + (begin + (set! pose + (applyProjectedHelmMove pose maneuver)) + (mutable-list-add! + out + (make-ProjectedHelmMove + impulse + maneuver + pose)) + ignored)) + ignored))) + out)))))) + (def (projectedHelmPoseAt + (position : (Nullable JSONObject)) + (projected : (List ProjectedHelmMove)) + (impulse : Int32)) : (Nullable ProjectedHelmPose) + (let ((found + (for/fold ((candidate (nullable-none ProjectedHelmPose))) + ((index (in-range (int32 0) (list-size projected)))) + (let ((move (list-ref projected index))) + (if (<= (ProjectedHelmMove-impulse move) impulse) + (nullable-some (ProjectedHelmMove-pose move)) + candidate))))) + (if (nullable-null? found) + (if (nullable-null? position) + (nullable-none ProjectedHelmPose) + (nullable-some + (startingProjectedHelmPose (nullable-get position)))) + found))) + (def (hasProjectedImpulse? + (projected : (List ProjectedHelmMove)) + (impulse : Int32)) : Bool + (for/fold ((found #f)) + ((index (in-range (int32 0) (list-size projected)))) + (or found + (= (ProjectedHelmMove-impulse + (list-ref projected index)) + impulse)))) + (def (projectedRangeForecast + (alphaPosition : (Nullable JSONObject)) + (betaPosition : (Nullable JSONObject)) + (alphaManeuversRaw : (Nullable Any)) + (betaManeuversRaw : (Nullable Any)) + (currentImpulse : Int32) + (maxMoves : Int32 (default (int32 8))) + (labelLimit : Int32 (default (int32 5)))) : (MutableList ProjectedRangePoint) + (let ((out (mutable-list-empty ProjectedRangePoint))) + (if (or (nullable-null? alphaPosition) + (nullable-null? betaPosition)) + out + (let ((alphaProjected + (projectedHelmMoves + alphaPosition + (maneuverMapFromJson alphaManeuversRaw) + currentImpulse + maxMoves)) + (betaProjected + (projectedHelmMoves + betaPosition + (maneuverMapFromJson betaManeuversRaw) + currentImpulse + maxMoves))) + (var ((previousRange (nullable-none Int32))) + (begin + (for/fold ((ignored (int32 0))) + ((impulse (in-range (int32 1) (int32 33)))) + (if (and (< (list-size out) (atLeastOne labelLimit)) + (or (hasProjectedImpulse? alphaProjected impulse) + (hasProjectedImpulse? betaProjected impulse))) + (let ((alphaPose + (projectedHelmPoseAt + alphaPosition + alphaProjected + impulse)) + (betaPose + (projectedHelmPoseAt + betaPosition + betaProjected + impulse))) + (if (or (nullable-null? alphaPose) + (nullable-null? betaPose)) + ignored + (let ((range + (hexRange + (ProjectedHelmPose-q + (nullable-get alphaPose)) + (ProjectedHelmPose-r + (nullable-get alphaPose)) + (ProjectedHelmPose-q + (nullable-get betaPose)) + (ProjectedHelmPose-r + (nullable-get betaPose))))) + (let ((important + (or (<= range (int32 2)) + (or (nullable-null? previousRange) + (not + (= range + (nullable-get + previousRange))))))) + (begin + (set! previousRange + (nullable-some range)) + (if important + (mutable-list-add! + out + (make-ProjectedRangePoint + impulse + range)) + (begin)) + ignored))))) + ignored)) + out)))))) + (def (maneuverAbbreviation (maneuver : String)) : String + (if (equal? maneuver "left") + "L" + (if (equal? maneuver "right") + "R" + (if (equal? maneuver "sideslip-left") + "SL" + (if (equal? maneuver "sideslip-right") + "SR" + (if (equal? maneuver "het") + "HET" + (string-uppercase + (string-take maneuver (int32 3))))))))) + (def (applyProjectedHelmMove + (pose : ProjectedHelmPose) + (maneuver : (Nullable String))) : ProjectedHelmPose + (if (nullable-null? maneuver) + (moveForward pose) + (let ((name (nullable-get maneuver))) + (if (equal? name "left") + (if (canProjectedTurn? pose) + (moveForward + (make-ProjectedHelmPose + (ProjectedHelmPose-q pose) + (ProjectedHelmPose-r pose) + (normalizedHelmFacing + (- (ProjectedHelmPose-facing pose) (int32 1))) + (ProjectedHelmPose-speed pose) + (ProjectedHelmPose-turnMode pose) + (int32 0))) + pose) + (if (equal? name "right") + (if (canProjectedTurn? pose) + (moveForward + (make-ProjectedHelmPose + (ProjectedHelmPose-q pose) + (ProjectedHelmPose-r pose) + (normalizedHelmFacing + (+ (ProjectedHelmPose-facing pose) (int32 1))) + (ProjectedHelmPose-speed pose) + (ProjectedHelmPose-turnMode pose) + (int32 0))) + pose) + (if (equal? name "sideslip-left") + (moveInDirection + pose + (normalizedHelmFacing + (- (ProjectedHelmPose-facing pose) (int32 1)))) + (if (equal? name "sideslip-right") + (moveInDirection + pose + (normalizedHelmFacing + (+ (ProjectedHelmPose-facing pose) (int32 1)))) + (if (equal? name "het") + (moveForward + (make-ProjectedHelmPose + (ProjectedHelmPose-q pose) + (ProjectedHelmPose-r pose) + (normalizedHelmFacing + (+ (ProjectedHelmPose-facing pose) (int32 3))) + (ProjectedHelmPose-speed pose) + (ProjectedHelmPose-turnMode pose) + (int32 0))) + (moveForward pose))))))))) + (def (moveForward (pose : ProjectedHelmPose)) : ProjectedHelmPose + (moveInDirection pose (ProjectedHelmPose-facing pose))) + (def (directionDeltaQ (direction : Int32)) : Int32 + (let ((facing (normalizedHelmFacing direction))) + (if (or (= facing (int32 0)) (= facing (int32 1))) + (int32 1) + (if (or (= facing (int32 3)) (= facing (int32 4))) + (int32 -1) + (int32 0))))) + (def (directionDeltaR (direction : Int32)) : Int32 + (let ((facing (normalizedHelmFacing direction))) + (if (or (= facing (int32 1)) (= facing (int32 2))) + (int32 -1) + (if (or (= facing (int32 4)) (= facing (int32 5))) + (int32 1) + (int32 0))))) + (def (moveInDirection + (pose : ProjectedHelmPose) + (direction : Int32)) : ProjectedHelmPose + (make-ProjectedHelmPose + (+ (ProjectedHelmPose-q pose) (directionDeltaQ direction)) + (+ (ProjectedHelmPose-r pose) (directionDeltaR direction)) + (ProjectedHelmPose-facing pose) + (ProjectedHelmPose-speed pose) + (ProjectedHelmPose-turnMode pose) + (+ (ProjectedHelmPose-distanceSinceTurn pose) (int32 1)))) + (def (canProjectedTurn? (pose : ProjectedHelmPose)) : Bool + (>= (ProjectedHelmPose-distanceSinceTurn pose) + (turnModeRequiredDistance + (ProjectedHelmPose-turnMode pose) + (ProjectedHelmPose-speed pose)))) + (def (turnModeRequiredDistance + (turnMode : String) + (speed : Int32)) : Int32 + (let ((mode (string-lowercase turnMode))) + (if (equal? mode "seeking-weapon") + (if (betweenInt32? speed (int32 1) (int32 32)) + (int32 1) + (int32 0)) + (if (equal? mode "shuttle") + (turnModeShuttleDistance speed) + (if (equal? mode "aa") + (turnModeAaDistance speed) + (if (equal? mode "a") + (turnModeADistance speed) + (if (equal? mode "b") + (turnModeBDistance speed) + (if (equal? mode "d") + (turnModeDDistance speed) + (if (equal? mode "e") + (turnModeEDistance speed) + (if (equal? mode "f") + (turnModeFDistance speed) + (turnModeCDistance speed))))))))))) + (def (turnModeShuttleDistance (speed : Int32)) : Int32 + (if (betweenInt32? speed (int32 1) (int32 11)) + (int32 1) + (if (betweenInt32? speed (int32 12) (int32 23)) + (int32 2) + (if (>= speed (int32 24)) (int32 3) (int32 0))))) + (def (turnModeAaDistance (speed : Int32)) : Int32 + (if (betweenInt32? speed (int32 2) (int32 8)) + (int32 1) + (if (betweenInt32? speed (int32 9) (int32 16)) + (int32 2) + (if (betweenInt32? speed (int32 17) (int32 24)) + (int32 3) + (if (>= speed (int32 25)) (int32 4) (int32 0)))))) + (def (turnModeADistance (speed : Int32)) : Int32 + (if (betweenInt32? speed (int32 2) (int32 6)) + (int32 1) + (if (betweenInt32? speed (int32 7) (int32 12)) + (int32 2) + (if (betweenInt32? speed (int32 13) (int32 19)) + (int32 3) + (if (betweenInt32? speed (int32 20) (int32 26)) + (int32 4) + (if (>= speed (int32 27)) (int32 5) (int32 0))))))) + (def (turnModeBDistance (speed : Int32)) : Int32 + (if (betweenInt32? speed (int32 2) (int32 5)) + (int32 1) + (if (betweenInt32? speed (int32 6) (int32 10)) + (int32 2) + (if (betweenInt32? speed (int32 11) (int32 15)) + (int32 3) + (if (betweenInt32? speed (int32 16) (int32 21)) + (int32 4) + (if (betweenInt32? speed (int32 22) (int32 28)) + (int32 5) + (if (>= speed (int32 29)) (int32 6) (int32 0)))))))) + (def (turnModeCDistance (speed : Int32)) : Int32 + (if (betweenInt32? speed (int32 2) (int32 4)) + (int32 1) + (if (betweenInt32? speed (int32 5) (int32 9)) + (int32 2) + (if (betweenInt32? speed (int32 10) (int32 14)) + (int32 3) + (if (betweenInt32? speed (int32 15) (int32 20)) + (int32 4) + (if (betweenInt32? speed (int32 21) (int32 27)) + (int32 5) + (if (>= speed (int32 28)) (int32 6) (int32 0)))))))) + (def (turnModeDDistance (speed : Int32)) : Int32 + (if (betweenInt32? speed (int32 2) (int32 4)) + (int32 1) + (if (betweenInt32? speed (int32 5) (int32 8)) + (int32 2) + (if (betweenInt32? speed (int32 9) (int32 12)) + (int32 3) + (if (betweenInt32? speed (int32 13) (int32 17)) + (int32 4) + (if (betweenInt32? speed (int32 18) (int32 24)) + (int32 5) + (if (>= speed (int32 25)) (int32 6) (int32 0)))))))) + (def (turnModeEDistance (speed : Int32)) : Int32 + (if (betweenInt32? speed (int32 2) (int32 3)) + (int32 1) + (if (betweenInt32? speed (int32 4) (int32 6)) + (int32 2) + (if (betweenInt32? speed (int32 7) (int32 10)) + (int32 3) + (if (betweenInt32? speed (int32 11) (int32 14)) + (int32 4) + (if (betweenInt32? speed (int32 15) (int32 20)) + (int32 5) + (if (betweenInt32? speed (int32 21) (int32 29)) + (int32 6) + (if (>= speed (int32 30)) (int32 7) (int32 0))))))))) + (def (turnModeFDistance (speed : Int32)) : Int32 + (if (betweenInt32? speed (int32 2) (int32 3)) + (int32 1) + (if (betweenInt32? speed (int32 4) (int32 5)) + (int32 2) + (if (betweenInt32? speed (int32 6) (int32 9)) + (int32 3) + (if (betweenInt32? speed (int32 10) (int32 13)) + (int32 4) + (if (betweenInt32? speed (int32 14) (int32 17)) + (int32 5) + (if (betweenInt32? speed (int32 18) (int32 23)) + (int32 6) + (if (betweenInt32? speed (int32 24) (int32 29)) + (int32 7) + (if (>= speed (int32 30)) (int32 8) (int32 0)))))))))) + (def (normalizedHelmFacing (facing : Int32)) : Int32 + (mod (+ (mod facing (int32 6)) (int32 6)) (int32 6))))) (kotlin-file-lines "com/jerboa/originaltactics/MainActivity.kt" ("package com.jerboa.originaltactics" ""