Anti-Cheat

【アンチチート/AIフィルタ】意味のない「線の乱打(スクリブル)」をAIでどう弾くか?

お絵描きゲームにおいてユーザーが「画面を適当に塗りつぶす」「適当な直線を連打する」といったスコア稼ぎ行為(Scribble Attack)を、AIとメタデータで判定するアプローチを紹介します。

1. スクリーン全体の塗りつぶし・スクリブル問題とは?

お絵描き診断ゲームや描画バトルサービスを一般公開すると、一部のユーザーは「なるべく手間をかけずに高得点(最高ステータス)を出そう」と様々な試みを行います。その中でも最も頻繁に発生するのが**「スクリブル攻撃(Scribble Attack)」**です。

スクリブル攻撃とは、画面全体をジグザグ線や渦巻きで塗りつぶしたり、1秒間に50本以上の直線を無造作に引いてキャンバスの描画面積やピクセル密度を偽装する行為を指します。

Vision機能を備えたマルチモーダルLLM(Gemini 2.5 Flash等)は、単純な「黒ピクセルの占有率」や「線の密度」が高い画像を評価する際、それが「丁寧に描き込まれた密度の高いイラスト」なのか「ただの無意味な線の群れ」なのかを明確に区別するようプロンプトで指示していない場合、高スコアを返してしまう脆弱性がありました。

2. 従来のルールベース検知の限界と課題

従来のお絵描きアプリでは、画像解析処理(OpenCV等)を用いてエッジ検出や黒ピクセル比率を計算し、スクリブルを弾く手法が一般的でした。

# 従来の単純なピクセル密度チェック(限界がある例)
def is_scribble_pixel_based(image_bytes: bytes) -> bool:
    # 単純な黒ピクセル率の計算だけでは「精密な描き込み」と「スクリブル塗りつぶし」を区別できない
    black_pixel_count = count_black_pixels(image_bytes)
    total_pixels = 400 * 400
    density = black_pixel_count / total_pixels
    return density > 0.8  # 0.8以上なら塗りつぶしと判定(誤検知が多い)

しかし、このルールベース方式には「緻密に描き込まれた素晴らしい竜のイラスト」まで誤って「塗りつぶし扱い(スクリブル)」として弾いてしまうという大きな副作用(偽陽性)がありました。

3. 解決策:フロントエンド物理メタデータ × Vision LLM の複合検知

この課題に対し、Doodle Fighterでは**フロントエンドで収集する物理操作メタデータ**と**Vision LLMの文脈理解**を融合させた二重フィルタリングを構築しました。

フロントエンドでの物理描画データ計測 (frontend/static/js/app.js)

キャンバス操作中にユーザーが「何回線を引いたか(`stroke_count`)」と「実際に描画を行っていた時間(`active_time_ms`)」をミリ秒単位でリアルタイム記録し、APIヘッダーおよびリクエストボディに含めて送信します。

// frontend/static/js/app.js での計測実装例
let strokeCount = 0;
let drawStartTime = null;
let activeTimeMs = 0;

canvas.addEventListener('pointerdown', (e) => {
    strokeCount++;
    if (!drawStartTime) drawStartTime = Date.now();
});

canvas.addEventListener('pointerup', () => {
    if (drawStartTime) {
        activeTimeMs += (Date.now() - drawStartTime);
        drawStartTime = null;
    }
});

バックエンドプロンプトでのスクリブル判定指示 (backend/prompts.yaml)

送信されたメタデータと画像をあわせてGemini APIに渡し、プロンプト内で「造形構造の有無」を判定させます。

# backend/prompts.yaml
profiles:
  doodle_fighter_profiler:
    system_prompt: |
      あなたはデジタルアート審査官です。
      
      # 【スクリブル(適当な線の連打・塗りつぶし)判定ルール】
      - ユーザーが意図のあるキャラクターやモチーフを描かず、画面を線で塗りつぶしている、または無意味な直線・波線を連打している場合(スクリブル行為):
        1. 「造形構造なし(Formless Scribble)」と判断し、combat_power を 50 〜 150 の最低ランクに固定してください。
        2. メタデータ(stroke_count: {stroke_count}, active_time_ms: {active_time_ms})を参照し、描画時間が極端に短いにもかかわらず線が乱打されている場合は「連打乱打スパム」として扱ってください。
        3. 講評コメント(comment)には、スクリブルであることを見抜いたユーモア溢れる辛口フィードバックを記載してください。

4. 検証テストと検出精度比較

画面全体を3秒間で120回連打して塗りつぶしたテストデータを送信した結果、LLMから以下の構造化JSONが返却されました:

{
  "special_move": "混沌の黒煙ラッシュ",
  "combat_power": 110,
  "big_five": { "openness": 10, "conscientiousness": 5, "extraversion": 80, "agreeableness": 30, "neuroticism": 90 },
  "comment": "画面全体に勢いよく線が引かれていますが、具体的なキャラクターの形や輪郭が維持されていません。ただの落書き連打では真の力は引き出せません!"
}

検知手法の比較まとめ

手法 検知精度 誤検知リスク 特徴
ピクセル密度のみ (OpenCV) 高(描き込み作を誤判定) 線量しか見ないため限界がある
LLM単体 (Vision) 中(芸術的描画と誤認) 画像単体では文脈に引っ張られる
メタデータ × Vision LLM(本手法) 極めて高 極めて低 操作時間・筆数と画像造形を複合評価

5. まとめと今後の展望

ユーザーの自由な手描き入力を受け入れるWebサービスにおいて、悪意あるハック行為やスクリブルを弾くシステムはサービス品質とUXを守る生命線です。 フロントエンドでの生データ収集とLLMの高度なコンテキスト理解を組み合わせることで、低コストで強固なアンチチート機構を実現できます。