[done] Activation condition optimization

Creation, discussion, and balancing of game content such as techs, buildings, ship parts.

Moderators: Oberlus, Committer

Post Reply
Message
Author
Ophiuchus
Programmer
Posts: 3433
Joined: Tue Sep 30, 2014 10:01 am
Location: Wall IV

[done] Activation condition optimization

#1 Post by Ophiuchus »

Trying to understand FOCS better I started optimizing activation conditions.
Please have a short look at the optimization commit and tell me if my reasoning is good.

Changes to the government function stealth building:

* move check of capital to the end of activation - The CONDITION_GOVERNMENT_TYPE_IS_ macro looks up the empires capital and checks if the correct government building is there. I think finding the government is not trivial, so i moved it to the end of the activation

* replace ContainedBy by Number and PlanetID - I want to find out which species stealth bonus is on the planet. ContainedBy will try to match against the planet, all fleets, and buildings on that planet. I only need the planet, and I already have the PlanetID. So I used the Object id condition to get the current planet. Also I guessed that ContainedBy will in itself do some work, so I used NumberOf high=1 to do this, not sure if there is a more suitable condition.

Other questions:
* the building should substitute the stealth bonus given by the species, so i used the stackinggroup used there.

* I generated effectgroups for all of the BAD_STEALTH, GOOD_STEALTH using macros. This works but feels very hackish and was quite error prone.

p.s. for the *_STEALTH macros, what I wished for was a ValueRef condition similar to

Code: Select all

// Find the BAD, GOOD.. stealth level from the tag and look up the fitting value
If condition = TagPart matchTag="%candidate%_STEALTH" effects = SetStealth value = value + [[STEALTH_VALUE_FOR(LocalCandidate)]] 
(only possible if condition results can be strings).
Or maybe something like

Code: Select all

SetStealth value = value + [[STEALTH_VALUE_FOR(TagSubstring regexp="(.*)_STEALTH")]]
Last edited by Ophiuchus on Mon Jun 19, 2017 4:26 pm, edited 1 time in total.
Any code or patches in anything posted here is released under the CC and GPL licences in use for the FO project.

Look, ma... four combat bouts!

User avatar
Geoff the Medio
Programming, Design, Admin
Posts: 13587
Joined: Wed Oct 08, 2003 1:33 am
Location: Munich

Re: Activtion condition optimization

#2 Post by Geoff the Medio »

Ophiuchus wrote:for the *_STEALTH macros, what I wished for was a ValueRef condition similar to...
ValueRefs and Conditions are distinct things; don't refer to them both as "condition". ValueRefs return values like numbers, strings, or planet sizes. Conditions test sets of candidate objects and return which do or don't match some criteria.

Code: Select all

[[STEALTH_VALUE_FOR(LocalCandidate)]]
This can't be just LocalCandidate.Stealth ?
* replace ContainedBy by Number and PlanetID - I want to find out which species stealth bonus is on the planet. ContainedBy will try to match against the planet, all fleets, and buildings on that planet. I only need the planet, and I already have the PlanetID. So I used the Object id condition to get the current planet. Also I guessed that ContainedBy will in itself do some work, so I used NumberOf high=1 to do this, not sure if there is a more suitable condition.
I don't follow your logic or description of the situation here, but the ContainedBy and Contains conditions are, at least supposed to be, a bit smart about how they handle various subcondition matches and local candidate objects. I don't know if your source object is the planet or the building on it, but in either case, using Object id to get the other single object should not result in a particularly slow ContainedBy condition.

Ophiuchus
Programmer
Posts: 3433
Joined: Tue Sep 30, 2014 10:01 am
Location: Wall IV

Re: Activtion condition optimization

#3 Post by Ophiuchus »

Geoff the Medio wrote:
Ophiuchus wrote:for the *_STEALTH macros, what I wished for was a ValueRef condition similar to...
ValueRefs and Conditions are distinct things; don't refer to them both as "condition". ValueRefs return values like numbers, strings, or planet sizes. Conditions test sets of candidate objects and return which do or don't match some criteria.

Code: Select all

[[STEALTH_VALUE_FOR(LocalCandidate)]]
This can't be just LocalCandidate.Stealth ?
i mixed valueRefs and conditions up. STEALTH_VALUE_FOR wasnt suppose to get a value from an object but to look up a constant (per macro) like

Code: Select all

GOOD_STEALTH_VALUE 
'''20'''
STEALTH_VALUE_FOR
'''[[@1@_STEALTH_VALUE]]'''
. So the first pseudocode up there wont work.
Any code or patches in anything posted here is released under the CC and GPL licences in use for the FO project.

Look, ma... four combat bouts!

Ophiuchus
Programmer
Posts: 3433
Joined: Tue Sep 30, 2014 10:01 am
Location: Wall IV

Re: Activtion condition optimization

#4 Post by Ophiuchus »

...
* replace ContainedBy by Number and PlanetID - I want to find out which species stealth bonus is on the planet. ContainedBy will try to match against the planet, all fleets, and buildings on that planet. I only need the planet, and I already have the PlanetID. So I used the Object id condition to get the current planet. Also I guessed that ContainedBy will in itself do some work, so I used NumberOf high=1 to do this, not sure if there is a more suitable condition.
I don't follow your logic or description of the situation here, but the ContainedBy and Contains conditions are, at least supposed to be, a bit smart about how they handle various subcondition matches and local candidate objects. I don't know if your source object is the planet or the building on it, but in either case, using Object id to get the other single object should not result in a particularly slow ContainedBy condition.
Neither nor. The source object is a building somewhere in the empire. I want to take use the stealth level from the species on that planet.

The scope is all your empire's planets with a scanning facility. I want to add the stealth level from the source's planet's species to the scanning facility contained in the target.
But if I understood correctly the following wouldnt be slower (?)

Code: Select all

            activation = And [
                // Source is the seat building
                ContainedBy And [
                    Planet
                    Species
                    HasTag name = "@2@_STEALTH"
                ]
                [[CONDITION_GOVERNMENT_TYPE_IS_(@1@)]]
            ]
Any code or patches in anything posted here is released under the CC and GPL licences in use for the FO project.

Look, ma... four combat bouts!

User avatar
Geoff the Medio
Programming, Design, Admin
Posts: 13587
Joined: Wed Oct 08, 2003 1:33 am
Location: Munich

Re: Activtion condition optimization

#5 Post by Geoff the Medio »

Ophiuchus wrote:The scope is all your empire's planets with a scanning facility. I want to add the stealth level from the source's planet's species to the scanning facility contained in the target.
But if I understood correctly the following wouldnt be slower (?)

Code: Select all

            activation = And [
                // Source is the seat building
                ContainedBy And [
                    Planet
                    Species
                    HasTag name = "@2@_STEALTH"
                ]
                [[CONDITION_GOVERNMENT_TYPE_IS_(@1@)]]
            ]
When evaluating an activation condition, the only tested object is the source object for the effects group. What the scope is is irrelevant. If the source is a building, and you want to check what it is ContainedBy, then the search domain for the ContainedBy condition is just that source object, and the only objects checked with the subcondition of ContainedBy will be those that contain the source object, which are the planet and the system of the building. That is, the subcondition of ContainedBy will be evaluated on just the object(s) that contain the object being tested with ContainedBy itself.

https://github.com/freeorion/freeorion/ ... .cpp#L2771

If you want to avoid testing the System, then (as you already have done) put a Planet condition in the subcondition, or Object id = LocalCandidate.PlanetID or similar.

It's possible that using ContainedBy might be slower than some other condition you could figure out, but probably not enough to warrant making things any more complicated than they already are.

User avatar
Geoff the Medio
Programming, Design, Admin
Posts: 13587
Joined: Wed Oct 08, 2003 1:33 am
Location: Munich

Re: Activtion condition optimization

#6 Post by Geoff the Medio »

Ophiuchus wrote:The source object is a building somewhere in the empire. I want to take use the stealth level from the species on that planet.
Depending what you mean by "from the species", you can probably use a Statistic If ValueRef.

Statistic If condition = [stuff]

returns 1 if there is at least one object that matches the subcondition [stuff], or returns 0 if nothing matches the subcondition [stuff]

It can be slow, however, as all potential candidates in the universe will be tested, rather than a short-cut optimized search that terminates as soon as one match is found, even in cases where the latter would be possible.

Regardless, you can set up a chain of Statistic If that are all added together, where just one is nonzero. Each would test the species on the planet, and be multiplied by the corresponding stealth factor, which you can get from a macro if desired.

Ophiuchus
Programmer
Posts: 3433
Joined: Tue Sep 30, 2014 10:01 am
Location: Wall IV

Re: Activtion condition optimization

#7 Post by Ophiuchus »

Geoff the Medio wrote:
Ophiuchus wrote:The source object is a building somewhere in the empire. I want to take use the stealth level from the species on that planet.
Depending what you mean by "from the species",
I meant the planet stealth bonus defined for the species (like BAD_STEALTH) which lives on the source buildings planet. Not the stealth value of an actual object.
Geoff the Medio wrote:you can probably use a Statistic If ValueRef.

Statistic If condition = [stuff]

returns 1 if there is at least one object that matches the subcondition [stuff], or returns 0 if nothing matches the subcondition [stuff]

It can be slow, however, as all potential candidates in the universe will be tested, rather than a short-cut optimized search that terminates as soon as one match is found, even in cases where the latter would be possible.
Reading the Condition.cpp and ValueRef.cpp code really helps in guessing complexity of the execution, thank you for the link.

Just to see if I understand correctly, ...
Edit (was wrong): If I understand correctly NumberOf low=1 condition = [stuff] would have the same effect as Statistic if condition = [stuff], right. Just the implementation is completely different.
And also NumberOf doesnt support short-cuts in the search. So probably both are similiar in performance.
Geoff the Medio wrote:Regardless, you can set up a chain of Statistic If that are all added together, where just one is nonzero. Each would test the species on the planet, and be multiplied by the corresponding stealth factor, which you can get from a macro if desired.
I am not able to follow you here. The multiplication must be outside of the If condition, right.
Why would i want multiple statistic Ifs - one for each existing *_STEALTH level ? In that case i wouldnt know outside of the If condition which stealth level was matched.
Any code or patches in anything posted here is released under the CC and GPL licences in use for the FO project.

Look, ma... four combat bouts!

User avatar
Geoff the Medio
Programming, Design, Admin
Posts: 13587
Joined: Wed Oct 08, 2003 1:33 am
Location: Munich

Re: Activtion condition optimization

#8 Post by Geoff the Medio »

Ophiuchus wrote:
Geoff the Medio wrote:Regardless, you can set up a chain of Statistic If that are all added together, where just one is nonzero. Each would test the species on the planet, and be multiplied by the corresponding stealth factor, which you can get from a macro if desired.
I am not able to follow you here. The multiplication must be outside of the If condition, right.
Why would i want multiple statistic Ifs - one for each existing *_STEALTH level ? In that case i wouldnt know outside of the If condition which stealth level was matched.
I'm thinking of something like this: https://github.com/freeorion/freeorion/ ... cs.txt#L16

Perhaps it doesn't fit your use case, though.
And also NumberOf doesnt support short-cuts in the search.
Conditions with subconditions are generally set up to evaluate all the subconditions first, then test the local candidates given the subcondition results. For NumberOf, it has some sorting options, so it is structured to test all the local candidates with the subcondition first, then sort those that passed and pick from the sorted list. For And and Or conditions, it's a bit smart, in that it tests the candidates that remain after applying each subcondition in succession, so if it has no candidates left to test left after 2 of the subconditions, it doesn't bother passing the empty set to the remaining subconditions (which themselves could have sub-subconditions with independent larger sets of matches).

For the Number condition, since it doesn't actually return the subcondition matches or depend on their order, it could in principle stop processing with a similar test as used in Or or And but with another threshold... But (if pre-evaluating all the subconditions) the only case in which that would be relevant would be if the subcondition for Number was and And or Or condition in which the sub-subconditions partially matched could lead to the number of candidates falling out of the range that could possibly meet the required number range. For that to be implemented, there would need to be a modified Or or And condition which takes an additional range-to-match parameter. It could work, but the benefit to work ratio is a bit skewed. If, instead, the subcondition for Number had to be re-evaluated for each candidate, then that could be halted if the number of potential candidates falls out of an acceptable range (too many already matched, or not enough left to match, given the allowed number of subcondition matches). I'm not sure if that occurs often enough to be worth prioritizing, though...

In the case of NumberOf, if no sorting is specified, it reverts to randomly picking from the subcondition matches, though this tries to make the returned items actual random from the sub-set, rather than the first N that are tested, and for that it has to test all the candidates, so can't be short-cut.

Ophiuchus
Programmer
Posts: 3433
Joined: Tue Sep 30, 2014 10:01 am
Location: Wall IV

Re: Activtion condition optimization

#9 Post by Ophiuchus »

Geoff the Medio wrote:
Ophiuchus wrote:
Geoff the Medio wrote:Regardless, you can set up a chain of Statistic If that are all added together, where just one is nonzero. Each would test the species on the planet, and be multiplied by the corresponding stealth factor, which you can get from a macro if desired.
I am not able to follow you here. The multiplication must be outside of the If condition, right.
Why would i want multiple statistic Ifs - one for each existing *_STEALTH level ? In that case i wouldnt know outside of the If condition which stealth level was matched.
I'm thinking of something like this: https://github.com/freeorion/freeorion/ ... cs.txt#L16
Thanks, looks very KISS. I will see if I can apply this.
Geoff the Medio wrote:
And also NumberOf doesnt support short-cuts in the search.
Conditions with subconditions are generally set up to evaluate all the subconditions first, then test the local candidates given the subcondition results. For NumberOf, it has some sorting options, so it is structured to test all the local candidates with the subcondition first, then sort those that passed and pick from the sorted list. For And and Or conditions, it's a bit smart, in that it tests the candidates that remain after applying each subcondition in succession, so if it has no candidates left to test left after 2 of the subconditions, it doesn't bother passing the empty set to the remaining subconditions (which themselves could have sub-subconditions with independent larger sets of matches).

For the Number condition, since it doesn't actually return the subcondition matches or depend on their order, it could in principle stop processing with a similar test as used in Or or And but with another threshold... But (if pre-evaluating all the subconditions) the only case in which that would be relevant would be if the subcondition for Number was and And or Or condition in which the sub-subconditions partially matched could lead to the number of candidates falling out of the range that could possibly meet the required number range. For that to be implemented, there would need to be a modified Or or And condition which takes an additional range-to-match parameter. It could work, but the benefit to work ratio is a bit skewed. If, instead, the subcondition for Number had to be re-evaluated for each candidate, then that could be halted if the number of potential candidates falls out of an acceptable range (too many already matched, or not enough left to match, given the allowed number of subcondition matches). I'm not sure if that occurs often enough to be worth prioritizing, though...
Ok. I will see if I run into performance issues. I guess there is nothing in place to measure effect processing times, is it?
As implemetation idea - would it make sense to switch from containers with to something more stream oriented? Reversing control by letting the outer conditions pull results from inner conditions. So the outer condition could simply stop pulling matches if the result is determined (e.g. because too many matches were found).
Geoff the Medio wrote:In the case of NumberOf, if no sorting is specified, it reverts to randomly picking from the subcondition matches, though this tries to make the returned items actual random from the sub-set, rather than the first N that are tested, and for that it has to test all the candidates, so can't be short-cut.
Ah, makes sense.

Also back to my current scripting. I am using a building on the capital to determine the government type (e.g. council, democracy..). The condition to find out if the current government type is the given one i use the following macro:

Code: Select all

CONDITION_GOVERNMENT_TYPE_IS_
'''/* if the government type is @1@ */ Number low = 1 condition = And [
        OwnedBy empire = Source.Owner
        Capital
        Contains condition = Building name = "BLD_IMPERIAL_GOVERNMENT_TYPE_@1@"
    ]'''
Would an alternative implementation make sense to get rid of the subcondition?
E.g. the government type building could tag all your empire's planets with the government type (and remove all other government type tags).
Any code or patches in anything posted here is released under the CC and GPL licences in use for the FO project.

Look, ma... four combat bouts!

User avatar
Geoff the Medio
Programming, Design, Admin
Posts: 13587
Joined: Wed Oct 08, 2003 1:33 am
Location: Munich

Re: Activtion condition optimization

#10 Post by Geoff the Medio »

Ophiuchus wrote:I guess there is nothing in place to measure effect processing times, is it?
Turn up effects logging to trace level.
As implemetation idea - would it make sense to switch from containers with to something more stream oriented? Reversing control by letting the outer conditions pull results from inner conditions. So the outer condition could simply stop pulling matches if the result is determined (e.g. because too many matches were found).
I'm not sure how to reply, other than by saying that doesn't make much sense.
Also back to my current scripting. I am using a building on the capital to determine the government type (e.g. council, democracy..). The condition to find out if the current government type is the given one i use the following macro:

Code: Select all

CONDITION_GOVERNMENT_TYPE_IS_
'''/* if the government type is @1@ */ Number low = 1 condition = And [
        OwnedBy empire = Source.Owner
        Capital
        Contains condition = Building name = "BLD_IMPERIAL_GOVERNMENT_TYPE_@1@"
    ]'''
Would an alternative implementation make sense to get rid of the subcondition?
E.g. the government type building could tag all your empire's planets with the government type (and remove all other government type tags).
You could, but I don't see why you would unless you just prefer using tags. Evaluating Contains, in this case, should only be checking the buildings located on the relevant planet.

Ophiuchus
Programmer
Posts: 3433
Joined: Tue Sep 30, 2014 10:01 am
Location: Wall IV

Re: Activtion condition optimization

#11 Post by Ophiuchus »

Also back to my current scripting. I am using a building on the capital to determine the government type (e.g. council, democracy..). The condition to find out if the current government type is the given one i use the following macro:

Code: Select all

CONDITION_GOVERNMENT_TYPE_IS_
'''/* if the government type is @1@ */ Number low = 1 condition = And [
        OwnedBy empire = Source.Owner
        Capital
        Contains condition = Building name = "BLD_IMPERIAL_GOVERNMENT_TYPE_@1@"
    ]'''
Would an alternative implementation make sense to get rid of the subcondition?
E.g. the government type building could tag all your empire's planets with the government type (and remove all other government type tags).
You could, but I don't see why you would unless you just prefer using tags. Evaluating Contains, in this case, should only be checking the buildings located on the relevant planet.
But the condition is looking for the capital every time it is evaluated, right? Or does the condition result get cached and only executed once?
With a tag I could just check the source planet.
Any code or patches in anything posted here is released under the CC and GPL licences in use for the FO project.

Look, ma... four combat bouts!

User avatar
Geoff the Medio
Programming, Design, Admin
Posts: 13587
Joined: Wed Oct 08, 2003 1:33 am
Location: Munich

Re: Activtion condition optimization

#12 Post by Geoff the Medio »

Ophiuchus wrote:But the condition is looking for the capital every time it is evaluated, right? Or does the condition result get cached and only executed once?
Depends what exact situation you're asking about, but probably it is not cached in the sense you mean. Whether evaluating it is slow enough to worry about is another question. For a relatively simple condition, possibly / probably not.
With a tag I could just check the source planet.
Yes... and if you prefer, you can do that.

Ophiuchus
Programmer
Posts: 3433
Joined: Tue Sep 30, 2014 10:01 am
Location: Wall IV

Re: Activtion condition optimization

#13 Post by Ophiuchus »

Thanks for sharing the knowledge.

I found using the Statistic If ValueRef does not work so well in this case as I would still need a second effect group for the average defense.

But I found the Effect::Conditional and tried unsuccessfully to use it.

It seems that none of the conditions were matching.The source is a building. Can I use the Source with Effect::Conditional?

Code: Select all

            effects = [
                If        condition = And [ Object id = Source.PlanetID  HasTag name = "NO_DEFENSE_TROOPS" ]
                    effects = SetMaxTroops value = [[NO_DEFENSE_TROOPS_MULTIPLICATOR]] * Max ([[MINIMAL_IMPERIAL_GARRISON]], Value)                    
                else = If condition = ContainedBy HasTag name = "BAD_DEFENSE_TROOPS" 
                    effects = SetMaxTroops value = [[BAD_DEFENSE_TROOPS_MULTIPLICATOR]] * Max ([[MINIMAL_IMPERIAL_GARRISON]], Value)
Probably I also found a bug: after the if-else-chain the final else did have an effect, but the accountinglabel was not used to show where the effect was from.
Any code or patches in anything posted here is released under the CC and GPL licences in use for the FO project.

Look, ma... four combat bouts!

User avatar
Geoff the Medio
Programming, Design, Admin
Posts: 13587
Joined: Wed Oct 08, 2003 1:33 am
Location: Munich

Re: Activtion condition optimization

#14 Post by Geoff the Medio »

Ophiuchus wrote:It seems that none of the conditions were matching.The source is a building. Can I use the Source with Effect::Conditional?
As far as I know, it should work.

Code: Select all

            effects = [
                If        condition = And [ Object id = Source.PlanetID  HasTag name = "NO_DEFENSE_TROOPS" ]
                    effects = SetMaxTroops value = [[NO_DEFENSE_TROOPS_MULTIPLICATOR]] * Max ([[MINIMAL_IMPERIAL_GARRISON]], Value)                    
                else = If condition = ContainedBy HasTag name = "BAD_DEFENSE_TROOPS" 
                    effects = SetMaxTroops value = [[BAD_DEFENSE_TROOPS_MULTIPLICATOR]] * Max ([[MINIMAL_IMPERIAL_GARRISON]], Value)
I can't really comment on that out of context... In particular, what is the scope condition / what are the target objects for this effect?
Probably I also found a bug: after the if-else-chain the final else did have an effect, but the accountinglabel was not used to show where the effect was from.
Please make a minimal test case that demonstrates the issue.

Ophiuchus
Programmer
Posts: 3433
Joined: Tue Sep 30, 2014 10:01 am
Location: Wall IV

Re: Activtion condition optimization

#15 Post by Ophiuchus »

Geoff the Medio wrote:
Ophiuchus wrote:It seems that none of the conditions were matching.The source is a building. Can I use the Source with Effect::Conditional?
As far as I know, it should work.
You are right, made stupid mistake. I was comparing to the wrong object.
Geoff the Medio wrote:
Ophiuchus wrote:Probably I also found a bug: after the if-else-chain the final else did have an effect, but the accountinglabel was not used to show where the effect was from.
Please make a minimal test case that demonstrates the issue.
Will do and raise an issue. (Edit: here it is --> https://github.com/freeorion/freeorion/issues/1613
Any code or patches in anything posted here is released under the CC and GPL licences in use for the FO project.

Look, ma... four combat bouts!

Post Reply