Can you make the MARKET ascii monospaced so that it will display properly, and can  you convert all other colored strings to using the same method of coloration as the ascii, instead of the def color_text function, since it's outputting the code as part of the string?


<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>MARKET: The Trading Game</title>
  
  <!-- Import Google Fonts -->
  <link href="https://fonts.googleapis.com/css2?family=Roboto+Mono:wght@400;700&display=swap" rel="stylesheet">
  
  <!-- Include PyScript CSS and JS -->
  <link rel="stylesheet" href="https://pyscript.net/latest/pyscript.css">
  <script defer src="https://pyscript.net/latest/pyscript.js"></script>
  
  <style>
    /* Reset default margins and paddings */
    * {
      margin: 0;
      padding: 0;
      box-sizing: border-box;
    }

    body {
      background-color: #1e1e2f;
      color: #f5f5f5;
      font-family: 'Press Start 2P', sans-serif;
      height: 100vh;
      display: flex;
      align-items: center;
      justify-content: center;
      position: relative;
      overflow: hidden;
    }

    /* Optional: Add a subtle background gradient to avoid external image loading */
    body::before {
      content: '';
      position: absolute;
      top: 0;
      left: 0;
      width: 100%;
      height: 100%;
      background: linear-gradient(135deg, #1e1e2f 25%, #2d2d3a 100%);
      opacity: 0.05;
      pointer-events: none;
    }

    #terminal {
      width: 95vw; /* Increased width from 90vw to 95vw */
      max-width: 1600px; /* Increased max-width from 800px to 1600px */
      height: 95vh; /* Increased height from 90vh to 95vh */
      background-color: #2d2d3a;
      padding: 25px;
      border: 2px solid #44475a;
      box-shadow: 0 0 20px rgba(255, 215, 0, 0.3); /* Gold glow */
      display: flex;
      flex-direction: column;
      justify-content: space-between;
      border-radius: 10px;
      position: relative;
      animation: pulseGlow 5s ease-in-out infinite;
      font-family: 'Roboto Mono', monospace;
    }

    /* Pulsing Glow Animation */
    @keyframes pulseGlow {
      0% {
        box-shadow: 0 0 10px rgba(255, 215, 0, 0.3);
      }
      50% {
        box-shadow: 0 0 30px rgba(255, 215, 0, 0.6);
      }
      100% {
        box-shadow: 0 0 10px rgba(255, 215, 0, 0.3);
      }
    }

    /* Header with Game Title and Logo */
    #header {
      display: flex;
      align-items: center;
      margin-bottom: 10px;
    }

    #logo {
      /* Using an emoji for the logo */
      margin-right: 15px;
      font-size: 48px; /* Adjust font size to match original logo size */
      display: flex;
      align-items: center;
      justify-content: center;
    }

    #game-title {
      font-size: 1.5em;
      font-weight: 700;
      color: #ffd700; /* Gold color */
    }

    #output {
      color: #f1fa8c; /* Light yellow */
      background-color: #1e1e2f;
      padding: 15px;
      margin-bottom: 15px;
      overflow-y: auto;
      flex-grow: 1;
      border-radius: 5px;
      box-shadow: inset 0 0 10px rgba(0,0,0,0.5);
      font-size: 1em;
    }

    #input-area {
      display: flex;
      flex-direction: column;
      align-items: stretch;
    }

    #user-input {
      width: 100%;
      height: 80px;
      background-color: #3e3e4a;
      color: #f1fa8c;
      border: 1px solid #44475a;
      padding: 10px;
      outline: none;
      resize: none;
      border-radius: 5px;
      font-size: 1em;
      font-family: 'Roboto Mono', monospace;
      margin-bottom: 10px;
    }

    #send-button {
      background-color: #44475a;
      color: #f1fa8c;
      border: none;
      padding: 10px 20px;
      font-size: 1em;
      cursor: pointer;
      border-radius: 5px;
      transition: background-color 0.3s, transform 0.2s;
      font-family: 'Roboto Mono', monospace;
    }

    #send-button:hover {
      background-color: #6272a4;
      transform: translateY(-2px);
    }

    #send-button:active {
      background-color: #44475a;
      transform: translateY(0);
    }

    #rulesButton {
      position: absolute;
      top: 15px;
      right: 15px;
      background-color: #44475a;
      color: #f1fa8c;
      border: none;
      padding: 8px 15px;
      font-size: 0.9em;
      cursor: pointer;
      border-radius: 5px;
      transition: background-color 0.3s, transform 0.2s;
      box-shadow: 0 0 5px rgba(255, 215, 0, 0.5);
      animation: pulseGlow 5s ease-in-out infinite;
      font-family: 'Roboto Mono', monospace;
    }

    #rulesButton:hover {
      background-color: #6272a4;
      transform: translateY(-2px);
    }

    #rulesButton:active {
      background-color: #44475a;
      transform: translateY(0);
    }

    #rulesPopup {
      display: none;
      position: fixed;
      top: 0; left: 0;
      width: 100%; height: 100%;
      background-color: rgba(30, 30, 47, 0.95);
      z-index: 999;
      font-family: 'Roboto', sans-serif;
      padding: 50px;
      overflow-y: auto;
    }

    #rulesContent {
      position: relative;
      background-color: #3e3e4a;
      color: #f1fa8c;
      padding: 30px;
      border-radius: 10px;
      max-width: 800px;
      margin: auto;
      box-shadow: 0 0 20px rgba(255, 215, 0, 0.3);
    }

    .closeButton {
      position: absolute;
      top: 15px;
      right: 20px;
      background-color: transparent;
      color: #ff5555;
      border: none;
      font-size: 2em;
      cursor: pointer;
      font-family: 'Roboto Mono', monospace;
    }

    .closeButton:hover {
      color: #ff6e6e;
    }

    #rulesContent h1 {
      text-align: center;
      margin-bottom: 20px;
      color: #ffd700;
    }

    #rulesContent p, #rulesContent ul {
      font-size: 1em;
      line-height: 1.6;
    }

    #rulesContent ul {
      list-style-type: disc;
      margin-left: 20px;
    }

    /* Responsive Adjustments */
    @media (max-width: 1600px) {
      #terminal {
        max-width: 1400px; /* Adjust max-width for slightly smaller screens */
      }
    }

    @media (max-width: 1200px) {
      #terminal {
        width: 100vw;
        max-width: none;
        height: 100vh;
      }
    }

    @media (max-width: 600px) {
      #user-input {
        height: 60px;
      }

      #send-button, #rulesButton {
        padding: 8px 12px;
        font-size: 0.8em;
      }

      #game-title {
        font-size: 1.2em;
      }
    }
  </style>
</head>
<body>
  <div id="terminal">
    <div id="header">
      <!-- Replaced the img tag with a span containing a money bag emoji -->
      <span id="logo">💰</span>
      <div id="game-title">MARKET: The Trading Game</div>
    </div>
    <div id="output"></div>
    <div id="input-area">
      <textarea id="user-input" placeholder="Enter your trading commands..."></textarea>
      <button id="send-button" onclick="handleSend()">Send</button>
    </div>
  </div>
  <button id="rulesButton" onclick="toggleRules()">Rules</button>
  <div id="rulesPopup">
    <div id="rulesContent">
      <button class="closeButton" onclick="toggleRules()">×</button>
      <h1>Game Rules</h1>
      <p>Welcome to <strong>MARKET: The Trading Game</strong>! Your objective is to accumulate wealth by strategically buying and selling goods across various towns. Here's how to play:</p>
      <ul>
        <li><strong>Commands:</strong> Use commands like <code>BUY</code>, <code>SELL</code>, <code>TRAVEL</code>, <code>STATUS</code>, <code>HELP</code>, and <code>EXIT</code> to interact with the game.</li>
        <li><strong>BUY:</strong> Purchase goods from the current town's market. Ensure you have enough money and cargo space.</li>
        <li><strong>SELL:</strong> Sell goods from your inventory to the current town's market. Maximize your profits by selling high-demand items.</li>
        <li><strong>TRAVEL:</strong> Move to a different town to explore new markets and opportunities.</li>
        <li><strong>STATUS:</strong> View your current money, inventory, cargo space, and remaining turns.</li>
        <li><strong>HELP:</strong> View the game rules and instructions.</li>
        <li><strong>EXIT:</strong> Quit the game at any time.</li>
        <li><strong>Events:</strong> Random events like economic booms, market crashes, and AI traders will influence the market. Adapt your strategies accordingly.</li>
      </ul>
      <p>Good luck, trader! May your investments be prosperous.</p>
    </div>
  </div>

  <!-- JavaScript Section -->
  <script>
    document.addEventListener('DOMContentLoaded', (event) => {
      const rulesPopup = document.getElementById('rulesPopup');
      rulesPopup.style.display = 'none';
    });

    function toggleRules() {
      const rulesPopup = document.getElementById('rulesPopup');
      rulesPopup.style.display = rulesPopup.style.display === 'block' ? 'none' : 'block';
    }

    const userInput = document.getElementById('user-input');
    const outputDiv = document.getElementById('output');
    const sendButton = document.getElementById('send-button');

    function handleSend() {
      const commandText = userInput.value.trim();
      if (!commandText) {
        return;
      }
      userInput.disabled = true;
      sendButton.disabled = true;

      const newDiv = document.createElement('div');
      newDiv.innerHTML = `<span style="color: #ffd700;">&gt;</span> ${commandText}`;
      outputDiv.appendChild(newDiv);

      outputDiv.scrollTop = outputDiv.scrollHeight;

      // Dispatch Custom Event with Command Text
      document.dispatchEvent(new CustomEvent('input-received', { detail: commandText }));
      userInput.value = '';
    }

    function instaPrint(message) {
      const newDiv = document.createElement('div');
      newDiv.innerHTML = `<span style="color: #f1fa8c;">${message}</span>`;
      outputDiv.appendChild(newDiv);
      outputDiv.scrollTop = outputDiv.scrollHeight;
    }

    function slowPrint(message) {
      const newDiv = document.createElement('div');
      newDiv.innerHTML = `<span style="color: #f1fa8c;"></span>`;
      outputDiv.appendChild(newDiv);
      outputDiv.scrollTop = outputDiv.scrollHeight;

      let index = 0;
      const interval = setInterval(() => {
        if (index < message.length) {
          newDiv.innerHTML += message.charAt(index);
          index++;
          outputDiv.scrollTop = outputDiv.scrollHeight;
        } else {
          clearInterval(interval);
          userInput.disabled = false;
          sendButton.disabled = false;
          userInput.focus();
        }
      }, 30); // Adjust typing speed here
    }
  </script>

  <!-- Corrected PyScript Section -->
<py-script>
from js import document, window
import asyncio
import random
from pyodide.ffi import create_proxy

# HTML Elements
output_div = document.getElementById('output')
user_input = document.getElementById('user-input')
send_button = document.getElementById('send-button')

# Asynchronous Print Functions
async def print_message(message, delay, batch_size=7):
    lines = message.split('\n')
    for line in lines:
        temp_div = document.createElement('div')
        output_div.appendChild(temp_div)
        partial_message = ''
        for i, char in enumerate(line):
            partial_message += char
            if (i + 1) % batch_size == 0 or i == len(line) - 1:
                temp_div.innerHTML += partial_message
                partial_message = ''
                await asyncio.sleep(delay)
        temp_div.innerHTML += '<br>'
        output_div.scrollTop = output_div.scrollHeight

async def insta_print(message):
    new_div = document.createElement('div')
    new_div.innerHTML = f"<br>{message}"
    output_div.appendChild(new_div)
    output_div.scrollTop = output_div.scrollHeight

async def slow_print(message):
    await print_message(message, delay=0.07, batch_size=5)

async def fast_print(message):
    await print_message(message, delay=0.0025, batch_size=5)

async def medium_print(message):
    await print_message(message, delay=0.005, batch_size=5)

# Asynchronous Input Handling
async def slow_input(prompt):
    await fast_print(prompt)
    user_input.disabled = False
    send_button.disabled = False
    user_input.value = ''
    user_input.focus()

    promise = asyncio.Future()

    def on_click(event):
        if user_input.value.strip() != '':
            promise.set_result(user_input.value.strip())
            send_button.disabled = True
            user_input.disabled = True

    input_handler = create_proxy(on_click)
    send_button.addEventListener('click', input_handler)
    
    def on_keypress(event):
        if event.key == 'Enter' and user_input.value.strip() != '':
            promise.set_result(user_input.value.strip())
            send_button.disabled = True
            user_input.disabled = True

    keypress_handler = create_proxy(on_keypress)
    user_input.addEventListener('keypress', keypress_handler)
    
    result = await promise
    send_button.removeEventListener('click', input_handler)
    user_input.removeEventListener('keypress', keypress_handler)
    return result.lower()

async def safe_int_input(prompt, min_val, max_val):
    while True:
        try:
            input_str = await slow_input(prompt)
            selected = int(input_str)
            if min_val <= selected <= max_val:
                return selected
            else:
                await fast_print(f"Please enter a number between {min_val} and {max_val}.")
        except ValueError:
            await fast_print("Invalid input. Please enter a valid number.")

# Helper Function for Coloring Text
def color_text(text, color):
    return f'<span style="color:{color};">{text}</span>'

# Game Constants and Data
GOODS = {
    "Spices": {"base_price": 100, "weight": 5, "base_supply": 500, "base_demand": 300},
    "Gems": {"base_price": 500, "weight": 10, "base_supply": 200, "base_demand": 150},
    "Food": {"base_price": 50, "weight": 3, "base_supply": 800, "base_demand": 600},
    "Silk": {"base_price": 200, "weight": 4, "base_supply": 300, "base_demand": 250},
    "Iron": {"base_price": 150, "weight": 15, "base_supply": 400, "base_demand": 350},
    "Wine": {"base_price": 120, "weight": 6, "base_supply": 350, "base_demand": 300},
    "Tea": {"base_price": 80, "weight": 2, "base_supply": 600, "base_demand": 500},
    "Herbs": {"base_price": 60, "weight": 3, "base_supply": 700, "base_demand": 550},
    "Cloth": {"base_price": 180, "weight": 8, "base_supply": 250, "base_demand": 200},
    "Gold": {"base_price": 1000, "weight": 20, "base_supply": 100, "base_demand": 80},
}

TOWNS = [
    "Amberville", "Brightstone", "Crimsonburg", "Dusktown",
    "Sunset City", "Northport", "Riverside", "Hilltop",
    "Lakeside", "Oakwood", "Stormhaven", "Fairview"
]

GLOBAL_EVENTS = [
    "Economic boom affecting all markets.",
    "Global trade embargo reducing supply worldwide.",
    "Pandemic affecting demand for certain goods globally.",
    "Technological advancements altering trade dynamics.",
    "Global inflation increasing prices across the board.",
    "Global recession decreasing purchasing power.",
]

LOCAL_EVENTS = [
    "Thieves attacked your caravan and stole some goods!",
    "A storm ruined your goods during travel.",
    "A festival in town increased demand for certain goods.",
    "Disease outbreak decreased the demand for food.",
    "New trade routes discovered, affecting supply.",
    "Pirates disrupted trade in the seas.",
    "Economic boom in Crimsonburg raised prices.",
    "Market crash in Brightstone lowered prices.",
    "Government imposed a tax on trade.",
    "Discovery of a rare gem deposit increased gem supply.",
    "Locusts devastated the food supplies in Dusktown.",
    "Artisans in Amberville created new uses for spices, increasing their demand.",
    "Floods in Riverside increased the supply of iron.",
    "Harvest season in Lakeside boosted the demand for food.",
    "Gold rush in Hilltop increased the supply and demand for gold.",
    "Silk shortage in Sunset City raised silk prices.",
    "Herbal harvest in Oakwood decreased the supply of herbs.",
    "Tea festival in Fairview increased the demand for tea.",
    "Ironworks expansion in Northport increased the supply of iron.",
    "Wine tasting event in Stormhaven boosted wine prices.",
    "Cloth market crash in Fairview lowered cloth prices.",
    "Bandit attack in Oakwood stole some of your goods.",
    "Natural disaster in Dusktown affecting multiple goods.",
    "Town marketplace upgrade in Riverside lowered base prices for certain goods.",
]

DIFFICULTIES = {
    "EASY": {
        "event_chance": 0.15,
        "ai_aggressiveness": 0.8,
        "starting_money": 3000,
        "starting_cargo": 300,
        "turns": 40
    },
    "MEDIUM": {
        "event_chance": 0.2,
        "ai_aggressiveness": 1.0,
        "starting_money": 2000,
        "starting_cargo": 200,
        "turns": 30
    },
    "HARD": {
        "event_chance": 0.25,
        "ai_aggressiveness": 1.2,
        "starting_money": 1500,
        "starting_cargo": 150,
        "turns": 25
    }
}

RELATIONSHIP_BASE = 0
RELATIONSHIP_MAX = 100
RELATIONSHIP_MIN = -100

class AITrader:
    """Represents an AI trader that interacts with the market each turn."""
    def __init__(self, trader_id, aggressiveness=1.0):
        self.trader_id = trader_id
        self.name = f"Trader_{trader_id}"
        self.aggressiveness = aggressiveness  # Determines transaction sizes and frequency

    def perform_action(self, market):
        """AI trader performs buy or sell actions based on strategy."""
        num_actions = random.randint(1, 3) if self.aggressiveness > 1.0 else random.randint(1, 2)
        for _ in range(num_actions):
            action = random.choices(["BUY", "SELL"], weights=[self.aggressiveness, 1.0])[0]
            good = random.choice(list(GOODS.keys()))
            quantity = random.randint(1, int(10 * self.aggressiveness))
            if action == "BUY":
                if market.supply[good] >= quantity:
                    market.supply[good] -= quantity
                    market.demand[good] += quantity * 0.5
            elif action == "SELL":
                market.supply[good] += quantity
                market.demand[good] -= quantity * 0.5
                if market.demand[good] < 50:
                    market.demand[good] = 50

class Market:
    """Represents the market of a specific town."""
    PRICE_FLOOR = 10
    PRICE_CEILING = 2000

    def __init__(self, town):
        self.town = town
        self.supply = {}
        self.demand = {}
        for good, info in GOODS.items():
            supply_variation = random.uniform(0.9, 1.1)
            demand_variation = random.uniform(0.9, 1.1)
            self.supply[good] = info["base_supply"] * supply_variation
            self.demand[good] = info["base_demand"] * demand_variation
        self.prices = {}
        self.generate_prices()
        self.ai_traders = [AITrader(trader_id=i+1, aggressiveness=random.uniform(0.8, 1.2)) for i in range(10)]

    def generate_prices(self):
        """Calculates current prices based on supply and demand with elasticity and price constraints."""
        for good, info in GOODS.items():
            if self.supply[good] == 0:
                price_multiplier = 2
            else:
                ratio = self.demand[good] / self.supply[good]
                elasticity = random.uniform(0.90, 1.10)
                price_multiplier = ratio * elasticity
            new_price = int(GOODS[good]["base_price"] * price_multiplier)
            new_price = max(self.PRICE_FLOOR, min(new_price, self.PRICE_CEILING))
            self.prices[good] = new_price

    async def display_prices(self, relationship):
        """Displays the current prices, supply, demand, and relationship for all goods in the market."""
        status = Game.get_relationship_status(relationship)
        await slow_print(f"\n{color_text('Market Overview in ' + self.town + ':', 'magenta')} {color_text('Relationship: ' + str(relationship) + ' (' + status + ')', 'green')}")
        for good, price in self.prices.items():
            supply = int(self.supply[good])
            demand = int(self.demand[good])
            await fast_print(f"{good}: ${price} (Supply: {supply}, Demand: {demand})")
        await fast_print("")

    def process_ai_traders(self):
        """Processes all AI traders' actions for the market."""
        for trader in self.ai_traders:
            trader.perform_action(self)
        self.generate_prices()

class Player:
    """Represents the player with money, inventory, and cargo capacity."""
    def __init__(self, starting_money, starting_cargo):
        self.money = starting_money
        self.inventory = {good: 0 for good in GOODS}
        self.location = random.choice(TOWNS)
        self.turns = 0
        self.max_cargo = starting_cargo
        self.current_cargo = 0
        self.cargo_upgrades = 0
        self.relationships = {town: RELATIONSHIP_BASE for town in TOWNS}
        self.event_log = []  # To track key events per turn

    async def status(self):
        """Displays the player's current status."""
        await slow_print(f"\n{color_text('Player Status:', 'blue')}")
        await fast_print(f"Location: {self.location}")
        await fast_print(f"Money: ${self.money}")
        await fast_print(f"Cargo: {self.current_cargo}/{self.max_cargo} units used")
        await fast_print("Inventory:")
        for good, qty in self.inventory.items():
            await fast_print(f"  {good}: {qty}")
        await fast_print("")

    def add_cargo(self, good, quantity):
        """Attempts to add cargo to the player's inventory."""
        weight = GOODS[good]["weight"]
        total_weight = weight * quantity
        if self.current_cargo + total_weight > self.max_cargo:
            return False, total_weight
        self.current_cargo += total_weight
        self.inventory[good] += quantity
        return True, total_weight

    def remove_cargo(self, good, quantity):
        """Removes cargo from the player's inventory."""
        weight = GOODS[good]["weight"]
        total_weight = weight * quantity
        self.current_cargo -= total_weight
        if self.current_cargo < 0:
            self.current_cargo = 0
        self.inventory[good] -= quantity
        if self.inventory[good] < 0:
            self.inventory[good] = 0

    async def upgrade_cargo(self):
        """Allows the player to upgrade cargo capacity."""
        upgrade_cost = 500 + (self.cargo_upgrades * 200)
        upgrade_amount = 50
        if self.money < upgrade_cost:
            await slow_print("Insufficient funds to upgrade cargo capacity.")
            return
        confirm = await slow_input(f"Upgrade cargo capacity by {upgrade_amount} for {upgrade_cost} gold? (Y/N): ")
        if confirm.upper() == 'Y':
            self.money -= upgrade_cost
            self.max_cargo += upgrade_amount
            self.cargo_upgrades += 1
            await slow_print(f"Cargo capacity increased by {upgrade_amount}. New capacity: {self.max_cargo}")
            self.event_log.append(f"Upgraded cargo capacity by {upgrade_amount} for {upgrade_cost} gold.")

class Game:
    """Represents the main game loop and logic."""
    def __init__(self):
        self.difficulty = None
        self.player = None
        self.turns_total = 0
        self.markets = {}
        self.current_market = None
        self.current_event = None
        self.supply_decay_rate = 0.02
        self.demand_decay_rate = 0.02
        self.turn_counter = 1
        self.game_over = False
        self.key_events = []

    @staticmethod
    def get_relationship_status(relation):
        """Converts a numerical relationship score into a descriptive status."""
        if relation > 50:
            return "Friendly"
        elif relation >= -50:
            return "Neutral"
        else:
            return "Hostile"

    async def choose_difficulty(self):
        """Allows the player to choose a difficulty level."""
        await fast_print("Choose Difficulty Level:")
        for idx, level in enumerate(DIFFICULTIES.keys(), 1):
            await fast_print(f"{idx}. {level.capitalize()}")
        choice = await safe_int_input(f"Enter the number corresponding to your choice (1-{len(DIFFICULTIES)}): ", 1, len(DIFFICULTIES))
        selected = list(DIFFICULTIES.keys())[choice - 1]
        await fast_print(f"Selected {selected.capitalize()} difficulty.\n")
        self.difficulty = selected
        self.player = Player(
            starting_money=DIFFICULTIES[self.difficulty]["starting_money"],
            starting_cargo=DIFFICULTIES[self.difficulty]["starting_cargo"]
        )
        self.turns_total = DIFFICULTIES[self.difficulty]["turns"]
        self.markets = {town: Market(town) for town in TOWNS}
        self.current_market = self.markets[self.player.location]

    async def start(self):
        """Starts the game by displaying the title and welcome message."""
        # Define ASCII Art Lines
        ascii_art_lines = [
            "  __  __    _    ____  _  _______ _____",
            "   |  \\/  |  / \\  |  _ \\| |/ / ____|_   _|",
            "   | |\\/| | / _ \\ | |_) | ' /|  _|   | |  ",
            "   | |  | |/ ___ \\|  _ <| . \\| |___  | |  ",
            "   |_|  |_/_/   \\_\\_| \\_\\_|\\_\\_____| |_|  ",
            "                                                "
        ]

        # Apply Coloring to ASCII Art Lines
        colored_art = ''.join([color_text(line, 'cyan') + '<br>' for line in ascii_art_lines])
        await insta_print(colored_art)

        # Display Welcome Message
        await slow_print(f"""
{color_text('='*60, 'yellow')}<br>
Welcome to the Text-Based Trading Game!<br>
{color_text('='*60, 'yellow')}<br>
Your goal is to accumulate as much wealth as possible within a set number of turns.
Explore a variety of goods, manage your cargo capacity, and navigate the dynamic market.
""".strip())
        await self.choose_difficulty()
        await self.game_loop()

    async def game_loop(self):
        """Main game loop that runs until the player runs out of turns."""
        while self.turn_counter <= self.turns_total and not self.game_over:
            await medium_print(f"\n--- Turn {self.turn_counter}/{self.turns_total} ---")
            self.apply_decay()
            for market in self.markets.values():
                market.process_ai_traders()
            self.trigger_event()
            current_relationship = self.player.relationships[self.player.location]
            await self.current_market.display_prices(current_relationship)
            await self.player.status()
            command = await slow_input("Enter command (BUY/SELL/TRAVEL/STATUS/UPGRADE/OVERVIEW/HELP/EXIT): ")
            command = command.lower()
            if command == "buy":
                await self.buy()
            elif command == "sell":
                await self.sell()
            elif command == "travel":
                await self.travel()
            elif command == "status":
                await self.player.status()
                continue
            elif command == "upgrade":
                await self.player.upgrade_cargo()
                continue
            elif command == "overview":
                await self.market_overview()
                continue
            elif command == "help":
                await slow_print(f"""
{color_text('='*60, 'yellow')}<br>
Available Commands:<br>
BUY - Purchase goods from the market.<br>
SELL - Sell goods from your inventory.<br>
TRAVEL - Move to a different town.<br>
STATUS - View your current status.<br>
UPGRADE - Upgrade your cargo capacity.<br>
OVERVIEW - View market overview across all towns.<br>
HELP - Display this help message.<br>
EXIT - Exit the game.<br>
{color_text('='*60, 'yellow')}
""".strip())
                continue
            elif command == "exit":
                await slow_print("Thank you for playing!")
                await self.end_game()
                return
            else:
                await fast_print("Invalid command.")
                continue
            self.turn_counter += 1
        await self.end_game()

    async def buy(self):
        """Handles the buying process for the player."""
        good = await self.choose_good("buy")
        if not good:
            return

        price = self.current_market.prices[good]
        max_affordable_quantity = self.player.money // price
        available_cargo_space = self.player.max_cargo - self.player.current_cargo
        max_cargo_quantity = available_cargo_space // GOODS[good]["weight"]
        max_supply = int(self.current_market.supply[good])
        max_quantity = min(max_affordable_quantity, max_cargo_quantity, max_supply)

        if max_quantity <= 0:
            await slow_print("You cannot buy any units of this good due to insufficient funds, cargo space, or supply.")
            return

        discount = 0
        if max_quantity >= 20:
            discount = 0.05
            discounted_price = int(price * (1 - discount))
            await fast_print(f"Bulk purchase discount applied! Price per unit: ${discounted_price} (Original: ${price})")
        else:
            discounted_price = price

        await fast_print(f"You can afford up to {max_quantity} units based on your constraints.")
        quantity = await safe_int_input(f"Enter the quantity of {good} to buy (max {max_quantity}): ", 1, max_quantity)

        cost = discounted_price * quantity
        success, added_weight = self.player.add_cargo(good, quantity)
        if not success:
            await slow_print(f"Cannot buy {quantity} {good}. Exceeds cargo capacity.")
            return

        self.player.money -= cost
        self.current_market.supply[good] -= quantity
        self.current_market.demand[good] += quantity * 0.3
        await slow_print(f"Bought {quantity} {good} for ${cost}.")
        await fast_print(f"Cargo Updated: {self.player.current_cargo}/{self.player.max_cargo} units used.")
        self.player.event_log.append(f"Turn {self.turn_counter}: Bought {quantity} {good} for ${cost}.")
        self.key_events.append(f"Turn {self.turn_counter}: Bought {quantity} {good} for ${cost}.")

    async def sell(self):
        """Handles the selling process for the player."""
        good = await self.choose_good("sell")
        if not good:
            return
        if self.player.inventory[good] == 0:
            await fast_print(f"You have no {good} to sell.")
            return

        max_sell_quantity = int(self.player.inventory[good])
        await fast_print(f"You can sell up to {max_sell_quantity} units based on your inventory.")
        quantity = await safe_int_input(f"Enter the quantity of {good} to sell (max {max_sell_quantity}): ", 1, max_sell_quantity)

        price = self.current_market.prices[good]
        revenue = price * quantity

        self.player.money += revenue
        self.player.remove_cargo(good, quantity)
        self.current_market.supply[good] += quantity
        self.current_market.demand[good] -= quantity * 0.3
        await slow_print(f"Sold {quantity} {good} for ${revenue}.")
        await fast_print(f"Cargo Updated: {self.player.current_cargo}/{self.player.max_cargo} units used.")
        self.player.event_log.append(f"Turn {self.turn_counter}: Sold {quantity} {good} for ${revenue}.")
        self.key_events.append(f"Turn {self.turn_counter}: Sold {quantity} {good} for ${revenue}.")

    async def travel(self):
        """Handles the travel process for the player to move between towns."""
        available_towns = [town for town in TOWNS if town != self.player.location]
        await slow_print("\nAvailable towns to travel to:")
        for idx, town in enumerate(available_towns, 1):
            await fast_print(f"{idx}. {town}")
        choice = await safe_int_input(f"Enter the number of the town you want to travel to (1-{len(available_towns)}): ", 1, len(available_towns))
        new_location = available_towns[choice - 1]
        self.player.location = new_location
        self.current_market = self.markets[new_location]
        await slow_print(f"Traveled to {new_location}.")
        self.player.event_log.append(f"Turn {self.turn_counter}: Traveled to {new_location}.")
        self.key_events.append(f"Turn {self.turn_counter}: Traveled to {new_location}.")

    async def choose_good(self, action):
        """Prompts the player to choose a good to buy or sell."""
        await fast_print("Available goods:")
        for idx, good in enumerate(GOODS.keys(), 1):
            await fast_print(f"{idx}. {good}")
        choice = await safe_int_input(f"Enter the number of the good to {action}: ", 1, len(GOODS))
        return list(GOODS.keys())[choice - 1]

    async def market_overview(self):
        """Provides a market overview of all towns."""
        await slow_print(f"\n{color_text('Market Overview Across All Towns:', 'green')}")
        for town, market in self.markets.items():
            avg_price = sum(market.prices.values()) / len(GOODS)
            await fast_print(f"{town}: Average Price ${int(avg_price)}")
        await fast_print("")

    def trigger_event(self):
        """Randomly triggers a global or local event based on difficulty settings."""
        event_chance = DIFFICULTIES[self.difficulty]["event_chance"]
        if random.random() < event_chance:
            event_type = random.choice(["GLOBAL", "LOCAL"])
            if event_type == "GLOBAL":
                event = random.choice(GLOBAL_EVENTS)
                asyncio.create_task(insta_print(f"\n{color_text('Global Event: ' + event, 'red')}"))
                self.apply_event(event, global_event=True)
                self.key_events.append(f"Turn {self.turn_counter}: Global Event - {event}")
            else:
                event = random.choice(LOCAL_EVENTS)
                asyncio.create_task(insta_print(f"\n{color_text('Local Event: ' + event, 'red')}"))
                self.apply_event(event, global_event=False)
                self.key_events.append(f"Turn {self.turn_counter}: Local Event - {event}")

    def apply_event(self, event, global_event=False):
        """Applies the effects of a triggered event to the game."""
        # Player-specific positive events (Example)
        if "found extra goods" in event:
            good = random.choice(list(GOODS.keys()))
            quantity = random.randint(5, 15)
            self.player.inventory[good] += quantity
            self.player.current_cargo += GOODS[good]["weight"] * quantity
            self.player.money += 100
            asyncio.create_task(slow_print(f"You found extra {quantity} units of {good} and gained $100!"))
            self.player.event_log.append(f"Turn {self.turn_counter}: Found extra {quantity} {good} and gained $100.")
            return

        # Player reactions to events
        if "tax" in event:
            tax_amount = 50
            self.player.money = max(0, self.player.money - tax_amount)
            asyncio.create_task(slow_print(f"The government imposed a tax on your trade. You lost ${tax_amount}."))
            self.player.event_log.append(f"Turn {self.turn_counter}: Imposed tax of ${tax_amount}.")
            return

        # Implement event effects
        if "Economic boom" in event:
            affected_good = "Gems"
            price_boost = int(GOODS[affected_good]["base_price"] * 0.4)
            self.current_market.demand[affected_good] += 50
            self.current_market.prices[affected_good] += price_boost
            asyncio.create_task(slow_print(f"An economic boom raised the demand and price of {affected_good} by ${price_boost}."))
            self.player.relationships[self.player.location] += 10
        elif "Market crash" in event:
            affected_good = "Spices"
            price_drop = int(GOODS[affected_good]["base_price"] * 0.35)
            self.current_market.demand[affected_good] = max(50, self.current_market.demand[affected_good] - 100)
            self.current_market.prices[affected_good] = max(10, self.current_market.prices[affected_good] - price_drop)
            asyncio.create_task(slow_print(f"A market crash decreased the demand and price of {affected_good} by ${price_drop}."))
            self.player.relationships[self.player.location] -= 10
        elif "thieves" in event or "bandit" in event:
            stolen_good = random.choice(list(GOODS.keys()))
            stolen_qty = random.randint(2, 10)
            actual_stolen = min(stolen_qty, self.player.inventory[stolen_good])
            self.player.inventory[stolen_good] -= actual_stolen
            self.player.remove_cargo(stolen_good, actual_stolen)
            self.current_market.supply[stolen_good] += actual_stolen * 0.2
            self.current_market.demand[stolen_good] -= actual_stolen * 0.2
            asyncio.create_task(slow_print(f"Bandits stole {actual_stolen} units of {stolen_good} from your cargo!"))
            self.player.relationships[self.player.location] -= 15
        elif "storm" in event or "natural disaster" in event:
            damaged_good = random.choice(list(GOODS.keys()))
            damaged_qty = random.randint(2, 10)
            actual_damaged = min(damaged_qty, self.player.inventory[damaged_good])
            self.player.inventory[damaged_good] -= actual_damaged
            self.player.remove_cargo(damaged_good, actual_damaged)
            self.current_market.supply[damaged_good] += actual_damaged * 0.1
            self.current_market.demand[damaged_good] -= actual_damaged * 0.1
            asyncio.create_task(slow_print(f"A storm damaged {actual_damaged} units of {damaged_good} in your cargo."))
            self.player.relationships[self.player.location] -= 5
        elif "festival" in event:
            affected_good = random.choice(["Spices", "Wine", "Cloth"])
            price_boost = int(GOODS[affected_good]["base_price"] * 0.3)
            self.current_market.demand[affected_good] += 50
            self.current_market.prices[affected_good] += price_boost
            asyncio.create_task(slow_print(f"The festival increased the demand for {affected_good}, boosting its price by ${price_boost}."))
            self.player.relationships[self.player.location] += 5
        elif "disease" in event:
            affected_good = "Food"
            price_drop = int(GOODS[affected_good]["base_price"] * 0.2)
            self.current_market.demand[affected_good] = max(50, self.current_market.demand[affected_good] - 100)
            self.current_market.prices[affected_good] = max(10, self.current_market.prices[affected_good] - price_drop)
            asyncio.create_task(slow_print(f"A disease outbreak decreased the demand for {affected_good}, lowering its price by ${price_drop}."))
            self.player.relationships[self.player.location] -= 10
        elif "trade routes" in event:
            affected_good = random.choice(list(GOODS.keys()))
            supply_change = random.randint(-30, 30)
            self.current_market.supply[affected_good] = max(10, self.current_market.supply[affected_good] + supply_change)
            asyncio.create_task(slow_print(f"New trade routes have altered the supply of {affected_good}, changing its supply by {supply_change} units."))
        elif "pirates" in event:
            affected_good = random.choice(list(GOODS.keys()))
            price_drop = int(GOODS[affected_good]["base_price"] * 0.25)
            self.current_market.demand[affected_good] = max(50, self.current_market.demand[affected_good] - 50)
            self.current_market.prices[affected_good] = max(10, self.current_market.prices[affected_good] - price_drop)
            asyncio.create_task(slow_print(f"Pirates disrupted trade, decreasing the demand and price of {affected_good} by ${price_drop}."))
            self.player.relationships[self.player.location] -= 10
        elif "rare gem" in event:
            affected_good = "Gems"
            supply_increase = 50
            self.current_market.supply[affected_good] = max(10, self.current_market.supply[affected_good] + supply_increase)
            self.current_market.demand[affected_good] -= 30
            asyncio.create_task(slow_print(f"Discovery of a rare gem deposit increased the supply of {affected_good}, lowering its price."))
        elif "locusts" in event:
            affected_good = "Food"
            damage = random.randint(5, 10)
            actual_damaged = min(damage, self.player.inventory[affected_good])
            self.player.inventory[affected_good] -= actual_damaged
            self.player.remove_cargo(affected_good, actual_damaged)
            self.current_market.supply[affected_good] += actual_damaged * 0.2
            self.current_market.demand[affected_good] -= actual_damaged * 0.2
            asyncio.create_task(slow_print(f"Locusts devastated your food supplies, destroying {actual_damaged} units of {affected_good}."))
            self.player.relationships[self.player.location] -= 5
        elif "artisans" in event:
            affected_good = "Spices"
            price_boost = int(GOODS[affected_good]["base_price"] * 0.25)
            self.current_market.demand[affected_good] += 40
            self.current_market.prices[affected_good] += price_boost
            asyncio.create_task(slow_print(f"Artisans in {self.player.location} created new uses for {affected_good}, increasing its demand and price by ${price_boost}."))
            self.player.relationships[self.player.location] += 10
        elif "global" in event.lower():
            if "economic boom" in event.lower():
                for market in self.markets.values():
                    affected_good = "Gold"
                    price_boost = int(GOODS[affected_good]["base_price"] * 0.2)
                    market.demand[affected_good] += 30
                    market.prices[affected_good] += price_boost
                asyncio.create_task(slow_print("A global economic boom has increased the demand and prices for Gold across all markets."))
            elif "global recession" in event.lower():
                for market in self.markets.values():
                    affected_good = "Food"
                    price_drop = int(GOODS[affected_good]["base_price"] * 0.15)
                    market.demand[affected_good] = max(50, market.demand[affected_good] - 50)
                    market.prices[affected_good] = max(10, market.prices[affected_good] - price_drop)
                asyncio.create_task(slow_print("A global recession has decreased the demand and prices for Food across all markets."))

    async def end_game(self):
        """Displays the final game results and concludes the game."""
        await slow_print("\nGame Over!")
        await fast_print(f"Final Money: ${self.player.money}")
        total_goods = sum(self.player.inventory.values())
        await fast_print(f"Total Goods: {total_goods}")
        net_worth = self.player.money + sum(
            self.player.inventory[good] * self.current_market.prices[good] for good in GOODS
        )
        await fast_print(f"Net Worth: ${net_worth}")
        if net_worth >= 10000:
            await slow_print("Congratulations! You became a legendary trader!")
        elif net_worth >= 5000:
            await slow_print("Well done! You made a substantial profit.")
        elif net_worth >= 2000:
            await slow_print("Good job! You made a respectable profit.")
        else:
            await slow_print("Better luck next time. Your trading skills need improvement.")

        # Display key events summary
        await slow_print("\nSummary of Your Journey:")
        for event in self.key_events:
            await fast_print(event)

        await slow_print("\nThank you for playing the Text-Based Trading Game!")
        # Disable further input
        user_input.disabled = True
        send_button.disabled = True

    def apply_decay(self):
        """Applies decay to supply and demand in all markets to move towards base values."""
        for good, info in GOODS.items():
            for market in self.markets.values():
                # Supply decay towards base_supply
                if market.supply[good] < info["base_supply"]:
                    market.supply[good] += info["base_supply"] * self.supply_decay_rate
                    if market.supply[good] > info["base_supply"]:
                        market.supply[good] = info["base_supply"]
                elif market.supply[good] > info["base_supply"]:
                    market.supply[good] -= info["base_supply"] * self.supply_decay_rate
                    if market.supply[good] < info["base_supply"]:
                        market.supply[good] = info["base_supply"]

                # Demand decay towards base_demand
                if market.demand[good] < info["base_demand"]:
                    market.demand[good] += info["base_demand"] * self.demand_decay_rate
                    if market.demand[good] > info["base_demand"]:
                        market.demand[good] = info["base_demand"]
                elif market.demand[good] > info["base_demand"]:
                    market.demand[good] -= info["base_demand"] * self.demand_decay_rate
                    if market.demand[good] < info["base_demand"]:
                        market.demand[good] = info["base_demand"]

# Initialize and start the game
game_instance = Game()
asyncio.create_task(game_instance.start())
</py-script>
</body>	
</html>