Equalize co-op gift magnitudes to kill 'always leave' dominant strategy

Old values (open ±7, leave ±4) had matching EV but asymmetric variance;
risk-averse parties had a strict preference to always leave gifts, which
collapsed the dilemma. New values (all ±6) keep EV=0 for both strategies
AND equalize variance. Choice now depends entirely on inferring sender
intent.

Adds TestCoopGiftVarianceSymmetric — fails loud if anyone reintroduces
asymmetric magnitudes. EV test was already in place but only checked the
mean; missed this on first pass.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
prosolis
2026-04-26 19:12:22 -07:00
parent 1ca9e77fa0
commit a1ede53fa9
3 changed files with 40 additions and 15 deletions

View File

@@ -16,21 +16,21 @@ import (
// ── Gift Constants ──────────────────────────────────────────────────────────
//
// Modifiers chosen so the EV of "always open" vs "always leave" is symmetric
// at a 50/50 sender mix — the party has to read the context, not the math.
// Magnitudes equalized across all four outcomes so neither "always open" nor
// "always leave" is variance-dominant at a 50/50 sender mix. Asymmetric
// magnitudes (e.g., open=±7, leave=±4) gave leave the same EV with lower
// variance, making it a dominant strategy for risk-averse parties — which
// collapses the dilemma into "always leave."
//
// basket opened: +7 basket unopened: -4
// mimic opened: -7 mimic unopened: +4
// basket opened: +6 basket unopened: -6
// mimic opened: -6 mimic unopened: +6
//
// At 50/50 sender mix:
// always open EV = 0.5×(+7) + 0.5×(7) = 0
// always leave EV = 0.5×(4) + 0.5×(+4) = 0
// Both strategies: EV=0, σ=6. The choice becomes "read sender intent" only.
const (
coopGiftBasketOpened = 7
coopGiftBasketUnopened = -4
coopGiftMimicOpened = -7
coopGiftMimicUnopened = 4
coopGiftBasketOpened = 6
coopGiftBasketUnopened = -6
coopGiftMimicOpened = -6
coopGiftMimicUnopened = 6
)
const (

View File

@@ -293,6 +293,31 @@ func TestCoopGiftEVSymmetric(t *testing.T) {
}
}
func TestCoopGiftVarianceSymmetric(t *testing.T) {
t.Parallel()
// Equal EV is not enough — if open and leave have different variance, a
// risk-averse party always picks the lower-variance side and the
// dilemma collapses. Magnitudes must be equal across all four outcomes.
abs := func(x int) int {
if x < 0 {
return -x
}
return x
}
mags := []int{
abs(coopGiftBasketOpened),
abs(coopGiftBasketUnopened),
abs(coopGiftMimicOpened),
abs(coopGiftMimicUnopened),
}
for i := 1; i < len(mags); i++ {
if mags[i] != mags[0] {
t.Errorf("gift magnitudes must be equal across all four outcomes; got %v", mags)
break
}
}
}
func TestCoopParimutuelNoWinners(t *testing.T) {
t.Parallel()
bets := []CoopBet{