/* * @Author: 白哉 * @Description: AI Action 生成器 * @Date: 2025年04月30日 星期三 11:04:38 * @Modify: */ using System.Collections.Generic; using Logic.Action; using RuntimeData; using TH1_Logic.Action; using TH1_Logic.Core; using UnityEngine; namespace Logic.AI { public class AIActionGenerator { private MapData _mapData; private PlayerData _player; private List _waitUnits; private List _waitGrids; private List _waitCity; private uint _actionRecord; private uint _recordCount; public AIActionType ActionType => (AIActionType)(_actionRecord % (int)AIActionType.Max); public void Init(MapData map, PlayerData player) { _mapData = map; _player = player; _waitUnits ??= new List(); _waitGrids ??= new List(); _waitCity ??= new List(); _waitUnits.Clear(); _waitGrids.Clear(); _waitCity.Clear(); var gridSet = map.GetPlayerTerritoryGridIdSet(player.Id); var unitList = new List(); map.GetUnitDataListByPlayerId(player.Id, unitList); var cityList = new List(); map.GetCityDataListByPlayerId(player.Id, cityList); foreach (var unit in unitList) _waitUnits.Add(unit); foreach (var city in cityList) _waitCity.Add(city); foreach (var grid in _mapData.GridMap.GridList) if(gridSet.Contains(grid.Id)) _waitGrids.Add(grid); _actionRecord = 0; _recordCount = 0; } public void GeneratorOneStepActions(MapData map, PlayerData player, List aiActions = null) { if (aiActions == null) aiActions = new List(); for (int i = 0; i < (int)AIActionType.Max; i++) { if (_recordCount > 100) break; _recordCount++; _actionRecord++; var actionType = (AIActionType)(_actionRecord % (int)AIActionType.Max); if (actionType == AIActionType.Unit) GeneratorUnitActions(aiActions); else if (actionType == AIActionType.Grid) GeneratorGridActions(aiActions); else if (actionType == AIActionType.City) GeneratorCityActions(aiActions); else if (actionType == AIActionType.Tech) GeneratorTechActions(aiActions); if (aiActions.Count != 0) break; } } private void GeneratorUnitActions(List aiActions) { if (_waitUnits.Count == 0 || aiActions == null) return; for (int i = _waitUnits.Count - 1; i >= 0; i--) { GeneratorOneUnitActions(_waitUnits[i], aiActions); if (aiActions.Count != 0) break; _waitUnits.RemoveAt(i); } } private void GeneratorOneUnitActions(UnitData unit, List aiActions) { if (ActionLogicFactory.UnitHasMoveAndAttackAction(_mapData, unit, out var aiActionList)) { foreach (var aiAction in aiActionList) aiActions.Add(aiAction); } var param = new CommonActionParams(_mapData, playerData:_player, unitData:unit); param.RefreshParams(); if (!ActionLogicFactory.UnitHasAction(param, out var actions)) return; foreach (var action in actions) { if (param.PlayerData.PlayerWealth < action.GetCost()) continue; aiActions.Add(new AIActionBase(param, action)); } } private void GeneratorGridActions(List aiActions) { if (_waitGrids.Count == 0 || aiActions == null) return; for (int i = _waitGrids.Count - 1; i >= 0; i--) { GeneratorOneGridActions(_waitGrids[i], aiActions); if (aiActions.Count != 0) break; _waitGrids.RemoveAt(i); } } private void GeneratorOneGridActions(GridData grid, List aiActions) { var gridParam = new CommonActionParams(_mapData, playerData:_player, gridData:grid, mainObjectType:MainObjectType.Grid); var actionList = new List(); if (!ActionLogicFactory.GridHasAction(gridParam, actionList)) return; foreach (var action in actionList) { if (action.ActionId.GridMiscActionType == GridMiscActionType.Destroy) continue; if (action.ActionId.GridMiscActionType == GridMiscActionType.ClearForest) continue; if (gridParam.PlayerData.PlayerWealth < action.GetCost()) continue; aiActions.Add(new AIActionBase(gridParam, action)); } } private void GeneratorCityActions(List aiActions) { if (_waitCity.Count == 0 || aiActions == null) return; for (int i = _waitCity.Count - 1; i >= 0; i--) { GeneratorOneCityActions(_waitCity[i], aiActions); if (aiActions.Count != 0) break; _waitCity.RemoveAt(i); } } private void GeneratorOneCityActions(CityData city, List aiActions) { var param = new CommonActionParams(_mapData, playerData:_player, cityData:city); param.RefreshParams(); if (!ActionLogicFactory.CityHasAction(param, out var actions)) return; foreach (var action in actions) { if (param.PlayerData.PlayerWealth < action.GetCost()) continue; aiActions.Add(new AIActionBase(param, action)); } } // 穷举科技树行动 private void GeneratorTechActions(List aiActions) { if (aiActions == null) aiActions = new List(); // 玩家行为 var param = new CommonActionParams(_mapData, playerData:_player, mainObjectType:MainObjectType.Player); param.RefreshParams(); if (!ActionLogicFactory.PlayerHasAction(param, out var actions)) return; foreach (var action in actions) { if (action.ActionId.ActionType != CommonActionType.LearnTech) continue; if (param.PlayerData.PlayerWealth < action.GetCost()) continue; aiActions.Add(new AIActionBase(param, action)); } } // 穷举式生成所有可能的行动 public void GeneratorActions(MapData map, PlayerData player, List aiActions = null) { if (aiActions == null) aiActions = new List(); // 小兵行为 var unitList = new List(); map.GetUnitDataListByPlayerId(player.Id, unitList); foreach (var unit in unitList) { if (ActionLogicFactory.UnitHasMoveAndAttackAction(map, unit, out var aiActionList)) { foreach (var aiAction in aiActionList) aiActions.Add(aiAction); } var param = new CommonActionParams(map, playerData: player, unitData: unit); param.RefreshParams(); if (!ActionLogicFactory.UnitHasAction(param, out var actions)) continue; foreach (var action in actions) { if (param.PlayerData.PlayerWealth < action.GetCost()) continue; aiActions.Add(new AIActionBase(param, action)); } } // 地块行为 var gridSet = map.GetPlayerTerritoryGridIdSet(player.Id); var gridParam = new CommonActionParams(map, playerData: player, gridData: null, mainObjectType: MainObjectType.Grid); var actionList = new List(); foreach (var gird in map.GridMap.GridList) { if (!gridSet.Contains(gird.Id)) continue; gridParam.GridData = gird; gridParam.RefreshParams(); if (!ActionLogicFactory.GridHasAction(gridParam, actionList)) continue; var param = new CommonActionParams(map, playerData: player, gridData: gird, mainObjectType: MainObjectType.Grid); foreach (var action in actionList) { if (param.PlayerData.PlayerWealth < action.GetCost()) continue; aiActions.Add(new AIActionBase(param, action)); } } // 城市行为 var cityList = new List(); map.GetCityDataListByPlayerId(player.Id, cityList); foreach (var city in cityList) { var param = new CommonActionParams(map, playerData: player, cityData: city); param.RefreshParams(); if (!ActionLogicFactory.CityHasAction(param, out var actions)) continue; foreach (var action in actions) { if (param.PlayerData.PlayerWealth < action.GetCost()) continue; aiActions.Add(new AIActionBase(param, action)); } } GeneratorTechActions(map, player, aiActions); } // 穷举科技树行动 public static void GeneratorTechActions(MapData map, PlayerData player, List aiActions = null) { if (aiActions == null) aiActions = new List(); // 玩家行为 var param = new CommonActionParams(map, playerData:player, mainObjectType:MainObjectType.Player); param.RefreshParams(); if (!ActionLogicFactory.PlayerHasAction(param, out var actions)) return; foreach (var action in actions) { if (action.ActionId.ActionType != CommonActionType.LearnTech) continue; if (param.PlayerData.PlayerWealth < action.GetCost()) continue; aiActions.Add(new AIActionBase(param, action)); } } // 穷举地图预期收益行动 public static void GeneratorFutureActions(MapData map, PlayerData player, List aiActions = null) { if (aiActions == null) aiActions = new List(); GeneratorTechActions(map, player, aiActions); for (int i = aiActions.Count - 1; i >= 0; i--) { if (aiActions[i].ActionLogic.ActionId == null) { aiActions.RemoveAt(i); continue; } if (aiActions[i].ActionLogic.ActionId.ActionType == CommonActionType.Gain) continue; if (aiActions[i].ActionLogic.ActionId.ActionType == CommonActionType.BuildWonder) continue; if (aiActions[i].ActionLogic.ActionId.ActionType == CommonActionType.Build) continue; if (aiActions[i].ActionLogic.ActionId.ActionType == CommonActionType.TrainUnit) continue; aiActions.RemoveAt(i); } } public static List GeneratorAllActionIdsForUse(MapData map, PlayerData selfPlayer) { var data = new AICalculatorData(); data.Map = map; data.Player = selfPlayer; var selfUnits = new HashSet(); map.GetUnitDataListByPlayerId(selfPlayer.Id, selfUnits); var selfCities = new HashSet(); map.GetCityDataListByPlayerId(selfPlayer.Id, selfCities); data.TargetParam.MapData = map; data.TargetParam.PlayerData = selfPlayer; GeneratorActionIds(data, CommonActionType.LearnTech); GeneratorActionIds(data, CommonActionType.StartWonder); GeneratorActionIds(data, CommonActionType.PlayerAction); foreach (var city in selfCities) { data.TargetParam.CityData = city; GeneratorActionIds(data, CommonActionType.Gain); GeneratorActionIds(data, CommonActionType.Build); GeneratorActionIds(data, CommonActionType.BuildWonder); GeneratorActionIds(data, CommonActionType.GridMisc); GeneratorActionIds(data, CommonActionType.TrainUnit); GeneratorActionIds(data, CommonActionType.CityLevelUpAction); } foreach (var unit in selfUnits) { data.TargetParam.UnitData = unit; GeneratorActionIds(data, CommonActionType.UnitAction); GeneratorActionIds(data, CommonActionType.UnitSkill); GeneratorActionIds(data, CommonActionType.UnitMove); GeneratorActionIds(data, CommonActionType.UnitAttack); //GeneratorActionIds(data, CommonActionType.AIParamControl); //GeneratorActionIds(data, CommonActionType.UnitAttackAlly); } var action = ActionLogicFactory.GetActionLogicByType(CommonActionType.TurnEnd); var param = new CommonActionParams(map, playerData:selfPlayer); data.AIActions.Add(new AIActionBase(param, action[0])); for (int i = data.AIActions.Count - 1; i >= 0; i--) { if (data.AIActions[i].ActionLogic.ActionId.PlayerActionType == PlayerActionType.FinishHeroTask) { data.AIActions.RemoveAt(i); continue; } if (data.AIActions[i].ActionLogic.ActionId.UnitActionType == UnitActionType.Disband) { data.AIActions.RemoveAt(i); continue; } if (data.AIActions[i].ActionLogic.ActionId.UnitActionType == UnitActionType.ForceDisband) { data.AIActions.RemoveAt(i); continue; } if (data.AIActions[i].ActionLogic.ActionId.GridMiscActionType == GridMiscActionType.Destroy) { data.AIActions.RemoveAt(i); continue; } } return data.AIActions; } public static List GeneratorAllActionIds(MapData map, PlayerData selfPlayer) { var data = new AICalculatorData(); data.Map = map; data.Player = selfPlayer; var selfUnits = new HashSet(); map.GetUnitDataListByPlayerId(selfPlayer.Id, selfUnits); var selfCities = new HashSet(); map.GetCityDataListByPlayerId(selfPlayer.Id, selfCities); data.TargetParam.MapData = map; data.TargetParam.PlayerData = selfPlayer; GeneratorActionIds(data, CommonActionType.LearnTech); GeneratorActionIds(data, CommonActionType.StartWonder); GeneratorActionIds(data, CommonActionType.PlayerAction); foreach (var city in selfCities) { data.TargetParam.CityData = city; GeneratorActionIds(data, CommonActionType.Gain); GeneratorActionIds(data, CommonActionType.Build); GeneratorActionIds(data, CommonActionType.BuildWonder); GeneratorActionIds(data, CommonActionType.GridMisc); GeneratorActionIds(data, CommonActionType.TrainUnit); GeneratorActionIds(data, CommonActionType.CityLevelUpAction); } foreach (var unit in selfUnits) { data.TargetParam.UnitData = unit; GeneratorActionIds(data, CommonActionType.UnitAction); GeneratorActionIds(data, CommonActionType.UnitSkill); GeneratorActionIds(data, CommonActionType.UnitMove); GeneratorActionIds(data, CommonActionType.UnitAttack); GeneratorActionIds(data, CommonActionType.AIParamControl); GeneratorActionIds(data, CommonActionType.UnitAttackAlly); } var action = ActionLogicFactory.GetActionLogicByType(CommonActionType.TurnEnd); var param = new CommonActionParams(map, playerData:selfPlayer); data.AIActions.Add(new AIActionBase(param, action[0])); return data.AIActions; } public static void GeneratorActionIds(AICalculatorData data, CommonActionType type) { var actions = ActionLogicFactory.GetActionLogicByType(type); if (actions == null || actions.Count == 0) return; if (type == CommonActionType.Gain || type == CommonActionType.Build || type == CommonActionType.BuildWonder || type == CommonActionType.GridMisc) { if (data.TargetParam.CityData == null) return; data.TargetParam.MainObjectType = ActionLogicFactory.GetMainObjectType(type); foreach (var gridId in data.TargetParam.CityData.Territory.TerritoryArea) { if (!data.Map.GridMap.GetGridDataByGid(gridId, out var grid)) continue; data.TargetParam.GridData = grid; data.TargetParam.OnParamChanged(); foreach (var action in actions) { if (!action.CheckCan(data.TargetParam)) continue; var param = data.TargetParam.GetCopyParam(); param.UnitData = null; param.TargetUnitData = null; param.TargetGridData = null; param.TargetPlayerData = null; param.OnParamChanged(); data.AIActions.Add(new AIActionBase(param, action)); } } } if (type == CommonActionType.StartWonder || type == CommonActionType.LearnTech) { data.TargetParam.MainObjectType = ActionLogicFactory.GetMainObjectType(type); foreach (var action in actions) { if (!action.CheckCan(data.TargetParam)) continue; var param = data.TargetParam.GetCopyParam(); param.UnitData = null; param.CityData = null; param.GridData = null; param.TargetUnitData = null; param.TargetGridData = null; param.TargetPlayerData = null; param.OnParamChanged(); data.AIActions.Add(new AIActionBase(param, action)); } } if (type == CommonActionType.TrainUnit || type == CommonActionType.CityLevelUpAction) { if (data.TargetParam.CityData == null) return; data.TargetParam.MainObjectType = ActionLogicFactory.GetMainObjectType(type); foreach (var action in actions) { if (!action.CheckCan(data.TargetParam)) continue; var param = data.TargetParam.GetCopyParam(); param.UnitData = null; param.GridData = null; param.TargetUnitData = null; param.TargetGridData = null; param.TargetPlayerData = null; param.OnParamChanged(); data.AIActions.Add(new AIActionBase(param, action)); } } if (type == CommonActionType.UnitAction || type == CommonActionType.UnitSkill) { if (data.TargetParam.UnitData == null) return; data.TargetParam.MainObjectType = ActionLogicFactory.GetMainObjectType(type); foreach (var action in actions) { if (!action.CheckCan(data.TargetParam)) continue; var param = data.TargetParam.GetCopyParam(); param.CityData = null; param.GridData = null; param.TargetUnitData = null; param.TargetGridData = null; param.TargetPlayerData = null; param.OnParamChanged(); data.AIActions.Add(new AIActionBase(param, action)); } } if (type == CommonActionType.UnitMove) { if (data.TargetParam.UnitData == null) return; if (!data.TargetParam.UnitData.Alive) return; if (!data.Map.GetGridDataByUnitId(data.TargetParam.UnitData.Id, out var unitGrid)) return; data.TargetParam.MainObjectType = ActionLogicFactory.GetMainObjectType(type); Main.UnitLogic.CalcUnitMoveInfo(data.Map, data.TargetParam.UnitData.Id); foreach (var grid in data.Map.GridMap.GridList) { if (grid.Id == unitGrid.Id) continue; var result = Main.UnitLogic.CheckUnitCanMoveOrAttack(data.Map, data.TargetParam.UnitData, grid); if (result != MoveAttackType.Move && result != MoveAttackType.MoveToPort && result != MoveAttackType.MoveAshore) continue; if (data.TargetParam.UnitData.MP <= 0) continue; data.TargetParam.GridData = grid; data.TargetParam.OnParamChanged(); foreach (var action in actions) { if (!action.CheckCan(data.TargetParam)) continue; var param = data.TargetParam.GetCopyParam(); param.CityData = null; param.TargetUnitData = null; param.TargetGridData = null; param.TargetPlayerData = null; param.OnParamChanged(); data.AIActions.Add(new AIActionBase(param, action)); } } } if (type == CommonActionType.UnitAttack) { if (data.TargetParam.UnitData == null) return; if (!data.TargetParam.UnitData.Alive) return; if (data.TargetParam.UnitData.AP <= 0) return; data.TargetParam.MainObjectType = ActionLogicFactory.GetMainObjectType(type); Main.UnitLogic.CalcUnitMoveInfo(data.Map, data.TargetParam.UnitData.Id); foreach (var grid in data.Map.GridMap.GridList) { if (!data.Map.GetUnitDataByGid(grid.Id, out var targetUnit)) continue; var result = Main.UnitLogic.CheckUnitCanMoveOrAttack(data.Map, data.TargetParam.UnitData, grid); if (result != MoveAttackType.Attack) continue; data.TargetParam.GridData = grid; data.TargetParam.TargetUnitData = targetUnit; data.TargetParam.OnParamChanged(); foreach (var action in actions) { if (!action.CheckCan(data.TargetParam)) continue; var param = data.TargetParam.GetCopyParam(); param.CityData = null; param.TargetGridData = null; param.TargetPlayerData = null; param.OnParamChanged(); data.AIActions.Add(new AIActionBase(param, action)); } } } if (type == CommonActionType.PlayerAction) { if (data.TargetParam.PlayerData == null) return; data.TargetParam.MainObjectType = ActionLogicFactory.GetMainObjectType(type); foreach (var player in data.TargetParam.MapData.PlayerMap.PlayerDataList) { if (player.Id == data.TargetParam.PlayerData.Id) continue; data.TargetParam.TargetPlayerData = player; data.TargetParam.OnParamChanged(); foreach (var action in actions) { if (action.ActionId.PlayerActionType == PlayerActionType.SelectHero || action.ActionId.PlayerActionType == PlayerActionType.FinishHeroTask) continue; if (!action.CheckCan(data.TargetParam)) continue; var param = data.TargetParam.GetCopyParam(); param.UnitData = null; param.CityData = null; param.GridData = null; param.TargetUnitData = null; param.TargetGridData = null; param.OnParamChanged(); data.AIActions.Add(new AIActionBase(param, action)); } } foreach (var action in actions) { if (action.ActionId.PlayerActionType != PlayerActionType.SelectHero && action.ActionId.PlayerActionType != PlayerActionType.FinishHeroTask) continue; if (!action.CheckCan(data.TargetParam)) continue; var param = data.TargetParam.GetCopyParam(); param.UnitData = null; param.CityData = null; param.GridData = null; param.TargetUnitData = null; param.TargetGridData = null; param.TargetPlayerData = null; param.OnParamChanged(); data.AIActions.Add(new AIActionBase(param, action)); } } if (type == CommonActionType.AIParamControl) { if (data.TargetParam.PlayerData == null) return; if (data.TargetParam.UnitData == null) return; if (!data.TargetParam.UnitData.IsAlive()) return; foreach (var action in actions) { if (action.ActionId.AIParamType == AIParamControlType.AIMoney) continue; if (!action.CheckCan(data.TargetParam)) continue; var param = data.TargetParam.GetCopyParam(); param.CityData = null; param.GridData = null; param.TargetUnitData = null; param.TargetGridData = null; param.TargetPlayerData = null; param.OnParamChanged(); data.AIActions.Add(new AIActionBase(param, action)); } } if (type == CommonActionType.UnitAttackAlly) { if (data.TargetParam.UnitData == null) return; if (!data.TargetParam.UnitData.Alive) return; if (data.TargetParam.UnitData.AP <= 0) return; data.TargetParam.MainObjectType = ActionLogicFactory.GetMainObjectType(type); foreach (var unit in data.TargetParam.MapData.UnitMap.UnitList) { if (unit.Id == data.TargetParam.UnitData.Id) continue; if (!data.TargetParam.MapData.IsLeagueUnitByUnit(unit.Id, data.TargetParam.UnitData.Id)) continue; data.TargetParam.TargetUnitData = unit; data.TargetParam.OnParamChanged(); foreach (var action in actions) { if (!action.CheckCan(data.TargetParam)) continue; var param = data.TargetParam.GetCopyParam(); param.CityData = null; param.GridData = null; param.TargetGridData = null; param.TargetPlayerData = null; param.OnParamChanged(); data.AIActions.Add(new AIActionBase(param, action)); } } } } } }