Make bot traces and position adjustments honest
This commit is contained in:
@@ -17,11 +17,17 @@ import kotlin.random.Random
|
||||
* LLM. The model narrates these values; it never computes them.
|
||||
*/
|
||||
data class DecisionTrace(
|
||||
val equity: Double,
|
||||
val breakEvenEquity: Double,
|
||||
/** The final threshold actually used after every adjustment. */
|
||||
val decisionThreshold: Double,
|
||||
val potOdds: String,
|
||||
/** Monte Carlo equity when equity is actually estimated; null pre-flop. */
|
||||
val estimatedEquity: Double?,
|
||||
/** Pre-flop chart percentile, where 0 is strongest; null post-flop. */
|
||||
val handStrengthPercentile: Double?,
|
||||
/** Raw pot-odds threshold when it drives the decision; null pre-flop. */
|
||||
val breakEvenEquity: Double?,
|
||||
/** Final post-flop equity threshold after adjustments; null pre-flop. */
|
||||
val decisionThreshold: Double?,
|
||||
/** Top fraction of starting hands played in this spot; null post-flop. */
|
||||
val preflopRangeThreshold: Double?,
|
||||
val potOdds: String?,
|
||||
/** What the strategy selected before a skill error was applied. */
|
||||
val intended: Action,
|
||||
val chosen: Action,
|
||||
@@ -33,6 +39,26 @@ data class DecisionTrace(
|
||||
|
||||
data class ThresholdAdjustment(val name: String, val factor: Double)
|
||||
|
||||
/**
|
||||
* Balances an in-position multiplier against all out-of-position seats.
|
||||
*
|
||||
* If exactly one of N active players is in position, the weighted average factor
|
||||
* is 1.0 for any table size. Positive [inPositionDelta] widens a range in
|
||||
* position; negative values lower a calling threshold in position.
|
||||
*/
|
||||
internal fun balancedPositionFactor(
|
||||
activeOpponents: Int,
|
||||
inPosition: Boolean,
|
||||
awareness: Double,
|
||||
inPositionDelta: Double,
|
||||
): Double {
|
||||
val players = (activeOpponents + 1).coerceAtLeast(2)
|
||||
val inPositionShare = 1.0 / players
|
||||
val delta = inPositionDelta * awareness
|
||||
val outOfPositionDelta = -delta * inPositionShare / (1.0 - inPositionShare)
|
||||
return if (inPosition) 1.0 + delta else 1.0 + outOfPositionDelta
|
||||
}
|
||||
|
||||
/**
|
||||
* A bot that decides from equity and pot odds, then distorts that decision through
|
||||
* its [SkillLevel] and [PlayStyle].
|
||||
@@ -91,9 +117,14 @@ class MathBot(
|
||||
bar *= styleFactor
|
||||
adjustments += ThresholdAdjustment("style risk tolerance", styleFactor)
|
||||
|
||||
// Position is worth real equity, and better players know it.
|
||||
val positionFactor = if (ctx.inPosition) 1.0 - 0.12 * skill.positionAwareness
|
||||
else 1.0 + 0.10 * skill.positionAwareness
|
||||
// Position changes where calls are made, not how often overall. Derive
|
||||
// the out-of-position counterpart from the active table size.
|
||||
val positionFactor = balancedPositionFactor(
|
||||
activeOpponents = ctx.activeOpponents,
|
||||
inPosition = ctx.inPosition,
|
||||
awareness = skill.positionAwareness,
|
||||
inPositionDelta = -0.12,
|
||||
)
|
||||
bar *= positionFactor
|
||||
adjustments += ThresholdAdjustment("position", positionFactor)
|
||||
|
||||
@@ -125,9 +156,11 @@ class MathBot(
|
||||
val mistakeApplied = chosen != intended
|
||||
|
||||
lastTrace = DecisionTrace(
|
||||
equity = equity,
|
||||
estimatedEquity = equity,
|
||||
handStrengthPercentile = null,
|
||||
breakEvenEquity = breakEven,
|
||||
decisionThreshold = bar,
|
||||
preflopRangeThreshold = null,
|
||||
potOdds = if (ctx.toCall > 0) "${ctx.pot}:${ctx.toCall}" else "no bet to call",
|
||||
intended = intended,
|
||||
chosen = chosen,
|
||||
@@ -182,13 +215,13 @@ class MathBot(
|
||||
action
|
||||
}
|
||||
val mistakeApplied = final != action
|
||||
val equityScore = 1.0 - pct
|
||||
val threshold = 1.0 - gate
|
||||
lastTrace = DecisionTrace(
|
||||
equity = equityScore,
|
||||
breakEvenEquity = Equity.potOdds(ctx.pot, ctx.toCall),
|
||||
decisionThreshold = threshold,
|
||||
potOdds = if (ctx.toCall > 0) "${ctx.pot}:${ctx.toCall}" else "no bet to call",
|
||||
estimatedEquity = null,
|
||||
handStrengthPercentile = pct,
|
||||
breakEvenEquity = null,
|
||||
decisionThreshold = null,
|
||||
preflopRangeThreshold = gate,
|
||||
potOdds = null,
|
||||
intended = action,
|
||||
chosen = final,
|
||||
mistakeApplied = mistakeApplied,
|
||||
@@ -340,11 +373,12 @@ class MathBot(
|
||||
}
|
||||
|
||||
private fun preflopPositionFactor(ctx: DecisionContext, awareness: Double): Double {
|
||||
val players = (ctx.activeOpponents + 1).coerceAtLeast(2)
|
||||
val inPositionShare = 1.0 / players
|
||||
val widening = 0.45 * awareness
|
||||
val narrowing = widening * inPositionShare / (1.0 - inPositionShare)
|
||||
return if (ctx.inPosition) 1.0 + widening else 1.0 - narrowing
|
||||
return balancedPositionFactor(
|
||||
activeOpponents = ctx.activeOpponents,
|
||||
inPosition = ctx.inPosition,
|
||||
awareness = awareness,
|
||||
inPositionDelta = 0.45,
|
||||
)
|
||||
}
|
||||
|
||||
private companion object {
|
||||
|
||||
Reference in New Issue
Block a user