import json import random from rich.console import Console from rich.table import Table from rich.prompt import Prompt from rich import box crafting_recipes = { 'recipe1': {'input': {'A': 4, 'B': 4}, 'output': {'C': 10}, 'turns': 3}, 'recipe2': {'input': {'A': 1, 'B': 1}, 'output': {'C': 2}, 'turns': 2} } def load_json(filename): try: with open(filename) as file: return json.load(file) except FileNotFoundError: print(f"Error: The file {filename} was not found.") exit(1) class Market: def __init__(self): self.current_turn = 0 self.companies = {} self.products = {'A': 10.0, 'B': 15.0, 'C': 20.0} self.events = load_json('market_events.json')["events"] 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 # Economic indicators self.inflation_rate = 0.02 # Example starting inflation rate (2%) self.unemployment_rate = 0.05 # Example starting unemployment rate (5%) self.gdp = 500000 # Example starting GDP value def update_stock_ledger(self, company_id, owner_id, amount): 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): 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) return round(company_value / 100.0, 2) def update_market(self): 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] if event["effect"] in ["new_competitor", "exit_competitor"]: self.handle_competitor_event(event["effect"]) 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 def adjust_prices(self): # Apply inflation more subtly and cumulatively 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 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 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 self.products.items()} def update_economic_indicators(self): # 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 # Update the unemployment rate self.unemployment_rate += random.uniform(-0.005, 0.005) # Random fluctuation self.unemployment_rate = min(max(self.unemployment_rate, 0), 1) # Bound between 0 and 1 # Update GDP based on market performance self.gdp += self.gdp * (random.uniform(-0.01, 0.03) + self.inflation_rate) def calculate_final_scores(self): final_scores = {} for company_id, company in self.companies.items(): final_score = company.get_value(self) # Determine the majority owner majority_owner = max(company.own_stock_ownership, key=lambda owner: (company.own_stock_ownership[owner], owner)) majority_percentage = company.own_stock_ownership[majority_owner] # Check if the majority owner owns 51% or more is_major_owner = majority_percentage >= 51 # Initialize or update the score if company_id not in final_scores: final_scores[company_id] = {'score': 0, 'note': ''} # 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'] += score_transfer # If no one owns 51% or more, assign the remainder to the company itself if not is_major_owner: remaining_score = final_score - sum( 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): self.player_id = player_id self.cash = 500.0 self.inventory = {'A': 0, 'B': 0, 'C': 0} self.crafting_queue = [] # 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 # 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 # 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) def crafting_decision(self): print("\nCrafting Decision") recipe_keys = list(crafting_recipes.keys()) print("\nAvailable Recipes:") for idx, recipe in enumerate(recipe_keys, 1): print(f" {idx}: {recipe}") 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): 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: self.cash += total_cost self.inventory[product] -= quantity def craft_product(self, recipe_key): recipe = crafting_recipes[recipe_key] if all(self.inventory[product] >= quantity for product, quantity in recipe['input'].items()): 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: 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] def trade_stock(self, action, market, company_id, amount): if company_id not in market.companies and company_id != self.player_id: print("Company not found in the market.") return 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] if amount > available_shares: print(f"Not enough available shares to buy. Available: {available_shares}") return if self.cash < total_value: print("Insufficient funds to buy stocks.") return self.cash -= total_value if company_id == self.player_id: # Buying shares of the player's own company 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: # 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}.") def make_decision(self, market, competitors): 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_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", "3": "Trade Stocks", "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": self.trade_products_decision(market) elif selected_action == "Craft": self.crafting_decision() elif selected_action == "Trade Stocks": stock_actions = ["Buy", "Sell"] stock_action_choice = Prompt.ask("Choose stock action", choices=stock_actions, default=stock_actions[0]) self.trade_stocks_decision(stock_action_choice.lower(), market, competitors) elif selected_action == "Skip Turn": pass # Skip turn else: console.print("[bold red]Invalid choice. Please enter a valid option.") def trade_products_decision(self, market): print("\nProduct Trading Decision") products = list(market.products.keys()) print("\nAvailable Products:") for idx, product in enumerate(products, 1): print(f" {idx}: {product} - Price: {market.products[product]},00 €") 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) def trade_stocks_decision(self, action, market, competitors): 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 for idx, company in enumerate(companies, 1): company_id = company.player_id stock_info = f" {idx}: {company_id} - Current stock price: {market.get_stock_price(company_id)}" if action == 'sell' and self.other_stock_holdings.get(company_id, 0) > 0: stock_info += f", Owned: {self.other_stock_holdings[company_id]}" print(stock_info) company_choice = self.get_user_choice(len(companies), "Enter the company number to trade stocks: ") company_id = companies[company_choice - 1].player_id amount = self.get_valid_input("Enter the amount of stocks to trade: ", int, "Stock amount must be positive.", lambda x: x > 0) self.trade_stock(action, market, company_id, amount) @staticmethod def get_user_choice(num_options, prompt): choice = 0 while choice < 1 or choice > num_options: try: choice = int(input(prompt)) if choice < 1 or choice > num_options: raise ValueError except ValueError: print(f"Please enter a number between 1 and {num_options}.") return choice @staticmethod def get_valid_input(prompt, input_type, error_message, validation_func=lambda x: True): while True: try: value = input_type(input(prompt)) if not validation_func(value): raise ValueError return value except ValueError: print(error_message) class AICompany(Company): def __init__(self, player_id, competitors_ids): super().__init__(player_id, competitors_ids) self.last_action = "None" self.average_prices = {'A': 10, 'B': 15, 'C': 20} def update_average_prices(self, market): turn = market.current_turn for product, price in 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): 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): 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 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.") def perform_stock_action(self, market, action): company_id, amount = self.select_stock_action(market, action) if company_id: self.trade_stock(action, market, company_id, amount) def select_stock_action(self, market, action): if action == 'buy': company_id = random.choice(list(market.companies.keys())) amount = random.randint(1, 10) # Random amount return company_id, amount 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 None, 0 def attempt_crafting(self): recipe_key = self.choose_best_crafting_option() if recipe_key: self.craft_product(recipe_key) self.last_action = f"Started crafting {recipe_key}" class RationalAI(AICompany): def make_decision(self, market, competitors): # Decides on actions based on market conditions and crafting opportunities if self.should_craft(market): self.attempt_crafting() elif self.is_market_boom(market) or market.current_turn > 7: self.perform_trade_action(market, 'sell') else: self.perform_trade_action(market, 'buy') self.perform_stock_action(market, 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}" class RiskTakingAI(AICompany): def make_decision(self, market, competitors): # Randomly chooses to buy or sell based on market conditions or a 50-50 chance if random.choice([True, False]): self.perform_trade_action(market, 'buy') elif self.should_craft(market): self.attempt_crafting() else: self.perform_trade_action(market, 'sell') self.perform_stock_action(market, 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 print_ai_actions(ai_competitors): for ai in ai_competitors: print(f"{ai.player_id} last action: {ai.last_action}") 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"])] # Initialize market with references to companies market.companies = {"Player": player, "Rational AI": ai_competitors[0], "Risk-Taking AI": ai_competitors[1]} for turn in range(10): print(f"\n---\n") market.update_market() player.update_crafting() player.make_decision(market, ai_competitors) for ai in ai_competitors: ai.make_decision(market, ai_competitors) ai.update_crafting() print_ai_actions(ai_competitors) # Calculate final scores based on company values and stock ownership final_scores = market.calculate_final_scores() print("\nFinal Company Values:") for company_id, value in final_scores.items(): print(f"{company_id}: {value}") if __name__ == "__main__": main()