Android target, suspending agents, and observable table snapshots
Build: - :engine now uses com.android.kotlin.multiplatform.library (AGP 9.2.1), the modern KMP Android integration, rather than plain androidTarget(). Produces engine.aar alongside the JVM target; compileAndroidMain verified. - Version catalog added; SDK levels match the other JSJ apps (compileSdk 37, minSdk 26). Engine: - PlayerAgent.act() and Table.playHand() are now suspend, so a human player can wait for input without blocking a thread. Bots are unaffected; the simulator wraps in runBlocking. - TableSnapshot/SeatSnapshot published after the deal, before and after every action, and at the finish. Immutable, aliasing no live Seat state, giving animation, hand history, saving, and replay one boundary to work against. - Snapshots carry the whole truth; maskedFor(viewer) is an explicit step that hides hole cards the viewer is not entitled to. Showdown reveals contenders; folded hands never are. - Terminal snapshots report the contested pot rather than 0. settle() zeroes contributions when awarding, so the naive value was empty at exactly the moment the UI needs to show what was won. Caught by a new test. - HumanAgent suspends on a CompletableDeferred and clears its pending state in a finally block, so cancelling an abandoned hand releases the wait instead of stranding it. Re-usable afterwards; a stale submit returns false. Tests: 37 -> 45. Chips still conserved, skill gradient still monotonic. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,10 +1,11 @@
|
||||
plugins {
|
||||
kotlin("jvm")
|
||||
alias(libs.plugins.kotlin.jvm)
|
||||
application
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation(project(":engine"))
|
||||
implementation(libs.kotlinx.coroutines.core)
|
||||
}
|
||||
|
||||
application {
|
||||
|
||||
@@ -12,6 +12,7 @@ import com.jsjdesigns.poker.game.Seat
|
||||
import com.jsjdesigns.poker.game.Street
|
||||
import com.jsjdesigns.poker.game.Table
|
||||
import kotlin.math.abs
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import kotlin.random.Random
|
||||
|
||||
private const val SMALL_BLIND = 1
|
||||
@@ -33,7 +34,7 @@ private class Stats(val name: String, val profile: BotProfile) {
|
||||
if (postflopCalls == 0) postflopBets.toDouble() else postflopBets.toDouble() / postflopCalls
|
||||
}
|
||||
|
||||
private fun runTable(label: String, roster: List<BotProfile>, hands: Int, seed: Long): List<Stats> {
|
||||
private fun runTable(label: String, roster: List<BotProfile>, hands: Int, seed: Long): List<Stats> = runBlocking {
|
||||
// The deck gets its own RNG. If bots drew from the same stream, the number of
|
||||
// Monte Carlo rollouts a bot performs — which varies by skill level — would
|
||||
// shift every subsequent deal, so changing a profile would silently change the
|
||||
@@ -109,7 +110,7 @@ private fun runTable(label: String, roster: List<BotProfile>, hands: Int, seed:
|
||||
)
|
||||
}
|
||||
println("chip conservation: %d (must be 0)".format(stats.sumOf { it.net }))
|
||||
return stats
|
||||
stats
|
||||
}
|
||||
|
||||
/** Dumps the starting-hand ranking so the ordering can be eyeballed against a real chart. */
|
||||
|
||||
Reference in New Issue
Block a user