Spaces:
Sleeping
Sleeping
File size: 13,216 Bytes
2f2fdc8 950537f 81ed7f7 f98fb0d 950537f 89e2588 2f2fdc8 89e2588 c5cd6a7 89e2588 2f2fdc8 c5cd6a7 89e2588 2f2fdc8 c5cd6a7 89e2588 950537f c5cd6a7 89e2588 950537f 89e2588 2f2fdc8 89e2588 2f2fdc8 89e2588 950537f c5cd6a7 950537f 89e2588 950537f 2f2fdc8 5c590c8 950537f c5cd6a7 950537f c5cd6a7 950537f c5cd6a7 1b9c831 b7e1415 1b9c831 950537f 89e2588 950537f 89e2588 950537f 2f2fdc8 89e2588 2f2fdc8 c5cd6a7 f98fb0d 81ed7f7 f98fb0d c5cd6a7 f98fb0d 81ed7f7 b234c35 f98fb0d 81ed7f7 f98fb0d 81ed7f7 c5cd6a7 81ed7f7 f98fb0d 81ed7f7 5c590c8 b234c35 c5cd6a7 b234c35 c5cd6a7 81ed7f7 f98fb0d b234c35 c5cd6a7 b234c35 c5cd6a7 1b9c831 b7e1415 1b9c831 b234c35 81ed7f7 950537f 89e2588 434b789 34085e8 2f2fdc8 81ed7f7 89e2588 81ed7f7 950537f 81ed7f7 c5cd6a7 81ed7f7 c5cd6a7 81ed7f7 c5cd6a7 81ed7f7 c5cd6a7 81ed7f7 c5cd6a7 81ed7f7 c5cd6a7 81ed7f7 c5cd6a7 81ed7f7 2f2fdc8 81ed7f7 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 | import gradio as gr
from squid_game_core import (
parse_tier_map,
get_expected_value,
compute_ev_win_lose_two_extremes,
finite_squid_game_probabilities,
dp_ev,
infinite_squid_game_expected_counts_partial,
get_expected_value_finite,
compute_ev_win_lose_finite_tuple,
)
from typing import List, Tuple
def validate_distribution(dist_str: str) -> Tuple[bool, str, List[int]]:
"""Validate the distribution string and convert to list of integers"""
try:
dist = [int(x.strip()) for x in dist_str.split(',')]
if any(x < 0 for x in dist):
return False, "Distribution cannot contain negative numbers", []
if not dist:
return False, "Distribution cannot be empty.", []
return True, "", dist
except ValueError:
return False, "Distribution must be comma-separated integers (e.g., '0,1,2')", []
def validate_folded_players(folded_str: str, num_players: int) -> Tuple[bool, str, List[int]]:
"""Validate the folded players string."""
try:
folded = [int(x.strip()) for x in folded_str.split(',')]
if len(folded) != num_players:
return False, f"Folded players list must have {num_players} entries, but it has {len(folded)}.", []
if any(x not in [0, 1] for x in folded):
return False, "Folded players list can only contain 0s and 1s.", []
return True, "", folded
except ValueError:
return False, "Folded players must be comma-separated integers (e.g., '0,0,1,0')", []
def validate_tier_map(tier_str: str) -> Tuple[bool, str]:
"""Validate the tier map string format"""
try:
lines = tier_str.strip().splitlines()
for line in lines:
if ':' not in line:
return False, "Each line must contain a colon (e.g., '1-2:1.5')"
range_part, mult_part = line.split(':')
float(mult_part.strip()) # Check multiplier is a valid number
if '-' in range_part:
low_str, high_str = range_part.split('-')
int(low_str), int(high_str)
else:
int(range_part.strip())
return True, ""
except ValueError:
return False, "Invalid format. Example: '1:1.0\\n2-4:2.0\\n5-6:3.0'"
def solve_game(distribution: str, folded_players_str: str, total_squids: int, tier_map_str: str) -> str:
"""Main function to solve the game and return formatted results"""
# Validate distribution
valid_dist, error_msg, dist = validate_distribution(distribution)
if not valid_dist:
return error_msg
# Validate folded players
valid_folded, error_msg, folded = validate_folded_players(folded_players_str, len(dist))
if not valid_folded:
return error_msg
# Validate tier map
valid_tier, error_msg = validate_tier_map(tier_map_str)
if not valid_tier:
return error_msg
# Validate total squids
try:
X = int(total_squids)
if X < 0:
return "Total squids cannot be negative"
if X < sum(dist):
return "Total squids cannot be less than current distribution sum"
except ValueError:
return "Total squids must be an integer"
# Parse tier map and convert to tuple for caching
try:
tier_map = parse_tier_map(tier_map_str)
tier_map_tuple = tuple((a, b, c) for a, b, c in tier_map)
dist_tuple = tuple(dist)
folded_tuple = tuple(folded)
# Calculate remaining squids to distribute
remaining = X - sum(dist)
# Get unforced expected values (full random assignment)
get_expected_value.cache_clear()
unforced_ev = get_expected_value(dist_tuple, remaining, tier_map_tuple)
result = "Unforced Expected Values:\n"
for i, ev in enumerate(unforced_ev):
player_status = "(Folded)" if folded[i] == 1 else ""
result += f"Player {i+1}: {ev:.3f} {player_status}\n"
# Compute each player's forced win/lose EV extremes:
win_lose_results = compute_ev_win_lose_two_extremes(dist_tuple, remaining, tier_map_tuple, folded_tuple)
result += "\nForced Win/Lose Results (for non-folded players):\n"
for i,r in enumerate(win_lose_results):
if folded_tuple[r['player']] == 0:
result += (f"Player {r['player']+1}: forcedWinEV = {r['forcedWinEV']:.3f}, "
f"forcedLoseEV = {r['forcedLoseEV']:.3f}, Diff = {r['difference']:.3f}\n")
# Add a human-friendly interpretation of the tier map
result += "\nTier Map Interpretation:\n"
for low, high, mult in tier_map:
if low == high:
result += f"• {low} squid(s): multiplier = {mult:.1f}\n"
else:
result += f"• {low}-{high} squids: multiplier = {mult:.1f}\n"
return result
except Exception as e:
return f"Error occurred: {str(e)}"
def solve_finite_game(distribution: str, folded_players_str: str, total_squids: int) -> str:
"""Calculate the Expected Value (EV) for each player in the finite variant using tuple-based state."""
# Validate distribution
valid_dist, error_msg, dist = validate_distribution(distribution)
if not valid_dist:
return error_msg
# Validate folded players
valid_folded, error_msg, folded = validate_folded_players(folded_players_str, len(dist))
if not valid_folded:
return error_msg
# Validate finite variant rules
if any(x > 1 for x in dist):
return "Error: In the finite variant, players can only have 0 or 1 squid."
# Validate total squids
try:
X = int(total_squids)
current_squids = sum(dist)
if X < 0:
return "Total squids cannot be negative."
if X > len(dist):
return f"Error: Total squids ({X}) cannot exceed the number of players ({len(dist)})."
if X < current_squids:
return f"Total squids ({X}) cannot be less than current distribution sum ({current_squids})."
except ValueError:
return "Total squids must be an integer."
try:
get_expected_value_finite.cache_clear()
remaining = X - sum(dist)
dist_tuple = tuple(dist)
folded_tuple = tuple(folded)
from squid_game_core import _is_terminal_finite, _compute_final_payout_finite_tuple
if _is_terminal_finite(dist_tuple, remaining):
final_payoffs = _compute_final_payout_finite_tuple(dist_tuple)
result = "Finite Squid Game Final Payouts:\n(Game has ended or no squids left)\n\n"
for i, payoff in enumerate(final_payoffs):
player_status = "(Folded)" if folded[i] == 1 else ""
result += f"Player {i+1}: {payoff:.4f} {player_status}\n"
return result
base_ev, win_lose_results = compute_ev_win_lose_finite_tuple(dist_tuple, remaining, folded_tuple)
result = "Finite Squid Game Expected Value (EV):\n"
result += "(Payout Rule: Losers pay for all winners' squids)\n\n"
result += "Baseline EV (fully random):\n"
for i, ev in enumerate(base_ev):
player_status = "(Folded)" if folded[i] == 1 else ""
result += f"Player {i+1}: {ev:.4f} {player_status}\n"
if win_lose_results:
result += "\nForced Win/Lose EV (for players without a squid who haven't folded):\n"
for i,r in enumerate(win_lose_results):
if folded_tuple[r['player']] == 0:
result += (f"Player {r['player']+1}: forcedWinEV = {r['forcedWinEV']:.4f}, "
f"forcedLoseEV = {r['forcedLoseEV']:.4f}, Diff = {r['difference']:.4f}\n")
return result
except Exception as e:
return f"Error occurred: {str(e)}"
def solve_infinite_game(distribution: str) -> str:
"""Calculate the expected final squid count for each player in the infinite variant"""
# Validate distribution
valid_dist, error_msg, dist = validate_distribution(distribution)
if not valid_dist:
return error_msg
try:
# Calculate expected final counts
expected_counts = infinite_squid_game_expected_counts_partial(dist)
result = "Infinite Squid Game Expected Final Counts:\n"
result += "(Expected number of squids each player will have at the end)\n\n"
for i, count in enumerate(expected_counts):
if count == float('inf'):
result += f"Player {i+1}: ∞ (infinite)\n"
else:
result += f"Player {i+1}: {count:.4f}\n"
# Add explanation based on zero count
zero_count = sum(1 for x in dist if x == 0)
if zero_count == 1:
result += "\nExplanation: Game ends immediately as there is exactly one player with 0 squids."
elif zero_count == 0:
result += "\nExplanation: Game never ends (infinite loop) as there are no players with 0 squids."
else:
H_z = sum(1.0 / k for k in range(1, zero_count+1))
increment = (H_z - 1.0)
result += f"\nExplanation: Each player is expected to receive {increment:.4f} additional squids before the game ends."
return result
except Exception as e:
return f"Error occurred: {str(e)}"
# Default value for tier map used in interface
DEFAULT_TIER_MAP = """0-0:0
1-2:1
3-4:1
5-6:1
7-7:1
8-8:1
9-9:1
10-100:1"""
with gr.Blocks(title="Squid Game Calculator") as iface:
gr.Markdown("""
# Squid Game Expected Value Calculator
Calculate the expected payoff for each player in the Squid Game.
**Classic Variant Rules:**
1. Players take turns collecting squids randomly.
2. The game ends when either:
- Exactly one player has 0 squids, OR
- There are no squids left to distribute.
**Finite Variant Rules:**
1. Players take turns collecting squids randomly.
2. Once a player gets a squid, they can't get another one.
3. The game ends when all squids are distributed or only one player remains without a squid.
**Infinite Variant Rules:**
1. Players take turns collecting squids randomly (unlimited supply).
2. Players accumulate squids over time.
3. The game ends only when exactly one player has 0 squids.
""")
with gr.Row():
with gr.Column():
distribution_input = gr.Textbox(
label="Players' Current Squids",
placeholder="0,0",
value="0,0",
info="""Enter each player's current squids, separated by commas.
Example: '1,0,1,2,0' represents 5 players."""
)
folded_players_input = gr.Textbox(
label="Folded Players (0 = Playing, 1 = Folded)",
placeholder="0,0",
value="0,0",
info="""Enter 0 for playing, 1 for folded. Must match the number of players.
Example: '0,0,1,0,0' means Player 3 has folded and won't receive any more squids."""
)
total_squids_input = gr.Number(
label="Total Squids in Game (Classic & Finite Variants Only)",
value=9,
minimum=0,
step=1,
precision=0,
info="The total number of squids to be distributed (must be ≥ sum of current squids for classic variant)"
)
tier_map_input = gr.Textbox(
label="Squid Value Tiers (Classic Variant Only)",
placeholder=DEFAULT_TIER_MAP,
value=DEFAULT_TIER_MAP,
lines=8,
info="""Define the value tiers for squids.
Format: range:multiplier (one per line)"""
)
with gr.Column():
results_output = gr.Textbox(label="Results", lines=20)
with gr.Row():
classic_btn = gr.Button("Calculate Classic Variant", variant="primary")
finite_btn = gr.Button("Calculate Finite Variant", variant="stop")
infinite_btn = gr.Button("Calculate Infinite Variant", variant="secondary")
gr.Examples(
examples=[
["0,0", "0,0", 9, DEFAULT_TIER_MAP],
["1,0,1", "0,0,0", 12, DEFAULT_TIER_MAP],
["2,0,2,0", "0,1,0,0", 14, DEFAULT_TIER_MAP],
],
inputs=[distribution_input, folded_players_input, total_squids_input, tier_map_input],
)
classic_btn.click(
fn=solve_game,
inputs=[distribution_input, folded_players_input, total_squids_input, tier_map_input],
outputs=results_output
)
finite_btn.click(
fn=solve_finite_game,
inputs=[distribution_input, folded_players_input, total_squids_input],
outputs=results_output
)
infinite_btn.click(
fn=solve_infinite_game,
inputs=[distribution_input],
outputs=results_output
)
if __name__ == "__main__":
iface.launch() |