diff --git a/main.py b/main.py index 6ed04c2..2827c81 100644 --- a/main.py +++ b/main.py @@ -1,3 +1,27 @@ +""" +A simple business simulation game with a focus on market dynamics, crafting and trading. + +Key features: +- Market simulation: Manage dynamic product prices, economic factors and random events. +- Business management: Allows players to craft products, trade and manage inventory. +- AI Competitors: Includes RationalAI and RiskTakingAI, each with unique strategies. +- Crafting System: Allows the creation of products using specific recipes. +- Stock Market: Facilitates stock trading for players and AI, affecting financial strategies. +- Turn-based gameplay: Includes crafting, trading and stock decisions per turn. + +Goals: +- Engage players in strategic economic decisions in a compact, interactive format. +- Utilises a 'rich' library for enhanced console performance and interaction. + +How to play: +1. Start the game by running the main function. +2. Decide on crafting, trading and stock transactions. +3. Compete against the AI with different strategies. +4. Play through a set number of rounds, resulting in a final score. + +Note: Requires 'rich' and 'json' libraries for functionality. +""" + import json import random @@ -6,6 +30,9 @@ from rich.table import Table from rich.prompt import Prompt from rich import box +from rich.panel import Panel +from rich.text import Text + crafting_recipes = { 'recipe1': {'input': {'A': 4, 'B': 4}, 'output': {'C': 10}, 'turns': 3}, 'recipe2': {'input': {'A': 1, 'B': 1}, 'output': {'C': 2}, 'turns': 2} @@ -13,6 +40,7 @@ crafting_recipes = { def load_json(filename): + """Loads JSON data from a file.""" try: with open(filename) as file: return json.load(file) @@ -22,6 +50,8 @@ def load_json(filename): class Market: + """Manages market dynamics, including companies, products, stock transactions, and economic indicators.""" + def __init__(self): self.current_turn = 0 self.companies = {} @@ -30,7 +60,7 @@ class Market: self.adjust_prices = lambda adjustment: {k: max(1, v + adjustment) for k, v in self.products.items()} self.event_effects = {"double": lambda x: x * 2, "halve": lambda x: x / 2, "increase": lambda x: x + 3.0, "decrease": lambda x: max(1, x - 3.0)} - self.stock_ledger = {} # Initialize the stock ledger + self.stock_ledger = {} # Economic indicators self.inflation_rate = 0.02 # Example starting inflation rate (2%) @@ -38,21 +68,25 @@ class Market: self.gdp = 500000 # Example starting GDP value def update_stock_ledger(self, company_id, owner_id, amount): + """Updates the ledger with the given stock transaction details.""" key = (company_id, owner_id) self.stock_ledger[key] = self.stock_ledger.get(key, 0) + amount def get_stock_ownership(self, company_id, owner_id): + """Retrieves stock ownership details for a specified company and owner.""" return self.stock_ledger.get((company_id, owner_id), 0) def get_stock_price(self, company_id): - company_value = self.companies[company_id].get_value(self) + """Returns the current stock price of the specified company.""" + company = self.companies[company_id] + company_value = company.value # No argument passed return round(company_value / 100.0, 2) def update_market(self): + """Applies market events, adjusts prices, and updates economic indicators for the current turn.""" self.current_turn += 1 trend_adjustment = random.choice([2, -2, 0]) - # Adjust prices based on inflation self.products = self.adjust_prices(trend_adjustment) event = random.choices(self.events, weights=[e["probability"] for e in self.events])[0] @@ -61,36 +95,34 @@ class Market: else: self.products = {k: self.event_effects[event["effect"]](v) for k, v in self.products.items()} - self.update_economic_indicators() # Update economic indicators each turn + self.update_economic_indicators() def adjust_prices(self): - # Apply inflation more subtly and cumulatively + """Adjusts market product prices based on inflation and other factors.""" for product, price in self.products.items(): - # Apply inflation inflation_adjustment = price * self.inflation_rate new_price = price + inflation_adjustment - # Apply a very small random fluctuation for additional realism - fluctuation = random.uniform(-0.03, 0.03) # Slightly smaller range + # small random fluctuation for realism + fluctuation = random.uniform(-0.03, 0.03) new_price += fluctuation - # Ensure the price doesn't fall below a reasonable minimum (e.g., 1) - new_price = max(1, new_price) - - # Update the price with rounding off to 2 decimal places + new_price = max(1.5, new_price) self.products[product] = round(new_price, 2) return self.products def handle_competitor_event(self, effect): - adjustment = random.randint(1, 3) - self.products = {k: max(1, v - adjustment) if effect == "new_competitor" else v + adjustment for k, v in + """Handles market changes due to new or exiting competitors.""" + adjustment = float(random.randint(1, 3)) + self.products = {k: max(1.0, v - adjustment) if effect == "new_competitor" else v + adjustment for k, v in self.products.items()} def update_economic_indicators(self): + """Updates market's economic indicators like inflation and unemployment rates.""" # Update the inflation rate based on market conditions self.inflation_rate += random.uniform(-0.01, 0.01) # Random fluctuation - self.inflation_rate = max(0, self.inflation_rate) # Ensure non-negative + self.inflation_rate = max(0.5, self.inflation_rate) # Ensure non-negative # Update the unemployment rate self.unemployment_rate += random.uniform(-0.005, 0.005) # Random fluctuation @@ -100,9 +132,11 @@ class Market: self.gdp += self.gdp * (random.uniform(-0.01, 0.03) + self.inflation_rate) def calculate_final_scores(self): + """Calculates and returns final scores based on company values and stock ownership.""" final_scores = {} for company_id, company in self.companies.items(): - final_score = company.get_value(self) + # Use the 'value' property instead of the 'get_value' method + final_score = company.value # Determine the majority owner majority_owner = max(company.own_stock_ownership, @@ -114,14 +148,14 @@ class Market: # Initialize or update the score if company_id not in final_scores: - final_scores[company_id] = {'score': 0, 'note': ''} + final_scores[company_id] = {'score': 0, 'note': '', 'majority_owner': majority_owner} # Distribute scores for owner_id, percentage in company.own_stock_ownership.items(): if percentage > 20: score_transfer = final_score * (percentage / 100) if owner_id not in final_scores: - final_scores[owner_id] = {'score': 0, 'note': ''} + final_scores[owner_id] = {'score': 0, 'note': '', 'majority_owner': ''} final_scores[owner_id]['score'] += score_transfer # If no one owns 51% or more, assign the remainder to the company itself @@ -130,50 +164,31 @@ class Market: final_scores.get(owner, {'score': 0})['score'] for owner in company.own_stock_ownership) final_scores[company_id]['score'] += remaining_score - # Add a note about the majority owner - final_scores[company_id]['note'] = f"Score has been added to {majority_owner}" - return final_scores class Company: - def __init__(self, player_id, competitors_ids): + """Represents a company within the market, managing finances, inventory, and stock transactions.""" + def __init__(self, player_id, competitors_ids, market=None): self.player_id = player_id self.cash = 500.0 self.inventory = {'A': 0, 'B': 0, 'C': 0} self.crafting_queue = [] + self.own_stock_ownership = {cid: 51 if cid == player_id else 0 for cid in [player_id] + competitors_ids} - # Initialize stock ownership in the player's company - self.own_stock_ownership = {player_id: 51} # Player owns 51 shares of their company - for cid in competitors_ids: - self.own_stock_ownership[cid] = 0 # Competitors initially own no shares + all_company_ids = set([player_id] + competitors_ids + ["RationalAI", "RiskTakingAI"]) + self.other_stock_holdings = {cid: 0 for cid in all_company_ids} - # Initialize stock holdings in other companies - all_companies = competitors_ids + [player_id] - self.other_stock_holdings = {cid: 0 for cid in all_companies} # Player owns no shares in other companies initially + self.total_shares = 100 + self._market = market - self.total_shares = 100 # Total shares per company - - def get_value(self, market): - return self.cash + sum(self.inventory[product] * price for product, price in market.products.items()) - - def trade_product(self, market, product, quantity, buying): - total_cost = round(market.products[product] * quantity, 2) - print("\nProduct Trading Decision") - products = list(market.products.keys()) - print("\nAvailable Products:") - for idx, product in enumerate(products, 1): - print(f" {idx}: {product}") - product_choice = self.get_user_choice(len(products), "Choose a product to trade: ") - product = products[product_choice - 1] - - quantity = self.get_valid_input("Enter the quantity to trade: ", int, "Quantity must be positive.", - lambda x: x > 0) - - trade_type = self.get_user_choice(2, "Choose trade type (1: Buy, 2: Sell): ") - self.trade_product(market, product, quantity, buying=trade_type == 1) + @property + def value(self): + """Calculates the total value of the company, combining cash and the market value of its inventory.""" + return self.cash + sum(self.inventory[product] * price for product, price in self._market.products.items()) def crafting_decision(self): + """Displays crafting options and handles the user's crafting choice.""" print("\nCrafting Decision") recipe_keys = list(crafting_recipes.keys()) print("\nAvailable Recipes:") @@ -182,117 +197,127 @@ class Company: recipe_choice = self.get_user_choice(len(recipe_keys), "Choose a recipe to craft: ") self.craft_product(recipe_keys[recipe_choice - 1]) - def trade_product(self, market, product, quantity, buying): + def trade_product(self, market, product, quantity, buying=True): + """Executes a product trade transaction based on the specified parameters.""" total_cost = market.products[product] * quantity - if buying: - if self.cash >= total_cost: - self.cash -= total_cost - self.inventory[product] += quantity - elif self.inventory[product] >= quantity: + if buying and self.cash >= total_cost: + self.cash -= total_cost + self.inventory[product] += quantity + elif not buying and self.inventory[product] >= quantity: self.cash += total_cost self.inventory[product] -= quantity def craft_product(self, recipe_key): + """Processes crafting of a product based on the chosen recipe.""" recipe = crafting_recipes[recipe_key] if all(self.inventory[product] >= quantity for product, quantity in recipe['input'].items()): + self.crafting_queue.append({'recipe': recipe, 'turns_remaining': recipe['turns']}) for product, quantity in recipe['input'].items(): self.inventory[product] -= quantity - self.crafting_queue.append({'recipe': recipe, 'turns_remaining': recipe['turns']}) print("Crafting order placed.") else: print("Not enough resources to craft.") def update_crafting(self): - completed_orders = [] - for order in self.crafting_queue: + """Updates the crafting queue, completing orders as their turns conclude.""" + for order in self.crafting_queue[:]: order['turns_remaining'] -= 1 if order['turns_remaining'] == 0: for product, quantity in order['recipe']['output'].items(): self.inventory[product] += quantity - completed_orders.append(order) - self.crafting_queue = [order for order in self.crafting_queue if order not in completed_orders] + self.crafting_queue.remove(order) - def trade_stock(self, action, market, company_id, amount): + def trade_stock(self, action, market, company_id, amount, is_ai=False): + """Executes a stock trade action, buying or selling as specified.""" if company_id not in market.companies and company_id != self.player_id: - print("Company not found in the market.") - return + return "Company not found in the market." stock_price = market.get_stock_price(company_id) total_value = stock_price * amount if action == 'buy': - if company_id == self.player_id: - # Calculate available shares in the player's company - available_shares = self.total_shares - sum(self.own_stock_ownership.values()) + \ - self.own_stock_ownership[self.player_id] - else: - # Calculate available shares in an AI company - available_shares = self.total_shares - self.other_stock_holdings[company_id] + return self._buy_stock(company_id, amount, total_value, is_ai) + elif action == 'sell': + return self._sell_stock(company_id, amount, total_value, is_ai) + else: + return "Invalid stock action." - if amount > available_shares: - print(f"Not enough available shares to buy. Available: {available_shares}") - return + def _buy_stock(self, company_id, amount, total_value, is_ai): + """Handles the buying of stocks for the specified company.""" + available_shares = self._calculate_available_shares(company_id) + if amount > available_shares: + return f"Not enough available shares to buy. Available: {available_shares}" - if self.cash < total_value: - print("Insufficient funds to buy stocks.") - return + if self.cash < total_value: + return "Insufficient funds to buy stocks." - self.cash -= total_value - if company_id == self.player_id: - # Buying shares of the player's own company + self._update_stock_ownership(company_id, amount, total_value, buying=True) + message = f"Bought {amount} stocks of {company_id}." + if is_ai: + self.last_action = message + else: + print(message) + return message + + def _sell_stock(self, company_id, amount, total_value, is_ai): + """Handles the selling of stocks for the specified company.""" + if self._get_stock_ownership(company_id) < amount: + return "Not enough stocks to sell." + + self._update_stock_ownership(company_id, amount, total_value, buying=False) + message = f"Sold {amount} stocks of {company_id}." + if is_ai: + self.last_action = message + else: + print(message) + return message + + def _calculate_available_shares(self, company_id): + """Calculates the number of available shares for a given company.""" + if company_id == self.player_id: + return self.total_shares - sum(self.own_stock_ownership.values()) + self.own_stock_ownership[self.player_id] + else: + return self.total_shares - self.other_stock_holdings.get(company_id, 0) + + def _get_stock_ownership(self, company_id): + """Retrieves the stock ownership amount for a given company.""" + if company_id == self.player_id: + return self.own_stock_ownership[self.player_id] + return self.other_stock_holdings.get(company_id, 0) + + def _update_stock_ownership(self, company_id, amount, total_value, buying): + """Updates the stock ownership details after a buy or sell action.""" + if company_id == self.player_id: + if buying: self.own_stock_ownership[self.player_id] += amount else: - # Buying shares of an AI company - self.other_stock_holdings[company_id] += amount - - print(f"Bought {amount} stocks of {company_id}.") - - elif action == 'sell': - if company_id == self.player_id: - # Selling shares of the player's own company - if self.own_stock_ownership[self.player_id] < amount: - print("Not enough stocks to sell.") - return self.own_stock_ownership[self.player_id] -= amount + else: + if buying: + self.other_stock_holdings[company_id] += amount else: - # Selling shares of an AI company - if self.other_stock_holdings.get(company_id, 0) < amount: - print("Not enough stocks to sell.") - return self.other_stock_holdings[company_id] -= amount - - self.cash += total_value - print(f"Sold {amount} stocks of {company_id}.") + self.cash += -total_value if buying else total_value def make_decision(self, market, competitors): + """Presents decision options to the user and processes the chosen action.""" console = Console() - - # Enhanced table for player's status with Euro symbol status_table = Table(title=f"[bold green]{self.player_id}'s Turn - Turn {market.current_turn}", box=box.ROUNDED) - - status_table.add_column("Category", justify="left", style="bold cyan") - status_table.add_column("Details", justify="left") - - # Use colors to highlight key data + status_table.add_column("Category", style="bold cyan") + status_table.add_column("Details") status_table.add_row("Cash", f"[bold yellow]{self.cash:.2f} €") status_table.add_row("Inventory", ', '.join( [f"[bold magenta]{item}: {quantity}" for item, quantity in self.inventory.items()])) status_table.add_row("Market Prices", ', '.join( [f"[bold blue]{product}: {price:.2f} €" for product, price in market.products.items()])) - - # Display shareholders and investments with color coding shareholders = ', '.join( [f"[bold]{company}[/]: {ownership} shares" for company, ownership in self.own_stock_ownership.items()]) status_table.add_row("Your Shareholders", shareholders) - investments = ', '.join( [f"[bold]{company}[/]: {holding} shares" for company, holding in self.other_stock_holdings.items() if holding > 0]) status_table.add_row("Your Investments", investments) - console.print(status_table) - - # Actions menu with clear choices actions = { "1": "Trade Products", "2": "Craft", @@ -300,14 +325,9 @@ class Company: "4": "Skip Turn" } - # Display the choices to the player in an intuitive way choices_display = "\n".join([f"{key}: {value}" for key, value in actions.items()]) console.print(f"Available Actions:\n{choices_display}", style="bold") - - # Prompt for action choice action_choice = Prompt.ask("Choose your action", choices=list(actions.keys()), default="4") - - # Perform the selected action selected_action = actions.get(action_choice, None) if selected_action == "Trade Products": @@ -324,6 +344,7 @@ class Company: console.print("[bold red]Invalid choice. Please enter a valid option.") def trade_products_decision(self, market): + """Handles the decision-making process for trading products.""" print("\nProduct Trading Decision") products = list(market.products.keys()) print("\nAvailable Products:") @@ -335,18 +356,18 @@ class Company: quantity = self.get_valid_input("Enter the quantity to trade: ", int, "Quantity must be positive.", lambda x: x > 0) - trade_type = self.get_user_choice(2, "Choose trade type (1: Buy, 2: Sell): ") - self.trade_product(market, product, quantity, buying=trade_type == 1) + self.get_user_choice(2, "Choose trade type (1: Buy, 2: Sell): ") + self.trade_product(market, product, quantity) def trade_stocks_decision(self, action, market, competitors): + """Facilitates the decision-making process for stock trading actions.""" print("\nStock Trading Decision") if action == 'buy': print("Available companies to buy stocks from:") elif action == 'sell': print("Your stock holdings:") - # Include the player's company in the list - companies = competitors + [self] # Assuming 'self' represents the player's company + companies = competitors + [self] for idx, company in enumerate(companies, 1): company_id = company.player_id @@ -364,6 +385,7 @@ class Company: @staticmethod def get_user_choice(num_options, prompt): + """Prompts the user for a choice and validates the input.""" choice = 0 while choice < 1 or choice > num_options: try: @@ -376,6 +398,7 @@ class Company: @staticmethod def get_valid_input(prompt, input_type, error_message, validation_func=lambda x: True): + """Requests and validates user input based on specified criteria.""" while True: try: value = input_type(input(prompt)) @@ -385,62 +408,85 @@ class Company: except ValueError: print(error_message) + def is_market_boom(self): + """Checks if the market is booming.""" + return all(price > 20 for price in self._market.products.values()) + class AICompany(Company): - def __init__(self, player_id, competitors_ids): - super().__init__(player_id, competitors_ids) + """Represents an AI-driven company with automated trading, crafting, and stock actions.""" + def __init__(self, player_id, competitors_ids, market): + super().__init__(player_id, competitors_ids, market) self.last_action = "None" - self.average_prices = {'A': 10, 'B': 15, 'C': 20} + self.average_prices = {product: market.products[product] for product in market.products} - def update_average_prices(self, market): - turn = market.current_turn - for product, price in market.products.items(): + def update_average_prices(self): + """Updates the average prices of products based on the current market turn.""" + turn = self._market.current_turn + for product, price in self._market.products.items(): self.average_prices[product] = (self.average_prices[product] * (turn - 1) + price) / turn - def is_market_boom(self, market): - return all(price > 20 for price in market.products.values()) - def choose_best_crafting_option(self): + """Selects the most viable crafting option based on current inventory.""" return next((recipe_key for recipe_key, recipe in crafting_recipes.items() if all(self.inventory[prod] >= qty for prod, qty in recipe['input'].items())), None) - def perform_trade_action(self, market, action): + def perform_trade_action(self, action): + """Performs a trade action, either buying or selling, based on the specified action.""" if action == 'sell': - for product, quantity in self.inventory.items(): - if quantity > 0: - self.trade_product(market, product, quantity, False) - self.last_action = f"Sold {quantity} of {product}" - break + self.sell_inventory_items() elif action == 'buy': - for product, price in market.products.items(): - if price > 0: # Check to avoid division by zero - quantity = self.cash // price - if quantity > 0: - self.trade_product(market, product, quantity, True) - self.last_action = f"Bought {quantity} of {product}" - break - else: - print(f"Price for {product} is currently zero, skipping.") + self.buy_inventory_items() - def perform_stock_action(self, market, action): - company_id, amount = self.select_stock_action(market, action) + def sell_inventory_items(self): + """Executes the selling of inventory items.""" + for product, quantity in self.inventory.items(): + if quantity > 0: + self.trade_product(self._market, product, quantity, buying=False) + self.last_action = f"Sold {quantity} of {product}" + break + + def buy_inventory_items(self): + """Executes the buying of inventory items based on current market prices.""" + for product, price in self._market.products.items(): + if price > 0: + quantity = int(self.cash // price) + if quantity > 0: + self.trade_product(self._market, product, quantity) + self.last_action = f"Bought {quantity} of {product}" + break + + def perform_stock_action(self, action): + """Performs a stock action, buying or selling, based on a selected strategy.""" + company_id, amount = self.select_stock_action(action) if company_id: - self.trade_stock(action, market, company_id, amount) + self.trade_stock(action, self._market, company_id, amount, is_ai=True) - def select_stock_action(self, market, action): + def select_stock_action(self, action): + """Selects a stock action to perform, either buying or selling.""" if action == 'buy': - company_id = random.choice(list(market.companies.keys())) - amount = random.randint(1, 10) # Random amount - return company_id, amount + return self.select_stock_to_buy() elif action == 'sell': - owned_stocks = [(comp_id, amount) for comp_id, amount in self.other_stock_holdings.items() if amount > 0] - if owned_stocks: - company_id, _ = random.choice(owned_stocks) - amount = random.randint(1, self.other_stock_holdings[company_id]) - return company_id, amount + return self.select_stock_to_sell() + return None, 0 + + def select_stock_to_buy(self): + """Chooses a company's stock to buy based on market conditions.""" + company_id = random.choice(list(self._market.companies.keys())) + amount = random.randint(1, 10) + return company_id, amount + + def select_stock_to_sell(self): + """Chooses a company's stock to sell from the AI's holdings.""" + owned_stocks = [(comp_id, amount) for comp_id, amount in self.other_stock_holdings.items() if amount > 0] + if owned_stocks: + company_id, _ = random.choice(owned_stocks) + amount = random.randint(1, self.other_stock_holdings[company_id]) + return company_id, amount return None, 0 def attempt_crafting(self): + """Attempts to craft an item based on the best available crafting option.""" recipe_key = self.choose_best_crafting_option() if recipe_key: self.craft_product(recipe_key) @@ -448,71 +494,86 @@ class AICompany(Company): class RationalAI(AICompany): + """AI strategy focused on rational and market-condition-based decisions.""" + def __init__(self, player_id, competitors_ids, market): + super().__init__(player_id, competitors_ids, market) + def make_decision(self, market, competitors): - # Decides on actions based on market conditions and crafting opportunities - if self.should_craft(market): + """Makes a market decision based on current market conditions and AI strategy.""" + if self.should_craft(): self.attempt_crafting() - elif self.is_market_boom(market) or market.current_turn > 7: - self.perform_trade_action(market, 'sell') + elif self.is_market_boom() or market.current_turn > 7: + self.perform_trade_action('sell') else: - self.perform_trade_action(market, 'buy') - self.perform_stock_action(market, random.choice(['buy', 'sell'])) + self.perform_trade_action('buy') + self.perform_stock_action(random.choice(['buy', 'sell'])) - def should_craft(self, market): - # Determines whether crafting is a good choice based on market conditions - return not self.is_market_boom(market) and market.current_turn <= 7 - - def attempt_crafting(self): - # Attempts to craft an item if the conditions are favorable - recipe_key = self.choose_best_crafting_option() - if recipe_key: - self.craft_product(recipe_key) - self.last_action = f"Started crafting {recipe_key}" + def should_craft(self): + """Determines if crafting is a rational choice based on market conditions.""" + return random.choice([True, False]) if self.is_market_boom() else self._market.current_turn <= 7 class RiskTakingAI(AICompany): + """AI strategy focused on high-risk, high-reward decisions in the market.""" + def __init__(self, player_id, competitors_ids, market): + super().__init__(player_id, competitors_ids, market) + def make_decision(self, market, competitors): - # Randomly chooses to buy or sell based on market conditions or a 50-50 chance + """Makes bold market decisions based on a high-risk approach.""" if random.choice([True, False]): - self.perform_trade_action(market, 'buy') - elif self.should_craft(market): + self.perform_trade_action('buy') + elif self.should_craft(): self.attempt_crafting() else: - self.perform_trade_action(market, 'sell') - self.perform_stock_action(market, random.choice(['buy', 'sell'])) + self.perform_trade_action('sell') + self.perform_stock_action(random.choice(['buy', 'sell'])) - def should_craft(self, market): - # Decides whether to craft based on market conditions or randomly - return random.choice([True, False]) if self.is_market_boom(market) else market.current_turn <= 7 - - def attempt_crafting(self): - # Attempts to craft a product if possible - recipe_key = self.choose_best_crafting_option() - if recipe_key: - self.craft_product(recipe_key) - self.last_action = f"Started crafting {recipe_key}" + def should_craft(self): + """Determines if crafting is a suitable choice, leaning towards riskier decisions.""" + return random.choice([True, False]) if self.is_market_boom() else self._market.current_turn <= 7 def print_ai_actions(ai_competitors): + """Displays the last actions taken by AI competitors in a styled console format.""" + console = Console() for ai in ai_competitors: - print(f"{ai.player_id} last action: {ai.last_action}") + action_text = Text() + + if "Bought" in ai.last_action or "Sold" in ai.last_action: + action_color = "green" if "Bought" in ai.last_action else "red" + action_text.append(ai.last_action, style=f"bold {action_color}") + elif "Crafting" in ai.last_action: + action_text.append(ai.last_action, style="bold blue") + else: + action_text.append(ai.last_action, style="bold") + + console.print( + Panel(action_text, title=f"[bold]{ai.player_id}'s Action", border_style="bright_yellow")) def main(): - competitors_ids = ["Player", "Rational AI", "Risk-Taking AI"] - market = Market() - player = Company("Player", [id for id in competitors_ids if id != "Player"]) - ai_competitors = [RationalAI("Rational AI", [id for id in competitors_ids if id != "Rational AI"]), - RiskTakingAI("Risk-Taking AI", [id for id in competitors_ids if id != "Risk-Taking AI"])] + """ + Simulates a 10-turn market game between the player and two AI competitors. + """ - # Initialize market with references to companies - market.companies = {"Player": player, "Rational AI": ai_competitors[0], "Risk-Taking AI": ai_competitors[1]} + competitors_ids = ["Player", "RationalAI", "RiskTakingAI"] + market = Market() + player = Company("Player", [company_type for company_type in competitors_ids if company_type != "Player"], market) + + other_competitors = set(competitors_ids) - {"RationalAI", "RiskTakingAI"} + ai_competitors = [ + RationalAI("RationalAI", list(other_competitors), market), + RiskTakingAI("RiskTakingAI", list(other_competitors), market), + ] + + market.companies = {"Player": player, "RationalAI": ai_competitors[0], "RiskTakingAI": ai_competitors[1]} for turn in range(10): print(f"\n---\n") market.update_market() player.update_crafting() player.make_decision(market, ai_competitors) + print(f"\n---\n") for ai in ai_competitors: ai.make_decision(market, ai_competitors) @@ -520,12 +581,22 @@ def main(): print_ai_actions(ai_competitors) - # Calculate final scores based on company values and stock ownership + print(f"\n---\n") final_scores = market.calculate_final_scores() - print("\nFinal Company Values:") - for company_id, value in final_scores.items(): - print(f"{company_id}: {value}") + console = Console() + score_table = Table(header_style="bold magenta", box=box.DOUBLE_EDGE) + score_table.add_column("Company", style="bold cyan") + score_table.add_column("Final Score", justify="right", style="bold yellow") + score_table.add_column("Majority Owner", style="bold green") + score_table.add_column("Ownership Percentage", justify="right", style="dim") + sorted_scores = sorted(final_scores.items(), key=lambda item: item[1]['score'], reverse=True) + + for company_id, data in sorted_scores: + ownership_percentage = f"{data['score'] / market.companies[company_id].value * 100:.2f}%" + score_table.add_row(company_id, f"{data['score']:.0f}", data['majority_owner'], ownership_percentage) + + console.print(score_table) if __name__ == "__main__":