Add language-only opponent personas

This commit is contained in:
Jay
2026-07-26 20:42:36 -04:00
parent 843957d154
commit b8087a9872
14 changed files with 613 additions and 1 deletions
@@ -111,5 +111,6 @@ class CoachPresentationTest {
toAct = null,
toActToken = null,
lastAction = null,
actionNumber = 0,
)
}
@@ -105,6 +105,7 @@ class VisibleHandSummaryTest {
toAct = null,
toActToken = null,
lastAction = null,
actionNumber = 0,
)
@Test
@@ -33,6 +33,7 @@ class LiveOfferTest {
toAct = toAct,
toActToken = toActToken,
lastAction = null,
actionNumber = 0,
)
private fun offer(token: Long = 7L, handNumber: Int = 1, street: Street = Street.FLOP) =
@@ -0,0 +1,155 @@
package com.jsjdesigns.poker
import com.jsjdesigns.poker.bot.BotProfile
import com.jsjdesigns.poker.bot.PersonaCueId
import com.jsjdesigns.poker.bot.PlayStyle
import com.jsjdesigns.poker.bot.SkillLevel
import com.jsjdesigns.poker.bot.TableTalkLine
import com.jsjdesigns.poker.game.Action
import com.jsjdesigns.poker.game.ActionType
import com.jsjdesigns.poker.game.HandEvent
import com.jsjdesigns.poker.game.SeatSnapshot
import com.jsjdesigns.poker.game.Street
import com.jsjdesigns.poker.game.TableSnapshot
import org.junit.Assert.assertEquals
import org.junit.Assert.assertNull
import org.junit.Assert.assertTrue
import org.junit.Test
class PersonaTalkTest {
private val bruno = BotProfile(
"Bruno",
SkillLevel.ADVANCED,
PlayStyle.LOOSE_AGGRESSIVE,
"Relentless pressure.",
)
private fun snapshot(
handNumber: Int = 4,
street: Street = Street.FLOP,
actionNumber: Int = 3,
event: HandEvent = HandEvent(
Street.FLOP,
1,
"Bruno",
Action(ActionType.RAISE, 12),
),
board: List<Int> = listOf(2, 7, 9),
opponentHole: List<Int>? = listOf(40, 41),
phase: TableSnapshot.Phase = TableSnapshot.Phase.BETTING,
) = TableSnapshot(
handNumber = handNumber,
street = street,
phase = phase,
board = board,
pot = 25,
currentBet = 12,
minRaiseSize = 8,
button = 0,
seats = listOf(
seat(0, "You", hole = listOf(0, 1)),
seat(1, "Bruno", hole = opponentHole),
),
toAct = null,
toActToken = null,
lastAction = event,
actionNumber = actionNumber,
)
private fun seat(index: Int, name: String, hole: List<Int>?) = SeatSnapshot(
index = index,
name = name,
stack = 180,
committedThisRound = if (index == 1) 12 else 2,
committedThisHand = if (index == 1) 12 else 2,
folded = false,
allIn = false,
hole = hole,
revealed = false,
isButton = index == 0,
)
@Test
fun `narration cue is independent of every card in the rich snapshot`() {
val profiles = mapOf(1 to bruno)
val first = personaCueFor(snapshot(), profiles)
val differentCards = personaCueFor(
snapshot(
board = listOf(14, 22, 38),
opponentHole = listOf(10, 11),
),
profiles,
)
assertEquals(first, differentCards)
assertEquals(PersonaCueId(4, 3), first!!.id)
assertEquals(ActionType.RAISE, first.actionType)
assertEquals(12, first.amount)
assertEquals(25, first.pot)
}
@Test
fun `hero actions and stale street events never become persona cues`() {
val heroAction = HandEvent(
Street.FLOP,
HERO_SEAT,
"You",
Action(ActionType.CALL, 10),
)
val staleAction = HandEvent(
Street.PREFLOP,
1,
"Bruno",
Action(ActionType.RAISE, 6),
)
assertNull(personaCueFor(snapshot(event = heroAction), mapOf(1 to bruno)))
assertNull(personaCueFor(snapshot(event = staleAction), mapOf(1 to bruno)))
assertNull(
personaCueFor(
snapshot(phase = TableSnapshot.Phase.STREET_COMPLETE),
mapOf(1 to bruno),
),
)
}
@Test
fun `identical actions remain distinct through authoritative action number`() {
val profiles = mapOf(1 to bruno)
val first = personaCueFor(snapshot(actionNumber = 3), profiles)!!
val later = personaCueFor(snapshot(actionNumber = 7), profiles)!!
assertEquals(first.copy(id = PersonaCueId(4, 7)), later)
}
@Test
fun `table talk is visible only beside the exact action frame`() {
val line = TableTalkLine(
cueId = PersonaCueId(4, 3),
street = Street.FLOP,
speakerSeat = 1,
speakerName = "Bruno",
text = "Let's turn the dial.",
)
val matching = UiState(snapshot = snapshot(), tableTalk = line)
assertEquals(line, matching.visibleTableTalk())
assertNull(matching.copy(snapshot = snapshot(actionNumber = 4)).visibleTableTalk())
assertNull(matching.copy(snapshot = snapshot(handNumber = 5)).visibleTableTalk())
assertNull(
matching.copy(
snapshot = snapshot(
street = Street.TURN,
event = HandEvent(
Street.TURN,
1,
"Bruno",
Action(ActionType.RAISE, 12),
),
),
).visibleTableTalk(),
)
assertTrue(matching.acceptsTableTalk(line))
}
}
@@ -33,6 +33,7 @@ class TableStatusTest {
toAct = toAct,
toActToken = toAct?.toLong(),
lastAction = lastAction,
actionNumber = if (lastAction == null) 0 else 1,
)
private fun seat(index: Int, name: String) = SeatSnapshot(