Upload folder using huggingface_hub
Browse files- squid_game.py +17 -4
- squid_game_core.py +10 -23
- test_squid_game.py +60 -4
squid_game.py
CHANGED
|
@@ -70,12 +70,25 @@ def solve_game(distribution: str, total_squids: int, tier_map_str: str) -> str:
|
|
| 70 |
for i, ev in enumerate(expected_values):
|
| 71 |
result += f"Player {i+1}: {ev:.3f}\n"
|
| 72 |
|
| 73 |
-
#
|
| 74 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 75 |
if gains:
|
| 76 |
result += "\nPotential Gains from Next Squid:\n"
|
| 77 |
-
for player_idx, gain in
|
| 78 |
-
result += f"Player {player_idx+1}: +{gain:.1f}
|
|
|
|
|
|
|
|
|
|
| 79 |
|
| 80 |
# Add tier map interpretation
|
| 81 |
result += "\nTier Map Interpretation:\n"
|
|
|
|
| 70 |
for i, ev in enumerate(expected_values):
|
| 71 |
result += f"Player {i+1}: {ev:.3f}\n"
|
| 72 |
|
| 73 |
+
# Calculate penalty from expected values of zero-squid players
|
| 74 |
+
zero_player_evs = [ev for i, ev in enumerate(expected_values) if dist[i] == 0]
|
| 75 |
+
if zero_player_evs:
|
| 76 |
+
# Verify all zero-squid players have same expected value
|
| 77 |
+
if not all(abs(ev - zero_player_evs[0]) < 1e-6 for ev in zero_player_evs):
|
| 78 |
+
return "Error: Zero-squid players have different expected values"
|
| 79 |
+
penalty = abs(zero_player_evs[0])
|
| 80 |
+
else:
|
| 81 |
+
penalty = 0
|
| 82 |
+
|
| 83 |
+
# Calculate potential gains using hypothetical_next_round_gain with actual penalty
|
| 84 |
+
gains = hypothetical_next_round_gain(dist, tier_map, penalty=penalty)
|
| 85 |
if gains:
|
| 86 |
result += "\nPotential Gains from Next Squid:\n"
|
| 87 |
+
for player_idx, gain in enumerate(gains):
|
| 88 |
+
result += f"Player {player_idx+1}: +{gain:.1f}"
|
| 89 |
+
if dist[player_idx] == 0:
|
| 90 |
+
result += " (includes avoiding payment share)"
|
| 91 |
+
result += "\n"
|
| 92 |
|
| 93 |
# Add tier map interpretation
|
| 94 |
result += "\nTier Map Interpretation:\n"
|
squid_game_core.py
CHANGED
|
@@ -55,7 +55,7 @@ def compute_final_payout(distribution, tier_map):
|
|
| 55 |
"""
|
| 56 |
distribution: e.g. (0,0,4)
|
| 57 |
|
| 58 |
-
|
| 59 |
- If exactly 1 zero-squid => that one pays sum_of_winners_tier
|
| 60 |
- If multiple zeros => each zero-squid pays sum_of_winners_tier individually
|
| 61 |
=> each winner gets (number_of_zero_squids * winner_tier_value).
|
|
@@ -73,22 +73,14 @@ def compute_final_payout(distribution, tier_map):
|
|
| 73 |
if m == 0:
|
| 74 |
# No zeros => no payment => everyone gets 0
|
| 75 |
return payoffs
|
| 76 |
-
elif m == 1:
|
| 77 |
-
# Exactly one zero-squid => that player pays the entire sum
|
| 78 |
-
z = zero_indices[0]
|
| 79 |
-
payoffs[z] = -sum_winner_values
|
| 80 |
-
# each winner receives exactly their own tierValue
|
| 81 |
-
# so we set payoffs[winner] = winner_values[i]
|
| 82 |
-
for i, w in enumerate(winner_indices):
|
| 83 |
-
payoffs[w] = winner_values[i]
|
| 84 |
-
return payoffs
|
| 85 |
else:
|
| 86 |
-
#
|
| 87 |
-
# => each winner receives m * (their own tierValue)
|
| 88 |
for z in zero_indices:
|
| 89 |
payoffs[z] = -sum_winner_values
|
|
|
|
|
|
|
| 90 |
for i, w in enumerate(winner_indices):
|
| 91 |
-
payoffs[w] = m * winner_values[i]
|
| 92 |
return payoffs
|
| 93 |
|
| 94 |
def format_state(distribution, remaining):
|
|
@@ -124,21 +116,16 @@ def next_squid_gain_for_nonzero(distribution, tier_map):
|
|
| 124 |
"""
|
| 125 |
Returns a dict: {player_index: gain in tierValue if that player goes from s_i to s_i+1}.
|
| 126 |
Only for players who currently hold > 0 squids.
|
| 127 |
-
|
| 128 |
-
Example:
|
| 129 |
-
if distribution=(4,0,2) with "1-4:1,5-6:3":
|
| 130 |
-
- Player0 has 4 => tierValue(4)=4 => tierValue(5)=15 => gain=11
|
| 131 |
-
- Player1 has 0 => skip
|
| 132 |
-
- Player2 has 2 => tierValue(2)=2*1=2 => tierValue(3)=3 => gain=1
|
| 133 |
"""
|
| 134 |
results = {}
|
| 135 |
for i, s in enumerate(distribution):
|
| 136 |
-
|
| 137 |
-
|
| 138 |
-
|
|
|
|
| 139 |
return results
|
| 140 |
|
| 141 |
-
def hypothetical_next_round_gain(distribution, tier_map, penalty
|
| 142 |
"""
|
| 143 |
Returns a list (or dict) of length N, indicating how much "extra" reward
|
| 144 |
each player would get if they, individually, are the *sole* winner next round.
|
|
|
|
| 55 |
"""
|
| 56 |
distribution: e.g. (0,0,4)
|
| 57 |
|
| 58 |
+
RULES:
|
| 59 |
- If exactly 1 zero-squid => that one pays sum_of_winners_tier
|
| 60 |
- If multiple zeros => each zero-squid pays sum_of_winners_tier individually
|
| 61 |
=> each winner gets (number_of_zero_squids * winner_tier_value).
|
|
|
|
| 73 |
if m == 0:
|
| 74 |
# No zeros => no payment => everyone gets 0
|
| 75 |
return payoffs
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 76 |
else:
|
| 77 |
+
# Each zero pays sum_winner_values
|
|
|
|
| 78 |
for z in zero_indices:
|
| 79 |
payoffs[z] = -sum_winner_values
|
| 80 |
+
# Each winner gets their tierValue (if single loser)
|
| 81 |
+
# or m * their tierValue (if multiple losers)
|
| 82 |
for i, w in enumerate(winner_indices):
|
| 83 |
+
payoffs[w] = winner_values[i] if m == 1 else m * winner_values[i]
|
| 84 |
return payoffs
|
| 85 |
|
| 86 |
def format_state(distribution, remaining):
|
|
|
|
| 116 |
"""
|
| 117 |
Returns a dict: {player_index: gain in tierValue if that player goes from s_i to s_i+1}.
|
| 118 |
Only for players who currently hold > 0 squids.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 119 |
"""
|
| 120 |
results = {}
|
| 121 |
for i, s in enumerate(distribution):
|
| 122 |
+
if s > 0: # Only include non-zero players
|
| 123 |
+
curr_val = tierValue(s, tier_map)
|
| 124 |
+
next_val = tierValue(s+1, tier_map)
|
| 125 |
+
results[i] = next_val - curr_val
|
| 126 |
return results
|
| 127 |
|
| 128 |
+
def hypothetical_next_round_gain(distribution, tier_map, penalty):
|
| 129 |
"""
|
| 130 |
Returns a list (or dict) of length N, indicating how much "extra" reward
|
| 131 |
each player would get if they, individually, are the *sole* winner next round.
|
test_squid_game.py
CHANGED
|
@@ -11,7 +11,8 @@ from squid_game_core import (
|
|
| 11 |
is_terminal,
|
| 12 |
compute_final_payout,
|
| 13 |
get_expected_value,
|
| 14 |
-
next_squid_gain_for_nonzero
|
|
|
|
| 15 |
)
|
| 16 |
|
| 17 |
def nearly_equal(a, b, tol=1e-9):
|
|
@@ -50,12 +51,15 @@ def test_multiple_losers_pay_individually(tier_map_example):
|
|
| 50 |
def test_single_loser(tier_map_example):
|
| 51 |
"""
|
| 52 |
Scenario: 2 players => final distribution=(1,0).
|
| 53 |
-
=> 1 zero => that zero pays sum_of_winners= tierValue(1)=1
|
|
|
|
|
|
|
|
|
|
| 54 |
"""
|
| 55 |
dist = (1,0)
|
| 56 |
payoffs = compute_final_payout(dist, tier_map_example)
|
| 57 |
-
assert nearly_equal(payoffs[0],
|
| 58 |
-
assert nearly_equal(payoffs[1], -1)
|
| 59 |
|
| 60 |
def test_no_losers(tier_map_example):
|
| 61 |
"""
|
|
@@ -142,3 +146,55 @@ def test_ev_multiple_losers_specific(tier_map_example):
|
|
| 142 |
assert nearly_equal(ev[2], 4)
|
| 143 |
# sum = 0
|
| 144 |
assert nearly_equal(sum(ev), 0.0)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 11 |
is_terminal,
|
| 12 |
compute_final_payout,
|
| 13 |
get_expected_value,
|
| 14 |
+
next_squid_gain_for_nonzero,
|
| 15 |
+
hypothetical_next_round_gain
|
| 16 |
)
|
| 17 |
|
| 18 |
def nearly_equal(a, b, tol=1e-9):
|
|
|
|
| 51 |
def test_single_loser(tier_map_example):
|
| 52 |
"""
|
| 53 |
Scenario: 2 players => final distribution=(1,0).
|
| 54 |
+
=> 1 zero => that zero pays sum_of_winners= tierValue(1)=1
|
| 55 |
+
=> payoff=(1, -1) because:
|
| 56 |
+
- Winner keeps their tierValue (1)
|
| 57 |
+
- Loser pays that amount (-1)
|
| 58 |
"""
|
| 59 |
dist = (1,0)
|
| 60 |
payoffs = compute_final_payout(dist, tier_map_example)
|
| 61 |
+
assert nearly_equal(payoffs[0], 1) # Winner gets tierValue(1)=1
|
| 62 |
+
assert nearly_equal(payoffs[1], -1) # Loser pays -1
|
| 63 |
|
| 64 |
def test_no_losers(tier_map_example):
|
| 65 |
"""
|
|
|
|
| 146 |
assert nearly_equal(ev[2], 4)
|
| 147 |
# sum = 0
|
| 148 |
assert nearly_equal(sum(ev), 0.0)
|
| 149 |
+
|
| 150 |
+
def test_hypothetical_next_round_gain_with_penalty():
|
| 151 |
+
"""
|
| 152 |
+
Test scenario: 3 players with distribution=(0,0,2)
|
| 153 |
+
Expected values would be (-2,-2,4) as shown in test_ev_multiple_losers_specific
|
| 154 |
+
So penalty should be 2 (abs of -2)
|
| 155 |
+
|
| 156 |
+
For zero-squid players (0,1):
|
| 157 |
+
- Getting 1 squid = tierValue(1) = 1
|
| 158 |
+
- Avoiding penalty share = 2/2 = 1 (penalty/zero_count)
|
| 159 |
+
- Total gain = 2
|
| 160 |
+
|
| 161 |
+
For player with 2 squids:
|
| 162 |
+
- Going from 2 to 3 = tierValue(3) - tierValue(2) = 3 - 2 = 1
|
| 163 |
+
"""
|
| 164 |
+
tier_map = (
|
| 165 |
+
(0,0,0.0),
|
| 166 |
+
(1,4,1.0),
|
| 167 |
+
(5,6,3.0)
|
| 168 |
+
)
|
| 169 |
+
dist = (0,0,2)
|
| 170 |
+
penalty = 2 # abs of -2 from expected values
|
| 171 |
+
|
| 172 |
+
gains = hypothetical_next_round_gain(dist, tier_map, penalty)
|
| 173 |
+
|
| 174 |
+
# Check zero-squid players
|
| 175 |
+
assert nearly_equal(gains[0], 2.0), f"Expected gain 2.0 for player 1, got {gains[0]}"
|
| 176 |
+
assert nearly_equal(gains[1], 2.0), f"Expected gain 2.0 for player 2, got {gains[1]}"
|
| 177 |
+
|
| 178 |
+
# Check non-zero player
|
| 179 |
+
assert nearly_equal(gains[2], 1.0), f"Expected gain 1.0 for player 3, got {gains[2]}"
|
| 180 |
+
|
| 181 |
+
def test_hypothetical_next_round_gain_no_zeros():
|
| 182 |
+
"""
|
| 183 |
+
Test scenario: 2 players with distribution=(1,2)
|
| 184 |
+
No zero-squid players, so penalty doesn't matter
|
| 185 |
+
|
| 186 |
+
Player 1: going from 1 to 2 = tierValue(2) - tierValue(1) = 2 - 1 = 1
|
| 187 |
+
Player 2: going from 2 to 3 = tierValue(3) - tierValue(2) = 3 - 2 = 1
|
| 188 |
+
"""
|
| 189 |
+
tier_map = (
|
| 190 |
+
(0,0,0.0),
|
| 191 |
+
(1,4,1.0),
|
| 192 |
+
(5,6,3.0)
|
| 193 |
+
)
|
| 194 |
+
dist = (1,2)
|
| 195 |
+
penalty = 0 # doesn't matter since no zero-squid players
|
| 196 |
+
|
| 197 |
+
gains = hypothetical_next_round_gain(dist, tier_map, penalty)
|
| 198 |
+
|
| 199 |
+
assert nearly_equal(gains[0], 1.0), f"Expected gain 1.0 for player 1, got {gains[0]}"
|
| 200 |
+
assert nearly_equal(gains[1], 1.0), f"Expected gain 1.0 for player 2, got {gains[1]}"
|