Rule ordering declarations explained, perhaps

Many people have been confused by what happens when you try to order rules in rulebooks with “The foo rule is listed before the bar rule in the check bazzing rulebook.” In particular, if you have two declarations (involving three rules), the rules may not wind up where you expect. See the example in this bug report.

Graham has submitted a new comment that explains what is going on:

Unpacking what he’s saying, I think this is what’s happening (and is supposed to happen, apparently):

First the compiler orders the rules in the rulebook by specificity order.

Then it goes through the rule-ordering declarations, in the order they appear in the source code.

If a rule is declared to be listed first, it gets moved to the front of the rulebook.

If a rule is declared to be listed last, it gets moved to the back of the rulebook.

If the foo rule is declared to be listed before the bar rule, then according to section 18.4 of the documenation, it is supposed to be placed before the bar rule in specificity order. But what happens if it winds up tied with another rule in specificity? Then it appears from the example listed in bug report 514 that it gets put last among the tied rules (so long as this keeps it before the bar rule). This can mean it gets moved later in the rulebook than it otherwise would be! That’s what happens in the first declaration Graham discusses, where the effect of listing the 1st rule before the 2nd rule is to move it after the 3rd rule.

Similarly if the foo rule is listed after the bar rule.

And the upshot is, if you declare “the foo rule is listed before the bar rule” and later declare something about the bar rule that moves it, the foo rule may not wind up before the bar rule. There isn’t any attempt to respect all the declarations at once. (This is at least somewhat in line with what is said in 18.4)

The confusion appears to arise from the way that “listed before” declarations handle rules that are tied with respect to specificity. I’m going to file a documentation bug on that to see what happens.

Oh, and Grahamologists may be interested to know that Graham is closing masses of bugs right now.

Ooh. That’s nasty. I guess it should be advised that you avoid putting the one rule as both the subject and the object in listing declarations.

Ow, my head.

Perhaps a useful feature could be a “listed Nth” modifier to allow precise reordering of rules? If I know how I want them to be arranged and I can see how they’re currently arranged, this seems like it would save a lot of trial and error for both me and the compiler.

Oh, that’s a great idea! …checks…

Internal error! Woohoo!

I meant in different rules. The example has rule 2 being a subject in one declaration and an object in another. That situation means one of the declarations won’t end up being fulfilled. If you avoid that I think all the declarations will be fulfilled as you’d normally expect.

Well, I don’t think that’s necessarily true; if you only include the first declaration (“The 1st rule is listed before the 2nd rule…”) it winds up reversing the 1st and 3rd rules from their source code order. I suppose that if you don’t use a rule twice in a declaration you can’t wind up with one of your declarations being violated, as the initial declaration will respect itself and no other declarations will move either of the rules involved, but I still wouldn’t say that it’s being fulfilled in the way you expect, if a rule gets reordered with respect to a third party in a way that’s not necessary to fulfill the declaration.

Draconis: You can always stack “The foo rule is listed first…” declarations until you’ve reordered the entire beginning of the rulebook up to the one you want to precisely place, I guess. “Listed Nth” doesn’t seem like it’d have much application – you’d have to know the contents of the rulebook with enough precision to be able to do those “listed first” declarations anyway.

True, but using a “listed Nth” declaration wouldn’t require relisting potentially half the rulebook.

Another thing you could probably do in that case – given that you know exactly where you want the rule to land – is (if the rule is listed later than you want it to be) to find the first rule after the spot where you want it listed, and list the rule before that rule. It should wind up in the right place.

The problem seems to be with less precise attempts to order the rules; people know they want to list the “think hard about touching the rock” rule before the “kill the player on touching the rock” rule, so they use a “listed before” declaration, but they want the rest of the rule orderings to take care of themselves. But in these cases the “listed before” winds up having unpredictable effects. I imagine this can be particularly worrisome for extension authors, who don’t have access to the ultimate contents of the rulebook.

Another way to think of what actually happens is that when the compiler processes the “listed before” it’s re-placing the rule in the rulebook, and that always breaks ties by placing the rule as late as it can go given the declaration and specificity. (That’s why rules wind up listed in source order, other things being equal.) But the issue here is that 'listed before"s get processed after all rule declarations, no matter where they are in the source. So this:

[code]The Cave is a room. A rock is in the cave.

Instead (this is the useless rule): do nothing.
Instead (this is the really useless rule): do nothing.

The useless rule is listed after the really useless rule in the Instead rulebook.

Instead (this is the quite useless rule): do nothing…[/code]

winds up with the useless rule last, even though its declaration and the “listed before” declaration are both before the declaration of the quite useless rule. Obviously you want to define all the rules before you process the “listed before” declarations, but the effects here are confusing.

Yeah, I checked a dissasembly of Code__Rules__list_add, and that’s a spot-on description of how the code’s written.

One way to guarantee that the foo rule always runs immediately before the bar rule would be to replace the bar rule with one that does the foo things before it calls for the bar things. I mean something like this—

[code]Foo rule:
do this and that;
follow the bar rule.

The foo rule is listed instead of the bar rule in the baz rulebook.
(Of course, I don’t know if rules listed instead of another rule will invariably eventually end up exactly where the original rule would, if the rules are re-ordered once more after such a declaration.)

One issue with that is that we may (often do) want the foo and bar rule to have different headings. So the foo rule may be “Check singing when the player is tired” and the bar rule may be “Check singing when the player is hungry.” Of course it wouldn’t be too hard to reproduce that logic given the above framework but then we have certainly lost the usual convenience of rule ordering.

On the one hand, “listed Nth” is a direct generalization of “listed first”, so it wouldn’t be much harder to implement. (You’d need to decide what happens if you say a rule is “listed third” when the rulebook has exactly two rules.)

On the other hand, being a generalization, it would be subject to exactly the same flaws as “listed first”. Only more confusing.

If the Standard Rules used “listed first”, “listed second”, etc then we’d be right back where we started. Two rules would wind up “listed second”, the compiler would pick a precedence… this is what we’re doing now if two rules are “listed first”.

The idea more or less works if extensions (including the Standard Rules) were forbidden from using the construct. (The game code, being interpreted last – not the same as “last” – would have a well-defined ordering outcome from the Standard Rules plus extensions, and could apply “listed second” in the way you’re thinking of.)

I think this is too arcane and special-casey to rely on, however.