Buy Sell
This commit is contained in:
15
game_balance/__init__.py
Normal file
15
game_balance/__init__.py
Normal file
@@ -0,0 +1,15 @@
|
||||
from game_balance.config import BalanceConfig
|
||||
from game_balance.assets_config import AssetsConfig
|
||||
from game_balance.players_config import PlayersConfig
|
||||
from game_balance.game_config import GameConfig
|
||||
from game_balance.economy_config import EconomyConfig
|
||||
from game_balance.events_config import EventsConfig
|
||||
|
||||
__all__ = [
|
||||
'BalanceConfig',
|
||||
'AssetsConfig',
|
||||
'PlayersConfig',
|
||||
'GameConfig',
|
||||
'EconomyConfig',
|
||||
'EventsConfig'
|
||||
]
|
||||
252
game_balance/assets_config.py
Normal file
252
game_balance/assets_config.py
Normal file
@@ -0,0 +1,252 @@
|
||||
"""
|
||||
НАСТРОЙКИ АКТИВОВ
|
||||
"""
|
||||
|
||||
|
||||
class AssetsConfig:
|
||||
# Категории активов
|
||||
ASSET_CATEGORIES = {
|
||||
'bonds': {'name': 'Облигации', 'color': '#4CAF50', 'icon': '🏦'},
|
||||
'stocks': {'name': 'Акции', 'color': '#2196F3', 'icon': '📈'},
|
||||
'real_estate': {'name': 'Недвижимость', 'color': '#FF9800', 'icon': '🏠'},
|
||||
'crypto': {'name': 'Криптовалюта', 'color': '#9C27B0', 'icon': '💰'},
|
||||
'commodities': {'name': 'Сырье', 'color': '#795548', 'icon': '⛽'},
|
||||
'business': {'name': 'Бизнес', 'color': '#F44336', 'icon': '🏢'},
|
||||
'unique': {'name': 'Уникальные', 'color': '#FFD700', 'icon': '🏆'},
|
||||
}
|
||||
|
||||
# Список всех активов
|
||||
ASSETS = {
|
||||
# ОБЛИГАЦИИ
|
||||
'gov_bonds': {
|
||||
'name': 'Государственные облигации',
|
||||
'category': 'bonds',
|
||||
'base_price': 10000,
|
||||
'volatility': 0.05,
|
||||
'income_per_month': 0.01,
|
||||
'risk_level': 1,
|
||||
'liquidity': 10,
|
||||
'total_quantity': None,
|
||||
'max_per_player': None,
|
||||
'min_purchase': 1,
|
||||
'description': 'Самый надежный актив. Защита от кризисов.'
|
||||
},
|
||||
|
||||
# АКЦИИ
|
||||
'stock_gazprom': {
|
||||
'name': 'Акции Газпрома',
|
||||
'category': 'stocks',
|
||||
'base_price': 1000,
|
||||
'volatility': 0.15,
|
||||
'income_per_month': 0.03,
|
||||
'risk_level': 3,
|
||||
'liquidity': 8,
|
||||
'total_quantity': 10000,
|
||||
'max_per_player': 2000,
|
||||
'min_purchase': 10,
|
||||
'description': 'Голубые фишки. Стабильные дивиденды.'
|
||||
},
|
||||
'stock_sberbank': {
|
||||
'name': 'Акции Сбербанка',
|
||||
'category': 'stocks',
|
||||
'base_price': 300,
|
||||
'volatility': 0.18,
|
||||
'income_per_month': 0.04,
|
||||
'risk_level': 4,
|
||||
'liquidity': 9,
|
||||
'total_quantity': 15000,
|
||||
'max_per_player': 3000,
|
||||
'min_purchase': 10,
|
||||
'description': 'Крупнейший банк. Высокая ликвидность.'
|
||||
},
|
||||
'stock_yandex': {
|
||||
'name': 'Акции Яндекса',
|
||||
'category': 'stocks',
|
||||
'base_price': 3500,
|
||||
'volatility': 0.25,
|
||||
'income_per_month': 0.00,
|
||||
'risk_level': 6,
|
||||
'liquidity': 7,
|
||||
'total_quantity': 5000,
|
||||
'max_per_player': 1000,
|
||||
'min_purchase': 1,
|
||||
'description': 'IT-гигант. Высокий рост, высокая волатильность.'
|
||||
},
|
||||
|
||||
# НЕДВИЖИМОСТЬ
|
||||
'apartment_small': {
|
||||
'name': 'Небольшая квартира',
|
||||
'category': 'real_estate',
|
||||
'base_price': 5000000,
|
||||
'volatility': 0.10,
|
||||
'income_per_month': 0.02,
|
||||
'risk_level': 2,
|
||||
'liquidity': 4,
|
||||
'total_quantity': 12,
|
||||
'max_per_player': 3,
|
||||
'min_purchase': 1,
|
||||
'description': 'Стабильный доход от аренды.'
|
||||
},
|
||||
'apartment_elite': {
|
||||
'name': 'Элитная квартира',
|
||||
'category': 'real_estate',
|
||||
'base_price': 15000000,
|
||||
'volatility': 0.08,
|
||||
'income_per_month': 0.03,
|
||||
'risk_level': 2,
|
||||
'liquidity': 2,
|
||||
'total_quantity': 3,
|
||||
'max_per_player': 1,
|
||||
'min_purchase': 1,
|
||||
'description': 'Статусный актив. Низкая волатильность.'
|
||||
},
|
||||
|
||||
# КРИПТОВАЛЮТА
|
||||
'bitcoin': {
|
||||
'name': 'Биткоин',
|
||||
'category': 'crypto',
|
||||
'base_price': 2000000,
|
||||
'volatility': 0.35,
|
||||
'income_per_month': 0.00,
|
||||
'risk_level': 9,
|
||||
'liquidity': 7,
|
||||
'total_quantity': None,
|
||||
'max_per_player': None,
|
||||
'min_purchase': 0.001,
|
||||
'description': 'Высокорисковый актив. Только спекуляции.'
|
||||
},
|
||||
|
||||
# СЫРЬЕ
|
||||
'oil': {
|
||||
'name': 'Нефть Brent',
|
||||
'category': 'commodities',
|
||||
'base_price': 5500,
|
||||
'volatility': 0.25,
|
||||
'income_per_month': 0.00,
|
||||
'risk_level': 6,
|
||||
'liquidity': 6,
|
||||
'total_quantity': 200,
|
||||
'max_per_player': 40,
|
||||
'min_purchase': 10,
|
||||
'description': 'Зависит от политики. Высокая волатильность.'
|
||||
},
|
||||
'natural_gas': {
|
||||
'name': 'Природный газ',
|
||||
'category': 'commodities',
|
||||
'base_price': 3000,
|
||||
'volatility': 0.20,
|
||||
'income_per_month': 0.00,
|
||||
'risk_level': 5,
|
||||
'liquidity': 5,
|
||||
'total_quantity': 300,
|
||||
'max_per_player': 50,
|
||||
'min_purchase': 100,
|
||||
'description': 'Сезонный актив. Пик зимой.'
|
||||
},
|
||||
|
||||
# БИЗНЕС
|
||||
'coffee_shop': {
|
||||
'name': 'Кофейня',
|
||||
'category': 'business',
|
||||
'base_price': 2000000,
|
||||
'volatility': 0.12,
|
||||
'income_per_month': 0.05,
|
||||
'risk_level': 5,
|
||||
'liquidity': 3,
|
||||
'total_quantity': 8,
|
||||
'max_per_player': 2,
|
||||
'min_purchase': 1,
|
||||
'description': 'Пассивный бизнес. Средний риск.'
|
||||
},
|
||||
'it_startup': {
|
||||
'name': 'IT-стартап',
|
||||
'category': 'business',
|
||||
'base_price': 1000000,
|
||||
'volatility': 0.40,
|
||||
'income_per_month': 0.00,
|
||||
'risk_level': 10,
|
||||
'liquidity': 1,
|
||||
'total_quantity': 4,
|
||||
'max_per_player': 1,
|
||||
'min_purchase': 1,
|
||||
'description': 'Венчурные инвестиции. Может взлететь или провалиться.'
|
||||
},
|
||||
|
||||
# УНИКАЛЬНЫЕ
|
||||
'shopping_mall': {
|
||||
'name': 'Торговый центр',
|
||||
'category': 'unique',
|
||||
'base_price': 50000000,
|
||||
'volatility': 0.10,
|
||||
'income_per_month': 0.06,
|
||||
'risk_level': 4,
|
||||
'liquidity': 1,
|
||||
'total_quantity': 1,
|
||||
'max_per_player': 1,
|
||||
'min_purchase': 1,
|
||||
'description': 'Ключевой актив. Контроль над малым бизнесом.',
|
||||
'special_rules': {
|
||||
'can_be_fractional': True,
|
||||
'min_fraction': 0.1,
|
||||
'control_bonus': 0.1
|
||||
}
|
||||
},
|
||||
'oil_field': {
|
||||
'name': 'Нефтяное месторождение',
|
||||
'category': 'unique',
|
||||
'base_price': 100000000,
|
||||
'volatility': 0.20,
|
||||
'income_per_month': 0.08,
|
||||
'risk_level': 7,
|
||||
'liquidity': 1,
|
||||
'total_quantity': 1,
|
||||
'max_per_player': 1,
|
||||
'min_purchase': 1,
|
||||
'description': 'Стратегический актив. Контроль над ценами на нефть.',
|
||||
'special_rules': {
|
||||
'affects_other_assets': ['oil'],
|
||||
'price_influence': 0.15
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@staticmethod
|
||||
def calculate_price(asset_data, demand_factor, month, total_players):
|
||||
"""Расчет текущей цены актива"""
|
||||
base_price = asset_data['base_price']
|
||||
volatility = asset_data['volatility']
|
||||
|
||||
# Базовое изменение от спроса
|
||||
price = base_price * (1 + demand_factor * volatility)
|
||||
|
||||
# Инфляция (0.5% в месяц)
|
||||
price *= (1.005 ** month)
|
||||
|
||||
# Ограничения
|
||||
price = max(price, base_price * 0.1) # Не ниже 10% от базы
|
||||
price = min(price, base_price * 5) # Не выше 500% от базы
|
||||
|
||||
# Округление
|
||||
if price < 1000:
|
||||
return round(price, 2)
|
||||
elif price < 1000000:
|
||||
return round(price, 0)
|
||||
else:
|
||||
return round(price, -3)
|
||||
|
||||
@staticmethod
|
||||
def calculate_income(asset_data, player_share, total_owners):
|
||||
"""Расчет доходности с учетом баланса"""
|
||||
base_income = asset_data['income_per_month']
|
||||
|
||||
if base_income <= 0:
|
||||
return 0
|
||||
|
||||
# Штраф за концентрацию (если много владельцев у одного игрока)
|
||||
if total_owners > 0:
|
||||
concentration_penalty = (player_share ** 2) * 0.2
|
||||
income = base_income * (1 - concentration_penalty)
|
||||
else:
|
||||
income = base_income
|
||||
|
||||
return max(income, 0.001) # Минимум 0.1%
|
||||
34
game_balance/config.py
Normal file
34
game_balance/config.py
Normal file
@@ -0,0 +1,34 @@
|
||||
"""
|
||||
КОНФИГУРАЦИЯ БАЛАНСА ИГРЫ "КАПИТАЛ & РЫНОК"
|
||||
"""
|
||||
|
||||
|
||||
class BalanceConfig:
|
||||
# Текущий режим баланса
|
||||
BALANCE_MODE = "standard" # standard, easy, hard, tournament
|
||||
|
||||
@classmethod
|
||||
def load_balance_mode(cls, mode):
|
||||
"""Загрузка определенного режима баланса"""
|
||||
if mode == "easy":
|
||||
from game_balance.test_balance.easy_mode import EasyBalance
|
||||
return EasyBalance()
|
||||
elif mode == "hard":
|
||||
from game_balance.test_balance.hard_mode import HardBalance
|
||||
return HardBalance()
|
||||
elif mode == "tournament":
|
||||
from game_balance.test_balance.tournament import TournamentBalance
|
||||
return TournamentBalance()
|
||||
else:
|
||||
from game_balance.test_balance.standard_mode import StandardBalance
|
||||
return StandardBalance()
|
||||
|
||||
@classmethod
|
||||
def get_available_modes(cls):
|
||||
"""Возвращает список доступных режимов"""
|
||||
return {
|
||||
'standard': 'Стандартный',
|
||||
'easy': 'Легкий (новички)',
|
||||
'hard': 'Сложный (эксперты)',
|
||||
'tournament': 'Турнирный'
|
||||
}
|
||||
74
game_balance/economy_config.py
Normal file
74
game_balance/economy_config.py
Normal file
@@ -0,0 +1,74 @@
|
||||
"""
|
||||
ЭКОНОМИЧЕСКИЕ ПАРАМЕТРЫ
|
||||
"""
|
||||
|
||||
class EconomyConfig:
|
||||
# Налоговая система
|
||||
TAX_SYSTEM = {
|
||||
'income_tax': [
|
||||
{'threshold': 0, 'rate': 0.00},
|
||||
{'threshold': 50000, 'rate': 0.10},
|
||||
{'threshold': 200000, 'rate': 0.15},
|
||||
{'threshold': 500000, 'rate': 0.20},
|
||||
{'threshold': 1000000, 'rate': 0.25}
|
||||
],
|
||||
'wealth_tax': {
|
||||
'threshold': 5000000,
|
||||
'rate': 0.01
|
||||
},
|
||||
'monopoly_tax': {
|
||||
'threshold': 0.4,
|
||||
'rate': 0.03
|
||||
},
|
||||
'transaction_fee': 0.01,
|
||||
'auction_fee': 0.02,
|
||||
}
|
||||
|
||||
# Кредитная система
|
||||
LOAN_SYSTEM = {
|
||||
'max_loan_multiplier': 10.0,
|
||||
'interest_rates': {
|
||||
'standard': 0.05,
|
||||
'crisis': 0.10,
|
||||
'black_market': 0.15,
|
||||
},
|
||||
'repayment_periods': [3, 6, 12, 24],
|
||||
'late_fee': 0.02,
|
||||
'default_threshold': 3,
|
||||
}
|
||||
|
||||
# Макроэкономика
|
||||
MACROECONOMICS = {
|
||||
'base_inflation': 0.005,
|
||||
'inflation_multipliers': {
|
||||
'crisis': 1.5,
|
||||
'boom': 0.7,
|
||||
'default': 2.0,
|
||||
},
|
||||
'central_bank_rate': 0.04,
|
||||
}
|
||||
|
||||
# Корреляции активов - ИСПРАВЛЕНО: ключи-строки вместо кортежей
|
||||
ASSET_CORRELATIONS = {
|
||||
'stock_gazprom:oil': 0.6,
|
||||
'stock_sberbank:stock_gazprom': 0.4,
|
||||
'oil:natural_gas': 0.7,
|
||||
'apartment_small:apartment_elite': 0.5,
|
||||
'gold:bitcoin': -0.3,
|
||||
'gov_bonds:stock_gazprom': -0.2,
|
||||
}
|
||||
|
||||
@staticmethod
|
||||
def get_correlation(asset1, asset2):
|
||||
"""Получить корреляцию между активами"""
|
||||
# Пробуем прямой ключ
|
||||
key1 = f"{asset1}:{asset2}"
|
||||
if key1 in EconomyConfig.ASSET_CORRELATIONS:
|
||||
return EconomyConfig.ASSET_CORRELATIONS[key1]
|
||||
|
||||
# Пробуем обратный ключ
|
||||
key2 = f"{asset2}:{asset1}"
|
||||
if key2 in EconomyConfig.ASSET_CORRELATIONS:
|
||||
return EconomyConfig.ASSET_CORRELATIONS[key2]
|
||||
|
||||
return 0 # Нет корреляции
|
||||
102
game_balance/events_config.py
Normal file
102
game_balance/events_config.py
Normal file
@@ -0,0 +1,102 @@
|
||||
"""
|
||||
СОБЫТИЯ И КРИЗИСЫ
|
||||
"""
|
||||
|
||||
|
||||
class EventsConfig:
|
||||
# Типы событий
|
||||
EVENT_TYPES = {
|
||||
'economic': {'name': 'Экономические', 'frequency': 0.4, 'color': '#FF9800'},
|
||||
'political': {'name': 'Политические', 'frequency': 0.3, 'color': '#F44336'},
|
||||
'natural': {'name': 'Природные', 'frequency': 0.15, 'color': '#4CAF50'},
|
||||
'technological': {'name': 'Технологические', 'frequency': 0.15, 'color': '#2196F3'}
|
||||
}
|
||||
|
||||
# События
|
||||
EVENTS = {
|
||||
'oil_boom': {
|
||||
'name': 'Бум нефти',
|
||||
'type': 'economic',
|
||||
'probability': 0.08,
|
||||
'effects': {
|
||||
'oil': {'price_change': 0.30, 'duration': 2},
|
||||
'stock_gazprom': {'price_change': 0.15, 'duration': 2},
|
||||
},
|
||||
'description': 'Цены на нефть взлетели на 30%'
|
||||
},
|
||||
'financial_crisis': {
|
||||
'name': 'Финансовый кризис',
|
||||
'type': 'economic',
|
||||
'probability': 0.10,
|
||||
'effects': {
|
||||
'ALL': {'price_change': -0.20, 'duration': 2},
|
||||
'gov_bonds': {'price_change': 0.05, 'duration': 2},
|
||||
},
|
||||
'description': 'Кризис ликвидности на рынках'
|
||||
},
|
||||
'sanctions': {
|
||||
'name': 'Международные санкции',
|
||||
'type': 'political',
|
||||
'probability': 0.07,
|
||||
'effects': {
|
||||
'stock_gazprom': {'price_change': -0.35, 'duration': 3},
|
||||
'stock_sberbank': {'price_change': -0.25, 'duration': 3},
|
||||
},
|
||||
'description': 'Новые санкции против российских компаний'
|
||||
},
|
||||
'tech_revolution': {
|
||||
'name': 'Технологическая революция',
|
||||
'type': 'technological',
|
||||
'probability': 0.05,
|
||||
'effects': {
|
||||
'it_startup': {'price_change': 0.50, 'duration': 3},
|
||||
'stock_yandex': {'price_change': 0.25, 'duration': 2},
|
||||
'bitcoin': {'price_change': 0.20, 'duration': 1},
|
||||
},
|
||||
'description': 'Прорыв в IT-технологиях'
|
||||
},
|
||||
'elections': {
|
||||
'name': 'Президентские выборы',
|
||||
'type': 'political',
|
||||
'probability': 0.06,
|
||||
'effects': {
|
||||
'ALL': {'price_change': -0.10, 'duration': 1},
|
||||
'gov_bonds': {'price_change': 0.05, 'duration': 1},
|
||||
},
|
||||
'description': 'Неопределенность перед выборами'
|
||||
},
|
||||
'earthquake': {
|
||||
'name': 'Землетрясение',
|
||||
'type': 'natural',
|
||||
'probability': 0.03,
|
||||
'effects': {
|
||||
'real_estate': {'price_change': -0.25, 'duration': 3},
|
||||
},
|
||||
'description': 'Разрушена инфраструктура в ключевом регионе'
|
||||
}
|
||||
}
|
||||
|
||||
# Кризисы
|
||||
CRISES = {
|
||||
'hyperinflation': {
|
||||
'name': 'Гиперинфляция',
|
||||
'probability': 0.02,
|
||||
'conditions': ['month > 6'],
|
||||
'effects': {
|
||||
'ALL': {'price_change': 0.50, 'duration': 3},
|
||||
'cash': {'value_change': -0.30, 'duration': 3},
|
||||
},
|
||||
'description': 'Гиперинфляция! Цены растут на 50% в месяц'
|
||||
},
|
||||
'market_crash': {
|
||||
'name': 'Обвал рынка',
|
||||
'probability': 0.025,
|
||||
'conditions': ['month > 4'],
|
||||
'effects': {
|
||||
'stocks': {'price_change': -0.60, 'duration': 4},
|
||||
'real_estate': {'price_change': -0.40, 'duration': 6},
|
||||
'crypto': {'price_change': -0.80, 'duration': 2},
|
||||
},
|
||||
'description': 'Лопнул финансовый пузырь. Рынки рухнули'
|
||||
}
|
||||
}
|
||||
43
game_balance/game_config.py
Normal file
43
game_balance/game_config.py
Normal file
@@ -0,0 +1,43 @@
|
||||
"""
|
||||
НАСТРОЙКИ ИГРОВОГО ПРОЦЕССА
|
||||
"""
|
||||
|
||||
|
||||
class GameConfig:
|
||||
# Основные параметры
|
||||
GAME_NAME = "Капитал & Рынок"
|
||||
VERSION = "1.0.0"
|
||||
|
||||
DEFAULT_TOTAL_MONTHS = 12
|
||||
MIN_TOTAL_MONTHS = 6
|
||||
MAX_TOTAL_MONTHS = 24
|
||||
|
||||
# Тайминги фаз (секунды)
|
||||
PHASE_DURATIONS = {
|
||||
'action': 120,
|
||||
'market': 30,
|
||||
'event': 30,
|
||||
'results': 45
|
||||
}
|
||||
|
||||
# Режимы скорости
|
||||
SPEED_MODES = {
|
||||
'slow': 2.0,
|
||||
'normal': 1.0,
|
||||
'fast': 0.5,
|
||||
'blitz': 0.25
|
||||
}
|
||||
|
||||
# Настройки комнат
|
||||
MAX_PLAYERS_PER_ROOM = 10
|
||||
MIN_PLAYERS_TO_START = 2
|
||||
MIN_PLAYERS_TO_CONTINUE = 2
|
||||
|
||||
# Ограничения
|
||||
MAX_TRANSACTIONS_PER_PHASE = 20
|
||||
MIN_BID_AMOUNT = 1000
|
||||
|
||||
# Настройки интерфейса
|
||||
DEFAULT_LANGUAGE = 'ru'
|
||||
CURRENCY_SYMBOL = '₽'
|
||||
CURRENCY_CODE = 'RUB'
|
||||
158
game_balance/players_config.py
Normal file
158
game_balance/players_config.py
Normal file
@@ -0,0 +1,158 @@
|
||||
"""
|
||||
НАСТРОЙКИ ИГРОКОВ
|
||||
"""
|
||||
|
||||
|
||||
class PlayersConfig:
|
||||
# Стартовые условия
|
||||
STARTING_CAPITAL = 100000
|
||||
TARGET_CAPITAL = 50000000
|
||||
|
||||
# Способности игроков
|
||||
ABILITIES = {
|
||||
'crisis_investor': {
|
||||
'name': 'Кризисный инвестор',
|
||||
'description': '+20% к доходу при падении рынка более 10%',
|
||||
'cooldown': 3,
|
||||
'effect': {
|
||||
'type': 'crisis_bonus',
|
||||
'value': 0.2,
|
||||
'condition': 'market_drop > 0.1'
|
||||
}
|
||||
},
|
||||
'lobbyist': {
|
||||
'name': 'Лоббист',
|
||||
'description': 'Может временно снизить налоги на 5%',
|
||||
'cooldown': 2,
|
||||
'effect': {
|
||||
'type': 'tax_reduction',
|
||||
'value': 0.05,
|
||||
'duration': 1
|
||||
}
|
||||
},
|
||||
'predictor': {
|
||||
'name': 'Предсказатель',
|
||||
'description': 'Видит следующее случайное событие за 1 месяц',
|
||||
'cooldown': 4,
|
||||
'effect': {
|
||||
'type': 'event_preview',
|
||||
'lookahead': 1
|
||||
}
|
||||
},
|
||||
'golden_pillow': {
|
||||
'name': 'Золотая подушка',
|
||||
'description': 'Может защитить 20% капитала от любых потерь',
|
||||
'cooldown': 6,
|
||||
'effect': {
|
||||
'type': 'capital_protection',
|
||||
'percentage': 0.2,
|
||||
'duration': 1
|
||||
}
|
||||
},
|
||||
'shadow_accountant': {
|
||||
'name': 'Теневая бухгалтерия',
|
||||
'description': 'Раз в игру уменьшить налоги на 50%',
|
||||
'cooldown': None,
|
||||
'effect': {
|
||||
'type': 'tax_evasion',
|
||||
'value': 0.5
|
||||
}
|
||||
},
|
||||
'credit_magnate': {
|
||||
'name': 'Кредитный магнат',
|
||||
'description': 'Может давать кредиты другим игрокам под свой процент',
|
||||
'cooldown': 1,
|
||||
'effect': {
|
||||
'type': 'lend_money',
|
||||
'max_amount_multiplier': 0.5
|
||||
}
|
||||
},
|
||||
'bear_raid': {
|
||||
'name': 'Медвежий набег',
|
||||
'description': 'Вызвать искусственное падение цены актива на 15%',
|
||||
'cooldown': 4,
|
||||
'effect': {
|
||||
'type': 'price_manipulation',
|
||||
'direction': 'down',
|
||||
'value': 0.15
|
||||
}
|
||||
},
|
||||
'fake_news': {
|
||||
'name': 'Фейковые новости',
|
||||
'description': 'Подменить одно случайное событие',
|
||||
'cooldown': 6,
|
||||
'effect': {
|
||||
'type': 'event_manipulation',
|
||||
'scope': 'next_event'
|
||||
}
|
||||
},
|
||||
'dividend_king': {
|
||||
'name': 'Король дивидендов',
|
||||
'description': '+10% дохода от всех дивидендных активов',
|
||||
'cooldown': 0,
|
||||
'effect': {
|
||||
'type': 'dividend_bonus',
|
||||
'value': 0.1
|
||||
}
|
||||
},
|
||||
'raider_capture': {
|
||||
'name': 'Рейдерский захват',
|
||||
'description': 'Попытаться отобрать 5% капитала у лидера',
|
||||
'cooldown': 3,
|
||||
'effect': {
|
||||
'type': 'capital_raid',
|
||||
'percentage': 0.05,
|
||||
'success_chance': 0.6,
|
||||
'penalty': 0.1
|
||||
}
|
||||
},
|
||||
'mafia_connections': {
|
||||
'name': 'Мафиозные связи',
|
||||
'description': 'Отменить одно негативное событие для себя',
|
||||
'cooldown': 5,
|
||||
'effect': {
|
||||
'type': 'event_block',
|
||||
'condition': 'negative_event'
|
||||
}
|
||||
},
|
||||
'economic_advisor': {
|
||||
'name': 'Экономический советник',
|
||||
'description': 'Раз в 3 месяца изменить налоговую ставку для всех',
|
||||
'cooldown': 3,
|
||||
'effect': {
|
||||
'type': 'tax_policy',
|
||||
'change_range': (-0.05, 0.05)
|
||||
}
|
||||
},
|
||||
'currency_speculator': {
|
||||
'name': 'Валютный спекулянт',
|
||||
'description': 'На 1 месяц отвязать рубль от нефти',
|
||||
'cooldown': 4,
|
||||
'effect': {
|
||||
'type': 'correlation_break',
|
||||
'assets': ['oil', 'stock_gazprom'],
|
||||
'duration': 1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# Ограничения по владению
|
||||
MAX_ASSETS_PER_TYPE = {
|
||||
'bonds': None,
|
||||
'stocks': 0.4,
|
||||
'real_estate': 0.5,
|
||||
'crypto': None,
|
||||
'commodities': 0.3,
|
||||
'business': 0.4,
|
||||
'unique': 1.0,
|
||||
}
|
||||
|
||||
# Лимиты по месяцам
|
||||
PURCHASE_LIMITS_BY_MONTH = {
|
||||
1: 0.1,
|
||||
2: 0.2,
|
||||
3: 0.4,
|
||||
4: 0.6,
|
||||
5: 0.8,
|
||||
6: 1.0,
|
||||
}
|
||||
11
game_balance/test_balance/__init__.py
Normal file
11
game_balance/test_balance/__init__.py
Normal file
@@ -0,0 +1,11 @@
|
||||
from game_balance.test_balance.easy_mode import EasyBalance
|
||||
from game_balance.test_balance.hard_mode import HardBalance
|
||||
from game_balance.test_balance.tournament import TournamentBalance
|
||||
from game_balance.test_balance.standard_mode import StandardBalance
|
||||
|
||||
__all__ = [
|
||||
'EasyBalance',
|
||||
'HardBalance',
|
||||
'TournamentBalance',
|
||||
'StandardBalance'
|
||||
]
|
||||
73
game_balance/test_balance/easy_mode.py
Normal file
73
game_balance/test_balance/easy_mode.py
Normal file
@@ -0,0 +1,73 @@
|
||||
"""
|
||||
ЛЕГКИЙ РЕЖИМ БАЛАНСА
|
||||
Для новичков
|
||||
"""
|
||||
|
||||
from game_balance.test_balance.standard_mode import StandardBalance
|
||||
|
||||
|
||||
class EasyBalance(StandardBalance):
|
||||
"""Облегченный режим"""
|
||||
|
||||
@classmethod
|
||||
def get_assets_config(cls):
|
||||
config = super().get_assets_config()
|
||||
|
||||
# Делаем активы дешевле и стабильнее
|
||||
for asset_id, asset_data in config.items():
|
||||
if asset_data.get('base_price'):
|
||||
asset_data['base_price'] *= 0.5
|
||||
if asset_data.get('volatility'):
|
||||
asset_data['volatility'] *= 0.7
|
||||
if asset_data.get('income_per_month'):
|
||||
asset_data['income_per_month'] *= 1.3
|
||||
|
||||
return config
|
||||
|
||||
@classmethod
|
||||
def get_players_config(cls):
|
||||
config = super().get_players_config()
|
||||
config['STARTING_CAPITAL'] = 200000
|
||||
config['TARGET_CAPITAL'] = 25000000
|
||||
|
||||
# Более мягкие ограничения
|
||||
config['MAX_ASSETS_PER_TYPE'] = {
|
||||
'bonds': None,
|
||||
'stocks': 0.6,
|
||||
'real_estate': 0.7,
|
||||
'crypto': None,
|
||||
'commodities': 0.5,
|
||||
'business': 0.6,
|
||||
'unique': 1.0,
|
||||
}
|
||||
|
||||
# Быстрое снятие лимитов
|
||||
config['PURCHASE_LIMITS_BY_MONTH'] = {
|
||||
1: 0.3,
|
||||
2: 0.6,
|
||||
3: 1.0,
|
||||
}
|
||||
|
||||
return config
|
||||
|
||||
@classmethod
|
||||
def get_economy_config(cls):
|
||||
config = super().get_economy_config()
|
||||
|
||||
# Меньше налогов
|
||||
config['TAX_SYSTEM']['income_tax'] = [
|
||||
{'threshold': 0, 'rate': 0.00},
|
||||
{'threshold': 100000, 'rate': 0.05},
|
||||
{'threshold': 500000, 'rate': 0.10},
|
||||
{'threshold': 2000000, 'rate': 0.15},
|
||||
{'threshold': 5000000, 'rate': 0.20},
|
||||
]
|
||||
config['TAX_SYSTEM']['wealth_tax']['threshold'] = 10000000
|
||||
|
||||
# Ниже ставки по кредитам
|
||||
config['LOAN_SYSTEM']['interest_rates']['standard'] = 0.03
|
||||
|
||||
# Меньше инфляции
|
||||
config['MACROECONOMICS']['base_inflation'] = 0.002
|
||||
|
||||
return config
|
||||
73
game_balance/test_balance/hard_mode.py
Normal file
73
game_balance/test_balance/hard_mode.py
Normal file
@@ -0,0 +1,73 @@
|
||||
"""
|
||||
ЛЕГКИЙ РЕЖИМ БАЛАНСА
|
||||
Для новичков
|
||||
"""
|
||||
|
||||
from game_balance.test_balance.standard_mode import StandardBalance
|
||||
|
||||
|
||||
class HardBalance(StandardBalance):
|
||||
"""Облегченный режим"""
|
||||
|
||||
@classmethod
|
||||
def get_assets_config(cls):
|
||||
config = super().get_assets_config()
|
||||
|
||||
# Делаем активы дешевле и стабильнее
|
||||
for asset_id, asset_data in config.items():
|
||||
if asset_data.get('base_price'):
|
||||
asset_data['base_price'] *= 0.5
|
||||
if asset_data.get('volatility'):
|
||||
asset_data['volatility'] *= 0.7
|
||||
if asset_data.get('income_per_month'):
|
||||
asset_data['income_per_month'] *= 1.3
|
||||
|
||||
return config
|
||||
|
||||
@classmethod
|
||||
def get_players_config(cls):
|
||||
config = super().get_players_config()
|
||||
config['STARTING_CAPITAL'] = 200000
|
||||
config['TARGET_CAPITAL'] = 25000000
|
||||
|
||||
# Более мягкие ограничения
|
||||
config['MAX_ASSETS_PER_TYPE'] = {
|
||||
'bonds': None,
|
||||
'stocks': 0.6,
|
||||
'real_estate': 0.7,
|
||||
'crypto': None,
|
||||
'commodities': 0.5,
|
||||
'business': 0.6,
|
||||
'unique': 1.0,
|
||||
}
|
||||
|
||||
# Быстрое снятие лимитов
|
||||
config['PURCHASE_LIMITS_BY_MONTH'] = {
|
||||
1: 0.3,
|
||||
2: 0.6,
|
||||
3: 1.0,
|
||||
}
|
||||
|
||||
return config
|
||||
|
||||
@classmethod
|
||||
def get_economy_config(cls):
|
||||
config = super().get_economy_config()
|
||||
|
||||
# Меньше налогов
|
||||
config['TAX_SYSTEM']['income_tax'] = [
|
||||
{'threshold': 0, 'rate': 0.00},
|
||||
{'threshold': 100000, 'rate': 0.05},
|
||||
{'threshold': 500000, 'rate': 0.10},
|
||||
{'threshold': 2000000, 'rate': 0.15},
|
||||
{'threshold': 5000000, 'rate': 0.20},
|
||||
]
|
||||
config['TAX_SYSTEM']['wealth_tax']['threshold'] = 10000000
|
||||
|
||||
# Ниже ставки по кредитам
|
||||
config['LOAN_SYSTEM']['interest_rates']['standard'] = 0.03
|
||||
|
||||
# Меньше инфляции
|
||||
config['MACROECONOMICS']['base_inflation'] = 0.002
|
||||
|
||||
return config
|
||||
61
game_balance/test_balance/standard_mode.py
Normal file
61
game_balance/test_balance/standard_mode.py
Normal file
@@ -0,0 +1,61 @@
|
||||
"""
|
||||
СТАНДАРТНЫЙ РЕЖИМ БАЛАНСА
|
||||
Используется по умолчанию
|
||||
"""
|
||||
|
||||
from game_balance.assets_config import AssetsConfig
|
||||
from game_balance.players_config import PlayersConfig
|
||||
from game_balance.economy_config import EconomyConfig
|
||||
from game_balance.events_config import EventsConfig
|
||||
from game_balance.game_config import GameConfig
|
||||
|
||||
class StandardBalance:
|
||||
"""Стандартный сбалансированный режим"""
|
||||
|
||||
@classmethod
|
||||
def get_assets_config(cls):
|
||||
"""Возвращает конфигурацию активов"""
|
||||
return AssetsConfig.ASSETS.copy()
|
||||
|
||||
@classmethod
|
||||
def get_players_config(cls):
|
||||
"""Возвращает конфигурацию игроков"""
|
||||
return {
|
||||
'STARTING_CAPITAL': PlayersConfig.STARTING_CAPITAL,
|
||||
'TARGET_CAPITAL': PlayersConfig.TARGET_CAPITAL,
|
||||
'MAX_ASSETS_PER_TYPE': PlayersConfig.MAX_ASSETS_PER_TYPE.copy(),
|
||||
'PURCHASE_LIMITS_BY_MONTH': PlayersConfig.PURCHASE_LIMITS_BY_MONTH.copy(),
|
||||
'ABILITIES': PlayersConfig.ABILITIES.copy()
|
||||
}
|
||||
|
||||
@classmethod
|
||||
def get_economy_config(cls):
|
||||
"""Возвращает экономическую конфигурацию"""
|
||||
return {
|
||||
'TAX_SYSTEM': EconomyConfig.TAX_SYSTEM.copy(),
|
||||
'LOAN_SYSTEM': EconomyConfig.LOAN_SYSTEM.copy(),
|
||||
'MACROECONOMICS': EconomyConfig.MACROECONOMICS.copy(),
|
||||
'ASSET_CORRELATIONS': EconomyConfig.ASSET_CORRELATIONS.copy()
|
||||
}
|
||||
|
||||
@classmethod
|
||||
def get_events_config(cls):
|
||||
"""Возвращает конфигурацию событий"""
|
||||
return {
|
||||
'EVENT_TYPES': EventsConfig.EVENT_TYPES.copy(),
|
||||
'EVENTS': EventsConfig.EVENTS.copy(),
|
||||
'CRISES': EventsConfig.CRISES.copy()
|
||||
}
|
||||
|
||||
@classmethod
|
||||
def get_game_config(cls):
|
||||
"""Возвращает игровую конфигурацию"""
|
||||
return {
|
||||
'PHASE_DURATIONS': GameConfig.PHASE_DURATIONS.copy(),
|
||||
'SPEED_MODES': GameConfig.SPEED_MODES.copy(),
|
||||
'MAX_PLAYERS_PER_ROOM': GameConfig.MAX_PLAYERS_PER_ROOM,
|
||||
'MIN_PLAYERS_TO_START': GameConfig.MIN_PLAYERS_TO_START,
|
||||
'MIN_PLAYERS_TO_CONTINUE': GameConfig.MIN_PLAYERS_TO_CONTINUE,
|
||||
'MAX_TRANSACTIONS_PER_PHASE': GameConfig.MAX_TRANSACTIONS_PER_PHASE,
|
||||
'MIN_BID_AMOUNT': GameConfig.MIN_BID_AMOUNT
|
||||
}
|
||||
83
game_balance/test_balance/tournament.py
Normal file
83
game_balance/test_balance/tournament.py
Normal file
@@ -0,0 +1,83 @@
|
||||
"""
|
||||
ТУРНИРНЫЙ РЕЖИМ БАЛАНСА
|
||||
Для соревнований
|
||||
"""
|
||||
|
||||
from game_balance.test_balance.standard_mode import StandardBalance
|
||||
|
||||
|
||||
class TournamentBalance(StandardBalance):
|
||||
"""Турнирный режим"""
|
||||
|
||||
@classmethod
|
||||
def get_assets_config(cls):
|
||||
config = super().get_assets_config()
|
||||
|
||||
# Балансировка цен
|
||||
price_adjustments = {
|
||||
'gov_bonds': 1.0,
|
||||
'stock_gazprom': 0.8,
|
||||
'stock_sberbank': 0.8,
|
||||
'apartment_small': 0.7,
|
||||
'apartment_elite': 0.6,
|
||||
'bitcoin': 1.2,
|
||||
'oil': 1.0,
|
||||
'coffee_shop': 0.8,
|
||||
'it_startup': 1.0,
|
||||
'shopping_mall': 0.5,
|
||||
'oil_field': 0.4,
|
||||
}
|
||||
|
||||
for asset_id, multiplier in price_adjustments.items():
|
||||
if asset_id in config and config[asset_id].get('base_price'):
|
||||
config[asset_id]['base_price'] *= multiplier
|
||||
|
||||
return config
|
||||
|
||||
@classmethod
|
||||
def get_players_config(cls):
|
||||
config = super().get_players_config()
|
||||
config['STARTING_CAPITAL'] = 500000
|
||||
config['TARGET_CAPITAL'] = 100000000
|
||||
|
||||
# Жесткие ограничения против монополий
|
||||
config['MAX_ASSETS_PER_TYPE'] = {
|
||||
'bonds': 0.5,
|
||||
'stocks': 0.3,
|
||||
'real_estate': 0.4,
|
||||
'crypto': 0.5,
|
||||
'commodities': 0.3,
|
||||
'business': 0.4,
|
||||
'unique': 0.5,
|
||||
}
|
||||
|
||||
# Быстрый старт
|
||||
config['PURCHASE_LIMITS_BY_MONTH'] = {
|
||||
1: 0.5,
|
||||
2: 1.0,
|
||||
}
|
||||
|
||||
return config
|
||||
|
||||
@classmethod
|
||||
def get_economy_config(cls):
|
||||
config = super().get_economy_config()
|
||||
|
||||
# Высокие налоги
|
||||
config['TAX_SYSTEM']['income_tax'] = [
|
||||
{'threshold': 0, 'rate': 0.00},
|
||||
{'threshold': 100000, 'rate': 0.15},
|
||||
{'threshold': 500000, 'rate': 0.25},
|
||||
{'threshold': 2000000, 'rate': 0.35},
|
||||
{'threshold': 5000000, 'rate': 0.45},
|
||||
]
|
||||
config['TAX_SYSTEM']['wealth_tax']['rate'] = 0.02
|
||||
config['TAX_SYSTEM']['transaction_fee'] = 0.02
|
||||
|
||||
# Высокие ставки
|
||||
config['LOAN_SYSTEM']['interest_rates']['standard'] = 0.08
|
||||
|
||||
# Высокая инфляция
|
||||
config['MACROECONOMICS']['base_inflation'] = 0.01
|
||||
|
||||
return config
|
||||
124
game_balance/validator.py
Normal file
124
game_balance/validator.py
Normal file
@@ -0,0 +1,124 @@
|
||||
"""
|
||||
ВАЛИДАТОР БАЛАНСА
|
||||
Проверяет корректность настроек
|
||||
"""
|
||||
|
||||
class BalanceValidator:
|
||||
@staticmethod
|
||||
def validate_assets(assets):
|
||||
"""Проверка конфигурации активов"""
|
||||
errors = []
|
||||
|
||||
if not isinstance(assets, dict):
|
||||
errors.append("Assets config must be a dictionary")
|
||||
return errors
|
||||
|
||||
for asset_id, asset_data in assets.items():
|
||||
if not isinstance(asset_data, dict):
|
||||
errors.append(f"Asset {asset_id}: data must be a dictionary")
|
||||
continue
|
||||
|
||||
# Проверка обязательных полей
|
||||
required_fields = ['name', 'category', 'base_price', 'volatility']
|
||||
for field in required_fields:
|
||||
if field not in asset_data:
|
||||
errors.append(f"Asset {asset_id}: missing required field '{field}'")
|
||||
|
||||
# Проверка цены
|
||||
price = asset_data.get('base_price', 0)
|
||||
if not isinstance(price, (int, float)) or price <= 0:
|
||||
errors.append(f"Asset {asset_id}: base_price must be positive number")
|
||||
|
||||
# Проверка волатильности
|
||||
volatility = asset_data.get('volatility', 0)
|
||||
if not isinstance(volatility, (int, float)) or volatility < 0 or volatility > 1:
|
||||
errors.append(f"Asset {asset_id}: volatility must be between 0 and 1")
|
||||
|
||||
# Проверка доходности
|
||||
income = asset_data.get('income_per_month', 0)
|
||||
if not isinstance(income, (int, float)) or income < 0 or income > 0.5:
|
||||
errors.append(f"Asset {asset_id}: income_per_month must be between 0 and 0.5")
|
||||
|
||||
# Проверка количества
|
||||
total = asset_data.get('total_quantity')
|
||||
if total is not None:
|
||||
if not isinstance(total, (int, float)) or total <= 0:
|
||||
errors.append(f"Asset {asset_id}: total_quantity must be positive or None")
|
||||
|
||||
return errors
|
||||
|
||||
@staticmethod
|
||||
def validate_economy(economy_config):
|
||||
"""Проверка экономической конфигурации"""
|
||||
errors = []
|
||||
|
||||
if not isinstance(economy_config, dict):
|
||||
errors.append("Economy config must be a dictionary")
|
||||
return errors
|
||||
|
||||
# Проверка налогов
|
||||
tax_system = economy_config.get('TAX_SYSTEM', {})
|
||||
income_tax = tax_system.get('income_tax', [])
|
||||
|
||||
if not isinstance(income_tax, list):
|
||||
errors.append("income_tax must be a list")
|
||||
else:
|
||||
last_threshold = -1
|
||||
for i, bracket in enumerate(income_tax):
|
||||
if not isinstance(bracket, dict):
|
||||
errors.append(f"Tax bracket {i}: must be a dictionary")
|
||||
continue
|
||||
threshold = bracket.get('threshold', 0)
|
||||
if threshold <= last_threshold:
|
||||
errors.append(f"Tax brackets must be in ascending order at index {i}")
|
||||
last_threshold = threshold
|
||||
|
||||
# Проверка кредитов
|
||||
loan_system = economy_config.get('LOAN_SYSTEM', {})
|
||||
max_multiplier = loan_system.get('max_loan_multiplier', 0)
|
||||
if not isinstance(max_multiplier, (int, float)) or max_multiplier <= 0:
|
||||
errors.append("max_loan_multiplier must be positive")
|
||||
|
||||
# Проверка корреляций
|
||||
correlations = economy_config.get('ASSET_CORRELATIONS', {})
|
||||
if not isinstance(correlations, dict):
|
||||
errors.append("ASSET_CORRELATIONS must be a dictionary")
|
||||
else:
|
||||
for key, value in correlations.items():
|
||||
if not isinstance(key, str):
|
||||
errors.append(f"Correlation key must be string, got {type(key)}")
|
||||
if not isinstance(value, (int, float)) or value < -1 or value > 1:
|
||||
errors.append(f"Correlation value must be between -1 and 1, got {value}")
|
||||
|
||||
return errors
|
||||
|
||||
@staticmethod
|
||||
def validate_balance_mode(balance):
|
||||
"""Полная проверка режима баланса"""
|
||||
all_errors = []
|
||||
|
||||
# Проверяем наличие необходимых методов
|
||||
required_methods = ['get_assets_config', 'get_players_config', 'get_economy_config']
|
||||
for method in required_methods:
|
||||
if not hasattr(balance, method):
|
||||
all_errors.append(f"Balance mode missing required method: {method}")
|
||||
return all_errors
|
||||
|
||||
try:
|
||||
# Проверка активов
|
||||
assets = balance.get_assets_config()
|
||||
all_errors.extend(BalanceValidator.validate_assets(assets))
|
||||
|
||||
# Проверка экономики
|
||||
economy = balance.get_economy_config()
|
||||
all_errors.extend(BalanceValidator.validate_economy(economy))
|
||||
|
||||
# Проверка конфигурации игроков
|
||||
players = balance.get_players_config()
|
||||
if not isinstance(players, dict):
|
||||
all_errors.append("Players config must be a dictionary")
|
||||
|
||||
except Exception as e:
|
||||
all_errors.append(f"Error validating balance: {str(e)}")
|
||||
|
||||
return all_errors
|
||||
Reference in New Issue
Block a user