import { RankDataManager } from '../../../manager/RankDataManager'; import { aiLog } from './AiLog'; /** 普通字策略类型(Rank.aiusetext 两档) */ export enum AiTextPolicyType { /** 类型 1:等级优先 */ Type1 = 1, /** 类型 2:单字冲级 */ Type2 = 2, } /** 开战时按对手段位权重抽样,整局固定 */ export interface AiBattlePersonality { /** 本局普通字策略类型 */ readonly textPolicy: AiTextPolicyType; } /** 从 Rank.aiusetext 加权抽样整局策略 */ export class AiBattlePersonalityFactory { /** * 按对手段位配表抽样 personality。 * @param opponentRankId 对手段位 id * @param seed 随机种子 */ roll(opponentRankId: number, seed: number): AiBattlePersonality | null { const rank = RankDataManager.getRankConfig(opponentRankId); if (!rank) { console.warn('[AiBattlePersonalityFactory] Rank missing,使用默认策略', opponentRankId); return { textPolicy: AiTextPolicyType.Type1, }; } const text = this._pickType(rank.aiusetext, 2, seed ^ 0x85ebca6b, 1); if (text == null) { console.warn('[AiBattlePersonalityFactory] 权重无效,使用默认策略', opponentRankId); return { textPolicy: AiTextPolicyType.Type1, }; } return { textPolicy: text as AiTextPolicyType, }; } /** * 加权抽类型(返回 1-based 类型号)。 * @param weights 权重数组 * @param expectLen 期望长度 * @param seed 种子 * @param typeBase 起始类型号 */ private _pickType(weights: number[] | undefined, expectLen: number, seed: number, typeBase: number): number | null { if (!weights || weights.length !== expectLen) { aiLog('[AiBattlePersonalityFactory] weight length invalid', expectLen); return null; } const w: number[] = []; let total = 0; for (let i = 0; i < expectLen; i++) { const v = Math.max(0, Math.floor(Number(weights[i]) || 0)); w.push(v); total += v; } if (total <= 0) { aiLog('[AiBattlePersonalityFactory] weight total <= 0'); return null; } let x = (Math.floor(Number(seed) || 0) >>> 0) || 1; x ^= x << 13; x ^= x >>> 17; x ^= x << 5; let roll = ((x >>> 0) / 4294967296) * total; for (let i = 0; i < expectLen; i++) { roll -= w[i]; if (roll < 0) return typeBase + i; } return typeBase + expectLen - 1; } }