SorryToPerson logo
返回
算法2026-05-01·12 分钟

算法知识库:游戏算法实现

JavaScript/TypeScript 实现游戏相关算法,如路径寻找、决策树、遗传算法等。

游戏算法实现

1. A* 路径寻找

ts
interface Node {
  x: number;
  y: number;
  g: number; // 从起点到当前节点的代价
  h: number; // 从当前节点到终点的启发式估计代价
  f: number; // f = g + h
  parent?: Node;
}

class AStarPathfinding {
  private grid: number[][];
  private rows: number;
  private cols: number;

  constructor(grid: number[][]) {
    this.grid = grid;
    this.rows = grid.length;
    this.cols = grid[0].length;
  }

  findPath(start: { x: number; y: number }, end: { x: number; y: number }): { x: number; y: number }[] {
    const openList: Node[] = [];
    const closedList: Set<string> = new Set();

    const startNode: Node = {
      x: start.x,
      y: start.y,
      g: 0,
      h: this.heuristic(start, end),
      f: 0,
    };
    startNode.f = startNode.g + startNode.h;

    openList.push(startNode);

    while (openList.length > 0) {
      // 找到 f 值最小的节点
      openList.sort((a, b) => a.f - b.f);
      const currentNode = openList.shift()!;

      const key = `${currentNode.x},${currentNode.y}`;
      if (closedList.has(key)) continue;
      closedList.add(key);

      // 到达终点
      if (currentNode.x === end.x && currentNode.y === end.y) {
        return this.reconstructPath(currentNode);
      }

      // 检查邻居节点
      const neighbors = this.getNeighbors(currentNode);
      for (const neighbor of neighbors) {
        const neighborKey = `${neighbor.x},${neighbor.y}`;
        if (closedList.has(neighborKey) || this.grid[neighbor.y][neighbor.x] === 1) continue;

        const gScore = currentNode.g + 1; // 假设每个移动代价为 1
        const existingNode = openList.find((node) => node.x === neighbor.x && node.y === neighbor.y);

        if (!existingNode || gScore < existingNode.g) {
          neighbor.g = gScore;
          neighbor.h = this.heuristic(neighbor, end);
          neighbor.f = neighbor.g + neighbor.h;
          neighbor.parent = currentNode;

          if (!existingNode) {
            openList.push(neighbor);
          }
        }
      }
    }

    return []; // 没有找到路径
  }

  private heuristic(a: { x: number; y: number }, b: { x: number; y: number }): number {
    // 曼哈顿距离
    return Math.abs(a.x - b.x) + Math.abs(a.y - b.y);
  }

  private getNeighbors(node: Node): Node[] {
    const neighbors: Node[] = [];
    const directions = [
      { x: 0, y: -1 }, // 上
      { x: 1, y: 0 }, // 右
      { x: 0, y: 1 }, // 下
      { x: -1, y: 0 }, // 左
    ];

    for (const dir of directions) {
      const newX = node.x + dir.x;
      const newY = node.y + dir.y;

      if (newX >= 0 && newX < this.cols && newY >= 0 && newY < this.rows) {
        neighbors.push({ x: newX, y: newY, g: 0, h: 0, f: 0 });
      }
    }

    return neighbors;
  }

  private reconstructPath(node: Node): { x: number; y: number }[] {
    const path: { x: number; y: number }[] = [];
    let current: Node | undefined = node;

    while (current) {
      path.unshift({ x: current.x, y: current.y });
      current = current.parent;
    }

    return path;
  }
}

2. 极大极小算法 (Minimax)

ts
class MinimaxGame {
  evaluate(board: number[][]): number {
    // 简化版评估函数
    // 返回正数表示 AI 优势,负数表示玩家优势
    return Math.random() * 10 - 5;
  }

  isGameOver(board: number[][]): boolean {
    // 检查游戏是否结束
    return Math.random() > 0.9; // 10% 概率结束
  }

  getPossibleMoves(board: number[][]): number[][] {
    // 返回所有可能的移动
    const moves: number[][] = [];
    for (let i = 0; i < 3; i += 1) {
      for (let j = 0; j < 3; j += 1) {
        if (board[i][j] === 0) {
          const newBoard = board.map((row) => [...row]);
          newBoard[i][j] = 1; // AI 移动
          moves.push(newBoard);
        }
      }
    }
    return moves;
  }

  minimax(board: number[][], depth: number, isMaximizing: boolean): number {
    if (depth === 0 || this.isGameOver(board)) {
      return this.evaluate(board);
    }

    if (isMaximizing) {
      let maxEval = -Infinity;
      const moves = this.getPossibleMoves(board);

      for (const move of moves) {
        const eval = this.minimax(move, depth - 1, false);
        maxEval = Math.max(maxEval, eval);
      }

      return maxEval;
    } else {
      let minEval = Infinity;
      const moves = this.getPossibleMoves(board);

      for (const move of moves) {
        const eval = this.minimax(move, depth - 1, true);
        minEval = Math.min(minEval, eval);
      }

      return minEval;
    }
  }

  findBestMove(board: number[][]): { row: number; col: number } | null {
    let bestVal = -Infinity;
    let bestMove: { row: number; col: number } | null = null;

    for (let i = 0; i < 3; i += 1) {
      for (let j = 0; j < 3; j += 1) {
        if (board[i][j] === 0) {
          const newBoard = board.map((row) => [...row]);
          newBoard[i][j] = 1; // AI 移动

          const moveVal = this.minimax(newBoard, 3, false);

          if (moveVal > bestVal) {
            bestVal = moveVal;
            bestMove = { row: i, col: j };
          }
        }
      }
    }

    return bestMove;
  }
}

3. 遗传算法

ts
interface Individual {
  genes: number[];
  fitness: number;
}

class GeneticAlgorithm {
  private populationSize: number;
  private geneLength: number;
  private mutationRate: number;
  private crossoverRate: number;
  private population: Individual[] = [];

  constructor(populationSize: number = 100, geneLength: number = 10, mutationRate: number = 0.01, crossoverRate: number = 0.7) {
    this.populationSize = populationSize;
    this.geneLength = geneLength;
    this.mutationRate = mutationRate;
    this.crossoverRate = crossoverRate;
    this.initializePopulation();
  }

  private initializePopulation(): void {
    for (let i = 0; i < this.populationSize; i += 1) {
      const genes = Array.from({ length: this.geneLength }, () => Math.random());
      this.population.push({
        genes,
        fitness: this.calculateFitness(genes),
      });
    }
  }

  private calculateFitness(genes: number[]): number {
    // 示例适应度函数:寻找所有基因值为 1 的个体
    return genes.reduce((sum, gene) => sum + gene, 0);
  }

  evolve(generations: number = 100): Individual {
    for (let gen = 0; gen < generations; gen += 1) {
      this.population.sort((a, b) => b.fitness - a.fitness);

      const newPopulation: Individual[] = [];

      // 精英选择:保留最好的个体
      newPopulation.push(...this.population.slice(0, Math.floor(this.populationSize * 0.1)));

      // 生成新个体
      while (newPopulation.length < this.populationSize) {
        const parent1 = this.selectParent();
        const parent2 = this.selectParent();

        let child: Individual;
        if (Math.random() < this.crossoverRate) {
          child = this.crossover(parent1, parent2);
        } else {
          child = { genes: [...parent1.genes], fitness: 0 };
        }

        this.mutate(child);
        child.fitness = this.calculateFitness(child.genes);
        newPopulation.push(child);
      }

      this.population = newPopulation;
    }

    return this.population[0];
  }

  private selectParent(): Individual {
    // 轮盘赌选择
    const totalFitness = this.population.reduce((sum, ind) => sum + ind.fitness, 0);
    let pick = Math.random() * totalFitness;

    for (const individual of this.population) {
      pick -= individual.fitness;
      if (pick <= 0) {
        return individual;
      }
    }

    return this.population[0];
  }

  private crossover(parent1: Individual, parent2: Individual): Individual {
    const crossoverPoint = Math.floor(Math.random() * this.geneLength);
    const childGenes = [...parent1.genes.slice(0, crossoverPoint), ...parent2.genes.slice(crossoverPoint)];

    return { genes: childGenes, fitness: 0 };
  }

  private mutate(individual: Individual): void {
    for (let i = 0; i < this.geneLength; i += 1) {
      if (Math.random() < this.mutationRate) {
        individual.genes[i] = Math.random();
      }
    }
  }

  getBestIndividual(): Individual {
    return this.population.reduce((best, current) => (current.fitness > best.fitness ? current : best));
  }
}

4. 蒙特卡洛树搜索 (MCTS)

ts
interface MCTSNode {
  state: any;
  parent?: MCTSNode;
  children: MCTSNode[];
  visits: number;
  wins: number;
  untriedMoves: any[];
}

class MonteCarloTreeSearch {
  private root: MCTSNode;

  constructor(initialState: any) {
    this.root = {
      state: initialState,
      children: [],
      visits: 0,
      wins: 0,
      untriedMoves: this.getPossibleMoves(initialState),
    };
  }

  search(iterations: number = 1000): any {
    for (let i = 0; i < iterations; i += 1) {
      const node = this.selectNode();
      const winner = this.simulateGame(node);
      this.backpropagate(node, winner);
    }

    return this.getBestMove();
  }

  private selectNode(): MCTSNode {
    let node = this.root;

    while (node.untriedMoves.length === 0 && node.children.length > 0) {
      node = this.uctSelect(node);
    }

    if (node.untriedMoves.length > 0) {
      const move = node.untriedMoves.pop()!;
      const newState = this.makeMove(node.state, move);
      const childNode: MCTSNode = {
        state: newState,
        parent: node,
        children: [],
        visits: 0,
        wins: 0,
        untriedMoves: this.getPossibleMoves(newState),
      };
      node.children.push(childNode);
      return childNode;
    }

    return node;
  }

  private uctSelect(node: MCTSNode): MCTSNode {
    let bestChild: MCTSNode | null = null;
    let bestValue = -Infinity;

    for (const child of node.children) {
      const uctValue = child.wins / child.visits + Math.sqrt((2 * Math.log(node.visits)) / child.visits);
      if (uctValue > bestValue) {
        bestValue = uctValue;
        bestChild = child;
      }
    }

    return bestChild!;
  }

  private simulateGame(node: MCTSNode): number {
    let state = node.state;
    let currentPlayer = 1;

    while (!this.isGameOver(state)) {
      const moves = this.getPossibleMoves(state);
      const randomMove = moves[Math.floor(Math.random() * moves.length)];
      state = this.makeMove(state, randomMove);
      currentPlayer = 3 - currentPlayer; // 切换玩家
    }

    return this.getWinner(state);
  }

  private backpropagate(node: MCTSNode, winner: number): void {
    let current: MCTSNode | undefined = node;

    while (current) {
      current.visits += 1;
      if (winner === 1) {
        // 假设 AI 是玩家 1
        current.wins += 1;
      }
      current = current.parent;
    }
  }

  private getBestMove(): any {
    let bestChild: MCTSNode | null = null;
    let bestVisits = -1;

    for (const child of this.root.children) {
      if (child.visits > bestVisits) {
        bestVisits = child.visits;
        bestChild = child;
      }
    }

    return bestChild ? this.getMoveFromStates(this.root.state, bestChild.state) : null;
  }

  // 以下方法需要根据具体游戏实现
  private getPossibleMoves(state: any): any[] {
    // 返回可能的移动
    return [];
  }

  private makeMove(state: any, move: any): any {
    // 执行移动,返回新状态
    return state;
  }

  private isGameOver(state: any): boolean {
    // 检查游戏是否结束
    return false;
  }

  private getWinner(state: any): number {
    // 返回获胜者 (1 或 2)
    return 0;
  }

  private getMoveFromStates(fromState: any, toState: any): any {
    // 从状态变化推断移动
    return null;
  }
}

5. 实现要点

  • A* 使用启发式函数优化路径寻找。
  • Minimax 探索游戏树找到最优移动。
  • 遗传算法模拟进化过程。
  • MCTS 通过随机模拟学习策略。
算法游戏JavaScript