/// <summary> /// Generates possible direct ability use actions. /// </summary> public static bool GenerateDirectAbilityUse(GameInstance state, CachedMob mob, List <UctAction> result) { bool foundAbilityUse = false; var mobInfo = mob.MobInfo; var mobId = mob.MobId; foreach (var abilityId in mobInfo.Abilities) { if (!GameInvariants.IsAbilityUsableNoTarget(state, mobId, abilityId)) { continue; } foreach (var targetId in state.MobManager.Mobs) { if (GameInvariants.IsAbilityUsable(state, mob, state.CachedMob(targetId), abilityId)) { foundAbilityUse = true; var action = UctAction.AbilityUseAction(abilityId, mobId, targetId); GameInvariants.AssertValidAction(state, action); result.Add(action); } } } return(foundAbilityUse); }
/// <summary> /// Calculates an action according to a simple default policy. Used mainly /// in MCTS playouts. /// </summary> public static UctAction DefaultPolicyAction(GameInstance state) { var mobId = state.CurrentMob; if (mobId == null) { throw new InvalidOperationException("Requesting mob action when there is no current mob."); } Debug.Assert(state.State.MobInstances[mobId.Value].Hp > 0, "Current mob is dead"); var mob = state.CachedMob(mobId.Value); if (mob.MobInstance.Ap == 0) { return(UctAction.EndTurnAction()); } var abilityIds = new List <int>(); foreach (var possibleAbilityId in mob.MobInfo.Abilities) { if (GameInvariants.IsAbilityUsableNoTarget(state, mobId.Value, possibleAbilityId)) { abilityIds.Add(possibleAbilityId); } } int moveTargetId = MobInstance.InvalidId; var actions = new List <UctAction>(); foreach (var possibleTargetId in state.MobManager.Mobs) { var possibleTarget = state.CachedMob(possibleTargetId); moveTargetId = possibleTargetId; if (!GameInvariants.IsTargetable(state, mob, possibleTarget)) { continue; } if (abilityIds.Count == 0) { continue; } foreach (var abilityId in abilityIds) { if (GameInvariants.IsAbilityUsableApRangeCheck(state, mob, possibleTarget, abilityId)) { actions.Add(UctAction.AbilityUseAction(abilityId, mob.MobId, possibleTargetId)); } } } if (actions.Count > 0) { return(MaxAbilityRatio(state, actions)); } if (moveTargetId != MobInstance.InvalidId) { return(PickMoveTowardsEnemyAction(state, state.CachedMob(mobId.Value), state.CachedMob(moveTargetId))); } else { Utils.Log(LogSeverity.Error, nameof(ActionGenerator), "No targets, game should be over"); throw new InvalidOperationException("No targets, game should be over."); } }