squidgame / test_squid_game.py
sidnvy's picture
Upload folder using huggingface_hub
4a67ae9 verified
# test_squid_game.py
import pytest
from math import isclose
from functools import lru_cache
# Import from our main module
from squid_game_core import (
parse_tier_map,
tierValue,
is_terminal,
compute_final_payout,
get_expected_value,
next_squid_gain_for_nonzero,
hypothetical_next_round_gain
)
def nearly_equal(a, b, tol=1e-9):
return isclose(a, b, abs_tol=tol)
@pytest.fixture
def tier_map_example():
"""
We'll define a bracket:
1-4:1 => if you have 1..4 squids => total = k * 1
5-6:3 => if you have 5..6 squids => total = k * 3
0 => always 0
"""
# We'll store it as a tuple for use in DP
# parse_tier_map would do the same, but let's define it directly
return (
(0,0,0.0),
(1,4,1.0),
(5,6,3.0)
)
def test_multiple_losers_pay_individually(tier_map_example):
"""
Scenario: 3 players => final distribution=(0,0,4).
According to bracket (1-4:1 => 4=>4*1=4).
- 2 losers => each pays 4 => each has payoff=-4
- 1 winner => receives 2 * 4=8.
"""
dist = (0,0,4)
payoffs = compute_final_payout(dist, tier_map_example)
# payoffs => [ -4, -4, 8 ]
assert nearly_equal(payoffs[0], -4)
assert nearly_equal(payoffs[1], -4)
assert nearly_equal(payoffs[2], 8)
def test_single_loser(tier_map_example):
"""
Scenario: 2 players => final distribution=(1,0).
=> 1 zero => that zero pays sum_of_winners= tierValue(1)=1
=> payoff=(1, -1) because:
- Winner keeps their tierValue (1)
- Loser pays that amount (-1)
"""
dist = (1,0)
payoffs = compute_final_payout(dist, tier_map_example)
assert nearly_equal(payoffs[0], 1) # Winner gets tierValue(1)=1
assert nearly_equal(payoffs[1], -1) # Loser pays -1
def test_no_losers(tier_map_example):
"""
Scenario: 2 players => final distribution=(2,3).
=> no zero => no payment => payoff=(0,0).
"""
dist = (2,3)
payoffs = compute_final_payout(dist, tier_map_example)
# 2 => bracket(1..4 => *1)=>2
# 3 => bracket(1..4 => *1)=>3
# but since no zero => payoff=(0,0).
assert nearly_equal(payoffs[0], 0)
assert nearly_equal(payoffs[1], 0)
def test_next_squid_gain_for_nonzero(tier_map_example):
"""
distribution=(4,0,2) =>
- Player0=4 => tierValue(4)=4 => tierValue(5)=15 => gain=11
(since 5 squids => bracket => 5*3=15)
- Player1=0 => skip
- Player2=2 => tierValue(2)=2 => tierValue(3)=3 => gain=1
"""
dist = (4,0,2)
gains = next_squid_gain_for_nonzero(dist, tier_map_example)
assert 0 in gains
assert gains[0] == 11 # 15-4
assert 2 in gains
assert gains[2] == 1 # 3-2
assert 1 not in gains # because that player has 0
def test_ev_with_leftover_multiple_losers(tier_map_example):
"""
We'll test a DP scenario:
N=3, X=4 total squids
Current distribution=(0,1,1)
=> sum=2 => leftover=2 => not terminal.
Let's see possible final states:
- They could keep awarding 2 more squids in 2 rounds.
- It's possible we end with multiple zeros if the 2 additional squids both go to the same non-zero player,
or exactly one zero, etc.
We'll just check the computed EV matches a hand-run or at least we confirm no errors.
"""
dist = (0,1,1)
X = 4
r = X - sum(dist) # 4-2=2
# We'll do a quick partial analysis by enumerating the 2 leftover squids:
# Round 1 => three possibilities: P0, P1, P2
# But let's just rely on the solver to give us a final result,
# and we'll assert that we get a numeric 3-tuple and
# the sum of payoffs is near 0 (since it's a zero-sum game).
from squid_game import get_expected_value
get_expected_value.cache_clear()
ev = get_expected_value(dist, r, tier_map_example)
assert len(ev) == 3
# Because it's zero-sum, the sum of EVs should be very close to 0:
total_ev = sum(ev)
assert nearly_equal(total_ev, 0.0), f"Sum of EVs is not near 0, got {total_ev}"
# We won't do a full hand enumeration here,
# but we at least confirm the DP runs and yields a plausible sum=0 result.
def test_ev_multiple_losers_specific(tier_map_example):
"""
A more direct test for multiple losers via DP:
N=3, X=2, distribution=(0,0,2)
=> sum=2 => leftover=0 => terminal => multiple zero => each zero pays tierValue(2)=2 => payoff=(-2,-2,4)
Then we check that the DP logic returns the same final payoff if is_terminal is triggered.
"""
dist = (0,0,2)
X = 2
r = X - sum(dist) # leftover=0 => terminal immediately
from squid_game import get_expected_value
get_expected_value.cache_clear()
ev = get_expected_value(dist, r, tier_map_example)
# With bracket => 2 => 2*1=2 => each zero pays 2 => 2 losers => winner gets 2*2=4
# payoff=( -2, -2, 4 )
assert nearly_equal(ev[0], -2)
assert nearly_equal(ev[1], -2)
assert nearly_equal(ev[2], 4)
# sum = 0
assert nearly_equal(sum(ev), 0.0)
def test_hypothetical_next_round_gain_with_penalty():
"""
Test scenario: 3 players with distribution=(0,0,2)
Expected values would be (-2,-2,4) as shown in test_ev_multiple_losers_specific
So penalty should be 2 (abs of -2)
For zero-squid players (0,1):
- Getting 1 squid = tierValue(1) = 1
- Avoiding penalty share = 2/2 = 1 (penalty/zero_count)
- Total gain = 2
For player with 2 squids:
- Going from 2 to 3 = tierValue(3) - tierValue(2) = 3 - 2 = 1
"""
tier_map = (
(0,0,0.0),
(1,4,1.0),
(5,6,3.0)
)
dist = (0,0,2)
penalty = 2 # abs of -2 from expected values
gains = hypothetical_next_round_gain(dist, tier_map, penalty)
# Check zero-squid players
assert nearly_equal(gains[0], 2.0), f"Expected gain 2.0 for player 1, got {gains[0]}"
assert nearly_equal(gains[1], 2.0), f"Expected gain 2.0 for player 2, got {gains[1]}"
# Check non-zero player
assert nearly_equal(gains[2], 1.0), f"Expected gain 1.0 for player 3, got {gains[2]}"
def test_hypothetical_next_round_gain_no_zeros():
"""
Test scenario: 2 players with distribution=(1,2)
No zero-squid players, so penalty doesn't matter
Player 1: going from 1 to 2 = tierValue(2) - tierValue(1) = 2 - 1 = 1
Player 2: going from 2 to 3 = tierValue(3) - tierValue(2) = 3 - 2 = 1
"""
tier_map = (
(0,0,0.0),
(1,4,1.0),
(5,6,3.0)
)
dist = (1,2)
penalty = 0 # doesn't matter since no zero-squid players
gains = hypothetical_next_round_gain(dist, tier_map, penalty)
assert nearly_equal(gains[0], 1.0), f"Expected gain 1.0 for player 1, got {gains[0]}"
assert nearly_equal(gains[1], 1.0), f"Expected gain 1.0 for player 2, got {gains[1]}"