Skip to content

AI設計

概要

ぷよぷよゲームにおけるAI自動プレイ機能の設計について説明します。ヘキサゴナルアーキテクチャに従い、AI機能を独立したポートとして実装します。

AI機能要件

US-005: AI自動プレイ機能

受け入れ基準:

  • AIが自動でぷよを操作する
  • AIの思考過程が可視化される
  • 手動プレイとAIプレイの切り替えができる
  • AIのプレイ速度を調整できる

アーキテクチャ設計

AI層の配置(現在の実装)

uml diagram

AI実装戦略

フェーズ1: 基本AI(イテレーション3 - 完了)✅

TensorFlow.js統合とWeb Workers実装により、非同期AI処理基盤を構築しました。

uml diagram

評価関数(実装済み)

関数型評価サービス(domain/services/ai/EvaluationService.ts)

// 評価設定の型定義
export interface EvaluationSettings {
  heightWeight: number    // 高さの重要度(デフォルト: 10)
  centerWeight: number    // 中央位置の重要度(デフォルト: 5)
  mlWeight: number        // MLスコアの重要度(デフォルト: 20)
}

// 評価結果の型定義
export interface MoveEvaluation {
  heightScore: number          // 高さベースのスコア
  centerScore: number          // 中央位置ベースのスコア
  modeScore: number            // MLモデルによる追加スコア
  totalScore: number           // 総合スコア
  averageY: number             // 平均Y座標
  averageX: number             // 平均X座標
  distanceFromCenter: number   // 中央からの距離
  reason: string               // 評価理由の説明
}

// 純粋関数による評価実装
export const evaluateMove = (
  move: PossibleMove,
  gameState: AIGameState,
  settings?: EvaluationSettings
): MoveEvaluation

export const evaluateMoveWithML = (
  move: PossibleMove,
  gameState: AIGameState,
  mlScore: number,
  settings?: EvaluationSettings
): MoveEvaluation

評価アルゴリズム

uml diagram

AI思考プロセス

uml diagram

AIコンポーネント詳細(実装済み)

MLAIService(TensorFlow.js統合)

責務:

  • 4層ニューラルネットワークによるAI思考
  • 戦略設定に基づく評価
  • 非同期処理による高速判断

実装済みインターフェース:

export class MLAIService implements AIPort {
  // 次の手を決定(非同期)
  async decideMove(gameState: AIGameState): Promise<AIMove>

  // AI設定更新
  updateSettings(settings: AISettings): void

  // 戦略更新
  async updateStrategy(): Promise<void>

  // モデル準備状態
  isModelReady(): boolean

  // リソースクリーンアップ
  dispose(): void
}

WorkerAIService(Web Workers実装)

責務:

  • メインスレッド非ブロッキング処理
  • バックグラウンドでのAI計算
  • フォールバック機構

実装済みインターフェース:

export class WorkerAIService implements AIPort {
  // Web Worker経由でAI判断
  async decideMove(gameState: AIGameState): Promise<AIMove>

  // フォールバック処理
  private async fallbackToMainThread(gameState: AIGameState): Promise<AIMove>

  // Worker初期化
  private initializeWorker(): void

  // Worker終了処理
  terminate(): void
}

EvaluationService(関数型実装)

責務:

  • 純粋関数による盤面評価
  • 複数手の評価とソート
  • ML強化評価の統合

実装済み関数:

// 基本評価関数
export const evaluateMove = (
  move: PossibleMove,
  gameState: AIGameState,
  settings?: EvaluationSettings
): MoveEvaluation

// ML強化評価関数
export const evaluateMoveWithML = (
  move: PossibleMove,
  gameState: AIGameState,
  mlScore: number,
  settings?: EvaluationSettings
): MoveEvaluation

// 複数手の評価とソート
export const evaluateAndSortMoves = (
  moves: PossibleMove[],
  gameState: AIGameState,
  settings?: EvaluationSettings
): Array<PossibleMove & { evaluation: MoveEvaluation }>

// 最良手の取得
export const getBestMove = (
  moves: PossibleMove[],
  gameState: AIGameState,
  settings?: EvaluationSettings
): (PossibleMove & { evaluation: MoveEvaluation }) | null

MoveGenerator(実装済み)

責務:

  • 全ての可能な配置位置と回転状態の生成
  • 物理的制約の検証
  • 有効な手のフィルタリング

実装済みクラス:

export class MoveGenerator implements MoveGeneratorPort {
  // 可能な手を生成(6列×4回転 = 最大24通り)
  generateMoves(gameState: AIGameState): PossibleMove[] {
    const moves: PossibleMove[] = []
    const puyoPair = gameState.currentPuyoPair

    for (let x = 0; x < gameState.field.width; x++) {
      for (let rotation = 0; rotation < 4; rotation++) {
        const move = this.createMove(x, rotation, puyoPair, gameState.field)
        if (move.isValid) {
          moves.push(move)
        }
      }
    }

    return moves
  }

  // 配置可能性の検証
  private isValidPlacement(x: number, y: number, field: AIFieldState): boolean
}

ChainSimulator

責務:

  • 連鎖シミュレーション
  • 結果予測
  • スコア計算

インターフェース:

interface ChainSimulator {
  // 連鎖シミュレーション
  simulate(field: Field, move: Move): SimulationResult;

  // 連鎖数予測
  predictChainCount(field: Field): number;
}

AI可視化設計(実装済み)

AIControlPanel(制御パネル)

export const AIControlPanel: React.FC = () => {
  // AI有効/無効切り替え
  // 思考速度調整(100ms - 2000ms)
  // 戦略設定への遷移
  // AIモード選択(通常AI / ML AI / Worker AI)
}

AIInsights(思考プロセス可視化)

export const AIInsights: React.FC = () => {
  // 現在の評価スコア表示
  // 評価内訳(高さスコア、中央スコア、MLスコア)
  // 選択理由の表示
  // リアルタイム更新
}

思考プロセスの可視化

uml diagram

AIコントロールパネル

uml diagram

パフォーマンス考慮事項(実装済み)

Web Workers活用(実装済み)

uml diagram

実装済み最適化戦略

  1. 関数型評価システム:
  2. 純粋関数による予測可能な評価
  3. 副作用なしで並行処理安全
  4. テスト容易性の向上

  5. TensorFlow.js統合:

  6. 4層ニューラルネットワーク
  7. GPU加速対応
  8. リアルタイム推論

  9. 非同期処理:

  10. Web Workersによる並列処理実装
  11. UIブロッキング完全回避
  12. フォールバック機構

テスト戦略

AI単体テスト

describe('AIEngine', () => {
  describe('決定的な状況での判断', () => {
    test('4つ揃えられる時は即座に消去する', () => {
      // Arrange: 3つ揃っている状態
      // Act: AI判断
      // Assert: 4つ目を配置する手を選択
    });
  });

  describe('評価関数の妥当性', () => {
    test('高い塔は低評価される', () => {
      // Arrange: 高い塔がある盤面
      // Act: 評価
      // Assert: 低いスコア
    });
  });
});

パフォーマンステスト

describe('AIパフォーマンス', () => {
  test('思考時間が設定値以内', () => {
    // 100ms以内に判断完了
  });

  test('メモリリークがない', () => {
    // 1000回の思考でメモリ増加なし
  });
});

実装実績

イテレーション3(完了)✅

  • TensorFlow.js統合: 4層ニューラルネットワーク実装
  • Web Workers実装: 非同期AI処理基盤構築
  • AI可視化UI: AIControlPanel、AIInsights実装
  • 関数型評価サービス: EvaluationService純粋関数化(2025-08-19)
  • テスト: 17テストケース追加、100%カバレッジ達成

品質指標

  • テストカバレッジ: 80.57%(目標80%達成)
  • E2Eテスト: 65件(100%成功)
  • パフォーマンス: 思考時間100-2000ms(調整可能)
  • メモリ効率: TensorFlowリソース適切なdispose実装

将来の拡張性

フェーズ2: 高度な戦略システム(イテレーション4)

uml diagram

フェーズ3: 強化学習(イテレーション5)

uml diagram

フェーズ4: 高度な評価関数システム(次期実装予定)

mayah AI実装を参考にした評価関数再設計

mayah AI(@mayah_puyo)の実装から得られた知見を基に、より高度で人間らしい評価関数システムを設計します。

新評価システムの構成

mayah AIの4要素評価を参考に、以下の評価カテゴリを導入:

uml diagram

詳細設計仕様

1. 操作評価(OperationEvaluation)
export interface OperationEvaluation {
  frameCount: number      // 操作フレーム数(1フレーム = 0.1点減点)
  tearCount: number       // ちぎり回数(1回 = 100点減点)
  efficiency: number      // 配置効率性
}

export const evaluateOperation = (
  move: PossibleMove,
  gameState: AIGameState
): OperationEvaluation => {
  // フレーム数計算(6列目は若干遅い)
  const frameCount = calculateFrameCount(move.position, move.rotation)

  // ちぎり判定
  const tearCount = calculateTearCount(move, gameState.currentPuyoPair)

  // 効率性評価
  const efficiency = calculatePlacementEfficiency(move, gameState)

  return { frameCount, tearCount, efficiency }
}
2. 形評価(ShapeEvaluation)
export interface ShapeEvaluation {
  uShapeScore: number     // U字型スコア
  connectionScore: number // 連結スコア(2連結=10点、3連結=30点)
  valleyPenalty: number   // 谷ペナルティ(深さ4以上で2000点減点)
  mountainPenalty: number // 山ペナルティ(高さ4以上で2000点減点)
  heightBalance: number   // 高さバランス(二乗誤差)
}

export const evaluateShape = (
  field: AIFieldState,
  gamePhase: GamePhase
): ShapeEvaluation => {
  // U字型評価(理想高さからの二乗誤差)
  const idealHeights = calculateIdealUShape(field)
  const uShapeScore = calculateUShapeScore(field.heights, idealHeights, gamePhase)

  // 連結数評価
  const connectionScore = evaluateConnections(field)

  // 山谷評価
  const { valleyPenalty, mountainPenalty } = evaluateMountainsAndValleys(field)

  // 高さバランス
  const heightBalance = calculateHeightBalance(field.heights)

  return { 
    uShapeScore, 
    connectionScore, 
    valleyPenalty, 
    mountainPenalty, 
    heightBalance 
  }
}
3. 連鎖評価(ChainEvaluation)
export interface ChainEvaluation {
  mainChain: ChainInfo     // 本線連鎖(連鎖数 * 1000点)
  subChain: ChainInfo      // 副砲連鎖(2連鎖=1000点、3連鎖=500点)
  patternMatch: number     // パターンマッチスコア
  requiredPuyos: number    // 必要ぷよ数(二次関数的減点)
}

export interface ChainInfo {
  chainCount: number       // 連鎖数
  score: number           // 評価スコア
  shapeQuality: number    // 連鎖形状品質
  frameToFire: number     // 発火までのフレーム数
}

export const evaluateChain = (
  field: AIFieldState,
  patterns: ChainPattern[]
): ChainEvaluation => {
  // 連鎖パターンマッチング
  const possibleChains = enumerateChains(field, patterns)

  // 本線・副砲選択
  const mainChain = selectBestMainChain(possibleChains)
  const subChain = selectBestSubChain(possibleChains)

  // パターンマッチ評価
  const patternMatch = evaluatePatternMatching(field, patterns)

  // 必要ぷよ数計算(50%確率ベース)
  const requiredPuyos = calculateRequiredPuyos(mainChain, 0.5)

  return { mainChain, subChain, patternMatch, requiredPuyos }
}
4. 戦略評価(StrategyEvaluation)
export interface StrategyEvaluation {
  firingDecision: number   // 発火判断スコア
  riskAssessment: number   // リスク評価
  stareFunction: number    // 凝視機能
  defensiveNeed: number    // 防御必要性
}

export const evaluateStrategy = (
  gameState: AIGameState,
  rensaHandTree: RensaHandTree
): StrategyEvaluation => {
  // 発火判断(RensaHandTree使用)
  const firingDecision = evaluateFiringDecision(rensaHandTree)

  // リスク評価
  const riskAssessment = assessRisk(gameState)

  // 凝視機能(相手の攻撃への対応)
  const stareFunction = evaluateOpponentThreats(gameState)

  // 防御必要性
  const defensiveNeed = evaluateDefensiveNeed(gameState)

  return { firingDecision, riskAssessment, stareFunction, defensiveNeed }
}

RensaHandTree実装設計

export interface RensaHandNode {
  chainCount: number       // 連鎖数
  startFrame: number       // 開始フレーム
  endFrame: number         // 終了フレーム
  score: number           // 連鎖スコア
  children: RensaHandNode[] // 後続連鎖
}

export class RensaHandTree {
  private myTree: RensaHandNode[]
  private opponentTree: RensaHandNode[]

  // 連鎖木構築
  buildTree(field: AIFieldState, depth: number = 3): RensaHandNode[] {
    const chains = enumerateAllChains(field)
    const sortedChains = chains.sort((a, b) => a.endFrame - b.endFrame)

    const tree: RensaHandNode[] = []
    let maxScore = 0

    for (const chain of sortedChains) {
      if (chain.score > maxScore) {
        tree.push(chain)
        maxScore = chain.score

        // 再帰的に次段構築
        if (depth > 0) {
          const afterField = simulateChain(field, chain)
          chain.children = this.buildTree(afterField, depth - 1)
        }
      }
    }

    return tree
  }

  // 打ち合い評価
  evaluateBattle(): BattleResult {
    return evaluateChainBattle(this.myTree, this.opponentTree)
  }
}

ゲームフェーズ別調整

export enum GamePhase {
  EARLY = 'early',     // 序盤(ぷよ数 < 30)
  MIDDLE = 'middle',   // 中盤(30 <= ぷよ数 < 60)  
  LATE = 'late'        // 終盤(ぷよ数 >= 60)
}

export const getPhaseAdjustments = (phase: GamePhase): PhaseAdjustments => {
  switch (phase) {
    case GamePhase.EARLY:
      return {
        gapTolerance: 0.5,      // スキ許容度高
        chainPriority: 0.7,     // 連鎖優先度中
        shapePriority: 1.0      // 形重視
      }
    case GamePhase.MIDDLE:
      return {
        gapTolerance: 0.3,      // スキ許容度中
        chainPriority: 1.0,     // 連鎖優先度高
        shapePriority: 0.8      // 形重視維持
      }
    case GamePhase.LATE:
      return {
        gapTolerance: 0.1,      // スキ許容度低
        chainPriority: 1.2,     // 連鎖最優先
        shapePriority: 0.5      // 形より実用性
      }
  }
}

統合評価関数

export interface MayahStyleEvaluation extends MoveEvaluation {
  operationScore: number
  shapeScore: number  
  chainScore: number
  strategyScore: number
  phaseAdjustment: number
}

export const evaluateMoveWithMayahStyle = (
  move: PossibleMove,
  gameState: AIGameState,
  settings: MayahEvaluationSettings
): MayahStyleEvaluation => {
  // 各カテゴリ評価
  const operation = evaluateOperation(move, gameState)
  const shape = evaluateShape(gameState.field, getGamePhase(gameState))
  const chain = evaluateChain(gameState.field, settings.patterns)
  const strategy = evaluateStrategy(gameState, settings.rensaHandTree)

  // フェーズ別調整
  const phase = getGamePhase(gameState)
  const adjustments = getPhaseAdjustments(phase)

  // 重み付け統合
  const operationScore = calculateOperationScore(operation) * adjustments.operationWeight
  const shapeScore = calculateShapeScore(shape) * adjustments.shapeWeight  
  const chainScore = calculateChainScore(chain) * adjustments.chainWeight
  const strategyScore = calculateStrategyScore(strategy) * adjustments.strategyWeight

  const totalScore = operationScore + shapeScore + chainScore + strategyScore

  return {
    ...move,
    operationScore,
    shapeScore,
    chainScore, 
    strategyScore,
    phaseAdjustment: adjustments.phaseAdjustment,
    totalScore,
    reason: generateEvaluationReason({
      operation, shape, chain, strategy, phase
    })
  }
}

実装計画

Phase 4a: 基盤実装(イテレーション4前半)

  • mayah型評価システムの型定義
  • 操作評価・形評価の基本実装
  • 既存評価関数との統合テスト

Phase 4b: 高度機能実装(イテレーション4後半)

  • 連鎖パターンマッチング実装
  • RensaHandTree実装
  • 戦略評価システム統合

Phase 4c: 最適化・調整(イテレーション5)

  • パフォーマンス最適化
  • パラメータチューニング
  • 人間らしさの検証・調整

期待される効果

  1. 人間らしい思考: GTRなど定跡パターンの認識・活用
  2. 戦略的判断: 状況に応じた攻守のバランス調整
  3. 高い競技性: mayah AIレベルの強さを目指す
  4. 学習基盤: パターン学習・強化学習への発展

まとめ

このAI設計により、以下を実現しました:

実装済み成果

  1. TensorFlow.js統合: 4層ニューラルネットワークによる高度なAI判断 ✅
  2. Web Workers実装: 非同期処理によるUIブロッキング回避 ✅
  3. 関数型評価システム: 純粋関数による予測可能で安全な評価 ✅
  4. AI可視化: 思考プロセスのリアルタイム表示 ✅
  5. テスタビリティ: 17テストケースで包括的なカバレッジ ✅

次期実装予定

  1. mayah型評価システム: 4要素評価による人間らしい思考 🔄
  2. パターンマッチング: 定跡認識による戦略的配置 🔄
  3. RensaHandTree: 高度な打ち合い評価システム 🔄

技術的特徴

  • 並行処理安全: 状態なしの純粋関数による安全な並行実行
  • 拡張性: 新しい評価関数の追加が容易
  • 保守性: 関数型パラダイムによる理解しやすいコード
  • パフォーマンス: GPU加速対応、最適化された推論処理
  • フォールバック: Worker未対応環境への対応
  • 競技レベル: mayah AI参考による高度な戦略思考

評価関数の進化

  • 現行版: 高さ・中央・ML評価による基本AI ✅
  • 次期版: mayah型4要素評価による人間らしいAI 🔄
  • 操作評価(フレーム・ちぎり・効率性)
  • 形評価(U字型・連結・山谷・バランス)
  • 連鎖評価(本線・副砲・パターン・必要数)
  • 戦略評価(発火・凝視・リスク・防御)