# Economic Simulation Patterns ## Description Master supply/demand dynamics, market simulation, production chains, and economic balance for game economies. Apply faucet/sink analysis, exploit validation, and price stabilization to create stable, engaging economies that resist inflation, arbitrage loops, and market manipulation over months of gameplay. ## When to Use This Skill Use this skill when implementing or debugging: - Trading games with dynamic markets (space sims, MMO auction houses) - Resource-based economies (crafting, production chains) - Player-to-player marketplaces - NPC vendors and dynamic pricing - Currency systems and money supply management - Multi-month economic balance and stability Do NOT use this skill for: - Simple fixed-price vendors (no supply/demand) - Single-player games without trading - Abstract resources without markets (XP, skill points) - Cosmetic-only economies (no gameplay impact) ## Quick Start (Time-Constrained Implementation) If you need a working economy quickly (< 4 hours), follow this priority order: **CRITICAL (Never Skip)**: 1. **Faucets < Sinks**: Money entering system must be less than money leaving 2. **Validate chains**: Production chains must be net-negative vs NPCs (no infinite money) 3. **Price bounds**: Set min/max prices (prevent 0 or infinity) 4. **NPC bid-ask spread**: NPCs buy at 50-70% of sell price **IMPORTANT (Strongly Recommended)**: 5. Transaction fees (1-5% per trade removes money from system) 6. Consumables (ammo, fuel, food - require constant spending) 7. Price dampening (1.01x adjustments, not 1.1x) 8. Daily limits (prevent 24/7 bot farming) **CAN DEFER** (Optimize Later): - Complex market makers - Dynamic recipe costs - Regional price variations - Advanced anti-manipulation **Example - Minimal Stable Economy in 2 Hours**: ```python class MinimalEconomy: def __init__(self): # Prices with bounds self.prices = {'Iron': 10} self.MIN_PRICE = 1 self.MAX_PRICE = 100 def sell_to_npc(self, player, item, qty): # NPC buys at 60% of market price (bid-ask spread) revenue = self.prices[item] * 0.6 * qty player.credits += revenue * 0.95 # 5% transaction fee (SINK) def buy_from_npc(self, player, item, qty): cost = self.prices[item] * qty if player.credits >= cost: player.credits -= cost return True return False def update_price(self, item, demand, supply): # Dampened adjustment (1.01x not 1.1x) if demand > supply: self.prices[item] *= 1.01 else: self.prices[item] *= 0.99 # Enforce bounds self.prices[item] = clamp(self.prices[item], self.MIN_PRICE, self.MAX_PRICE) def validate_recipe(self, recipe): # CRITICAL: Verify no infinite money input_cost = sum(self.prices[mat] * qty for mat, qty in recipe.inputs.items()) output_value = self.prices[recipe.output] * 0.6 # NPC buy price assert output_value < input_cost, f"Recipe {recipe.output} creates infinite money!" ``` This gives you: - Money sinks (transaction fees) - No arbitrage (bid-ask spread + validation) - Stable prices (bounds + dampening) Refine later based on playtesting. ## Core Concepts ### 1. Faucets and Sinks (Money Supply Control) Every game economy has **faucets** (money entering) and **sinks** (money leaving). The balance determines long-term stability. **Faucets** (Money Creation): ```python # Examples of money entering the economy class MoneyFaucets: def new_player_bonus(self, player): player.credits += 10000 # +10k per new player def quest_reward(self, player, quest): player.credits += 5000 # +5k per quest def npc_mission(self, player): player.credits += 1000 # +1k per mission def monster_drops(self, player, monster): player.credits += 50 # +50 per kill def calculate_daily_faucet(self, player_count): # Total money entering per day new_players = 100 * 10000 # 1,000,000 quests = player_count * 5 * 5000 # 5 quests/day missions = player_count * 10 * 1000 # 10 missions/day monsters = player_count * 100 * 50 # 100 kills/day return new_players + quests + missions + monsters ``` **Sinks** (Money Destruction): ```python # Examples of money leaving the economy class MoneySinks: def transaction_fee(self, trade_value): # 2% of every trade removed from game fee = trade_value * 0.02 # Credits disappear (not given to anyone) return -fee def repair_cost(self, player, item): # Items degrade, require repairs cost = item.max_value * 0.1 player.credits -= cost def consumable_purchase(self, player): # Fuel, ammo, food - constant spending player.credits -= 100 # Must buy from NPC (sinks) def luxury_items(self, player): # Cosmetics, housing - pure sinks player.credits -= 50000 def market_listing_fee(self, listing): # Fee to list item on marketplace return listing.value * 0.05 def calculate_daily_sink(self, player_count): # Total money leaving per day fees = player_count * 20 * 1000 * 0.02 # 20 trades/day repairs = player_count * 5 * 500 # 5 repairs/day consumables = player_count * 100 # Daily consumables luxuries = player_count * 0.1 * 50000 # 10% buy luxury/day return fees + repairs + consumables + luxuries ``` **The Golden Rule**: ```python def validate_economy_stability(faucets, sinks, player_count): '''CRITICAL: Sinks must exceed faucets for stability''' daily_faucet = calculate_daily_faucet(player_count) daily_sink = calculate_daily_sink(player_count) ratio = daily_sink / daily_faucet if ratio < 0.8: print("CRITICAL: Runaway inflation! Sinks too weak.") print(f" Faucet: {daily_faucet}, Sink: {daily_sink}") print(f" Ratio: {ratio:.2f} (need > 0.8)") return False elif ratio > 1.2: print("WARNING: Deflation! Sinks too strong.") print(f" Players will run out of money.") return False else: print(f"HEALTHY: Sink/Faucet ratio = {ratio:.2f}") return True ``` **Real-World Target**: - **0.8-1.0**: Slight inflation (prices gradually rise) - **1.0-1.1**: Stable (target for most games) - **1.1-1.3**: Slight deflation (late-game sink) **Why This Matters**: - Ratio < 0.8: Money supply grows exponentially → hyperinflation - Ratio > 1.3: Money supply shrinks → players can't afford anything - Ratio 0.9-1.1: Stable economy for months/years ### 2. Arbitrage Prevention (No Free Money) **Arbitrage**: Buying low and selling high for guaranteed profit with no risk. **The Problem**: ```python # ❌ BROKEN: Infinite money exploit class BrokenEconomy: def __init__(self): self.npc_sell_price = 10 # NPC sells Iron for 10 self.npc_buy_price = 10 # NPC buys Iron for 10 (SAME!) def exploit(self, player): while True: # Buy from NPC player.credits -= 10 player.inventory['Iron'] += 1 # Sell to NPC player.inventory['Iron'] -= 1 player.credits += 10 # Net: 0 profit (but with manufacturing...) # Craft: 50 Iron → 1 Hull (takes time but guaranteed) if player.inventory['Iron'] >= 50: player.inventory['Iron'] -= 50 player.inventory['Hull'] += 1 # Sell Hull to NPC player.inventory['Hull'] -= 1 player.credits += 1000 # Hull sells for 1000 # Cost: 50 * 10 = 500 credits # Revenue: 1000 credits # Profit: 500 credits (100% guaranteed!) ``` **The Fix: Bid-Ask Spread**: ```python # ✅ SAFE: NPCs buy low, sell high class SafeEconomy: def __init__(self): self.market_price = 10 # "Fair" price # NPCs sell at premium self.npc_sell_price = self.market_price * 1.0 # 10 (sell to players) # NPCs buy at discount self.npc_buy_price = self.market_price * 0.6 # 6 (buy from players) def validate_no_arbitrage(self): '''Verify all production chains are net-negative''' recipes = { 'Hull': {'Iron': 50, 'Silicon': 20}, 'Electronics': {'Silicon': 30, 'Platinum': 10}, } for output, inputs in recipes.items(): # Calculate cost (buying materials from NPC) input_cost = sum( self.npc_sell_price[mat] * qty for mat, qty in inputs.items() ) # Calculate revenue (selling product to NPC) output_revenue = self.npc_buy_price[output] profit = output_revenue - input_cost if profit > 0: print(f"ERROR: {output} creates infinite money!") print(f" Cost: {input_cost}, Revenue: {output_revenue}") print(f" Profit: {profit} (should be negative!)") raise ValueError(f"Recipe {output} is exploitable!") else: print(f"SAFE: {output} requires player trading for profit") ``` **Why This Works**: - Players MUST trade with each other to profit - NPC loops are always net-negative - Crafting is only profitable if you sell to players (creates real economy) **Bid-Ask Spread Guidelines**: - **Conservative**: NPC buys at 50% (safe, forces player trading) - **Moderate**: NPC buys at 60-70% (reasonable, still safe) - **Aggressive**: NPC buys at 80% (risky, verify carefully!) - **Never**: NPC buys at 100% (guaranteed exploits) ### 3. Supply and Demand Dynamics **Basic Model**: ```python class SupplyDemandMarket: def __init__(self): self.price = 10 self.supply = 0 # Items available for sale self.demand = 0 # Items players want to buy def update_price_simple(self): '''Simple supply/demand adjustment''' if self.demand > self.supply: # Shortage: Price rises self.price *= 1.05 elif self.supply > self.demand: # Surplus: Price falls self.price *= 0.95 ``` **Problem**: This is unstable! Prices oscillate wildly. **Better: Dampened Adjustment**: ```python def update_price_dampened(self): '''Dampened adjustment for stability''' # Calculate imbalance if self.supply > 0: ratio = self.demand / self.supply else: ratio = 10.0 # High demand, no supply # Dampened adjustment (smaller steps) if ratio > 1.1: # Demand > Supply by 10%+ self.price *= 1.01 # Increase 1% (not 5%!) elif ratio < 0.9: # Supply > Demand by 10%+ self.price *= 0.99 # Decrease 1% # Reset counters for next period self.supply = 0 self.demand = 0 ``` **Best: Exponential Moving Average**: ```python def update_price_ema(self, recent_trades): '''Smooth price adjustment using EMA''' # Calculate average trade price from recent trades if len(recent_trades) == 0: return avg_trade_price = sum(t.price for t in recent_trades) / len(recent_trades) # Exponential moving average (smooth towards trade price) SMOOTHING = 0.1 # 10% weight to new data self.price = self.price * (1 - SMOOTHING) + avg_trade_price * SMOOTHING # Still enforce bounds self.price = clamp(self.price, self.MIN_PRICE, self.MAX_PRICE) ``` **Advanced: Market Clearing Price**: ```python class MarketClearingPrice: def __init__(self): self.buy_orders = [] # [(price, quantity), ...] self.sell_orders = [] # [(price, quantity), ...] def find_clearing_price(self): '''Find price where supply = demand''' # Sort orders self.buy_orders.sort(reverse=True) # Highest price first self.sell_orders.sort() # Lowest price first # Walk through order book for sell_price, sell_qty in self.sell_orders: # Count how many buyers at this price or higher demand_at_price = sum( qty for price, qty in self.buy_orders if price >= sell_price ) # Count how many sellers at this price or lower supply_at_price = sum( qty for price, qty in self.sell_orders if price <= sell_price ) if demand_at_price >= supply_at_price: # This is the clearing price! return sell_price # No clearing price found return None ``` **Decision Framework**: - **Simple games**: Use dampened adjustment (easy, stable enough) - **Trading-focused games**: Use market clearing price (realistic, complex) - **Hybrid**: EMA for NPC prices, order book for player marketplace ### 4. Price Bounds and Stabilization **Never Let Prices Go to Zero or Infinity**: ```python class BoundedPricing: def __init__(self, base_price): self.price = base_price self.MIN_PRICE = base_price * 0.1 # Never below 10% of base self.MAX_PRICE = base_price * 10.0 # Never above 10x base def update_price(self, adjustment): self.price *= adjustment # Enforce bounds (CRITICAL!) self.price = clamp(self.price, self.MIN_PRICE, self.MAX_PRICE) # Alternative: Asymptotic bounds (smooth approach to limits) # self.price = self.MIN_PRICE + (self.MAX_PRICE - self.MIN_PRICE) * ( # 1 / (1 + math.exp(-(self.price - base_price) / base_price)) # ) ``` **Why Bounds Matter**: - No bounds: Price can explode to 1,000,000+ or crash to 0.0001 - With bounds: Price stays playable (players can always afford basics) **NPC Market Makers** (Advanced Stabilization): ```python class NPCMarketMaker: def __init__(self, target_price, liquidity): self.target_price = target_price self.liquidity = liquidity # How much NPC will trade def provide_liquidity(self, market): '''NPC always offers to buy/sell at near-target price''' # NPC sells at slight premium npc_sell_price = self.target_price * 1.05 npc_sell_quantity = self.liquidity # NPC buys at slight discount npc_buy_price = self.target_price * 0.95 npc_buy_quantity = self.liquidity # Add NPC orders to market market.add_sell_order(npc_sell_price, npc_sell_quantity, is_npc=True) market.add_buy_order(npc_buy_price, npc_buy_quantity, is_npc=True) def adjust_target(self, current_price): '''NPC slowly adjusts target towards market price''' SMOOTHING = 0.01 # Very slow adjustment self.target_price = self.target_price * (1 - SMOOTHING) + current_price * SMOOTHING ``` **Effect**: - Players can always buy/sell (NPC provides liquidity) - Prices stabilize near target (NPC absorbs shocks) - Market still responds to player activity (target adjusts slowly) ### 5. Production Chain Balance **Every recipe must be balanced for profitability and gameplay**: ```python class ProductionChainBalancer: def __init__(self, market): self.market = market def analyze_chain(self, recipe): '''Analyze profitability and balance of a recipe''' # Calculate input cost (buying from players at market price) input_cost_market = sum( self.market.get_price(mat) * qty for mat, qty in recipe.inputs.items() ) # Calculate input cost (buying from NPCs) input_cost_npc = sum( self.market.npc_sell_price(mat) * qty for mat, qty in recipe.inputs.items() ) # Calculate output value (selling to players) output_value_market = self.market.get_price(recipe.output) # Calculate output value (selling to NPCs) output_value_npc = self.market.npc_buy_price(recipe.output) # Calculate time cost time_hours = recipe.crafting_time / 3600 # Convert to hours # Profit analysis profit_vs_npc = output_value_npc - input_cost_npc profit_vs_players = output_value_market - input_cost_market profit_per_hour_npc = profit_vs_npc / time_hours profit_per_hour_players = profit_vs_players / time_hours print(f"\nRecipe: {recipe.output}") print(f" Input cost (NPC): {input_cost_npc:.0f}") print(f" Output value (NPC): {output_value_npc:.0f}") print(f" Profit vs NPC: {profit_vs_npc:.0f} (should be NEGATIVE)") print(f" Profit vs Players: {profit_vs_players:.0f}") print(f" Profit/hour (players): {profit_per_hour_players:.0f}") # Validation if profit_vs_npc > 0: print(f" ⚠️ ERROR: Creates infinite money vs NPCs!") return False if profit_vs_players < 0: print(f" ⚠️ WARNING: Unprofitable even with player trading!") if profit_per_hour_players < 100: print(f" ⚠️ WARNING: Poor profit/hour (players won't craft)") return True def balance_all_chains(self, recipes): '''Ensure all recipes have similar profit/hour''' profits = [] for recipe in recipes: # Calculate profit per hour vs player market input_cost = sum( self.market.get_price(mat) * qty for mat, qty in recipe.inputs.items() ) output_value = self.market.get_price(recipe.output) time_hours = recipe.crafting_time / 3600 profit_per_hour = (output_value - input_cost) / time_hours profits.append((recipe.output, profit_per_hour)) # Sort by profit profits.sort(key=lambda x: x[1]) print("\nProduction Chain Balance:") for name, profit in profits: print(f" {name}: {profit:.0f} credits/hour") # Check for dominant strategies max_profit = profits[-1][1] min_profit = profits[0][1] if max_profit > min_profit * 3: print(f" ⚠️ WARNING: Imbalanced chains!") print(f" Best: {profits[-1][0]} ({max_profit:.0f}/hr)") print(f" Worst: {profits[0][0]} ({min_profit:.0f}/hr)") print(f" Ratio: {max_profit / min_profit:.1f}x (should be < 3x)") ``` **Balance Guidelines**: - All chains should have similar profit/hour (within 2-3x) - No chain should be strictly better (dominant strategy) - Complex chains can pay more (reward skill/knowledge) - Late-game chains should be more profitable (progression) ## Decision Frameworks ### Framework 1: Economy Complexity Level **Choose complexity based on game focus**: | Complexity | Use When | Examples | Implementation Time | |-----------|----------|----------|---------------------| | **Simple** | Economy is secondary to core gameplay | Action RPG, FPS | 1-2 days | | **Moderate** | Trading is important but not central | MMO, Strategy | 1-2 weeks | | **Complex** | Economy IS the game | Eve Online, Trading sim | 1-3 months | **Simple Economy**: ```python # Fixed prices, transaction fees, consumables class SimpleEconomy: PRICES = {'Sword': 100, 'Potion': 10} # Fixed def buy_from_npc(self, player, item): player.credits -= self.PRICES[item] def sell_to_npc(self, player, item): player.credits += self.PRICES[item] * 0.5 # 50% back ``` **Features**: - Fixed prices (no supply/demand) - NPC vendors only - Simple sinks (repair, consumables) - No player trading **Use for**: Games where economy is flavor, not focus **Moderate Economy**: ```python # Dynamic prices, player marketplace, production class ModerateEconomy: def __init__(self): self.prices = {} # Dynamic self.player_market = OrderBook() def update_prices(self, trades): # EMA based on recent trades for item in trades: self.prices[item] = ema(self.prices[item], trades[item].avg_price) def npc_buy_price(self, item): return self.prices[item] * 0.6 # 60% of market def npc_sell_price(self, item): return self.prices[item] * 1.0 # 100% of market ``` **Features**: - Dynamic prices (supply/demand) - Player marketplace (peer trading) - Production chains (crafting) - Faucet/sink balance **Use for**: MMOs, persistent world games **Complex Economy**: ```python # Full market simulation, regional prices, contracts class ComplexEconomy: def __init__(self): self.regions = {} # Different prices per region self.order_books = {} # Full order matching self.contracts = [] # Player contracts self.corporations = [] # Player organizations def match_orders(self, item, region): '''Full order book matching''' book = self.order_books[(item, region)] clearing_price = book.find_clearing_price() book.execute_trades(clearing_price) def transport_goods(self, item, from_region, to_region): '''Regional arbitrage (hauling gameplay)''' # Different prices in different regions # Players profit by hauling goods pass ``` **Features**: - Regional economies (different prices per zone) - Full order book matching - Player contracts and corporations - Hauling/transport gameplay - Complex production chains **Use for**: Eve Online-style games, trading simulators ### Framework 2: Player-Driven vs NPC-Driven Markets **NPC-Driven** (Simpler, More Stable): ```python class NPCDrivenMarket: '''NPCs set prices, players buy/sell from NPCs''' def __init__(self): self.npc_prices = {} # NPCs always available def buy_from_npc(self, player, item, qty): cost = self.npc_prices[item] * qty if player.credits >= cost: player.credits -= cost player.inventory[item] += qty return True return False def sell_to_npc(self, player, item, qty): revenue = self.npc_prices[item] * 0.6 * qty # 60% back player.credits += revenue player.inventory[item] -= qty ``` **Pros**: - Always available (no market failures) - Stable prices (predictable) - Simple to implement - Works for small populations **Cons**: - Less dynamic - No emergent gameplay - Artificial feeling **Use for**: Single-player, co-op, small multiplayer **Player-Driven** (Complex, Dynamic): ```python class PlayerDrivenMarket: '''Players set prices, NPCs only provide liquidity''' def __init__(self): self.order_book = OrderBook() self.npc_maker = NPCMarketMaker() # Backup liquidity def create_sell_order(self, seller, item, qty, price): '''Player lists item for sale''' order = {'seller': seller, 'item': item, 'qty': qty, 'price': price} self.order_book.add_sell_order(order) def create_buy_order(self, buyer, item, qty, max_price): '''Player offers to buy at price''' order = {'buyer': buyer, 'item': item, 'qty': qty, 'price': max_price} self.order_book.add_buy_order(order) def match_orders(self): '''Match buy and sell orders''' self.order_book.match() # If no liquidity, NPC provides backup if self.order_book.is_empty(): self.npc_maker.provide_liquidity(self.order_book) ``` **Pros**: - Emergent gameplay (market manipulation, speculation) - Dynamic prices (responds to player behavior) - Engaging economy (players set prices) - Scales to large populations **Cons**: - Can fail (no buyers/sellers) - Requires balancing - Complex implementation - Needs large player base **Use for**: MMOs, persistent worlds, large multiplayer **Hybrid** (Best of Both): ```python class HybridMarket: '''Players trade with each other, NPCs provide fallback''' def __init__(self): self.player_market = OrderBook() self.npc_vendor = NPCVendor() def buy_item(self, player, item, qty): '''Try player market first, fall back to NPC''' # Check player market orders = self.player_market.get_sell_orders(item) if len(orders) > 0: # Buy from cheapest player cheapest = min(orders, key=lambda o: o.price) if player.credits >= cheapest.price * qty: self.execute_player_trade(player, cheapest, qty) return True # Fall back to NPC (more expensive) return self.npc_vendor.buy_from_npc(player, item, qty) ``` **Use for**: Most games (combines stability + dynamics) ### Framework 3: When to Use Regional Economies **Single Global Market** (Simpler): - All players see same prices - No hauling gameplay - Works for small games **Regional Markets** (Complex): - Different prices per zone - Hauling creates arbitrage opportunities - Requires large world and population **Decision Table**: | Factor | Global Market | Regional Markets | |--------|---------------|------------------| | World size | Small (<10 zones) | Large (>20 zones) | | Player count | <1,000 | >5,000 | | Travel time | <1 minute | >5 minutes | | Hauling gameplay | No | Yes (core mechanic) | | Implementation time | 1 week | 1 month | **Regional Economy Example**: ```python class RegionalEconomy: def __init__(self): self.regions = { 'Mining Zone': {'Iron': 5, 'Ships': 4000}, # Iron cheap, ships expensive 'Industrial Zone': {'Iron': 15, 'Ships': 3000}, # Iron expensive, ships cheaper 'Trade Hub': {'Iron': 10, 'Ships': 3500}, # Average prices } def get_price(self, region, item): return self.regions[region][item] def arbitrage_opportunity(self, item): '''Find best buy/sell regions for hauling''' prices = [(region, self.get_price(region, item)) for region in self.regions] prices.sort(key=lambda x: x[1]) buy_region, buy_price = prices[0] # Cheapest sell_region, sell_price = prices[-1] # Most expensive profit_per_unit = sell_price - buy_price print(f"Haul {item} from {buy_region} to {sell_region}") print(f" Profit: {profit_per_unit} per unit") return buy_region, sell_region, profit_per_unit ``` ## Implementation Patterns ### Pattern 1: Faucet/Sink Validation System **Complete validation for economic stability**: ```python class EconomyValidator: def __init__(self, economy, player_count): self.economy = economy self.player_count = player_count def validate_stability(self, simulation_days=30): '''Simulate economy for N days to check stability''' print(f"Simulating economy for {simulation_days} days...") total_money = self.calculate_initial_money() daily_faucet = self.calculate_daily_faucet() daily_sink = self.calculate_daily_sink() print(f"\nInitial money supply: {total_money:,}") print(f"Daily faucet: {daily_faucet:,}") print(f"Daily sink: {daily_sink:,}") print(f"Sink/Faucet ratio: {daily_sink/daily_faucet:.2f}") # Simulate money_over_time = [total_money] for day in range(simulation_days): total_money += daily_faucet total_money -= daily_sink money_over_time.append(total_money) # Analyze final_money = money_over_time[-1] money_growth_rate = (final_money / money_over_time[0]) ** (1/simulation_days) - 1 print(f"\nAfter {simulation_days} days:") print(f" Total money: {final_money:,}") print(f" Growth rate: {money_growth_rate*100:.2f}% per day") # Check for inflation if money_growth_rate > 0.01: # >1% growth per day print(f" ⚠️ WARNING: Runaway inflation!") print(f" Money supply growing too fast.") print(f" Increase sinks or reduce faucets.") return False elif money_growth_rate < -0.01: # <-1% shrink per day print(f" ⚠️ WARNING: Deflation!") print(f" Money supply shrinking.") print(f" Reduce sinks or increase faucets.") return False else: print(f" ✓ STABLE: Money supply growth is healthy.") return True def calculate_initial_money(self): '''Money already in economy''' return self.player_count * 50000 # Average per player def calculate_daily_faucet(self): '''Money entering per day''' new_players = 100 * 10000 # New player bonuses quests = self.player_count * 5 * 5000 # Quests per player missions = self.player_count * 10 * 1000 # Missions monster_drops = self.player_count * 100 * 50 # Combat return new_players + quests + missions + monster_drops def calculate_daily_sink(self): '''Money leaving per day''' transaction_fees = self.player_count * 20 * 1000 * 0.02 # 20 trades/day, 2% fee repairs = self.player_count * 5 * 500 # 5 repairs/day consumables = self.player_count * 100 # Daily ammo/fuel listing_fees = self.player_count * 5 * 1000 * 0.05 # Market listings return transaction_fees + repairs + consumables + listing_fees def validate_no_arbitrage(self): '''Ensure no infinite money loops''' print("\nValidating production chains...") for recipe in self.economy.recipes: input_cost_npc = sum( self.economy.npc_sell_price(mat) * qty for mat, qty in recipe.inputs.items() ) output_value_npc = self.economy.npc_buy_price(recipe.output) profit = output_value_npc - input_cost_npc if profit > 0: print(f" ❌ ERROR: {recipe.output} creates infinite money!") print(f" Input cost: {input_cost_npc}") print(f" Output value: {output_value_npc}") print(f" Profit: {profit}") return False else: print(f" ✓ {recipe.output}: Safe (requires player trading)") return True def validate_price_bounds(self): '''Ensure prices have min/max''' print("\nValidating price bounds...") for item in self.economy.items: if not hasattr(item, 'min_price') or not hasattr(item, 'max_price'): print(f" ❌ ERROR: {item.name} missing price bounds!") return False if item.min_price <= 0: print(f" ❌ ERROR: {item.name} min_price is {item.min_price} (must be > 0)") return False if item.max_price < item.min_price * 5: print(f" ⚠️ WARNING: {item.name} max_price too close to min") print(f" Range: {item.min_price} - {item.max_price}") print(f" ✓ {item.name}: {item.min_price} - {item.max_price}") return True def run_full_validation(self): '''Run all validation checks''' print("="*60) print("ECONOMIC STABILITY VALIDATION") print("="*60) checks = [ ("Stability", self.validate_stability), ("Arbitrage", self.validate_no_arbitrage), ("Price Bounds", self.validate_price_bounds), ] results = [] for name, check in checks: print(f"\n{name} Check:") passed = check() results.append((name, passed)) print("\n" + "="*60) print("VALIDATION SUMMARY") print("="*60) all_passed = all(passed for _, passed in results) for name, passed in results: status = "✓ PASS" if passed else "❌ FAIL" print(f"{status}: {name}") if all_passed: print("\n✓ Economy is stable and ready for production!") else: print("\n❌ Economy has critical issues. Fix before launch!") return all_passed ``` ### Pattern 2: Dynamic Price Adjustment with Stabilization **Robust price adjustment system**: ```python class StabilizedPricing: def __init__(self, item_name, base_price): self.item_name = item_name self.base_price = base_price self.current_price = base_price # Bounds (10% to 10x base) self.min_price = base_price * 0.1 self.max_price = base_price * 10.0 # Smoothing parameters self.ema_alpha = 0.1 # 10% weight to new data self.adjustment_rate = 0.01 # 1% change per update # History for analysis self.price_history = [] self.trade_history = [] def update_from_supply_demand(self, supply, demand): '''Update price based on supply/demand imbalance''' if supply == 0 and demand == 0: return # No activity if supply == 0: # Pure demand, no supply self.current_price *= (1 + self.adjustment_rate) elif demand == 0: # Pure supply, no demand self.current_price *= (1 - self.adjustment_rate) else: # Calculate ratio ratio = demand / supply if ratio > 1.1: # 10% more demand than supply # Increase price (dampened) self.current_price *= (1 + self.adjustment_rate) elif ratio < 0.9: # 10% more supply than demand # Decrease price (dampened) self.current_price *= (1 - self.adjustment_rate) # Enforce bounds self.current_price = clamp(self.current_price, self.min_price, self.max_price) # Record self.price_history.append(self.current_price) def update_from_trades(self, trades): '''Update price based on actual trade prices (more accurate)''' if len(trades) == 0: return # Calculate average trade price avg_trade_price = sum(t.price * t.quantity for t in trades) / sum(t.quantity for t in trades) # Exponential moving average self.current_price = ( self.current_price * (1 - self.ema_alpha) + avg_trade_price * self.ema_alpha ) # Enforce bounds self.current_price = clamp(self.current_price, self.min_price, self.max_price) # Record self.price_history.append(self.current_price) self.trade_history.extend(trades) def get_volatility(self): '''Calculate recent price volatility''' if len(self.price_history) < 10: return 0.0 recent = self.price_history[-10:] mean = sum(recent) / len(recent) variance = sum((p - mean)**2 for p in recent) / len(recent) std_dev = variance ** 0.5 return std_dev / mean # Coefficient of variation def should_intervene(self): '''Check if NPC intervention is needed''' volatility = self.get_volatility() # High volatility: Market unstable if volatility > 0.2: # 20% volatility return True # Price at extremes if self.current_price <= self.min_price * 1.1: return True # Near floor if self.current_price >= self.max_price * 0.9: return True # Near ceiling return False ``` ### Pattern 3: NPC Market Maker for Stabilization **NPCs provide liquidity when markets fail**: ```python class NPCMarketMaker: def __init__(self, item, target_price, liquidity_amount): self.item = item self.target_price = target_price self.liquidity_amount = liquidity_amount # Max qty NPC will trade # Spread (NPC buys low, sells high) self.bid_spread = 0.95 # NPC buys at 95% of target self.ask_spread = 1.05 # NPC sells at 105% of target def get_npc_buy_order(self): '''NPC standing offer to buy''' price = self.target_price * self.bid_spread quantity = self.liquidity_amount return { 'item': self.item, 'price': price, 'quantity': quantity, 'is_npc': True } def get_npc_sell_order(self): '''NPC standing offer to sell''' price = self.target_price * self.ask_spread quantity = self.liquidity_amount return { 'item': self.item, 'price': price, 'quantity': quantity, 'is_npc': True } def adjust_target_price(self, current_market_price): '''Slowly adjust target towards market price''' # Very slow adjustment (1% per update) ADJUSTMENT_RATE = 0.01 self.target_price = ( self.target_price * (1 - ADJUSTMENT_RATE) + current_market_price * ADJUSTMENT_RATE ) def intervene_if_needed(self, market): '''Provide liquidity if market is thin''' order_book = market.get_order_book(self.item) # Check if market has liquidity total_buy_orders = sum(o.quantity for o in order_book.buy_orders if not o.is_npc) total_sell_orders = sum(o.quantity for o in order_book.sell_orders if not o.is_npc) # If market is thin, add NPC orders if total_buy_orders < self.liquidity_amount * 0.5: market.add_buy_order(self.get_npc_buy_order()) if total_sell_orders < self.liquidity_amount * 0.5: market.add_sell_order(self.get_npc_sell_order()) ``` **Effect**: - Market always has liquidity (players can always trade) - Prices stabilize near target (NPC absorbs volatility) - NPC adapts to market (target adjusts slowly) ### Pattern 4: Production Chain Simulator **Test economic balance before launch**: ```python class ProductionChainSimulator: def __init__(self, economy): self.economy = economy def simulate_player_behavior(self, num_players=1000, days=30): '''Simulate player economy for N days''' print(f"Simulating {num_players} players for {days} days...\n") # Track metrics total_money = num_players * 10000 # Starting credits resource_production = {item: 0 for item in self.economy.resources} goods_production = {item: 0 for item in self.economy.goods} trade_volume = 0 for day in range(days): # Players mine resources for resource in self.economy.resources: daily_mining = num_players * 100 # 100 units/player/day resource_production[resource] += daily_mining # Players craft (choose most profitable chain) best_recipe = self.find_best_recipe() if best_recipe: crafters = num_players * 0.3 # 30% of players craft daily_crafting = crafters * 2 # 2 items/day goods_production[best_recipe.output] += daily_crafting # Consume resources for mat, qty in best_recipe.inputs.items(): resource_production[mat] -= daily_crafting * qty # Players trade daily_trades = num_players * 10 # 10 trades/player/day avg_trade_value = 1000 trade_volume += daily_trades * avg_trade_value # Money faucets daily_faucet = self.economy.calculate_daily_faucet(num_players) total_money += daily_faucet # Money sinks daily_sink = self.economy.calculate_daily_sink(num_players) total_money -= daily_sink # Log every 5 days if day % 5 == 0: print(f"Day {day}:") print(f" Total money: {total_money:,}") print(f" Trade volume: {trade_volume:,}") print(f" Most produced: {max(goods_production, key=goods_production.get)}") # Final report print(f"\n{'='*60}") print(f"SIMULATION COMPLETE ({days} days)") print(f"{'='*60}") print(f"\nMoney Supply:") print(f" Initial: {num_players * 10000:,}") print(f" Final: {total_money:,}") print(f" Growth: {(total_money / (num_players * 10000) - 1) * 100:.1f}%") print(f"\nResource Production:") for resource, qty in resource_production.items(): print(f" {resource}: {qty:,}") print(f"\nGoods Production:") for good, qty in goods_production.items(): print(f" {good}: {qty:,}") # Check for imbalances self.check_for_imbalances(resource_production, goods_production) def find_best_recipe(self): '''Find most profitable recipe (dominant strategy)''' best_recipe = None best_profit_per_hour = 0 for recipe in self.economy.recipes: input_cost = sum( self.economy.get_price(mat) * qty for mat, qty in recipe.inputs.items() ) output_value = self.economy.get_price(recipe.output) time_hours = recipe.time / 3600 profit_per_hour = (output_value - input_cost) / time_hours if profit_per_hour > best_profit_per_hour: best_profit_per_hour = profit_per_hour best_recipe = recipe return best_recipe def check_for_imbalances(self, resource_production, goods_production): '''Detect economic imbalances''' print(f"\nImbalance Detection:") # Check for unused resources for resource, qty in resource_production.items(): if qty > 100000: # Excess production print(f" ⚠️ {resource}: Overproduced ({qty:,} units)") print(f" Consider increasing demand or decreasing production.") if qty < 0: # Deficit print(f" ⚠️ {resource}: Deficit ({abs(qty):,} units)") print(f" Production can't keep up with demand.") # Check for dominant strategies if len(goods_production) > 0: max_produced = max(goods_production.values()) min_produced = min(goods_production.values()) if max_produced > min_produced * 5: # 5x imbalance print(f" ⚠️ Production imbalance detected!") print(f" Most produced: {max(goods_production, key=goods_production.get)}") print(f" Least produced: {min(goods_production, key=goods_production.get)}") print(f" Ratio: {max_produced/min_produced:.1f}x") ``` ### Pattern 5: Anti-Exploit Validation **Comprehensive exploit detection**: ```python class ExploitDetector: def __init__(self, economy): self.economy = economy def find_arbitrage_loops(self): '''Detect cycles in production/trading that generate profit''' print("Searching for arbitrage loops...\n") loops_found = [] # Check direct buy-sell loops for item in self.economy.items: npc_sell = self.economy.npc_sell_price(item) npc_buy = self.economy.npc_buy_price(item) if npc_buy >= npc_sell: print(f"❌ CRITICAL: {item} - Direct arbitrage!") print(f" NPC sells for: {npc_sell}") print(f" NPC buys for: {npc_buy}") print(f" Profit: {npc_buy - npc_sell} per unit") loops_found.append(('direct', item)) # Check production loops for recipe in self.economy.recipes: input_cost = sum( self.economy.npc_sell_price(mat) * qty for mat, qty in recipe.inputs.items() ) output_value = self.economy.npc_buy_price(recipe.output) if output_value > input_cost: profit = output_value - input_cost print(f"❌ CRITICAL: {recipe.output} - Production arbitrage!") print(f" Input cost: {input_cost}") print(f" Output value: {output_value}") print(f" Profit: {profit}") loops_found.append(('production', recipe.output)) # Check multi-step loops (A→B→C→A) # This requires graph traversal to find cycles loops_found.extend(self.find_conversion_cycles()) if len(loops_found) == 0: print("✓ No arbitrage loops found!") else: print(f"\n❌ Found {len(loops_found)} exploitable loops!") return loops_found def find_conversion_cycles(self): '''Find multi-step conversion cycles''' # Build conversion graph graph = {} for recipe in self.economy.recipes: # Each recipe is an edge: inputs → output graph[recipe.output] = recipe.inputs # DFS to find cycles cycles = [] # ... graph traversal logic ... return cycles def test_market_manipulation(self): '''Simulate coordinated buying to check for cornering''' print("\nTesting market manipulation resistance...\n") for item in self.economy.items: # Simulate 100 players buying entire supply available_supply = self.economy.get_total_supply(item) market_price = self.economy.get_price(item) # Cost to buy entire supply total_cost = available_supply * market_price # Price after buying new_price = self.economy.simulate_price_after_buying(item, available_supply) price_increase = (new_price / market_price - 1) * 100 if price_increase > 500: # 5x price increase print(f"⚠️ {item}: Vulnerable to cornering!") print(f" Supply: {available_supply}") print(f" Cost to corner: {total_cost:,}") print(f" Price increase: {price_increase:.0f}%") print(f" Recommendation: Increase supply or add price ceiling") print("\n✓ Market manipulation test complete") def test_duping_detection(self): '''Verify transactions are atomic and validated''' print("\nTesting duping prevention...\n") # Check if trades are atomic if not hasattr(self.economy, 'execute_trade_atomic'): print("⚠️ WARNING: No atomic trade execution found!") print(" Implement database transactions to prevent duping") # Check if inventory is validated if not hasattr(self.economy, 'validate_inventory'): print("⚠️ WARNING: No inventory validation found!") print(" Add checks for negative quantities and overflow") print("✓ Duping prevention checks complete") def run_all_exploit_tests(self): '''Run comprehensive exploit detection''' print("="*60) print("EXPLOIT DETECTION SUITE") print("="*60 + "\n") tests = [ ("Arbitrage Loops", self.find_arbitrage_loops), ("Market Manipulation", self.test_market_manipulation), ("Duping Prevention", self.test_duping_detection), ] exploits_found = [] for name, test in tests: print(f"\n{name}:") print("-" * 40) result = test() if result: exploits_found.extend(result) print("\n" + "="*60) print("EXPLOIT DETECTION SUMMARY") print("="*60) if len(exploits_found) == 0: print("✓ No exploits detected!") else: print(f"❌ Found {len(exploits_found)} potential exploits!") print("Fix these before launch!") return exploits_found ``` ## Common Pitfalls ### Pitfall 1: No Money Sinks (Runaway Inflation) **The Mistake**: ```python # ❌ Money enters but never leaves class BrokenEconomy: def new_player_joins(self, player): player.credits += 10000 # Faucet def complete_quest(self, player): player.credits += 5000 # Faucet # NO SINKS! Money accumulates forever ``` **Why It Fails**: - Every player brings 10,000 credits - Quests add 5,000 credits each - After 1 month: Millions of excess credits - Hyperinflation: Prices skyrocket - New players can't afford anything **Real-World Example**: Early World of Warcraft had insufficient gold sinks. Players accumulated millions of gold, causing runaway inflation. Blizzard added repair costs, mounts, and consumables to drain gold. **The Fix**: ```python # ✅ Balance faucets with sinks class StableEconomy: def calculate_faucets(self, player_count): daily_faucet = player_count * 10000 # New players + quests return daily_faucet def calculate_sinks(self, player_count): # Transaction fees daily_trades = player_count * 10 trade_fee = daily_trades * 1000 * 0.02 # Repairs daily_repairs = player_count * 5 * 500 # Consumables (ammo, fuel) daily_consumables = player_count * 100 # Listing fees daily_listings = player_count * 5 * 1000 * 0.05 daily_sink = trade_fee + daily_repairs + daily_consumables + daily_listings return daily_sink def validate_balance(self, player_count): faucet = self.calculate_faucets(player_count) sink = self.calculate_sinks(player_count) ratio = sink / faucet assert 0.8 <= ratio <= 1.2, f"Sink/Faucet ratio is {ratio} (need 0.8-1.2)" ``` **Critical Sinks to Include**: - Transaction fees (1-5%) - Repair costs (items degrade) - Consumables (ammo, fuel, food) - Listing fees (marketplace) - Fast travel costs - Housing/storage fees - NPC luxury items (cosmetics) ### Pitfall 2: NPC Arbitrage (Infinite Money Loops) **The Mistake**: ```python # ❌ NPCs buy and sell at same price class ExploitableEconomy: def npc_sell_price(self, item): return self.market_price[item] def npc_buy_price(self, item): return self.market_price[item] # SAME! # Players discover: # Mine 50 Iron (free) → Craft Hull → Sell to NPC # Input: 50 Iron @ 10 = 500 credits # Output: 1 Hull @ 1000 = 1000 credits # Profit: 500 credits (INFINITE LOOP!) ``` **Why It Fails**: - Players find optimal craft chains - Everyone grinds the same exploit - Economy becomes meaningless (everyone prints money) - Hyperinflation accelerates **Real-World Example**: Path of Exile had a vendor recipe that generated profit. Players automated it, crashing the economy. GGG nerfed the recipe. **The Fix**: ```python # ✅ NPCs buy at discount (bid-ask spread) class SafeEconomy: def npc_sell_price(self, item): return self.market_price[item] * 1.0 # 100% (sell to players) def npc_buy_price(self, item): return self.market_price[item] * 0.6 # 60% (buy from players) def validate_no_arbitrage(self): for recipe in self.recipes: input_cost = sum( self.npc_sell_price(mat) * qty for mat, qty in recipe.inputs.items() ) output_value = self.npc_buy_price(recipe.output) if output_value >= input_cost: raise ValueError(f"{recipe.output} creates infinite money!") ``` **Bid-Ask Spread Guidelines**: - **Safe**: NPCs buy at 50-60% (forces player trading) - **Moderate**: NPCs buy at 60-70% - **Risky**: NPCs buy at 80%+ (validate carefully!) ### Pitfall 3: Unbounded Prices (Wild Swings) **The Mistake**: ```python # ❌ Prices multiply with no bounds class UnstablePrices: def update_price(self, item, demand, supply): if demand > supply: self.price[item] *= 1.1 # +10% every update # After 50 updates: 10 * (1.1^50) = 1,173 credits! # After 100 updates: 137,806 credits (unusable) ``` **Why It Fails**: - Prices can explode to infinity or crash to zero - Market becomes unusable - Players can't plan or budget - Coordinated manipulation causes chaos **Real-World Example**: Eve Online had periods of extreme price volatility before implementing market stabilization tools. **The Fix**: ```python # ✅ Bounded prices with dampening class StablePrices: def __init__(self, base_price): self.price = base_price self.min_price = base_price * 0.1 # Floor: 10% of base self.max_price = base_price * 10.0 # Ceiling: 10x base def update_price(self, demand, supply): if demand > supply: self.price *= 1.01 # Dampened: +1% not +10% else: self.price *= 0.99 # Dampened: -1% # Enforce bounds self.price = clamp(self.price, self.min_price, self.max_price) ``` **Key fixes**: - Price floors (minimum > 0) - Price ceilings (maximum < infinity) - Dampening (1.01x adjustments, not 1.1x) - Smoothing (EMA, not instant) ### Pitfall 4: Unbalanced Production Chains **The Mistake**: ```python # ❌ Some chains way more profitable than others class ImbalancedChains: # Hull: 800 cost → 1000 value = 200 profit (25%) # Electronics: 950 cost → 800 value = -150 profit (LOSS!) # Fuel Cell: 280 cost → 500 value = 220 profit (79%!) # Result: Everyone crafts Fuel Cells, nobody crafts Electronics ``` **Why It Fails**: - Dominant strategies emerge (one chain is best) - Other chains become useless - Market distorts (excess supply of one item) - Ships can't be built (missing Electronics) **Real-World Example**: Final Fantasy XIV regularly rebalances crafting recipes to ensure all disciplines are equally profitable. **The Fix**: ```python # ✅ Analyze and balance all chains class BalancedChains: def analyze_profit_margins(self): for recipe in self.recipes: input_cost = sum(self.price[mat] * qty for mat, qty in recipe.inputs.items()) output_value = self.price[recipe.output] time_hours = recipe.time / 3600 profit_per_hour = (output_value - input_cost) / time_hours print(f"{recipe.output}: {profit_per_hour:.0f} credits/hour") # Ensure all chains have similar profit/hour (within 2-3x) def balance_recipe(self, recipe, target_profit_per_hour): current_profit = self.calculate_profit_per_hour(recipe) if current_profit < target_profit_per_hour: # Increase output value or decrease input cost multiplier = target_profit_per_hour / current_profit recipe.output_quantity *= multiplier ``` **Balance targets**: - All chains should be within 2-3x profit/hour - No chain should be strictly better (dominant strategy) - Complex chains can pay more (reward knowledge) ### Pitfall 5: No Velocity Control (Resource Flood) **The Mistake**: ```python # ❌ Infinite resource production, no consumption class ResourceFlood: def mine(self, player, resource): # 100 units/hour per player # With 1000 players: 2.4M units/day # After 30 days: 72M units (infinite!) player.inventory[resource] += 100 ``` **Why It Fails**: - Resources accumulate infinitely - Supply >> demand always - Prices crash to near-zero - Mining becomes pointless (market flooded) **Real-World Example**: Runescape had resource inflation for years. They added item sinks (high-level equipment degrades and must be repaired with resources). **The Fix**: ```python # ✅ Add resource sinks class ControlledVelocity: def consume_fuel(self, player): # Travel requires fuel player.inventory['Fuel'] -= 10 def repair_ship(self, player, ship): # Repairs consume resources player.inventory['Iron'] -= 20 def decay_food(self, player): # Food spoils over time for item in player.inventory: if item.type == 'food': item.durability -= 1 if item.durability <= 0: player.inventory.remove(item) def storage_limits(self, player): # Can't hoard infinite resources max_storage = 10000 for resource in player.inventory: if player.inventory[resource] > max_storage: player.inventory[resource] = max_storage ``` **Resource sinks to add**: - Consumables (fuel, ammo, food) - Repairs (items degrade) - Decay (items expire) - Storage limits (can't hoard infinitely) - Crafting failures (chance to lose materials) ### Pitfall 6: New Player Hyperinflation Trap **The Mistake**: ```python # ❌ New players join into inflated economy class NewPlayerTrap: def __init__(self): # Month 1: Ships cost 3,500 # Month 6: Ships cost 500,000 (inflation) # New player has: 10,000 credits # Can't afford basic items! pass ``` **Why It Fails**: - Veterans have millions of credits - Prices inflate to match veteran wealth - New players can't afford anything - New player retention plummets **Real-World Example**: Eve Online addresses this with "new player areas" where prices are capped and subsidized. **The Fix**: ```python # ✅ Scale starting credits with inflation class NewPlayerProtection: def calculate_starting_credits(self): # Calculate current price index current_price_level = self.calculate_average_price() base_price_level = 100 # Launch prices inflation_ratio = current_price_level / base_price_level # Scale starting credits base_starting_credits = 10000 adjusted_credits = base_starting_credits * inflation_ratio return adjusted_credits def new_player_marketplace(self, player): # Separate market for new players if player.account_age_days < 7: # Access to subsidized prices return self.new_player_market else: return self.main_market ``` **Protections to add**: - Scaling starting credits (track inflation) - New player zones (capped prices) - Subsidized vendors (sell basics cheaply) - Progressive taxation (veterans lose money to system) ### Pitfall 7: Market Death Spirals **The Mistake**: ```python # ❌ Price spikes cause demand collapse class DeathSpiral: # Platinum spikes to 500 (10x normal) # → Nobody buys Platinum # → Supply accumulates # → Price crashes to 5 (10x too low) # → Nobody mines Platinum # → Supply dries up # → Price spikes again # OSCILLATION FOREVER ``` **Why It Fails**: - Unstable equilibrium (no damping) - Market never settles - Players can't rely on prices - Crafting becomes impossible **The Fix**: ```python # ✅ NPC market makers stabilize prices class StabilizedMarket: def __init__(self): self.npc_maker = NPCMarketMaker( target_price=50, liquidity=10000 ) def update(self): if self.is_market_unstable(): # NPC provides liquidity to stabilize self.npc_maker.provide_liquidity(self.order_book) ``` **Stabilization techniques**: - NPC market makers (provide liquidity) - Price floors (NPCs always buy at minimum) - Supply decay (excess inventory expires) - Inventory limits (can't stockpile infinitely) ## Real-World Examples ### Example 1: Eve Online (Complex Player-Driven Economy) **Eve Online** has one of the most complex game economies, with: - 300,000+ active players - $100+ million USD equivalent traded annually - Full order book matching - Regional markets (different prices per system) - Player corporations (guilds with shared resources) **Key Patterns**: ```python # Conceptual Eve economy system class EveOnlineEconomy: def __init__(self): # Regional markets (different prices per system) self.regions = {} # {region_id: Market} # Full order book matching self.order_books = {} # {(item, region): OrderBook} # NPC sinks self.npc_seeding = True # NPCs sell blueprints (faucets) self.transaction_tax = 0.025 # 2.5% sales tax (sink) self.broker_fee = 0.03 # 3% listing fee (sink) def regional_arbitrage(self, item): '''Hauling creates gameplay (buy cheap, sell expensive)''' # Example: Minerals cheap in mining systems jita_price = self.order_books[(item, 'Jita')].get_best_price() null_sec_price = self.order_books[(item, 'Null-Sec')].get_best_price() profit_per_unit = null_sec_price - jita_price # Hauling is risky (pirates, travel time) # But profitable if successful return profit_per_unit def production_chains(self): '''Deep production chains (10+ steps)''' # Minerals → Components → Subsystems → Ships # Each step adds value # Complex chains require specialization # Example: Building a Titan (supercarrier) # - Requires 100+ different materials # - Takes 6+ weeks of production time # - Costs 70+ billion ISK (700 PLEX ≈ $11,000 USD) def destruction_as_sink(self): '''Ships are destroyed in combat (major sink)''' # When ship explodes, 50% of materials are lost # Creates constant demand for new ships # Player-driven conflict = economy engine ``` **What Eve Gets Right**: 1. **Strong sinks**: 50% of destroyed ship value removed from game 2. **Regional economies**: Hauling creates gameplay and arbitrage 3. **Deep production chains**: Specialization and interdependence 4. **Player-driven conflict**: PvP creates demand (ships blow up) 5. **Full transparency**: All market data is public (third-party tools) **Lessons**: - Destruction is the ultimate sink (items permanently removed) - Regional markets create hauling gameplay - Complex chains encourage specialization - Transparency builds trust ### Example 2: Path of Exile (Currency Item Economy) **Path of Exile** has no gold. Instead, currency items are: - Functional (used for crafting) - Tradeable (player-to-player) - Self-regulating (supply/demand natural) **Key Patterns**: ```python # Conceptual PoE currency system class PathOfExileEconomy: def __init__(self): # Currency items are consumable (sinks built-in) self.currencies = { 'Chaos Orb': {'function': 'reroll_item', 'drop_rate': 0.001}, 'Exalted Orb': {'function': 'add_mod', 'drop_rate': 0.0001}, 'Mirror': {'function': 'duplicate_item', 'drop_rate': 0.000001}, } # No NPC trading (player-driven only) self.npc_vendors = None # NPCs only sell basic items def currency_as_sink(self, player, item): '''Using currency consumes it (automatic sink)''' if player.inventory['Chaos Orb'] > 0: player.inventory['Chaos Orb'] -= 1 # Consumed! item.reroll_mods() # Item gets random mods # This creates natural demand: # - Players use currency to craft # - Currency is removed from economy # - Prices remain stable def player_trading(self): '''No auction house - player negotiation''' # Trade ratios emerge naturally: # - 1 Exalted Orb ≈ 150 Chaos Orbs # - 1 Mirror ≈ 300 Exalted Orbs ≈ 45,000 Chaos Orbs # No NPC prices mean: # - Supply/demand sets prices naturally # - No arbitrage loops (no NPCs to exploit) # - Barter economy (currency for currency) ``` **What PoE Gets Right**: 1. **Currency is consumable**: Using it removes it from economy (built-in sink) 2. **No NPC trading**: Eliminates arbitrage exploits 3. **Functional currency**: Items have inherent value (not abstract credits) 4. **Player-driven prices**: Natural supply/demand equilibrium 5. **Scarcity tiers**: Common to ultra-rare currencies (progression) **Lessons**: - Make currency consumable (sinks built-in) - Eliminate NPCs from core trading (no exploits) - Functional currency has inherent value - Let players set prices (emergent economy) ### Example 3: World of Warcraft Auction House **WoW** has a hybrid economy: - Player auction house (peer-to-peer) - NPC vendors (fixed prices) - Gold faucets (quests, dailies) - Gold sinks (repairs, mounts, consumables) **Key Patterns**: ```python # Conceptual WoW auction house class WoWAuctionHouse: def __init__(self): self.listings = [] # Player listings self.deposit_fee = 0.05 # 5% to list (sink) self.auction_cut = 0.05 # 5% when sold (sink) def create_listing(self, seller, item, price, duration): '''Player lists item for sale''' # Deposit fee (lost even if item doesn't sell) deposit = price * self.deposit_fee seller.gold -= deposit # SINK listing = { 'seller': seller, 'item': item, 'buyout_price': price, 'duration': duration } self.listings.append(listing) def buy_listing(self, buyer, listing): '''Player buys item''' price = listing['buyout_price'] # Buyer pays full price buyer.gold -= price # Auction house takes cut (SINK) ah_cut = price * self.auction_cut # Seller receives (price - cut) listing['seller'].gold += (price - ah_cut) # Item transferred buyer.inventory.add(listing['item']) def gold_sinks(self, player): '''Various gold sinks''' # Repairs (items degrade) repair_cost = 100 player.gold -= repair_cost # Mounts (one-time purchase) mount_cost = 5000 player.gold -= mount_cost # Consumables (potions, food) consumable_cost = 50 player.gold -= consumable_cost # Fast travel flight_cost = 10 player.gold -= flight_cost ``` **What WoW Gets Right**: 1. **Auction house fees**: 5-10% removed from every trade (major sink) 2. **Repair costs**: Items degrade, require gold to fix 3. **One-time purchases**: Mounts, pets, transmog (large sinks) 4. **Consumables**: Constant demand (potions, food, enchants) 5. **NPC luxury items**: Cosmetics, toys (pure sinks) **Lessons**: - Transaction fees are effective sinks (every trade removes gold) - Durability/repairs create constant spending - One-time purchases (mounts) remove large amounts - Consumables provide perpetual sinks ### Example 4: Diablo 3 (Failed Economy → Fixed) **Diablo 3 at launch**: - Real Money Auction House (RMAH) - Item drops balanced around trading - Players could buy best gear (pay-to-win) **What Went Wrong**: ```python # Diablo 3 original economy (FAILED) class Diablo3OriginalEconomy: def __init__(self): # Real money auction house self.rmah = AuctionHouse(currency='USD') # Item drops nerfed (force players to trade) self.drop_rate_multiplier = 0.1 # 10x lower drops def perverse_incentives(self): '''Players stop playing, start trading''' # Best gear comes from RMAH, not gameplay # Players farm gold → buy gear # OR: Farm items → sell for $$$ # Result: Game becomes job, not fun # Players quit # Diablo 3 fixed economy (SUCCESSFUL) class Diablo3FixedEconomy: def __init__(self): # RMAH removed entirely self.rmah = None # No trading (account-bound loot) self.trading = False # Drop rates massively increased self.drop_rate_multiplier = 10.0 # 10x higher def loot_as_reward(self): '''Playing is rewarding (not trading)''' # Best gear comes from playing # No economy, no inflation, no exploits # Pure game balance ``` **Lessons from Diablo 3**: 1. **Real-money trading is toxic**: Creates pay-to-win, farming bots 2. **Removing economy can work**: Account-bound loot eliminates exploits 3. **Don't force trading**: Let players opt-in to economy 4. **Gameplay should reward players**: Not trading/grinding ### Example 5: Albion Online (Full Loot PvP Economy) **Albion Online** economy: - Full loot PvP (killed players drop everything) - Player-crafted gear (no NPC vendors) - Localized resources (encourages regional markets) ```python # Conceptual Albion Online economy class AlbionOnlineEconomy: def __init__(self): # All gear is player-crafted self.npc_vendors = None # Full loot PvP (major sink) self.full_loot = True def death_as_sink(self, killed_player): '''Player death removes items from economy''' # Killed player drops ALL gear # 50% is destroyed, 50% is loot for item in killed_player.equipment: if random() < 0.5: # Destroyed (SINK) item.delete() else: # Dropped (loot for killer) killed_player.position.spawn_loot(item) # This creates constant demand for new gear # Players must re-equip after death # Crafters always have customers def localized_resources(self): '''Different zones have different resources''' # Tier 8 resources only in dangerous zones # Forces risk vs reward decisions # Creates regional markets (hauling gameplay) ``` **What Albion Gets Right**: 1. **Full loot PvP**: Massive item sink (gear destroyed on death) 2. **Player-crafted economy**: No NPC vendors (pure player-driven) 3. **Localized resources**: Regional markets and hauling 4. **Risk vs reward**: Dangerous zones have best resources **Lessons**: - Destruction (death) is powerful sink - Player-crafting creates interdependence - Regional resources create hauling gameplay ## Cross-References ### Use This Skill WITH: - **game-balance/economy-balancing**: Overall game economy (XP, rewards, progression) - **multiplayer-netcode**: Synchronizing economic state across clients - **database-design**: Storing transactions, inventories, market data - **anti-cheat**: Preventing duping, botting, and exploits ### Use This Skill AFTER: - **game-design-fundamentals**: Understanding core loops and player motivations - **progression-systems**: Balancing rewards with economic constraints - **systems-thinking**: Understanding feedback loops and equilibrium ### Related Skills: - **crafting-systems**: Production chains and recipes - **trading-ui-patterns**: Interface for player marketplaces - **auction-house-algorithms**: Order matching and price discovery ## Testing Checklist ### Pre-Launch Validation - [ ] **Faucet/Sink Balance**: Sink/Faucet ratio is 0.8-1.2 - [ ] **No Arbitrage**: All production chains are net-negative vs NPCs - [ ] **Price Bounds**: All items have min/max prices set - [ ] **Bid-Ask Spread**: NPCs buy at 50-70% of sell price - [ ] **Production Balance**: All chains within 3x profit/hour of each other - [ ] **Transaction Fees**: 1-5% fee on all trades (major sink) - [ ] **Consumables**: Items that require constant spending exist - [ ] **Repair Costs**: Items degrade and require gold/resources to fix - [ ] **New Player Protection**: Starting credits scale with inflation OR subsidized market - [ ] **Rate Limiting**: Daily caps on mining/trading to prevent bots ### Stability Testing - [ ] **30-Day Simulation**: Simulate 1,000 players for 30 days, verify stable - [ ] **Exploit Search**: Run exploit detector to find arbitrage loops - [ ] **Market Manipulation**: Test coordinated buying (cornering) - [ ] **Bot Resistance**: Verify daily limits prevent 24/7 farming - [ ] **Death Spiral**: Check if price spikes cause permanent instability ### Post-Launch Monitoring - [ ] **Track Money Supply**: Log total credits in economy daily - [ ] **Track Inflation**: Monitor price index over time - [ ] **Detect Exploits**: Alert if player earns credits too fast - [ ] **Monitor Imbalances**: Flag if one production chain dominates - [ ] **New Player Metrics**: Track if new players can afford basics ### Emergency Fixes (If Economy Breaks) - [ ] **Rollback Database**: Restore to before exploit was discovered - [ ] **Patch Exploit**: Fix infinite money loop immediately - [ ] **Emergency Sinks**: Add temporary high-cost NPC items - [ ] **Ban Exploiters**: Remove profits from players who exploited - [ ] **Communication**: Announce fixes to player base transparently ## Summary Economic simulation in games requires balancing **faucets** (money entering) and **sinks** (money leaving), validating production chains for **arbitrage exploits**, and implementing **price stabilization** to prevent wild swings. The core principles are: 1. **Faucets < Sinks**: Money entering must be less than money leaving (0.8-1.0 ratio) 2. **Validate chains**: Production chains must be net-negative vs NPCs (no infinite money) 3. **Price bounds**: Set min/max prices (prevent 0 or infinity) 4. **Bid-ask spread**: NPCs buy at 50-70% of sell price (forces player trading) 5. **Balance chains**: All production chains should have similar profit/hour (within 3x) 6. **Resource sinks**: Consumables, repairs, decay (control velocity) 7. **New player protection**: Scale starting credits with inflation OR subsidized markets 8. **Test before launch**: Run 30-day simulation, exploit detection, balance analysis Master these patterns and avoid the common pitfalls (no sinks, NPC arbitrage, unbounded prices, unbalanced chains), and your economy will be stable, engaging, and exploit-resistant for months or years.