diff --git a/main.py b/main.py index 5b12d47..6ed04c2 100644 --- a/main.py +++ b/main.py @@ -1,5 +1,10 @@ -import random 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}, @@ -20,17 +25,17 @@ class Market: def __init__(self): self.current_turn = 0 self.companies = {} - self.products = {'A': 10, 'B': 15, 'C': 20} + 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, - "decrease": lambda x: max(1, x - 3)} + 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 = 10000 # Example starting GDP value + self.gdp = 500000 # Example starting GDP value def update_stock_ledger(self, company_id, owner_id, amount): key = (company_id, owner_id) @@ -41,20 +46,42 @@ class Market: def get_stock_price(self, company_id): company_value = self.companies[company_id].get_value(self) - return company_value / 100 + 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()} - print(f"Market Event: {event['name']}") + 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 @@ -78,7 +105,8 @@ class Market: 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_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 @@ -111,21 +139,49 @@ class Market: class Company: def __init__(self, player_id, competitors_ids): self.player_id = player_id - self.cash = 100 + self.cash = 500.0 self.inventory = {'A': 0, 'B': 0, 'C': 0} self.crafting_queue = [] - # Initialize own stock ownership with player and competitors - self.own_stock_ownership = {self.player_id: 51} # Majority stock owned by the player - for cid in competitors_ids: - self.own_stock_ownership[cid] = 0 - # Initialize other stock holdings + # 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} + 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: @@ -157,7 +213,7 @@ class Company: 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: + if company_id not in market.companies and company_id != self.player_id: print("Company not found in the market.") return @@ -165,75 +221,169 @@ class Company: 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 - self.other_stock_holdings[company_id] += amount - market.companies[company_id].own_stock_ownership[self.player_id] += amount + 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 self.other_stock_holdings[company_id] < amount: - print("Not enough stocks to sell.") - return + 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 - self.other_stock_holdings[company_id] -= amount - market.companies[company_id].own_stock_ownership[self.player_id] -= amount print(f"Sold {amount} stocks of {company_id}.") def make_decision(self, market, competitors): - print(f"\n{self.player_id}'s Turn - Turn {market.current_turn}") - print(f"Cash: {self.cash}") - print(f"Inventory: {self.inventory}") - print(f"Market Prices: {market.products}") - print(f"Own Stock Ownership: {self.own_stock_ownership}") - print(f"Stock Holdings in Other Companies: {self.other_stock_holdings}") + console = Console() - while True: - action = input("Choose an action (1: Trade Products, 2: Craft, 3: Trade Stocks, 4: Skip Turn): ") + # 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) - if action == '1': - self.trade_products_decision(market) - elif action == '2': - self.crafting_decision() - elif action == '3': - stock_action = input("Choose stock action (1: Buy, 2: Sell): ") - if stock_action == '1': - self.trade_stocks_decision('buy', market, competitors) - elif stock_action == '2': - self.trade_stocks_decision('sell', market, competitors) - elif action == '4': - break # Skip turn - else: - print("Invalid choice. Please enter a valid option.") + 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("Stock Trading Decision") - - # Common logic for displaying stock information + print("\nStock Trading Decision") if action == 'buy': print("Available companies to buy stocks from:") elif action == 'sell': print("Your stock holdings:") - for competitor in competitors: - stock_info = f"{competitor.player_id}: Current stock price - {market.get_stock_price(competitor.player_id)}" - if action == 'sell' and self.other_stock_holdings[competitor.player_id] > 0: - stock_info += f", Owned: {self.other_stock_holdings[competitor.player_id]}" + # 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_id = input("Enter the company ID to trade stocks: ") - if company_id not in market.companies: - print("Invalid company ID.") - return + company_choice = self.get_user_choice(len(companies), "Enter the company number to trade stocks: ") + company_id = companies[company_choice - 1].player_id - try: - amount = int(input("Enter the amount of stocks to trade: ")) - if amount <= 0: - raise ValueError("Stock amount must be positive.") - self.trade_stock(action, market, company_id, amount) - except ValueError as e: - print(f"Invalid amount: {e}") + 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): @@ -263,12 +413,14 @@ class AICompany(Company): break elif action == 'buy': for product, price in market.products.items(): - if price < self.average_prices[product] * 0.9: + 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) @@ -357,7 +509,7 @@ def main(): market.companies = {"Player": player, "Rational AI": ai_competitors[0], "Risk-Taking AI": ai_competitors[1]} for turn in range(10): - print(f"\n--- Turn {turn + 1} ---") + print(f"\n---\n") market.update_market() player.update_crafting() player.make_decision(market, ai_competitors)