AI improved

This commit is contained in:
Isaak Buslovich 2023-11-19 00:23:35 +01:00
parent 02b9ce142a
commit 5ff9f68404
Signed by: Isaak
GPG Key ID: EEC31D6437FBCC63

378
main.py
View File

@ -24,7 +24,6 @@ Note: Requires 'rich' and 'json' libraries for functionality.
import json
import random
from rich.console import Console
from rich.table import Table
from rich.prompt import Prompt
@ -38,8 +37,36 @@ crafting_recipes = {
}
def main():
console = Console()
competitors_ids = ["Player", "RationalAI", "RiskTakingAI"]
market = Market()
# Initialise the player
player = Company("Player", [cid for cid in competitors_ids if cid != "Player"], market)
# Initialise AI competitors
rational_ai = AICompany("RationalAI", competitors_ids, market, risk_tolerance=0.3)
risk_taking_ai = AICompany("RiskTakingAI", competitors_ids, market, risk_tolerance=0.7)
ai_competitors = [rational_ai, risk_taking_ai]
market.companies = {company.player_id: company for company in ai_competitors + [player]}
# Game loop
for turn in range(1, 11):
console.print(f"\n--- Turn {turn} ---\n", style="grey50")
market.update_market()
for company in [player] + ai_competitors:
company.update_crafting()
company.make_decision(market, ai_competitors)
print_ai_actions(ai_competitors)
final_scores = market.calculate_final_scores()
display_final_scores(console, final_scores, market)
def load_json(filename):
"""Loads JSON data from a file."""
try:
with open(filename) as file:
return json.load(file)
@ -49,125 +76,82 @@ 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 = {}
self.products = {'A': 10.0, 'B': 15.0, 'C': 20.0}
self.starting_prices = self.products.copy()
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 = {}
# 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
self.inflation_rate = 0.02
self.unemployment_rate = 0.05
self.gdp = 500000
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
self.stock_ledger[company_id, owner_id] = self.stock_ledger.get((company_id, owner_id), 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):
"""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)
return round(self.companies[company_id].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])
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()
def adjust_prices(self):
"""Adjusts market product prices based on inflation and other factors."""
for product, price in self.products.items():
inflation_adjustment = price * self.inflation_rate
new_price = price + inflation_adjustment
# small random fluctuation for realism
fluctuation = random.uniform(-0.03, 0.03)
new_price += fluctuation
new_price = max(1.5, new_price)
self.products[product] = round(new_price, 2)
self.products[product] = round(max(1.5, price + inflation_adjustment + fluctuation), 2)
return self.products
def handle_competitor_event(self, effect):
"""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.5, 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.inflation_rate = max(0.5, self.inflation_rate + random.uniform(-0.01, 0.01))
self.unemployment_rate = min(max(self.unemployment_rate + random.uniform(-0.005, 0.005), 0), 1)
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():
# 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,
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': '', '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': '', 'majority_owner': ''}
final_scores[owner_id]['score'] += score_transfer
# If no one owns 51% or more, assign the remainder to the company itself
final_scores[owner_id] = final_scores.get(owner_id, {'score': 0, 'note': '', 'majority_owner': ''})
final_scores[owner_id]['score'] += final_score * (percentage / 100)
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
return final_scores
class Company:
"""Represents a company within the market, managing finances, inventory, and stock transactions."""
def __init__(self, player_id, competitors_ids, market=None):
def __init__(self, player_id, competitors_ids, market=None, debug=False):
self.player_id = player_id
self.cash = 500.0
self.inventory = {'A': 0, 'B': 0, 'C': 0}
@ -176,6 +160,7 @@ class Company:
self.stock_holdings = {cid: 0 for cid in set([player_id] + competitors_ids + ["RationalAI", "RiskTakingAI"])}
self.total_shares = 100
self._market = market
self._debug = debug
@property
def value(self):
@ -184,6 +169,7 @@ class Company:
def craft_product(self, recipe_key):
"""Processes crafting of a product based on the chosen recipe."""
if self._debug:
print(f"Inventory before crafting: {self.inventory}")
recipe = crafting_recipes[recipe_key]
if all(self.inventory[product] >= quantity for product, quantity in recipe['input'].items()):
@ -192,33 +178,41 @@ class Company:
print("Crafting order placed.")
else:
print("Not enough resources to craft.")
if self._debug:
print(f"Inventory after crafting: {self.inventory}")
def _update_inventory(self, items, decrease=False):
"""Updates the inventory based on the given items."""
if self._debug:
print(f"Inventory before update: {self.inventory}")
for product, quantity in items.items():
if decrease:
self.inventory[product] -= quantity
else:
self.inventory[product] += quantity
if self._debug:
print(f"Inventory after update: {self.inventory}")
def update_crafting(self):
"""Updates the crafting queue, completing orders as their turns conclude."""
if self._debug:
print(f"Crafting queue before update: {self.crafting_queue}")
completed_orders = []
for order in self.crafting_queue:
if self._debug:
print(f"Processing order: {order}")
order['turns_remaining'] -= 1
if order['turns_remaining'] == 0:
if self._debug:
print(f"Completing order: {order}")
self._update_inventory(order['recipe']['output'], decrease=False)
self._update_inventory(order['recipe']['output'])
if self._debug:
print(f"Inventory after completing order: {self.inventory}")
completed_orders.append(order)
for order in completed_orders:
self.crafting_queue.remove(order)
if self._debug:
print(f"Crafting queue after update: {self.crafting_queue}")
def trade_product(self, market, product, quantity, buying=True):
@ -265,33 +259,34 @@ class Company:
if self.cash < total_value:
return "Insufficient funds to buy stocks."
self._update_stock_ownership(company_id, amount, total_value, buying=True)
message = f"Bought {amount} stocks of {company_id}."
# Update stock ownership
if is_ai:
self.last_action = message
self.stock_holdings[company_id] += amount # Increase AI's stock holdings
else:
print(message)
return message
self.own_stock_ownership[company_id] += amount # Increase player's own stock holdings
self.cash -= total_value # Deduct the cost from the buyer's cash
return f"Bought {amount} stocks of {company_id}."
def _sell_stock(self, company_id, amount, total_value, is_ai):
"""Handles the selling of stocks for the specified company."""
# Check if the seller has enough stocks to sell
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}."
# Update stock ownership
if is_ai:
self.last_action = message
self.stock_holdings[company_id] -= amount # Decrease AI's stock holdings
else:
print(message)
return message
self.own_stock_ownership[company_id] -= amount # Decrease player's own stock holdings
self.cash += total_value # Add the proceeds to the seller's cash
return f"Sold {amount} stocks of {company_id}."
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.stock_holdings.get(company_id, 0)
total_owned = sum(self.stock_holdings.values()) + sum(self.own_stock_ownership.values())
return self.total_shares - total_owned
def _get_stock_ownership(self, company_id):
"""Retrieves the stock ownership amount for a given company."""
@ -316,12 +311,12 @@ class Company:
def make_decision(self, market, competitors):
"""Presents decision options to the user and processes the chosen action."""
console = Console()
status_table = Table(title=f"[bold green]{self.player_id}'s Turn - Turn {market.current_turn}", box=box.ROUNDED)
status_table = Table(title=f"[bold cyan]{self.player_id}'s Turn - Turn {market.current_turn}", box=box.ROUNDED)
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("Cash", f"[bold blue]{self.cash:.2f}")
status_table.add_row("Inventory", ', '.join(
[f"[bold magenta]{item}: {quantity}" for item, quantity in self.inventory.items()]))
[f"[bold]{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()]))
shareholders = ', '.join(
@ -341,7 +336,7 @@ class Company:
choices_display = "\n".join([f"{key}: {value}" for key, value in actions.items()])
console.print(f"Available Actions:\n{choices_display}", style="bold")
action_choice = Prompt.ask("Choose your action", choices=list(actions.keys()), default="4")
action_choice = Prompt.ask("Choose your action", default="4")
selected_action = actions.get(action_choice, None)
if selected_action == "Trade Products":
@ -427,186 +422,113 @@ class Company:
return all(price > 20 for price in self._market.products.values())
def is_market_bust(market):
return all(price < 0.75 * market.starting_prices[product] for product, price in market.products.items())
class AICompany(Company):
"""Represents an AI-driven company with automated trading, crafting, and stock actions."""
def __init__(self, player_id, competitors_ids, market):
def __init__(self, player_id, competitors_ids, market, risk_tolerance):
super().__init__(player_id, competitors_ids, market)
self.last_action = "None"
self.risk_tolerance = risk_tolerance
self.actions_history = []
self.average_prices = {product: market.products[product] for product in market.products}
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
self.average_prices = {product: (self.average_prices[product] * (self._market.current_turn - 1) + price) / self._market.current_turn
for product, price in self._market.products.items()}
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 make_decision(self, market, competitors):
if self.risk_tolerance > 0.5: # High-Risk AI
self.high_risk_decision(market, competitors)
else: # Low-Risk AI
self.low_risk_decision(market, competitors)
def perform_trade_action(self, action):
"""Performs a trade action, either buying or selling, based on the specified action."""
if action == 'sell':
self.sell_inventory_items()
elif action == 'buy':
self.buy_inventory_items()
def low_risk_decision(self, market, competitors):
if market.current_turn == 1:
self.buy_product('A', self.cash / 3)
elif market.current_turn == 2:
self.buy_product('B', min(self.inventory['A'], self.cash / market.products['B']))
elif market.current_turn >= 3:
self.buy_and_craft(market)
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 high_risk_decision(self, market, competitors):
if market.current_turn == 1:
self.sell_own_shares(market)
self.buy_product('A', self.cash / 2)
self.buy_product('B', self.cash)
else:
if self.should_craft(market):
self.buy_and_craft(market)
if self.should_sell_products(market):
self.sell_high_value_products(market)
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)
def buy_product(self, product, budget):
quantity = int(budget // self._market.products[product])
if quantity > 0:
self.trade_product(self._market, product, quantity)
self.last_action = f"Bought {quantity} of {product}"
break
action = f"Bought {quantity} of {product}"
self.actions_history.append(action)
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, self._market, company_id, amount, is_ai=True)
def sell_own_shares(self, market):
amount_to_sell = int(self.own_stock_ownership[self.player_id] * 0.25) # Sell 25% of own shares
if amount_to_sell > 0:
self.trade_stock('sell', market, self.player_id, amount_to_sell, is_ai=True)
action = f"Sold {amount_to_sell} of own shares"
self.actions_history.append(action)
def select_stock_action(self, action):
"""Selects a stock action to perform, either buying or selling."""
if action == 'buy':
return self.select_stock_to_buy()
elif action == 'sell':
return self.select_stock_to_sell()
return None, 0
def should_craft(self, market):
return all(self.inventory[product] >= qty for product, qty in crafting_recipes['recipe2']['input'].items())
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 should_sell_products(self, market):
return any(market.products[product] >= 2 * self.average_prices[product] for product in self.inventory if self.inventory[product] > 0)
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.stock_holdings.items() if amount > 0]
if owned_stocks:
company_id, _ = random.choice(owned_stocks)
amount = random.randint(1, self.stock_holdings[company_id])
return company_id, amount
return None, 0
def sell_high_value_products(self, market):
for product, quantity in self.inventory.items():
if quantity > 0 and market.products[product] >= 2 * self.average_prices[product]:
self.trade_product(market, product, quantity, buying=False)
action = f"Sold high value products"
self.actions_history.append(action)
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)
self.last_action = f"Started crafting {recipe_key}"
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):
"""Makes a market decision based on current market conditions and AI strategy."""
if self.should_craft():
self.attempt_crafting()
elif self.is_market_boom() or market.current_turn > 7:
self.perform_trade_action('sell')
def buy_and_craft(self, market):
chosen_recipe = crafting_recipes['recipe2']
if all(self.inventory[product] >= qty for product, qty in chosen_recipe['input'].items()):
self.craft_product('recipe2')
print(f"Crafting using recipe2")
action = "Crafted products using recipe2"
self.actions_history.append(action)
else:
self.perform_trade_action('buy')
self.perform_stock_action(random.choice(['buy', 'sell']))
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):
"""Makes bold market decisions based on a high-risk approach."""
if random.choice([True, False]):
self.perform_trade_action('buy')
elif self.should_craft():
self.attempt_crafting()
else:
self.perform_trade_action('sell')
self.perform_stock_action(random.choice(['buy', 'sell']))
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
print("Not enough resources to craft using recipe2")
def print_ai_actions(ai_competitors):
"""Displays the last actions taken by AI competitors in a styled console format."""
"""Displays the actions history, current cash, and inventory of AI competitors."""
console = Console()
for ai in ai_competitors:
action_text = Text()
panel_text = Text()
inventory_text = ', '.join([f"{product}: {quantity}" for product, quantity in ai.inventory.items()])
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")
# List all actions
for idx, action in enumerate(ai.actions_history, 1):
panel_text.append(f"{idx}. Action: {action}\n", style="bold cyan")
console.print(
Panel(action_text, title=f"[bold]{ai.player_id}'s Action", border_style="bright_yellow"))
# Append current cash and inventory to the text
panel_text.append(f"\nCurrent Cash: {ai.cash:.2f}\n", style="bold yellow")
panel_text.append(f"Inventory: {inventory_text}", style="bold magenta")
# Display in a panel
console.print(Panel(panel_text, title=f"[bold]{ai.player_id}'s Status", border_style="grey50"))
def main():
"""
Simulates a 10-turn market game between the player and two AI competitors.
"""
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)
ai.update_crafting()
print_ai_actions(ai_competitors)
print(f"\n---\n")
final_scores = market.calculate_final_scores()
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")
def display_final_scores(console, final_scores, market):
"""Displays the final scores in a styled table."""
score_table = Table(header_style="bold cyan", box=box.DOUBLE_EDGE)
score_table.add_column("Company", style="bold")
score_table.add_column("Final Score", justify="right")
score_table.add_column("Majority Owner", style="bold")
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:
for company_id, data in sorted(final_scores.items(), key=lambda item: item[1]['score'], reverse=True):
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)