Best Practices

tbritton
29 Oct 2013, 18:20
I'm interested in the best practices in regards to performance when coding a game. Here's a couple I've come across.

1. Make items invisible rather than destroying them.
2. Don't use GetBoolean

Also is there any advantage in using the text processor for simple if's rather than using a script (room description, for example)? Is case/switch better than if/else? Is firstime or just setting a flag more effecient. Flags vs attributes?

Any other input is appreciated.

jaynabonne
30 Oct 2013, 00:57
So then have you seen a performance problem with GetBoolean?

Liam315
30 Oct 2013, 01:45
I would say that creating a "recycle bin" style room and moving objects to it is better than making objects invisible, just because you're removing them from active game areas and making sure there's absolutely no chance they interfere with any functions.

Using boolean attributes is far superior to flags for two reasons:
First is that it's easier to check for an attribute name if you forget what it is and need to reference it later on. Setting flags means you have to comb the code searching for what you wrote, but with attributes you can just go to the tab and check.
Second is that it's much simpler to refer to when writing, e.g. if (object.attribute) is quick and easy as opposed to all the faffing with flags.

FirstTime is more efficient than setting flags if the first instance will always happen.

There's no advantage in using the text processor for simple ifs, other than it looks a bit tidier on the screen, and is perhaps a bit quicker than lining up a bunch of print message (no line break) scripts.

A switch is advantageous when you have a large number of else if statements. Especially if you use the GUI rather than code view. If you have 4 or more cases in total I would tend to go with a switch.

Here's a couple I'd wish I'd known when starting.
Give all objects a systematic name, use an alias to label them how you'd like to appear in the actual game.
Never duplicate scripts. Either create a function or use the do or invoke scripts to reference the original. This way future editing becomes much, much simpler.

HegemonKhan
30 Oct 2013, 02:39
I'm not quite clear on what the difference is between flags and booleans. If you wouldn't mind explaining this difference to me, I'd be appreciate.

Taking a guess, by "flags", you mean using the "=true~false", and by booleans, you use: GetBoolean(Object.Attribute ~ no Value is a shortened and understood by quest engine as meaning that it is "=true") instead, is my guess even somewhat on the mark, or no?

Liam315
30 Oct 2013, 05:56
They are essentially the same thing. A flag is a boolean attribute, the difference being that it is only "created" as far as the game is concerned when it is first mentioned in the code, whereas a boolean attribute is a permanent feature of the object.

As an example, I have objectA, I create a custom attribute called turnedon and set it to false. At some point in the game I write some script that sets it to true. It doesn't matter at what point in the game that happens, objectA.turnedon will always exist as a true or false statement, and I can reference it in other scripts in that way.

A flag on the other hand does not exist from the start. At some point in the game I write a script that sets a flag called "turnedon" on objectA, but until that script runs the turnedon attribute doesn't exist, and if I try to reference it before it is set I will get an error. (I should point out this doesn't really happen in practice as you would use the "if object has flag" i.e. GetBoolean(objectA,"turnedon") condition.)

GetBoolean is what is used when there is a possibility of a non-boolean result (e.g. if a flag doesn't exist), thus it is the function used in conjunction with flags. It is unnecessary in cases where you have made a boolean attribute, as it always exists and is already true or false by definition. objectA.turnedon will evaluate as true or false without sticking the GetBoolean in front of it.

There are no real practical differences when the game is being played that I am aware of, it is more about your own convenience when writing. An attribute that exists permanently is both easier to check for if you forget what you called it, and it makes for neater looking and easier to read code when scanning through what you've written.

I feel like I've rambled a bit and repeated myself without actually making anything any clearer, but hopefully this explains it.

HegemonKhan
30 Oct 2013, 06:33
I'm still not quite clear... my apologies

are "flags" created in scripts? ... off-hand I don't remember there being a "CreateAttribute", only a "CreateObject" (my memory is bad, there's neither in label, only the "create" label, though it does seem to Create an Object, at least):

http://quest5.net/wiki/Create

------

how do you create an attribute (Object.Attribute ~ permanent-global attribute) within a script? Don't you have to have the actual Attribute existing (attached to some Object) ???

does the Set ( http://quest5.net/wiki/Set ), SetObjectFlagOn ( http://quest5.net/wiki/SetObjectFlagOn ), SetObjectFlagOff ( http://quest5.net/wiki/SetObjectFlagOff ), and~or Setting Variables ( http://quest5.net/wiki/Setting_variables ), create (it'll actually exist ~ once the script is run, of course) an Attribute on an Object?

------

you're not talking about "Variables", right?:

<fight type="script">
-> you_go_first=false // Variable? = no attachment to an Object, only exists locally ?~ for this script and it's parameter-nested scripts ?
-> if (player.speed > orc.speed) {
->-> you_go_first=true
-> }
-> if (you_go_first=true) {
->-> msg ("you attack the orc")
->-> orc.hp = orc.hp - player.damage
-> } else {
->-> msg ("the orc attacks you")
->-> player.hp = player.hp - orc.damage
-> }
</fight>

jaynabonne
30 Oct 2013, 09:56
My take on this:

First, I would *not* create a distinction between "boolean" and "flag". It's confusing enough that Quest uses two names for the same thing (I can guess why it evolved that way, but it's unfortunate). Adding a semantic distinction between two things that are actually the same is only going to confuse things even further.

So, "flag" equals "boolean attribute", exactly. No difference. The APIs use the word "flag" to be more non-techie friendly, but that's all it is, a difference in a term. There is no semantic difference, and (in my opinion) it's dangerous and confusing to assign one.

Ok, terminology out of the way... what are we really talking about here? Basically: do I pre-define my attributes (of any kind) or not? And the answer, as with all things, is "it depends".

I think for the purposes that most people use attributes for, it's better to define them up front. They are part of the "static type" of the object (to the extent that Quest has that). There is also a *big* gain, which is that using functions like GetBoolean, GetInt, etc. opens you up to untold grief with typos. If I get the name of an attribute wrong when I reference it directly, Quest will tell me right away. If I get the name wrong in "GetBoolean" without realizing it, then I'm probably going to lose a good chunk of my hair trying to figure out why things aren't working, and I'll really hate myself when I figure out why.

So I would say: only use “on-the-fly” attributes when it makes sense to from a design point of view.

(An aside: Performance? I'd be really interested to see the code where GetBoolean over direct access makes a noticeable difference in the performance of some code. (And please, don't contrive one: I know it's possible. I'm just saying, it's of such minor concern, that I wouldn't even go there.) Per Knuth: "Programmers waste enormous amounts of time thinking about, or worrying about, the speed of *noncritical* parts of their programs, and these attempts at efficiency actually have a strong negative impact when debugging and maintenance are considered. We should forget about small efficiencies, say about 97% of the time: premature optimization is the root of all evil." Or more to the point: only optimize after measuring. If you have a problem, then optimize. Otherwise, don't worry about it. Even something like "first time" vs setting and checking flags - we're probably talking about milliseconds difference here. Focus on what makes sense to you from a code style point of view, leave efficiency for those rare times where it actually makes a difference, and be at peace. ;) )

To go the other way, when does it make sense to not pre-define attributes, to define them “on-the-fly”? That is a design question, and as such, there are no hard-and-fast rules. Given the above problem with the Get* APIs regarding typos, I'd say you'd want to have good reason to not. But there are good reasons.

As an example: in my “response library”, I use a good number of on-the-fly attributes within the response objects. And the reason why is that I'm trying to make the code easier for people to use. There could potentially be so many response objects that I wanted to keep them as simple as possible, with a minimal footprint. All those attributes I'd be using were *internal bookkeeping* for the object, not a part of the fundamental definition of a response object. I wanted people to focus on defining the parts of the objects that needed it, not worry about adding in all the internal attributes the library needed. (That can be overcome with a type, to inject the attributes into the object, but I didn't even want to clutter up an object with that. I wanted the objects to be purely data.)

So I can think of several reasons to define attributes as needed:
1) They are not a fundamental part of the object but are used only in special case situations.
2) They correspond to “hidden” internal state which is best kept private. If I had said to those using my response objects, “You need to define these attributes in your objects” and then with a later rev, I found I needed more and said, “Now you have to go to each of your hundreds of objects and all these additional ones in, not for your use but for mine”, then people would have found out where I lived and hunted me down.
3) You are operating on a heterogeneous set of objects with no common base type. Quest is similar to JavaScript in that it supports “duck typing” (“if it walks like a duck and talks like a duck...”) where objects don't really have types but are measured by the attributes they possess. On-the-fly attributes allow you to "make ducks" dynamically. Let's say I want to create some code to operate across all Quest objects, regardless of what they are (room, player, apple, sword). It would be very inconvenient to a user of my code if I said, “For every object you pass to this function, you need to make sure these attributes are defined.” It's just too cumbersome, and it exposes users of my code to the internal workings. It's much more preferable to simply create the attributes on-the-fly as needed. It allows the code to inject its own personal state into the object without the user of the code needing to be aware of what is being done. It allows for encapsulation and data hiding, both good things in object-oriented design.
4) It could also be stylistic. I can imagine some wanting minimal objects, with attributes appearing only as needed. Why take up a slot for an attribute that's not needed? That really comes down to design and style. (But given the grief you can have with the Get* methods and typos, you'd have to really be dedicated to that approach.)

I hope I didn't waffle on too much. The short version is: you generally want to pre-define your attributes, of all kinds. It makes for a saner world in the common cases. And there is good company for that: there are languages like C, C++, Pascal, Ada, etc where you *must* define attributes in the static definition of an object.

But there are also really valid cases where you want to define attributes on-the-fly. As with many things, it all comes down to design.

(A minor note: GetBoolean returns false if the attribute doesn't exist, not null. I wish the other “Get” functions also returned reasonable defaults instead of null. It would make life so much easier.)

Alex
30 Oct 2013, 17:39
jaynabonne wrote:(A minor note: GetBoolean returns false if the attribute doesn't exist, not null. I wish the other “Get” functions also returned reasonable defaults instead of null. It would make life so much easier.)


You could define your own GetIntOrDefault, GetStringOrDefault functions easily enough (and I'll happily accept a pull request for these if you think they should be part of Core).

tbritton
30 Oct 2013, 17:46
Thanks for the input guys, that helped clarify things a lot.

jaynabonne
30 Oct 2013, 18:53
Alex wrote:

"jaynabonne"

(A minor note: GetBoolean returns false if the attribute doesn't exist, not null. I wish the other “Get” functions also returned reasonable defaults instead of null. It would make life so much easier.)



You could define your own GetIntOrDefault, GetStringOrDefault functions easily enough (and I'll happily accept a pull request for these if you think they should be part of Core).



Sounds like a good idea. I'll think about that. :) (As they say, put my money where my mouth is...)

Liam315
01 Nov 2013, 06:27
jaynabonne wrote:A minor note: GetBoolean returns false if the attribute doesn't exist, not null.


Thanks for that, reading back I realised what I wrote wasn't very clear, I've corrected that point in my post. Although for anyone else reading, Jay's summary is on the money and it's well worth your time reading through and making sure you understand the distinctions.