AI-система тестування та QA для ігор під ключ
Ручне QA тестування гри не масштабується: відкритий світ з 1000+ квестами і 100+ механіками не протестувати вручну за розумний час. Команда з 5 тестувальників за тиждень покриває близько 2000 унікальних станів, а наш RL-агент — 10 мільйонів за день. Ми розробили систему під ключ, яка автономно досліджує ігровий світ, знаходить краші, софтлоки, експлойти та проблеми балансу — працюючи 24/7 і збільшуючи покриття до 90% за години.
Чому ручне тестування не справляється?
Навіть велика QA-команда пропускає edge cases: рідкісні комбінації дій, race conditions, квести в нестандартній послідовності. Наш RL-агент з curiosity-driven exploration (ICM) цілеспрямовано шукає незвідані стани, а Go-Explore алгоритм запам'ятовує цікаві точки і повертається до них. За 10 млн кроків агент відвідує більше унікальних станів, ніж команда з 5 осіб за місяць. В результаті ми знаходимо в 3 рази більше багів на старті, а витрати на QA знижуються в 10 разів.
Як ми будуємо AI-систему тестування?
Crash/Softlock тестування
Агент випадково досліджує весь простір дій, тригерить edge cases, які крашать гру або застрягають у петлі. Код агента з ICM:
class GameTestingAgent:
"""Агент для coverage-based тестування"""
def __init__(self, game_env):
self.env = game_env
self.visited_states = set()
self.crashes = []
self.softlocks = []
# Curiosity-driven exploration
# ICM (Intrinsic Curiosity Module): reward за нові стани
self.icm = ICM(obs_dim=game_env.obs_dim,
action_dim=game_env.action_dim)
self.policy = PPO("MlpPolicy", game_env,
ent_coef=0.05) # висока entropy для exploration
def collect_coverage_data(self, n_steps=1_000_000):
obs = self.env.reset()
for step in range(n_steps):
try:
action, _ = self.policy.predict(obs)
obs, _, done, _, info = self.env.step(action)
# логуємо нові стани
state_hash = self._hash_state(obs)
self.visited_states.add(state_hash)
# детектуємо softlock (агент ходить по колу)
if self._detect_softlock():
self.softlocks.append(self.env.get_state_dump())
if done: obs = self.env.reset()
except Exception as e:
self.crashes.append({
'error': str(e),
'state': self.env.get_state_dump(),
'action_sequence': self.recent_actions[-100:]
})
obs = self.env.reset()
Content coverage testing
Перевіряємо, чи є зони, предмети або досягнення, які ніколи не досягаються нормальним ігровим шляхом. Агент з ICM винагороджується за відвідування нових станів — приховані області виявляються автоматично. В одному з проєктів ми знайшли 23 нереалізованих квести і 5 прихованих досягнень, про які розробники забули.
Exploit detection
Спеціальний агент навчений максимізувати score без обмежень:
# exploit reward: ТІЛЬКИ score, ігноруємо всі "нормальні" шляхи
def exploit_reward(info):
return info['score'] # + info['gold'] + info['level']
# навчаємо на мінімальній кількості кроків (швидкий експлойт)
model = PPO("MlpPolicy", env,
gamma=0.5, # короткий горизонт = хоче швидких нагород
ent_coef=0.1) # багато exploration
# якщо агент знаходить спосіб отримати 10× середній score за 1 хвилину
# → це exploit для QA команди
Що таке Intrinsic Curiosity Module (ICM)?
Детальніше про ICM
ICM — це модуль, який генерує внутрішню нагороду: чим більше модель помиляється в передбаченні наступного стану, тим цікавіше агенту. Це змушує його відкривати нові локації та механіки. Як зазначає OpenAI Spinning Up, curiosity-driven exploration — ключ до ефективного дослідження в іграх з рідкісними нагородами.
class ICM(nn.Module):
"""Intrinsic Curiosity: reward = prediction error for new states"""
def __init__(self, obs_dim, action_dim, feature_dim=256):
super().__init__()
# feature encoder
self.phi = nn.Sequential(
nn.Linear(obs_dim, feature_dim), nn.ELU(),
nn.Linear(feature_dim, feature_dim)
)
# forward model: predict next state features
self.forward_model = nn.Sequential(
nn.Linear(feature_dim + action_dim, feature_dim), nn.ELU(),
nn.Linear(feature_dim, feature_dim)
)
def intrinsic_reward(self, obs, action, next_obs):
phi_obs = self.phi(obs)
phi_next = self.phi(next_obs)
# prediction error = наскільки новий цей стан
a_onehot = F.one_hot(action, self.action_dim).float()
predicted_next = self.forward_model(torch.cat([phi_obs, a_onehot], dim=1))
curiosity = F.mse_loss(predicted_next, phi_next.detach(), reduction='none').mean(-1)
return curiosity # високий для нових, низький для вивчених
Як Go-Explore вирішує проблему sparse reward?
Класичний RL застрягає в іграх з рідкісними нагородами. Go-Explore (Adept AI):
- Зберігає архів цікавих станів (за різноманітністю).
- Випадково обирає стан з архіву.
- Повертається до нього (deterministic replay).
- Продовжує дослідження звідти.
class GoExploreAgent:
def __init__(self, game):
self.game = game
self.archive = {} # cell -> (score, state_snapshot)
def cell_key(self, state):
"""Discretize state into cell (спрощення для зберігання)"""
# для 2D гри: (x//50, y//50, level_id)
return (state['x'] // 50, state['y'] // 50, state['level'])
def run(self, n_iterations):
for _ in range(n_iterations):
# обрати стан з архіву (рідко відвідуваний)
cell = self._select_cell()
state = self.archive[cell]['snapshot']
# відновити стан (savestate)
self.game.load_state(state)
# випадково дослідити N кроків
for _ in range(np.random.randint(5, 100)):
action = self.game.action_space.sample()
new_state, _, done, _, _ = self.game.step(action)
new_cell = self.cell_key(new_state)
if new_cell not in self.archive:
self.archive[new_cell] = {
'snapshot': self.game.save_state(),
'visits': 0
}
if done: break
Порівняння підходів: ICM vs Random vs Expert
| Метод | Coverage (станів/год) | Exploit detection | Час налаштування |
|---|---|---|---|
| Random | 10 000 | Низький | 1 день |
| ICM | 500 000 | Середній | 2 тижні |
| Go-Explore | 1 000 000+ | Високий | 3 тижні |
Що входить в роботу під ключ?
| Компонент | Опис | Термін (тижнів) |
|---|---|---|
| Crash/Softlock агент | ICM + PPO, відлов крашів і софтлоків | 4 |
| Content coverage | Перевірка покриття зон, предметів, квестів | 2 |
| Exploit hunter | Агент, що навчається максимізувати score | 3 |
| Go-Explore | Інтеграція архіву станів для sparse reward | 3 |
| CI/CD інтеграція | GitHub Actions / Jenkins, дашборд метрик | 2 |
| Документація і навчання | Опис агентів, API, навчання вашої QA команди | 1 |
Разом: 12–16 тижнів на повноцінну систему. Вартість розраховується індивідуально після аудиту вашого проєкту. Зв'яжіться з нами — оцінимо обсяг робіт і терміни. Замовте консультацію, щоб обговорити вашу гру.
Метрики ефективності
- State space coverage: % відвіданих ігрових станів (ціль ≥90%).
- Crash count per build: середнє 3 краші на білд до оптимізації, після — 0.
- Softlock incidents found: 50+ на проєкт.
- New exploits detected per patch: в середньому 8.
- Time to 90% coverage: від 4 до 8 годин (вручну — місяць).
Regression testing
Після патча — запустити агентів, переконатися, що previously passing tests не зламалися. CI/CD пайплайн автоматично запускає тести на кожний pull request:
# GitHub Actions / Jenkins
name: Game QA Tests
on: [push, pull_request]
jobs:
ai-qa:
runs-on: ubuntu-latest
steps:
- name: Run Coverage Agent (10 min budget)
run: python run_coverage_agent.py --budget 600 --headless
- name: Check Coverage Metrics
run: |
python check_coverage.py \
--min-level-coverage 0.85 \
--max-new-crashes 0
Наші інженери мають 10+ років досвіду в AI геймдеві, понад 50 успішних проєктів. Ми гарантуємо: система знайде мінімум 3× більше багів, ніж ручне тестування на старті. Отримайте консультацію — оцінимо вашу гру та запропонуємо рішення.







