Hormuz Crisis Simulator (2026)

A turn-based oil trading simulation set during the Iran War — playable in your terminal

python3 hormuz_sim.py

!/usr/bin/env python3

"""
HORMUZ CRISIS SIMULATOR
========================
A turn-based economic simulation of the Iran War oil disruption (2026).
You play as the CEO of an independent oil trading firm.
Navigate 30 days of crisis: buy/sell cargoes, short/long positions,
avoid insurance lapses, survive the chaos.

Strategy: survive with positive capital. Score = final capital / 1,000,000.
"""

import random
import sys
import time

─── WORLD STATE ────────────────────────────────────────────────────────────

class World:
def init(self):
self.day = 1
self.brent = 108.0 # $/barrel
self.hormuz_open = 0.10 # 10% capacity (war ongoing)
self.insurance_multiplier = 4.5 # × normal premium
self.global_reserve_days = 58 # SPR + commercial stocks
self.us_military_posture = 0.7 # 0=withdraw, 1=full offensive
self.ceasefire_prob_base = 0.03 # per day

    # News event pool
    self.events = [
        ("TANKER HIT", "Houthi missile strikes VLCC near Ras al-Khaimah. Hormuz throughput drops 3%.", 
         {"hormuz_open": -0.03, "brent": +8, "insurance_multiplier": +0.8}),
        ("CEASEFIRE RUMOR", "Qatar shuttle diplomacy: indirect talks reported. Markets rally.",
         {"brent": -12, "ceasefire_prob_base": +0.04}),
        ("SPR RELEASE", "US, EU, Japan release 60M barrels from strategic reserves.",
         {"brent": -7, "global_reserve_days": +5}),
        ("IRAN PIPELINE", "Iran opens Iraq-Turkey pipeline as partial bypass. Adds 0.8M bpd.",
         {"brent": -5, "hormuz_open": +0.04}),
        ("DRONE SWARM", "100+ drones target Saudi Aramco facilities at Ras Tanura.",
         {"brent": +15, "hormuz_open": -0.02}),
        ("US CARRIERS", "Two more carrier groups arrive. Iran threatens mine deployment.",
         {"brent": +6, "hormuz_open": -0.01, "us_military_posture": +0.1}),
        ("MINE FIELD", "IRGC deploys 200 mines in northern Gulf. Lloyds suspends coverage.",
         {"brent": +22, "hormuz_open": -0.08, "insurance_multiplier": +2.0}),
        ("INDIA DEAL", "India negotiates direct pipeline from Basra, bypassing Hormuz.",
         {"brent": -4}),
        ("CHINA MEDIATION", "China threatens to cut tech exports to Israel. De-escalation signal.",
         {"brent": -9, "ceasefire_prob_base": +0.02}),
        ("TANKER CLEARED", "US Navy escorts 3 VLCCs through Hormuz under fire. Markets cautious.",
         {"brent": -3, "hormuz_open": +0.02}),
        ("OPEC EMERGENCY", "OPEC+ emergency cut: further 2M bpd reduction to stabilize markets.",
         {"brent": +11}),
        ("CEASEFIRE TALKS", "Geneva talks begin. 72-hour humanitarian pause announced.",
         {"brent": -18, "ceasefire_prob_base": +0.06, "hormuz_open": +0.05}),
        ("NUCLEAR SIGNAL", "Israeli aircraft overfly Natanz. Iran places forces on highest alert.",
         {"brent": +35, "us_military_posture": +0.15}),
        ("FOOD CRISIS", "UN: 40 countries face acute fertilizer shortage. Political instability spreads.",
         {"brent": +4}),
        ("CURRENCY SHOCK", "Dollar surges. Emerging market currencies crater. Oil denominated re-anchoring begins.",
         {"brent": +3, "insurance_multiplier": +0.3}),
        ("TECH BLACKOUT", "Iran cyber-attack takes down 3 Gulf-state port management systems.",
         {"hormuz_open": -0.05, "brent": +9}),
        ("PARTIAL REOPENING", "IRGC: 'limited commercial passage' offer. US skeptical. Markets test it.",
         {"hormuz_open": +0.12, "brent": -14}),
        ("US ELECTION NOISE", "Congress threatens to cut military funding. Pentagon scrambles.",
         {"brent": +5, "us_military_posture": -0.1}),
        ("CALM DAY", "No major incidents reported. Traders cautious but markets steady.",
         {}),
        ("CALM DAY", "No major incidents. Oil futures volatile but directionless.",
         {}),
    ]

def roll_event(self):
    """Return an event dict or None."""
    # 60% chance of event per day
    if random.random() > 0.60:
        return None
    return random.choice(self.events)

def apply_event(self, event):
    name, desc, effects = event
    for k, v in effects.items():
        setattr(self, k, getattr(self, k) + v)
    # Clamp
    self.hormuz_open = max(0.01, min(1.0, self.hormuz_open))
    self.brent = max(30.0, min(300.0, self.brent))
    self.insurance_multiplier = max(1.0, self.insurance_multiplier)
    self.global_reserve_days = max(0, self.global_reserve_days)
    self.us_military_posture = max(0.0, min(1.0, self.us_military_posture))
    self.ceasefire_prob_base = max(0.0, min(0.5, self.ceasefire_prob_base))

def check_ceasefire(self):
    """Randomly end the war based on cumulative ceasefire probability."""
    if random.random() < self.ceasefire_prob_base:
        return True
    return False

def natural_drift(self):
    """Daily price drift based on fundamentals."""
    # Supply/demand gap widens reserves
    supply_gap = (1.0 - self.hormuz_open) * 18  # M bpd
    reserve_pressure = max(0, (45 - self.global_reserve_days) / 45)

    drift = random.gauss(0, 2.5)  # random walk
    drift += supply_gap * 0.4   # shortage drives price up
    drift += reserve_pressure * 5  # reserve panic
    drift -= (self.brent - 80) * 0.02  # mean reversion

    self.brent += drift
    self.brent = max(30, min(300, self.brent))

    # Reserves draw down daily
    self.global_reserve_days -= (supply_gap / 365)

─── PLAYER ─────────────────────────────────────────────────────────────────

class Player:
def init(self):
self.capital = 50_000_000 # $50M starting capital
self.cargoes = [] # list of (barrels, buy_price, cargo_day)
self.shorts = [] # list of (barrels, short_price, expiry_day)
self.longs = [] # list of (barrels, long_price, expiry_day)
self.insurance_held = True
self.risk_level = "MEDIUM"
self.days_without_insurance = 0

def net_worth(self, brent):
    nw = self.capital
    for barrels, buy_price, _ in self.cargoes:
        nw += barrels * (brent - buy_price)
    for barrels, short_price, _ in self.shorts:
        nw += barrels * (short_price - brent)
    for barrels, long_price, _ in self.longs:
        nw += barrels * (brent - long_price)
    return nw

def insure_cost(self, world):
    """Daily insurance cost on held cargoes."""
    if not self.cargoes:
        return 0
    total_barrels = sum(b for b,_,_ in self.cargoes)
    base = total_barrels * 0.15  # $0.15/barrel/day normal
    return base * world.insurance_multiplier

─── DISPLAY ────────────────────────────────────────────────────────────────

COLORS = {
"RED": "\033[91m",
"GREEN": "\033[92m",
"YELLOW": "\033[93m",
"CYAN": "\033[96m",
"BOLD": "\033[1m",
"DIM": "\033[2m",
"RESET": "\033[0m",
}

def color(text, *codes):
prefix = "".join(COLORS[c] for c in codes)
return f"{prefix}{text}{COLORS['RESET']}"

def header(world, player):
nw = player.net_worth(world.brent)
nw_str = f"${nw/1e6:.2f}M"
nw_color = "GREEN" if nw > 50e6 else ("YELLOW" if nw > 20e6 else "RED")

hormuz_pct = world.hormuz_open * 100
hormuz_bar = "█" * int(hormuz_pct / 5) + "░" * (20 - int(hormuz_pct / 5))

print("\n" + "─" * 60)
print(color(f"  HORMUZ CRISIS SIMULATOR  ─  Day {world.day}/30", "BOLD", "CYAN"))
print("─" * 60)
print(f"  Brent Crude:   {color(f'${world.brent:.2f}', 'YELLOW', 'BOLD')}/bbl")
print(f"  Hormuz Flow:   [{hormuz_bar}] {hormuz_pct:.0f}%")
print(f"  Reserves:      {world.global_reserve_days:.0f} days")
print(f"  War Posture:   {'🔴 Escalating' if world.us_military_posture > 0.6 else '🟡 Holding' if world.us_military_posture > 0.3 else '🟢 Backing off'}")
print(f"  Net Worth:     {color(nw_str, nw_color, 'BOLD')}")
print(f"  Cash:          ${player.capital/1e6:.2f}M")
cargo_b = sum(b for b,_,_ in player.cargoes)
if cargo_b: print(f"  Cargo:         {cargo_b:,} barrels")
if player.shorts: print(f"  Shorts:        {len(player.shorts)} position(s)")
if player.longs: print(f"  Longs:         {len(player.longs)} position(s)")
print("─" * 60)

def show_positions(player, world):
if player.cargoes:
print(color("\n PHYSICAL CARGOES:", "BOLD"))
for i, (b, bp, cd) in enumerate(player.cargoes):
pnl = b * (world.brent - bp)
pnl_s = color(f"+${pnl:,.0f}", "GREEN") if pnl >= 0 else color(f"-${abs(pnl):,.0f}", "RED")
print(f" [{i}] {b:,} bbl @ ${bp:.2f} PnL: {pnl_s} (day {cd})")
if player.shorts:
print(color("\n SHORT POSITIONS (profit if price falls):", "BOLD"))
for i, (b, sp, ed) in enumerate(player.shorts):
pnl = b * (sp - world.brent)
pnl_s = color(f"+${pnl:,.0f}", "GREEN") if pnl >= 0 else color(f"-${abs(pnl):,.0f}", "RED")
print(f" [{i}] {b:,} bbl short @ ${sp:.2f} PnL: {pnl_s} expires day {ed}")
if player.longs:
print(color("\n LONG POSITIONS (profit if price rises):", "BOLD"))
for i, (b, lp, ed) in enumerate(player.longs):
pnl = b * (world.brent - lp)
pnl_s = color(f"+${pnl:,.0f}", "GREEN") if pnl >= 0 else color(f"-${abs(pnl):,.0f}", "RED")
print(f" [{i}] {b:,} bbl long @ ${lp:.2f} PnL: {pnl_s} expires day {ed}")

─── ACTIONS ────────────────────────────────────────────────────────────────

def action_buy_cargo(player, world):
"""Buy physical oil cargo (you own actual barrels)."""
print(color("\n BUY PHYSICAL CARGO", "BOLD"))
print(f" Current Brent: ${world.brent:.2f}")
print(f" Available cash: ${player.capital/1e6:.2f}M")
print(" Cargo sizes: [1] 250,000 bbl [2] 500,000 bbl [3] 1,000,000 bbl")
sizes = {1: 250_000, 2: 500_000, 3: 1_000_000}
try:
ch = int(input(" Choose size (or 0 to cancel): "))
if ch == 0: return
barrels = sizes.get(ch, 0)
if not barrels: return

    # Insurance required to buy physical
    if not player.insurance_held:
        print(color("  ⚠ Cannot buy physical cargo without insurance!", "RED"))
        return

    cost = barrels * world.brent * 1.02  # 2% transaction cost
    if cost > player.capital:
        print(color(f"  ✗ Insufficient capital! Need ${cost/1e6:.2f}M", "RED"))
        return
    player.capital -= cost
    player.cargoes.append((barrels, world.brent, world.day))
    print(color(f"  ✓ Bought {barrels:,} barrels @ ${world.brent:.2f} for ${cost/1e6:.2f}M", "GREEN"))
except (ValueError, EOFError):
    return

def action_sell_cargo(player, world):
"""Sell a physical cargo at current price."""
if not player.cargoes:
print(color(" No cargoes to sell.", "YELLOW"))
return
show_positions(player, world)
try:
idx = int(input(" Select cargo to sell (index): "))
if 0 <= idx < len(player.cargoes):
barrels, buy_price, cd = player.cargoes.pop(idx)
proceeds = barrels * world.brent * 0.98 # 2% spread
player.capital += proceeds
pnl = proceeds - barrels * buy_price * 1.02
pnl_s = color(f"+${pnl/1e6:.3f}M", "GREEN") if pnl >= 0 else color(f"-${abs(pnl)/1e6:.3f}M", "RED")
print(color(f" ✓ Sold {barrels:,} bbl @ ${world.brent:.2f} PnL: {pnl_s}", "GREEN" if pnl >= 0 else "RED"))
except (ValueError, EOFError, IndexError):
return

def action_short(player, world):
"""Open a short futures position (profit if price falls)."""
print(color("\n OPEN SHORT POSITION", "BOLD"))
print(f" Current Brent: ${world.brent:.2f}")
print(f" Margin required: 15% of position value")
print(" Position sizes: [1] 100,000 bbl [2] 500,000 bbl [3] 1,000,000 bbl")
sizes = {1: 100_000, 2: 500_000, 3: 1_000_000}
try:
ch = int(input(" Choose size (or 0 to cancel): "))
if ch == 0: return
barrels = sizes.get(ch, 0)
if not barrels: return
margin = barrels * world.brent * 0.15
if margin > player.capital:
print(color(f" ✗ Insufficient margin! Need ${margin/1e6:.2f}M", "RED"))
return
expiry = world.day + random.randint(5, 10)
player.capital -= margin
player.shorts.append((barrels, world.brent, expiry))
print(color(f" ✓ Short {barrels:,} bbl @ ${world.brent:.2f} Margin: ${margin/1e6:.2f}M Expires day {expiry}", "GREEN"))
except (ValueError, EOFError):
return

def action_long(player, world):
"""Open a long futures position (profit if price rises)."""
print(color("\n OPEN LONG POSITION", "BOLD"))
print(f" Current Brent: ${world.brent:.2f}")
print(f" Margin required: 15% of position value")
print(" Position sizes: [1] 100,000 bbl [2] 500,000 bbl [3] 1,000,000 bbl")
sizes = {1: 100_000, 2: 500_000, 3: 1_000_000}
try:
ch = int(input(" Choose size (or 0 to cancel): "))
if ch == 0: return
barrels = sizes.get(ch, 0)
if not barrels: return
margin = barrels * world.brent * 0.15
if margin > player.capital:
print(color(f" ✗ Insufficient margin! Need ${margin/1e6:.2f}M", "RED"))
return
expiry = world.day + random.randint(5, 10)
player.capital -= margin
player.longs.append((barrels, world.brent, expiry))
print(color(f" ✓ Long {barrels:,} bbl @ ${world.brent:.2f} Margin: ${margin/1e6:.2f}M Expires day {expiry}", "GREEN"))
except (ValueError, EOFError):
return

def action_close_position(player, world):
"""Close a short or long futures position."""
all_positions = []
print(color("\n CLOSE POSITION", "BOLD"))
for i, (b, sp, ed) in enumerate(player.shorts):
pnl = b * (sp - world.brent)
pnl_s = color(f"+${pnl:,.0f}", "GREEN") if pnl >= 0 else color(f"-${abs(pnl):,.0f}", "RED")
print(f" [S{i}] SHORT {b:,} bbl @ {sp:.2f} PnL: {pnl_s}")
all_positions.append(('S', i))
for i, (b, lp, ed) in enumerate(player.longs):
pnl = b * (world.brent - lp)
pnl_s = color(f"+${pnl:,.0f}", "GREEN") if pnl >= 0 else color(f"-${abs(pnl):,.0f}", "RED")
print(f" [L{i}] LONG {b:,} bbl @ {lp:.2f} PnL: {pnl_s}")
all_positions.append(('L', i))
if not all_positions:
print(color(" No open positions.", "YELLOW"))
return
try:
raw = input(" Enter position to close (e.g. S0 or L1, or 0 to cancel): ").strip().upper()
if raw == '0': return
kind = raw[0]
idx = int(raw[1:])
if kind == 'S' and 0 <= idx < len(player.shorts):
barrels, short_price, _ = player.shorts.pop(idx)
pnl = barrels * (short_price - world.brent)
margin_returned = barrels * short_price * 0.15
player.capital += margin_returned + pnl
pnl_s = color(f"+${pnl/1e6:.3f}M", "GREEN") if pnl >= 0 else color(f"-${abs(pnl)/1e6:.3f}M", "RED")
print(color(f" ✓ Short closed PnL: {pnl_s}", "GREEN" if pnl >= 0 else "RED"))
elif kind == 'L' and 0 <= idx < len(player.longs):
barrels, long_price, _ = player.longs.pop(idx)
pnl = barrels * (world.brent - long_price)
margin_returned = barrels * long_price * 0.15
player.capital += margin_returned + pnl
pnl_s = color(f"+${pnl/1e6:.3f}M", "GREEN") if pnl >= 0 else color(f"-${abs(pnl)/1e6:.3f}M", "RED")
print(color(f" ✓ Long closed PnL: {pnl_s}", "GREEN" if pnl >= 0 else "RED"))
else:
print(color(" Invalid selection.", "RED"))
except (ValueError, EOFError, IndexError):
return

def action_toggle_insurance(player, world):
"""Toggle war-risk insurance (expensive but required for cargo)."""
if player.insurance_held:
print(color("\n ⚠ CANCEL INSURANCE?", "YELLOW"))
daily = player.insure_cost(world)
print(f" Current daily cost: ${daily:,.0f}")
print(" Warning: Without insurance, any tanker hit = cargo lost!")
try:
confirm = input(" Confirm cancel? (yes/no): ").strip().lower()
if confirm == 'yes':
player.insurance_held = False
print(color(" ✓ Insurance cancelled. Sailing dark.", "YELLOW"))
except EOFError:
return
else:
cost = 500_000 # flat reinstatement fee
print(color(f"\n REINSTATE INSURANCE — flat fee ${cost:,}", "BOLD"))
if player.capital < cost:
print(color(" ✗ Insufficient capital!", "RED"))
return
try:
confirm = input(" Confirm? (yes/no): ").strip().lower()
if confirm == 'yes':
player.capital -= cost
player.insurance_held = True
player.days_without_insurance = 0
print(color(" ✓ War-risk insurance reinstated.", "GREEN"))
except EOFError:
return

def action_intel(world):
"""Pay for intelligence briefing on ceasefire probability."""
print(color("\n INTELLIGENCE BRIEFING", "BOLD"))
print(f" Hormuz throughput: {world.hormuz_open*100:.1f}% of normal")
print(f" Global reserves: {world.global_reserve_days:.0f} days (critical < 30)")
print(f" Insurance market: {world.insurance_multiplier:.1f}× normal premium")
print(f" US posture: {world.us_military_posture:.0%} (escalation index)")

ceasefire_pct = world.ceasefire_prob_base * 100
if ceasefire_pct < 5:
    outlook = color("VERY LOW — no talks, full war", "RED")
elif ceasefire_pct < 10:
    outlook = color("LOW — some back-channels", "YELLOW")
elif ceasefire_pct < 20:
    outlook = color("MODERATE — active diplomacy", "YELLOW")
else:
    outlook = color("HIGH — ceasefire likely soon", "GREEN")

print(f"  Ceasefire odds (daily): {ceasefire_pct:.1f}% — {outlook}")

reserve_warn = ""
if world.global_reserve_days < 30:
    reserve_warn = color("  ⚠ CRITICAL: Reserves below 30 days — panic likely!", "RED", "BOLD")
    print(reserve_warn)
if world.brent > 150:
    print(color("  ⚠ DEMAND DESTRUCTION ZONE: Recession signals emerging", "YELLOW"))

─── TURN LOOP ───────────────────────────────────────────────────────────────

def process_day_end(player, world):
"""Handle end-of-day mechanics: insurance costs, position expiries, tanker risk."""
messages = []

# Insurance cost
if player.cargoes:
    if player.insurance_held:
        ins_cost = player.insure_cost(world)
        player.capital -= ins_cost
        messages.append(f"  💰 Insurance paid: ${ins_cost:,.0f}")
    else:
        player.days_without_insurance += 1
        # Tanker loss risk
        loss_prob = (1 - world.hormuz_open) * 0.08  # 8% max
        if random.random() < loss_prob:
            if player.cargoes:
                lost = player.cargoes.pop(0)
                loss_val = lost[0] * world.brent
                messages.append(color(f"   TANKER DESTROYED! Lost {lost[0]:,} bbl cargo worth ${loss_val/1e6:.2f}M!", "RED", "BOLD"))

# Futures expiry
expired_shorts = [p for p in player.shorts if world.day >= p[2]]
for barrels, short_price, _ in expired_shorts:
    pnl = barrels * (short_price - world.brent)
    margin_returned = barrels * short_price * 0.15
    player.capital += margin_returned + pnl
    pnl_s = f"+${pnl/1e6:.3f}M" if pnl >= 0 else f"-${abs(pnl)/1e6:.3f}M"
    messages.append(color(f"  📋 Short expired: PnL {pnl_s}", "GREEN" if pnl >= 0 else "RED"))
player.shorts = [p for p in player.shorts if world.day < p[2]]

expired_longs = [p for p in player.longs if world.day >= p[2]]
for barrels, long_price, _ in expired_longs:
    pnl = barrels * (world.brent - long_price)
    margin_returned = barrels * long_price * 0.15
    player.capital += margin_returned + pnl
    pnl_s = f"+${pnl/1e6:.3f}M" if pnl >= 0 else f"-${abs(pnl)/1e6:.3f}M"
    messages.append(color(f"  📋 Long expired: PnL {pnl_s}", "GREEN" if pnl >= 0 else "RED"))
player.longs = [p for p in player.longs if world.day < p[2]]

# Margin call check
nw = player.net_worth(world.brent)
if nw < 0:
    messages.append(color("  💀 MARGIN CALL  BANKRUPT", "RED", "BOLD"))

return messages

def play():
world = World()
player = Player()

print(color("""

╔══════════════════════════════════════════════════════════╗
║ HORMUZ CRISIS SIMULATOR · 2026 ║
║ Iran War — Day 10. Hormuz at 10% capacity. ║
║ You have $50M. Survive 30 days. Grow it. ║
╚══════════════════════════════════════════════════════════╝""", "BOLD", "CYAN"))
print("""
You are CEO of Void Trading Ltd, an independent energy firm.
Navigate the most volatile oil market since 1973.

KEY MECHANICS:
• Physical cargoes: own real barrels, move with price
• Futures shorts: profit when price falls (war ends)
• Futures longs: profit when price rises (war escalates)
• Insurance: required for cargoes; ~$0.15/bbl/day × crisis multiplier
• Random world events change everything every day
• Ceasefire = price collapses instantly

Press ENTER to begin...
""")
try:
input()
except EOFError:
pass

game_over = False
ceasefire = False

while world.day <= 30 and not game_over:
    # ── Daily event ──
    event = world.roll_event()
    world.natural_drift()
    if event:
        name, desc, _ = event
        world.apply_event(event)

    # ── Display ──
    header(world, player)

    if event:
        print(f"\n  📰 {color(name, 'YELLOW', 'BOLD')}")
        print(f"  {desc}")
        print(f"  → Brent now ${world.brent:.2f}")
    else:
        print(f"\n  📰 No major events today. Markets drift.")
        print(f"  → Brent now ${world.brent:.2f}")

    # ── Ceasefire check ──
    if world.check_ceasefire() and world.day > 5:
        ceasefire = True
        crash = -world.brent * 0.35  # 35% crash on ceasefire
        world.brent += crash
        world.brent = max(60, world.brent)
        world.hormuz_open = min(1.0, world.hormuz_open + 0.6)
        print(color(f"\n  🕊 CEASEFIRE DECLARED! War ends on day {world.day}!", "GREEN", "BOLD"))
        print(f"  Brent crashes to ${world.brent:.2f}. Hormuz reopening.")
        print("  Settling all positions...")

        # Force-settle everything
        for barrels, bp, _ in player.cargoes:
            proceeds = barrels * world.brent * 0.98
            player.capital += proceeds
        for barrels, sp, _ in player.shorts:
            pnl = barrels * (sp - world.brent)
            player.capital += barrels * sp * 0.15 + pnl
        for barrels, lp, _ in player.longs:
            pnl = barrels * (world.brent - lp)
            player.capital += barrels * lp * 0.15 + pnl
        player.cargoes = []
        player.shorts = []
        player.longs = []
        break

    # ── Player actions ──
    print("\n  ACTIONS:")
    print("  [1] Buy physical cargo  [2] Sell cargo")
    print("  [3] Open short (bet on fall)  [4] Open long (bet on rise)")
    print("  [5] Close position  [6] Toggle insurance")
    print("  [7] Intel briefing (free)  [8] View positions  [P] Pass")

    actions_taken = 0
    max_actions = 2  # can do 2 actions per day

    while actions_taken < max_actions:
        remaining = max_actions - actions_taken
        try:
            choice = input(f"\n  Action ({remaining} remaining, P to end turn): ").strip().upper()
        except EOFError:
            choice = 'P'

        if choice == 'P' or choice == '':
            break
        elif choice == '1':
            action_buy_cargo(player, world)
            actions_taken += 1
        elif choice == '2':
            action_sell_cargo(player, world)
            actions_taken += 1
        elif choice == '3':
            action_short(player, world)
            actions_taken += 1
        elif choice == '4':
            action_long(player, world)
            actions_taken += 1
        elif choice == '5':
            action_close_position(player, world)
            actions_taken += 1
        elif choice == '6':
            action_toggle_insurance(player, world)
            actions_taken += 1
        elif choice == '7':
            action_intel(world)
            # intel is free, doesn't count
        elif choice == '8':
            show_positions(player, world)
            # viewing is free
        else:
            print(color("  Unknown action.", "YELLOW"))

    # ── End of day ──
    eod_msgs = process_day_end(player, world)
    if eod_msgs:
        print("\n  END OF DAY:")
        for m in eod_msgs:
            print(m)

    # Bankruptcy check
    if player.capital < 0 and not player.cargoes and not player.shorts and not player.longs:
        print(color("\n  ☠ BANKRUPT — Game Over", "RED", "BOLD"))
        game_over = True
        break

    world.day += 1
    if world.day <= 30:
        try:
            input("\n  Press ENTER for next day...")
        except EOFError:
            pass

# ── Final score ──
final_nw = player.net_worth(world.brent)
print("\n" + "═" * 60)
print(color("  GAME OVER", "BOLD", "CYAN"))
print("═" * 60)

if ceasefire:
    print(color(f"  🕊 War ended on Day {world.day}. Ceasefire!", "GREEN"))
elif game_over:
    print(color("  You were wiped out.", "RED"))
else:
    print(f"  Survived all 30 days. Final Brent: ${world.brent:.2f}")

print(f"\n  Final Net Worth: ${final_nw/1e6:.3f}M")
print(f"  Starting Capital: $50.000M")
pnl = final_nw - 50_000_000
pnl_s = color(f"+${pnl/1e6:.3f}M", "GREEN") if pnl >= 0 else color(f"-${abs(pnl)/1e6:.3f}M", "RED")
print(f"  Total PnL: {pnl_s}")
score = final_nw / 1_000_000
print(f"\n  SCORE: {score:.1f}")

if score > 200:
    print(color("  ★★★ LEGENDARY: You broke the market.", "YELLOW", "BOLD"))
elif score > 100:
    print(color("  ★★☆ EXCELLENT: Soros-tier crisis trading.", "GREEN", "BOLD"))
elif score > 60:
    print(color("  ★☆☆ GOOD: Solid returns in chaos.", "GREEN"))
elif score > 40:
    print(color("  Survived. Capital preserved.", "YELLOW"))
else:
    print(color("  Took losses. The market humbled you.", "RED"))

print("\n" + "═" * 60)

if name == "main":
play()

Edit

Pub: 09 Mar 2026 05:38 UTC

Views: 14