Advertisement

Ordering system question

Started by September 27, 2002 11:09 AM
26 comments, last by Dauntless 22 years, 3 months ago
quote: Original post by Dauntless
so, once I figure out how to implement the order object...and I''m still not sure if I should take Doolwind''s multiple order approach, or your templated approach (since templates are still real fuzzy to me), I''ll have to figure out how to carry the orders to the officer and unit objects.


Dauntless-
If you require multiple messages then I would steer clear of the style I have given. I was talking from a much simpler RTS style where you can give only one order per unit at a time. If this still fits in with what you are planning then perhaps my way would be simpler from a design perspective, but as I was thinking in terms of my own simple C&C clone it is not very scalable. Perhaps a hybrid of what Oluseyi and I have been saying .

For multiple orders (and a more scalable system) I would stick with each unit having a vector of Order''s (which in turn would have their correpsonding actions). This way you can issue as many orders as you like and the unit can still take them on a per-order or even per-action basis and reject or accept them.

To do this you would need to have all of your order objects inheriting from a base "Order" object. This way your vector can contain any type of derirved order object (move, attack, etc).

When a communication token comes in it may have an order or orders imbedded in it and these can then be put into the units order vector.

Doolwind
Consider also that a heap could be very useful for prioritized orders.

If you know how to implement an Array-based Heap Dauntless, you could use that as a very elegant way of dealing with units interpretation of orders.

When a unit recieves its compound order from on high, have it assign internal priority values to the list of orders and then perform a heap-build on the list (always faster than sorting) and subsequently use the heap to read off orders that need doing.

Then when that unit comes under fire, orders like "take cover" will gain priority over other orders, also orders like "infiltrate enemy bunker" may loose priority. These priorities can be made very flexible, and again reheapifying an array is very cheap after a priority change.

Oluseyi, I don't think I understand why on earth you would mention templates. Any type of order (to the best of my reckoning) could be done as an enumerator+data class:

class Order
{
public:
enum {Move, Kill, Maim, Death, Destroy, ... } Type;
union unData{int i, float f} Data[3]; // Data size variable
}

Then when it comes time for a unit to execute an order, it can use a predefined (static) array of function pointers to its own action functions, where the offset in the array is the type of order. Then each of those functions take a Order::unData type object as an argument and change the state of the object accordingly.

For the benefit of the general public here is an example of a function pointer array...

// C++ code

//////////
// Stuff.h

#ifndef STUFF.H
#define STUFF.H

// #include's go here


class Order
{
public:
enum {Move, Kill, Maim, Death, Destroy, ... } Type;
union unData{int i, float f} Data[3]; // Data size variable
}

class Dude
{
private:
// Stuff
public:
// More stuff

Order& GetFirstOrder();
int SendOrderError(Order);

int DoMove(Order);
int DoKill(Order);
// Imagine that a unit of type Dude cannot perform a DoMaim action...
int DoDeath(Order);
int DoDestroy(Order);
// etc.

static int (*FuncList[8])(Order);

void DoOrder() {FuncList[int(GetFirstOrder().type)](GetFirstOrder());};
};

#ifdef STUFF.CPP
static int Dude:: (*FuncList[8])(Order::unData, Dude*) = {&Dude::DoMove, &Dude::DoKill, &Dude::SendOrderError, &Dude::DoDeath, etc...};
// the reason I put this line in this file is that it is easier to collate the function names to what's in the declaration of the class (saves time from jumping between files)
#endif

#endif

////////////
// Stuff.cpp
#define STUFF_CPP
#include "Stuff.h"

// implementation goes here


Of course, there's no need to put all unit types in the same source files, just make a little Order.h that defines what an order is and you're set.

I've used an exact copy of the code above (with type/object name changes) and it works exactly as expected. (however, I didn't do a copy-paste into this post so PLEASE check my code for errors and let me know so I can fix it, because function pointers can be very hard to use and I should not be misleading anyone who is learning from my example)

George D. Filiotis
Are you in support of the ban of Dihydrogen Monoxide? You should be!

[edited by - symphonic on September 30, 2002 10:44:03 PM]
Geordi
George D. Filiotis
Advertisement
quote: Original post by Symphonic
Oluseyi, I don''t think I understand why on earth you would mention templates. Any type of order (to the best of my reckoning) could be done as an enumerator+data class

Forgive me, but I dislike enums. In fact, I dislike any method that may involve severe reworking (just a general observation; not related to your post), which is why I wrote the version I did - extensibility.

Of course, I could write a data-driven object factory method as well... Then I''d be scriptable too! (Stop me; I''m getting out of hand).
Symphonic-
Since I don''t fully understand templates and vectors yet (I''m getting a rough idea, but I''m still having trouble seeing exactly how to implement them), what I was at first was thinking of doing was doing almost exactly as you had pointed out.

But then I started thinking I would need to create a base abstract Order class. From this I was going to derive more explicit and concrete Order types. And like in your examples, you dereference the pointer within the order to call the function on the object in question (or maybe call a function within the Order object that returned a pointer to the unit''s function....thereby calling that function) .

My main problem with this idea was how to create aggregrates of orders AND to assign them priorities or overrides. I suppose I could have made some sort of internal ID number that assigned a priority value, but somehow that didn''t feel right.

I''ve been slacking about reading up on templates and vectors, but since I have tomorrow off, hopefully I can get a little studying in...Iv''e been posting here and doodling on my sketch pad too much

Doolwind-
Why wouldn''t derivatives of a abstract Order class be sufficient to create a chain of orders? Like I mentioned above, I do see some problems with this, but couldn''t I create a linked list and create some kind of sorting algorithm that determined the priority of the order? It seems kind of kludgy, but I''m no expert
The world has achieved brilliance without wisdom, power without conscience. Ours is a world of nuclear giants and ethical infants. We know more about war than we know about peace, more about killing than we know about living. We have grasped the mystery of the atom and rejected the Sermon on the Mount." - General Omar Bradley
Dauntless-

Vectors are easy, you should be able to pick them up in an hour or so, they are basically funky arrays. I am at work at the moment, so I don''t want to start giving you any code on how to use vectors (incase i get bits wrong), but if anyone else would like to...otherwise when I get home I''ll give you a quick example.

Anyways....A linked list would work just as well as a vector I guess, it just seems a lot easier for you to use vectors, the initial 30 mins to learn to use them will far outway the time it would take to do all the stuffing around with linked lists.

Do you want me to give you a crash course in vectors when I get home??

quote:
Why wouldn''t derivatives of a abstract Order class be sufficient to create a chain of orders


What do you mean by that exactly?? Do you mean the attack order would be a child of the move object?? I would certainly steer away from this if that is what you mean.

The way I was thinking is you have the base order class which has move, attack etc as its subclasses. If you issue an attack order then move order the units Orders vector would have two orders in it, attack and move. Which you could process one by one, and perhaps when you put them into the vector you could order them based on priority.

Doolwind
Dauntless, Like I said in my post, function pointers can be very messy, but remember that your compiler is going to inline the Order::GetType() function so it looks like "Order.Type". The reason I use a member function to do it is that you may want to make Type and Data private.

What this is supposed to look like is that looking at the type of order, the unit looks up how to interpret that order, and performs it. In the code: use Order.Type to dereference the array of function pointers, and call the resulting function (which is a member-function of the unit-class) with the order as its arguement.

If you were to use a large function which just has a switch(){} statement in it, you would get the same effect with a slight overhead cost.

Oluseyi:
Sorry, I get it now, I assumed that you meant Dauntless should write his own templates, but using the STL is a very good idea (better than implementing your own dynamic arrays by far). Why don''t you like enums Oluseyi? did they do something to you as a child? is there something you''d like to share with us? We''re all here for you...

On the issue of maintenance and reworkability:
Though function pointers are considered realitively low maintenance (highly expandable) code alteration for insertion of new features is still difficult, the nature of function pointers makes it very easy to mess up and subsequent bugs are almost impossible to trace and debug.

So care should be taken to develop your ideas before hand (you''ll never be completely decided before you start, but be close) so that the necessary features can be accounted for within development.

The expandability of function pointers (as I used them) is evident in the example of wanting to add a new order. Add an item to the enum type, create interpreting functions for all the units that will use that order. update the function pointers of all units that may recieve this order (wether they will want to do it or not). While tedious, this method is sound.

George D. Filiotis
Are you in support of the ban of Dihydrogen Monoxide? You should be!
Geordi
George D. Filiotis
Advertisement
quote: Original post by Symphonic
Why don''t you like enums Oluseyi? did they do something to you as a child? is there something you''d like to share with us? We''re all here for you...

*sniff*

Well... When I was just learning to program, an enum came up to me and showed me its... its... oh, I can''t!

*ahem*
Seriously, though, the use of enums seems to indicate type-specific processing. If the problem in question is either sufficiently large or involves data reinterpretation I usually feel that classes and inheritance leave me with a more robust and intentional solution. There''s also the question of extensibility, because - even though I know that Brooks law which goes "Plan to throw one away. You will anyhow." - I hate having to reengineer massive amounts of code. If I implement an enumeration-based selection of process, then the addition of new tokens means having to extend/maintain/fix all code that references those values. If, OTOH, I implement a template- or inheritance-based solution, extension is transparent because the original code was type agnostic anyway.

To complete my childhood story, an enum showed me its fundamental redundancy. An enum is only syntactic sugar for magic numbers (good sugar, but sugar nonetheless). Of course, if I am appropriately refuted I will be the first to capitulate and will even Use Enums Where They Are Not Needed (*shudder*) as punishment.
No your arguement is quite sound and I accept. It is only my own short-coming as a programmer which prevents me from following your example because I''m still in the process of learning how to fully use inheritance and polymorphism.

However, for the application I just provided (function pointer arrays) enums are a very useful tool for the marginally forgetful programmer. Because remembering what a certain offset means is easier with a suitable mnemonic device. Plus, enums are good practice if space is a consideration, and it is important to consider their use if your code is likely to be read by many other programmers who don''t have time to spend figuring out what you do with integers.

A fair trade, in my opinion.

George D. Filiotis
Are you in support of the ban of Dihydrogen Monoxide? You should be!
Geordi
George D. Filiotis

This topic is closed to new replies.

Advertisement