import { RankDataManager } from '../../../manager/RankDataManager'; import { aiLog } from './AiLog'; /** 铲子策略类型(Rank.aiusechanzi 三档) */ export enum AiShovelPolicyType { /** 类型 1:随机可铲格 */ Type1 = 1, /** 类型 2:逐步扩张 */ Type2 = 2, /** 类型 3:贴路 */ Type3 = 3, } /** 普通字策略类型(Rank.aiusetext 两档) */ export enum AiTextPolicyType { /** 类型 1:等级优先 */ Type1 = 1, /** 类型 2:单字冲级 */ Type2 = 2, } /** 开战时按对手段位权重抽样,整局固定 */ export interface AiBattlePersonality { /** 本局铲子策略类型 */ readonly shovelPolicy: AiShovelPolicyType; /** 本局普通字策略类型 */ readonly textPolicy: AiTextPolicyType; } /** 从 Rank.aiusechanzi / aiusetext 加权抽样整局策略 */ export class AiBattlePersonalityFactory { /** * 按对手段位配表抽样 personality。 * @param opponentRankId 对手段位 id * @param seed 随机种子 */ roll(opponentRankId: number, seed: number): AiBattlePersonality | null { const rank = RankDataManager.getRankConfig(opponentRankId); if (!rank) { aiLog('[AiBattlePersonalityFactory] Rank missing', opponentRankId); return null; } const shovel = this._pickType(rank.aiusechanzi, 3, seed, 1); const text = this._pickType(rank.aiusetext, 2, seed ^ 0x85ebca6b, 1); if (shovel == null || text == null) return null; return { shovelPolicy: shovel as AiShovelPolicyType, 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; } }