AI-Driven Game Testing & QA: Turnkey Development

AI-Driven Game Testing and QA System: Turnkey Development

AI Development Areas

Frequently Asked Questions

Latest works

  • image_web-applications_feedme_466_0.webp
    Development of a web application for FEEDME
    1284
  • image_ecommerce_furnoro_435_0.webp
    Development of an online store for the company FURNORO
    1240
  • image_logo-advance_0.webp
    B2B Advance company logo design
    696
  • image_crm_enviok_479_0.webp
    Development of a web application for Enviok
    982
  • image_logo-aider_0.webp
    AIDER company logo development
    917
  • image_crm_chasseurs_493_0.webp
    CRM development for Chasseurs
    1031

AI-Driven Game Testing and QA System: Turnkey Development

Manual QA testing doesn't scale: an open world with 1000+ quests and 100+ mechanics cannot be manually tested in reasonable time. A team of 5 testers covers about 2000 unique states per week, while our RL agent covers 10 million in a day. We developed a turnkey system that autonomously explores the game world, finds crashes, softlocks, exploits, and balance issues — working 24/7 and increasing coverage to 90% in hours.

Why Manual Testing Falls Short

Even a large QA team misses edge cases: rare action combinations, race conditions, quests in non-standard order. Our RL agent with curiosity-driven exploration (ICM) purposefully seeks unknown states, while the Go-Explore algorithm remembers interesting points and returns to them. In 10 million steps, the agent visits more unique states than a team of 5 in a month. As a result, we find 3x more bugs at launch, and QA costs drop by 10x.

How We Build the AI Testing System

Crash/Softlock Testing

The agent randomly explores the entire action space, triggering edge cases that crash the game or get stuck in a loop. Agent code with ICM:

class GameTestingAgent: """Agent for coverage-based testing""" 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 for new states 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) # high entropy for 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) # log new states state_hash = self._hash_state(obs) self.visited_states.add(state_hash) # detect softlock (agent goes in circles) 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

We check if there are zones, items, or achievements never reached by normal gameplay. The agent with ICM is rewarded for visiting new states — hidden areas are detected automatically. On one project, we found 23 unimplemented quests and 5 hidden achievements that developers had forgotten about.

Exploit Detection

A special agent trained to maximize score without constraints:

# exploit reward: ONLY score, ignore all "normal" paths def exploit_reward(info): return info['score'] # + info['gold'] + info['level'] # train on minimal steps (fast exploit) model = PPO("MlpPolicy", env, gamma=0.5, # short horizon = wants fast rewards ent_coef=0.1) # lots of exploration # if agent finds a way to get 10x average score in 1 minute # → this is an exploit for QA team 

What Is Intrinsic Curiosity Module (ICM)?

More about ICM

ICM is a module that generates intrinsic rewards: the more the model errs in predicting the next state, the more curious the agent becomes. This drives it to discover new locations and mechanics. As noted in OpenAI Spinning Up, curiosity-driven exploration is key to efficient exploration in games with sparse rewards.

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 = how new this state is 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 # high for new, low for known 

How Go-Explore Solves the Sparse Reward Problem

Classic RL gets stuck in games with sparse rewards. Go-Explore (Adept AI):

  1. Stores an archive of interesting states (by diversity).
  2. Randomly selects a state from the archive.
  3. Returns to it (deterministic replay).
  4. Continues exploration from there.
class GoExploreAgent: def __init__(self, game): self.game = game self.archive = {} # cell -> (score, state_snapshot) def cell_key(self, state): """Discretize state into cell (simplified for storage)""" # for 2D game: (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): # select a state from archive (rarely visited) cell = self._select_cell() state = self.archive[cell]['snapshot'] # restore state (savestate) self.game.load_state(state) # randomly explore N steps 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 

Approach Comparison: ICM vs Random vs Expert

Method Coverage (states/hour) Exploit detection Setup time
Random 10,000 Low 1 day
ICM 500,000 Medium 2 weeks
Go-Explore 1,000,000+ High 3 weeks

What's Included in the Turnkey Package?

Component Description Duration (weeks)
Crash/Softlock Agent ICM + PPO, catches crashes and softlocks 4
Content Coverage Checks coverage of zones, items, quests 2
Exploit Hunter Agent trained to maximize score 3
Go-Explore State archive integration for sparse rewards 3
CI/CD Integration GitHub Actions / Jenkins, metrics dashboard 2
Documentation & Training Agent descriptions, API, training your QA team 1

Total: 12–16 weeks for a full system. Cost is calculated individually after auditing your project. Contact us for a consultation — we'll evaluate your game and propose a solution.

Efficiency Metrics

  • State space coverage: % of game states visited (goal ≥90%).
  • Crash count per build: average 3 crashes per build before optimization, 0 after.
  • Softlock incidents found: 50+ per project.
  • New exploits detected per patch: average 8.
  • Time to 90% coverage: 4 to 8 hours (manually — a month).

Regression Testing

After a patch — launch agents to ensure previously passing tests are not broken. CI/CD pipeline automatically runs tests on each 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 

Our engineers have 10+ years of experience in AI game development, with over 50 successful projects. We guarantee the system will find at least 3x more bugs than manual testing at launch. Get a consultation — we'll evaluate your game and propose a solution.