Ayoob AI

Eliminating Bot Networks: Two-Phase GPU Pattern Matching for Gaming Anti-Cheat

·14 min read·Ayoob AI
GamingAnti-CheatWebGPUPattern MatchingFraud Detection

The fraud detection problem in online gaming

Coordinated bot networks cost online gaming platforms millions in revenue every year. In poker, bots collude across multiple tables by sharing hand information through coded chat messages and behavioural signals. In competitive multiplayer games, bot farms manipulate matchmaking, inflate rankings, and exploit in-game economies. In virtual casinos, automated players execute optimized strategies that erode the house edge.

The detection challenge is not recognizing a single bot. A single bot can be identified by its betting patterns, its reaction times, or its statistical anomalies over hundreds of hands. The hard problem is detecting coordination: identifying that 8 accounts across 4 tables are operated by the same entity, communicating through chat patterns that look innocuous in isolation.

This requires searching massive volumes of text and behavioural data for subtle patterns. Chat logs, in-game actions, timing sequences, betting patterns. The corpus grows by hundreds of thousands of entries per gaming session. The search must be fast enough to act on the current session, not the last one.

Batch processing (run regex over last hour's logs every 60 minutes) misses the first 59 minutes of fraud in every cycle. By the time the batch job flags a bot network, the damage is done. The bots have extracted value and moved on.

You need real-time search. Sub-second latency across the full corpus. And you need it to run at the edge or in the client, because round-tripping to a central server for every pattern check introduces the latency you are trying to eliminate.

Why standard regex fails at scale

The natural tool for pattern matching is regular expressions. Every language has a regex engine. JavaScript's RegExp is available on every browser and every server.

The problem is not correctness. Regex can express the patterns you need: coded phrases, repeated character sequences, base64-encoded payloads, suspicious URL fragments, timing-correlated message templates.

The problem is throughput. JavaScript's RegExp.prototype.test() processes one string at a time on a single thread. For 1 million chat messages averaging 80 characters each, a moderately complex regex takes 500 to 900 ms. That is nearly a second of blocking compute for a single search query.

Web Workers help. Eight workers processing 125,000 messages each bring the total to 70 to 120 ms. Acceptable for a single query, but a real-time detection pipeline runs dozens of patterns per second across overlapping windows. At 100 ms per query with 20 patterns, you consume 2 full seconds of CPU per detection cycle. On a 4-core device, that saturates the CPU.

The obvious next step: move the regex to the GPU. Three thousand cores instead of eight. Massive parallelism.

It does not work. And the reason is architectural.

The SIMD divergence problem with regex on GPUs

A regex engine evaluates a pattern against a string by traversing a nondeterministic finite automaton (NFA) or a compiled deterministic finite automaton (DFA). Each character in the input string triggers one or more state transitions. The sequence of transitions depends entirely on the content of the string.

On a GPU, each thread in a 32-wide warp processes a different chat message. Each message has different content. At every character position, each thread follows a different state transition. The warp must serialize.

This is not bounded branch divergence with 2 or 3 paths. It is categorical divergence with up to 32 completely independent execution paths per warp. The GPU's SIMT architecture cannot parallelize this. Every thread effectively runs sequentially, occupying a GPU core that contributes nothing to throughput while waiting for the other 31 threads to complete their state transitions.

Our measurements on a discrete GPU with 3,072 cores:

Method1 million messagesThroughput
CPU single-thread (RegExp)820 ms1.2M msg/s
CPU 8-thread (Workers + RegExp)108 ms9.3M msg/s
GPU naive regex (NFA per thread)940 ms1.1M msg/s

The GPU regex is slower than single-threaded CPU. Three thousand cores, performing worse than one. The categorical GPU inhibition system in our engine assigns a penalty of negative infinity to NFA/DFA traversal patterns, preventing this dispatch from ever occurring. But blocking is not a solution. We need a way to use the GPU for text search that avoids the divergence problem entirely.

Our two-phase GPU pipeline

The insight: regex is expensive because it examines every character in every message sequentially. But most messages in a corpus do not match the pattern. For a specific fraud indicator (a coded phrase, a particular character sequence), 95% to 99% of messages are irrelevant.

If you can identify the irrelevant messages cheaply, you only pay the expensive per-character cost on the 1% to 5% that might match.

Our Adaptive WebGPU Pattern Matching Engine splits the search into two GPU dispatches. Phase 1 is cheap, parallel, and GPU-friendly. Phase 2 is expensive, sequential, and GPU-hostile. But Phase 2 runs on 3% of the corpus instead of 100%.

Phase 1: Character frequency histogram pre-filter

Phase 1 does not search for the pattern. It asks whether each message has the right character composition to possibly contain the pattern.

For a query pattern like "FOLD_TABLE3", any matching message must contain at least: 1x F, 1x O, 1x L, 1x D, 1x _, 1x T, 1x A, 1x B, 1x E, 1x 3. A message that contains zero _ characters cannot match, regardless of its other content.

Phase 1 builds a 128-bin character frequency histogram for each message. Each bin corresponds to an ASCII code point (0 to 127). The histogram is built in workgroup shared memory: 16 KB of on-chip SRAM that runs at register-like speeds (20 to 50 cycles latency versus 200 to 400 for global GPU memory).

Each workgroup of 256 threads processes one message. The workgroup ID maps to the message index. Threads cooperatively scan the message's bytes at strided positions (thread i processes positions i, i+256, i+512, ...), incrementing shared memory counters via atomicAdd on the local histogram. Because the histogram has 128 bins and contention is distributed across bins, the atomic contention is manageable.

After the histogram is built, thread 0 compares it against the query pattern's character requirements. The requirements are passed via a storage buffer encoding the character code and required count per entry. If the message's histogram shows fewer of any required character than the pattern demands, the message is marked as non-candidate (0); otherwise it is marked as candidate (1) in a per-message bitmask buffer.

This comparison is a tight loop over 10 to 15 unique characters in the pattern. It completes in tens of nanoseconds per message. The cost is negligible.

Phase 1 effectiveness on gaming chat data

Gaming chat is highly distinctive. Coded bot communications use specific character combinations (underscores, digits, mixed case) that normal player chat lacks. Natural chat is dominated by common letters (e, t, a, o, i) and spaces.

Pattern typeExampleMessages eliminated
Coded command with underscoreFOLD_TABLE397.8%
Base64 fragmentaGVsbG8=96.2%
IP address pattern192.168.98.4%
Repeated character sequenceXXXX93.1%
Common word (false positive heavy)call68.4%

For specific, targeted patterns (the majority of anti-cheat detection rules), Phase 1 eliminates 93% to 98% of messages. Even for common words, it eliminates over two-thirds.

Phase 2: Targeted byte-level matching

Phase 2 dispatches a second compute shader across all byte positions in the corpus. The search pattern (up to 64 bytes) is loaded into workgroup shared memory by the first threads of each workgroup, ensuring register-speed access during comparison.

Each thread is assigned a byte position in the corpus via its global invocation ID. The thread determines which message contains its position using a binary search over the message offset array, then checks the candidate bitmask for that message. If the message was marked non-candidate by Phase 1, the thread returns immediately with no work performed. This is the key efficiency gain: threads in non-candidate messages (93% to 98% of the corpus) perform zero work.

For threads in candidate messages whose position plus pattern length falls within the message bounds, a byte-by-byte comparison is performed between the corpus (read from global memory) and the pattern (read from shared memory). On match, the thread atomically increments a result counter and writes the message ID and position to a results buffer.

This design avoids the divergence problem of one-thread-per-message approaches. All threads execute the same instruction sequence (bounds check, bitmask check, compare loop). Threads in non-candidate messages exit uniformly at the bitmask check. Threads in candidate messages execute the same tight comparison loop. On SIMT hardware, this uniform control flow preserves warp-level parallelism.

End-to-end timing

1 million chat messages, average 80 characters, 80 MB total corpus:

PhaseWorkTime (discrete GPU)
Phase 1: Histogram build + evaluateAll 1M messages2.8 ms
Bitmask prefix sum + compaction1M bits0.3 ms
Phase 2: Byte matching~25,000 candidates (2.5%)4.1 ms
Result readbackMatch indices0.2 ms
Total7.4 ms

Compare:

MethodTime
CPU single-thread (RegExp)820 ms
CPU 8-thread (Workers + RegExp)108 ms
GPU naive (NFA per thread)940 ms
GPU two-phase (our engine)7.4 ms

111x faster than single-threaded regex. 14.6x faster than the 8-worker pool. And the GPU is not running regex. It is running a structurally different algorithm designed for the hardware.

Detecting coordinated bot networks

Single-pattern search finds individual indicators. Detecting coordination requires correlating multiple indicators across accounts, tables, and time windows.

A typical bot network detection pipeline on a poker platform:

Step 1: Behavioural stream indexing

Player actions (bet, fold, check, raise) are encoded as typed events with timestamps, table IDs, and player IDs. These are loaded into columnar SharedArrayBuffer storage. A session of 200,000 events occupies roughly 8 MB.

Step 2: Chat log pattern sweep

The two-phase GPU pipeline runs multiple detection patterns against the session's chat corpus:

  • Coded command patterns (table references, hand signals)
  • Timing-encoded messages (messages sent at specific intervals that encode game state)
  • Unusual character frequency profiles (messages that look like encoded data, not natural language)
  • Repeated templates (bot-generated messages with minor variations)

Each pattern search takes 7 to 12 ms. A sweep of 20 patterns completes in 150 to 240 ms.

Step 3: Cross-account correlation

The pattern sweep produces a set of flagged messages with account IDs and timestamps. The query engine runs a temporal correlation query: for each flagged message, find all other flagged messages from different accounts within a 5-second window at the same table.

This is a windowed self-join on the flagged subset. Because the flagged subset is small (typically a few hundred messages from millions), the join runs on the CPU in under 1 ms.

Step 4: Network graph construction

Correlated account pairs are assembled into a graph. Connected components identify bot networks: groups of accounts that repeatedly produce time-correlated flagged messages across shared tables. A cluster of 6 accounts with 50+ temporal correlations over a 2-hour session is not coincidence.

Total pipeline latency: under 300 ms for a full session analysis. Fast enough to run every 30 seconds on live data.

The 18% revenue recovery

We deployed this pipeline on a large-scale online poker platform. The platform had existing anti-cheat: hourly batch regex over server-side logs, statistical anomaly detection on betting patterns, manual review queues.

The existing system caught bots, but slowly. A sophisticated bot network could operate for 45 to 60 minutes before the hourly batch flagged suspicious patterns. The manual review queue added another 2 to 4 hours. By the time action was taken, the bots had played thousands of hands and extracted substantial value from human players.

Our real-time pipeline changed the detection window from hours to seconds. Specific results over the first 90-day deployment:

Detection latency dropped from 60+ minutes to under 30 seconds. The pipeline runs every 30 seconds on the trailing window of chat and behavioural data. New bot activity is flagged within one cycle.

Bot network identification improved by 340%. The temporal correlation step (Step 3) caught multi-account coordination that the batch regex missed entirely. Batch regex found individual suspicious messages. It did not correlate them across accounts within time windows.

18% of previously lost revenue was recovered. The platform measured revenue leakage from bot activity (value extracted from human player pools by automated play) before and after deployment. The 18% figure represents the difference: revenue that bots were previously capturing that now stays in the human player pool because the bots are detected and removed before they accumulate meaningful winnings.

False positive rate: under 0.3%. The multi-stage pipeline (pattern match, temporal correlation, graph analysis) requires multiple independent signals before flagging an account. A single unusual message does not trigger action. A cluster of time-correlated unusual messages across linked accounts does.

Why this cannot run as standard server-side regex

Three constraints make the server-side approach insufficient for real-time detection.

Constraint 1: Latency. A round-trip to the server adds 50 to 300 ms depending on geography, VPN overhead, and server load. For a pipeline that must complete in under 500 ms to enable 30-second detection cycles, spending 100 to 600 ms (two round-trips: send data, receive results) on network alone is prohibitive.

Constraint 2: Throughput at scale. The server must process chat data from all concurrent sessions simultaneously. A platform with 10,000 concurrent tables generating 200 messages per minute per table produces 2 million messages per minute. Server-side regex at 1.2 million messages per second (single-threaded) requires 1.67 seconds per minute of ingestion. That is 2.8% of a single core just for regex, scaling linearly with platform growth. At peak load (50,000 tables), it consumes 14% of a core for pattern matching alone. Offloading to the client means each client processes only its own session data.

Constraint 3: Pattern update agility. Bot operators adapt. They change their coded phrases, alter their timing patterns, modify their templates. Detection patterns must update frequently. In a server-side architecture, pattern updates require deployment. In a client-side architecture with our engine, the pattern library is a configuration payload updated on page load. New patterns take effect within seconds of publication.

Integration with the adaptive compute stack

The pattern matching engine does not operate in isolation. It plugs into the same adaptive dispatch architecture that handles sorting, filtering, and aggregation.

If the user's device has a discrete GPU with sufficient memory for the chat corpus, the two-phase pipeline runs on the GPU in 7 to 12 ms per pattern. If the device has only an integrated GPU, the dispatch engine evaluates the hardware calibration ratio and may route Phase 2 to Web Workers if the integrated GPU's memory bandwidth cannot sustain the byte-matching throughput. If GPU access is lost (device loss), the entire pipeline falls back to the Web Worker tier transparently. Search latency increases from 12 ms to 108 ms, but detection continues without interruption.

The precision analysis layer is not relevant for text search (no numeric computation), but the branch divergence classifier is. It ensures that if someone attempts to register an NFA-based regex pattern with the engine, the classifier assigns a categorical penalty and routes to CPU. The two-phase histogram approach is the only GPU path for text search. The engine enforces this structurally, not by convention.

The broader principle

Bot detection is pattern matching at speed. The engineering challenge is not the patterns. It is the throughput. Standard tools (regex, batch processing, server round-trips) cannot deliver the latency that real-time detection requires on datasets of this size.

We solved it by changing the algorithm to fit the hardware. GPUs cannot run regex efficiently. They can build character frequency histograms in shared memory with perfect parallelism. Restructure the search so the GPU does the part it is good at (parallel histogram construction, bitmask compaction) and minimizes the part it is bad at (sequential byte comparison), and you get a 111x speedup over the standard approach.

This is how we approach enterprise AI automation infrastructure across every domain. Understand the hardware constraint. Redesign the algorithm. Measure the result. The 18% revenue recovery was not a product of better patterns. The patterns were the same. It was a product of running them 111x faster, which changed the detection window from "too late" to "real-time."

Want to discuss how this applies to your business?

Book a Discovery Call