Calculated Property vs. Global Key Command

I’ve generally been using Global Key Commands to change Properties across multiple Pieces. Then it dawned upon me that Calculated Property can change the state of a Piece without any Key Command. I did a quick test and the Calculated Property changed even before the map that the Piece was on was even drawn - which has been as issue for me with GKCs. If this is a stable way to change Properties, it would seem potentially much more powerful than GKCs. Also, you only need a single Calculated Property trait instead of potentially a slew of GKCs, Dynamic Properties, and Restrict Commands all over the place. It also seems much more resilient to weirdness from the dreaded UNDO button. Is one method more processor intensive than the other? Does anyone have experience comparing/contrasting the two methods?

The main thing to keep in mind about Calculated Property traits is that they fully recompute themselves every time they are “asked their value”. Which is certainly usually an advantage, as you’ve seen. The problem can come if you end up with a big nested set of them and you’re suddenly doing something really inefficient like calculating something big and complex over-and-over-and-over e.g. as you draw every single individual piece.

Calculated Properties aren’t inherently CPU-intensive – they can just be abused in ways that become so.

I’ve started noticing some really unpleasant behavior in my module in multi-player mode because of all the undos combined with GKCs and sticky mouse behavior. The catastrophic failure that seems to occur every few thousand clicks is that somebody inadvertently erases every single piece on the map. We’ve seen it happen 4-5 times now in about 50 hours of testing. I finally got a hint as to the culprit when I found all 500+ pieces in a single mega-stack that coincided with an undo of a Send to Location. It seems that somehow in clicking undo, the mouse (or backward logic) inadvertently selected every piece on the map and sent it to one location. The game is pretty much trashed at that point because every OldLocation OldZone bug plus every other undo bug is unearthed as if zombies were coming out of the ground since every Piece was “touched” by the undo function…

Anyway, I’m going to start experimenting with eliminating GKCs in the suspected problem areas…

So the burning question I guess is “WHEN” are they “asked their value”? It’s neither triggered by a Key Command nor the piece being drawn. Are they happening constantly? If so, I can see that becoming a huge performance drag in a hurry…

Well, Calculated Properties are in fact inherently CPU-intensive. As they are implemented as Beanshell expressions, there are overheads in calling into Beanshell to evaluate them each time they are needed and Beanshell being a fully interpreted language running in an interpreted language are relatively slow and CPU intensive.

You should definitely avoid using a Beanshell expression or referencing a Calculated Property as the target of a ‘Layer follows Expression value’ field in Layer trait. This can cause a very high rate of re-calculation of the property across a number of units. You should also avoid referring to Calculated Properties in Text labels. These 2 case cause the CP to be recalculated every time the piece is redrawn.

Most other usages are fine and only cause a re-calculation of the Property as a result of a user clicking on something, although you will get noticeable delays when issuing GKCs to many counters that require CP’s to be calculated.

Calculated Properties are recalculated whenever ‘something’ requests their value. This will depend on where you have used them.

W/r/t “CPU intensive” point taken, but I was comparing to OP’s original alternative of GKC’s which are of course way more CPU intensive than the average Calculated Property. And I was assuming the dynamic properties were using beanshell expressions anyway.

W/r/t the Undo problems note that a major “undo bug” has recently been fixed. I’m not sure if it’s in 3.3.1 or won’t be pushed until 3.3.2 (I suspect the latter). But anyway it should make the undo situation a huge step-function more stable.

Sadly I started changing everything to Beanshell based on some offhanded derogatory comments by others about using Classic. Thinking it through though, it’s obvious that using Beanshell when you don’t need it will slow things down a tad. Then again, I’m not going to lose sleep over one millisecond after a keystroke. I’ll just be mindful of applying Beanshell if it has many iterations or far-reaching effects.

Brent, maybe you walk me through this one instance so I’ll understand the difference between using a Calculated Property and a GKC?

I have a GPIW for deploying new units that is brought up by Hotkey and shows only pieces with the property {Mobilized>0}. Every unit in the game has a Dynamic Property with 7 different Key Commands that sets Mobilized to 0, 1, or 2. So when a player plays a card that allows him to deploy units, it fires off a GKC. One of the first things I learned as a Vassal newbie (i.e. 30 days ago) was to filter at the GKC level and NOT to fire it off at everything and filter at the piece level. So I’m very selective with piece count on a GKC. Anyway, the resulting logic is a huge web of Dynamic Properties, GKCs back and forth (for mobilize then demobilize) combined with Trigger Actions and Restrict Commands depending on what piece level right-click options I want to disable or hide.

The Beanshell expression that mobilizes any unit is {LocationName==“Mobilized”&&“Power==”$Attacker$"}. It’s basically the same expression whether it’s a GKC or Calculated Property. I also have a GKC {Mobilized>0} that demobilizes everything after each card play, but before the next. Of those 7 Key Commands, the remainder demobilize a unit once it is been deployed, moves, or is eliminated (return to deck, not actually deleted piece) so they affect that one piece only. It seems obvious to leave those alone as Dynamic Property Key Commands.

So LocationName is checked when a piece is moved, drawn, and when else? $Power$ is a Marker. $Attacker$ is a Global Property that is set each time a player plays a card for ops. So when the German player plays a card for ops, he becomes the Attacker and all pieces in the “Mobilized” location that have the marker Power=Germany show up in his GPIW. I’m wondering how many times Mobilized is calculated if it is a Calculated Property?

Where exactly are you using this Beanshell expression?

GKC in a Prototype for when certain cards are played for “ops”. The exact expression is:

Matching Properties: {Power=="$Power$"&&LocationName==“Mobilized”}

5-player game. There are 5 locations named “Mobilized”, each in a private map. Power is a marker on each card and unit. I used Prototypes on top of Prototypes for everything and basically turned all units and cards into shells for Prototypes prefaced with a bunch of Markers that populate all the values in those Prototypes. Really made a 5 player game with 300 cards and 5 sets of everything scalable…

Probably best to explain exactly what happens when you play an Ops card that fires off a GKC trait

  1. Any $…$ variables in the GKC Property Match Expression are replaced by property Values from the OPS card.
  2. Vassal retrieves a list of every map in the module
  3. For each Map in the list, Vassal retrieves a list of EVERY counter on that map.
  4. For each counter, Vassal evaluates the GKC Property Match Expression against that counter.
  5. If the Property Match Expression for that counter evaluates to true, then the KeyCommand is issued against that counter.

The useful nugget here is how it creates lists. I had assumed it looked at every piece. If maps are a high-level filter, then I’ll go back and specify individual maps wherever possible. Are there any other high-level filters that shrink the size of the search list? I think I have all my GKCs optimized at the execution level so no units are issuing unnecessary keystrokes. I basically add a Marker every time I want to narrow a list…

So revisiting my original question, I have GKCs that “mobilize” a handful of units (usually between 1 to 10) upon card play, then “demobilizes” them after that card, but before the next. If I eliminate the GKCs and turn Mobilized into a Calculated Property within every unit with an expression {(LocationName==“Mobilized”&&"Power==Attacker)?1:0}. Is that CPU intensive? Doesn’t seem like it would be too bad. There are no image draws related to that expression. It can only change if a piece is moved or a card is played.

Map is only a high-level filter in a Ma-level Global Key Command command when you check the ‘Apply to this map only?’ checkbox. In all other cases, all units on all maps are checked.

The efficiency depends on how many times and when the expression is ultimately executed, which depends on how you have structured the module.

If you can run a GKC, at the time of a card flip, evaluate the expression once on each unit and change the unit is that it is not completely ‘Mobilised’ or ‘Mobilised’, then you have a one-off large-scale processing at the time of the card flip, but no processing at all during subsequent play.

If each unit individually checks the expression each time it is right-clicked (for example) to check if certain commands are available, then you get an on-going, repeated execution of the expression, but only on cards that ‘matter’ and you don’t get any delay at the card flip time.

It is all relative. With simple expressions like yours, you may not notice much difference at all. If you need to change the visual look of mobilised units, then I would probably go with method 1, despite the upfront cost. If not, I would probably go with method 2.

I use method 1 in my GTS modules to highlight units eligible to move after a formation chit is drawn and de-highlight all active units at the end of the phase. There is a noticeable delay of 1-2 seconds in a campaign scenario with 1500 pieces on the board while the GKC runs, but as that function does not happen often (e.g. usually several minutes at least between times when it is used), then no-one seems to mind.