Home World Forum
Stars! AutoHost web forums

Jump to Stars! AutoHost


 
 
Home » Stars! Clones, Extensions, Modding » FreeStars » Design Delimma
Design Delimma Fri, 13 June 2003 15:26 Go to next message
LEit is currently offline LEit

 
Lt. Commander

Messages: 879
Registered: April 2003
Location: CT
I've got an OO design delimma, if anyone can help, I'd appreciate it.

I'm trying to design a Ship class that is composed of a collection of Components.
Each component can be of a certain type (armor, shield, weapon, and several others). Some components can have capabilities of more then one type (armor with some shielding). So far it's pretty easy, use inheritance for the types, and multiple inheritance for the multiple type components.

The complication is that I'd like to be able to read a file of components:
Croby Sharmor, type shield, (generic details, cost, mass, etc), shield strength 65, armor strength 60.

Most shields don't have armor, this one is special. There are some components that have 5 or more sub capabilities, and I'd even like to leave that open, so defining a class for each possible combination is impractical. I want to read a file and make a list of components from that file.

Two options I've considered are:
Make Component class generic enough to handle every combination (put shield and armor and all other variables into it) and have it return some default value if a particular component isn't defined.

The other idea is have Component have a list of Capabilities, and you add to the list as needed. Then you can ask a Component to give you the Armor Capability. This is what I'm currently leaning toward, I feel it's a better design, however, it'll be slightly harder to code.


[Updated on: Thu, 19 June 2003 11:20]




- LEit

Report message to a moderator

Re: Design Delima Fri, 13 June 2003 20:47 Go to previous messageGo to next message
gible

 
Commander

Messages: 1343
Registered: November 2002
Location: Wellington, New Zealand

A third option is to create the required component class on the fly using multiple inheritance to get the various quialities. Of course I'm a java programmer and I have no idea whether you can do that in C++.

Failing that I'd use the second choice...and create an accessor method to access the list contents.

Report message to a moderator

Re: Design Delima Mon, 16 June 2003 20:42 Go to previous messageGo to next message
boneandrew is currently offline boneandrew

 
Crewman 1st Class

Messages: 35
Registered: June 2003
Location: Detroit
I'm a bit rusty with my coding skills right now, but as I understand it, Java is basically C++ with direct control over pointers removed. And actually, I think dealing with classes/inheritance issues is easier with Java. But yes, any of this stuff is possible with C++.

There are quite a few ways of handling this, actually. Another option would be to define each component as an object that directly adjusts the ship's capabilities. For example, you define the Croby Sharmor as an object that will only fit into shield slots (or shield/elec/mech, whatever), and whenever Stars! accesses the ship, it calls each object and the Croby Sharmor adds the 65 shields and 60 armor per object. (Such objects actually contain executionable code, not just data.) No other condition checking needed. You'd need to define each component, but this will make it VERY easy to design those MT trader parts and other components that have multiple abilities (for example, pen scanners).

Or, if you're really worried about saving memory space, do a condition check on the type of component in each slot and call an appropriate procedure that similarly adds the required armor/shields/whatever. This'll take up less memory space as you're not storing separate exectuionable code in active memory for each component for each ship, but more coding as you'll need to define all the condition checks and stuff. And it'll probably be a little slower.

Anyway, some ideas that I hope help.


[Updated on: Mon, 16 June 2003 20:43]

Report message to a moderator

Re: Design Delima Mon, 16 June 2003 21:35 Go to previous messageGo to next message
LEit is currently offline LEit

 
Lt. Commander

Messages: 879
Registered: April 2003
Location: CT
You cannot dynamicly create a class in C++, I think you can in Java.

I could create a ShieldArmorComponent class, but I want to make it extendable, so I'd really need to anticipate what some one might want to add. For example a shield and jammer combination.

The possible combinations are pretty large, so making that many classes would be impractical.

After some discussion on IRC, I'll probably go with the generic class that can handle all types. It'll be easier to code, and still do what I want. I won't start coding till tomorrow, so if you've got a better idea, let me know soon.



- LEit

Report message to a moderator

icon5.gif  Re: Design Delima Mon, 16 June 2003 21:58 Go to previous messageGo to next message
John Marasco is currently offline John Marasco

 
Crewman 3rd Class

Messages: 5
Registered: June 2003
If it's not too late for suggestions you could define a <component> class that contains an array of <sub component> classes. The <sub component> classes are based on a virtual <basic component> class that provide methods like IsShield, IsArmor, IsComputer, PrimaryValue, SecondaryValue, etc... The <component> class will contain a function to parse the array and return pertinent parameters from the various <sub component> classes. Disk storage could be via XML, which would mirror the in memory structure. The <component> class could even have generic GetArmor, GetShield, GetComputer, GetJamming, GetManeuverJet, etc... methods built into it to parse and find the basic <sub component> variables and then more generic methods for future <sub component> types. You could build all this functionality into the <component> class (like you suggest) which is the equivalent of the multi-inheritance component class. The trouble is that you cannot strip off individual component objects and pass them to other functions unless you actually have "sub components" to strip off.

Report message to a moderator

Re: Design Delima Mon, 16 June 2003 23:28 Go to previous messageGo to next message
LEit is currently offline LEit

 
Lt. Commander

Messages: 879
Registered: April 2003
Location: CT
Not too late yet.

That sounds a lot like my second option in my first note (where my Capability is your subcomponent). Component contains a list of Capabilities, they inherit from a virtual class (an ABC really). Component would have a GetArmor function that would return the Capability class if it was in the container.

However, I don't really need to pass a Capability class to anything else, to figure out a ship's total shields, just add the shield value of each component.

That can be done either by getting a Capability from the Component and adding the value from that, or by asking the Component directly (which either has all values stored - first option above, or searches the list for the right Capability - second option).

Capabilities are usually one or two integers, so having a bunch of them set to a default shouldn't be too bad.



- LEit

Report message to a moderator

Re: Design Delima Tue, 17 June 2003 20:16 Go to previous messageGo to next message
John Marasco is currently offline John Marasco

 
Crewman 3rd Class

Messages: 5
Registered: June 2003
Oops, I see now where you had already mentioned this option. Sad

One of the nice things about using an array of Capability classes is that you can then pass the capabilities up to various ship-sub systems to calculate composite values (as opposed to simple values like armor and/or shields).

cloaking is cloaking capability combined with ship mass calculated by the cloaking ship sub system class.

accuracy is computer capability combined with torpedo/missile accuracy calculated by the targeting ship sub system class.

maneuverability is the maneuvering jet capability combined with the engine capability combined with ship mass and can be calculated by the maneuver ship sub system class.

etc...

The ship class is then a container class (hull) that accesses various sub-systems stored in an array and passes component capabilities classes to sub-system classes to calculate ship parameters (like cloaking, targeting, maneuverability, etc...).

You could expand a specific capability class and/or add new capabilities to support new ship sub systems in future enhancements without having to hack into unrelated classes.

As with most OO designs, it's more work up front but provides a component based method for expanding the systems.

You could get away with using as few as 4 bits for any Stars! capability and map that 4 bit number to whatever final range is desired in the interface methods. You could do so through a hardcoded map (00 = 25, 01 = 40, 02 = 60, 03 = 100, 04 = 175, 05 = 300, 06 = 500, 07 = 75, 08 = 120 for shields) or you could calculate a non-linear algorithm to give the desired range and variation therein. It saves space but at the expense of limited flexibility to change values. It is probably a pointless exercise given the number of capabilities stored in RAM during a game (not many) and how cheap a gig of RAM is these days. Crying or Very Sad If you had any thoughts of someday expanding the game to extreme sizes such techniques applied today could pay off later when trying to expand the game to handle 100s (1000s) of players.

If the source for this project is public? I wouldn't mind following along if it's allowed...

These are approaches you may have already considered and rejected. If so then excuse the ramble.

Report message to a moderator

Re: Design Delima Wed, 18 June 2003 13:09 Go to previous messageGo to next message
LEit is currently offline LEit

 
Lt. Commander

Messages: 879
Registered: April 2003
Location: CT
In reverse order:

The source will be public, but I don't yet have it publicly accessable. It's no where near complete, and is changing daily. If you still want it I can send it to you.

Compression is NOT a goal, I'll be using integeres to represent most of the data, maybe a floating point value here or there. Expanding to handle massive numbers is also not a goal. If some one wants to do that, and the code doesn't collapse based on the data size, that's fine, but I'm not going to worry about it.

I've currently made Components be a monolithic class that has every capability stored in it, it's about 50 numbers and 5 lists of PRTs/LRTs/Hulls that are disallowed/allowed (Component can (cannot) be used by this PRT/LRT or on this hull). One of the lists is not stricly needed (I've got both allowed and denied PRTs) but it should make defining and coding them easier.

I don't undertand how passing a Capability instead of a Component makes things any easier (or harder). The Ship class will still have to extract the information from the Capability or Component and do calculations with it. Cloaking, for example, is highly dependant on what other components are on the ship and not just the mass, that calculation seems to go naturally into the Ship class.



- LEit

Report message to a moderator

Re: Design Delima Wed, 18 June 2003 19:57 Go to previous messageGo to next message
John Marasco is currently offline John Marasco

 
Crewman 3rd Class

Messages: 5
Registered: June 2003
Please forgive this answer if it seems belittling. I don't know how much OO theory you have. I have a little...

OO design is about interface segregation. Every time you change a class's public parameters or methods you change the interface (VB handles this in a very "in your face" method via Binary Compatibility). When you change the interface of a class you need to ensure that every class using that class reflects that change. By making a single ship class and a single capability class every time you change the capability class interface you need to check the entire ship class for compatibility with the interface change. If you design the interface up front and never change it then no problem. If you are the only one working on the code then the problem is usually nonexistent because of your intimate knowledge of the entire code set. If you are looking at the development processes as a single developer writing code alone then more objects will not appear to offer any benefits. But if you look at the effort of maintaining code over time (in a year or two you may loose some of that intimate knowledge) or working with multiple developers then more objects have more obvious benefits.

On that note, I would put the build rules in a separate class that assembles capability classes into ship classes. KISS - Keep It Simple Stupid is an engineering principle that fits nicely into the OO design methodology. No need to haul build rules around with the ship class when they really only need apply when instantiating capability classes into the ship class.

I hope these comments are taken as suggestions only. If you want me to stop please say so. I'll try to tailor my comments to fit the scope/vision better.

I'll shut up now, yes? Nod


[Updated on: Wed, 18 June 2003 20:23]

Report message to a moderator

Re: Design Delimma Thu, 26 June 2003 00:59 Go to previous messageGo to next message
JeffMC is currently offline JeffMC

 
Petty Officer 1st Class
Stars! V.I.P


Messages: 66
Registered: March 2003
This was a problem we spent a lot of time thinking about for Supernova. We eventually settled on defining a list of possible behaviors (capabilities) that any techonology might have. The capabilities do not have to have a one-to-one mapping with the properties that you show the players. For example, a Stars! 2 style engine technology would probably want to override the default values for the following capabilities:

Maximum Free Warp Speed
Fuel Usage at Each Warp Speed
Is Radiating?
Is Ramscoop?
Combat Speed (Movement)
and so on...

This model removes any assumptions about the capabilities of any particular technology or category of technology. If you want a bomb part that is radioactive and forces you to put your colonists/troops into a different fleet then just mark the technology as Is Radiating. When you ask the Fleet if it "Is Radiating" it will ask each of its parts the same question....

Naturally, this model is far more powerful when combined with a rules scripting language like RDL. However, even without a rules scripting language you can save yourself a lot of grief by defining your base behaivors (capabilities) ahead of time. Define the capabilities that you will want to implement in the future so that the question exists even if the answer is always NO or zero for the current game engine.

Even if you don't implement a rules scripting language you'll need to define the additive nature of each capability. Think about how scanner ranges add together in Stars! 2 v/s cloaking v/s armor.

In general, defining a new technology or changing the capabilities of an existing technology should involve changing data only. This was not true in the Stars! 2 sources which was a huge pain in the rear.

Jeff

Report message to a moderator

Re: Design Delimma Fri, 26 March 2004 07:07 Go to previous messageGo to next message
Ptolemy is currently offline Ptolemy

 
Commander

Messages: 1008
Registered: September 2003
Location: Finland

If you use a generic component class that contains all potential variables, all that would be needed is to return null for any variable that the component doesn't have. There isn't any reason that you couldn't have an engine with a shield component for example.

A component type needs to be used to define what type of slot the component occupies and the class contains all variables that any component could have - only one class is then needed.

Ptolemy





Though we often ask how and why, we must also do to get the answers to the questions.

Report message to a moderator

Re: Design Delimma Fri, 26 March 2004 13:09 Go to previous messageGo to next message
LEit is currently offline LEit

 
Lt. Commander

Messages: 879
Registered: April 2003
Location: CT
That's what happends in either case, the question was one of how to store it. What we have now is a class that variables for everything. Hulls are a derived class, so regualr components cannot carry other components, but a hull can have all the abilities of any other component.


- LEit

Report message to a moderator

Re: Design Delimma Fri, 26 March 2004 14:21 Go to previous messageGo to next message
Ptolemy is currently offline Ptolemy

 
Commander

Messages: 1008
Registered: September 2003
Location: Finland

Well, personally, I'd just store it in a dynamic array of structures.

Ptolemy


[Updated on: Fri, 26 March 2004 14:22]





Though we often ask how and why, we must also do to get the answers to the questions.

Report message to a moderator

Re: Design Delimma Fri, 09 April 2004 13:42 Go to previous messageGo to next message
multilis is currently offline multilis

 
Lt. Commander

Messages: 789
Registered: October 2003
Location: Edmonton, Canada
In my opinion you want to do it as a relational database (multiple seperate tables). The actual database can be xml or some other simple text format and loads like a spreadsheet into whatever structure your server feels like using.

In real application, Id field would likely be larger than 3 digits to make more room for expansion.


So for example you might have 3 tables:

Table language
Id, Code*, Name
301, SmtB, Smart Bomb
302, CrbS, Crobby Shield
501, Bomb, Bomb
502, Shld, Shield
503, GenP, General Purpose slot
801, AShi, Add Shields
802, AShN, Add Shield with no gain from RS
803, AArm, Add Armour
804, AArN, Add Armour with no loss to RS
805, ASma, Add Smart Bomb

*code field is for making modifications easier in the long term. One day translator programs could convert Id and Code back and forth in other tables if they prefer a code in their language rather than remembering an id. Number ranges would be clearly defined including space for public expansion and end user private addins.


Table SlotType
Id, Slot
301, 501
302, 502
302, 503


Table Property
Id , Property, Value
301, 805, 15
302, 801, 60
302, 804, 65


As an added note: I feel relational design is more flexible than object oriented, it is easier to change your perspective of what is on top... the base object.

For example sometimes you may care about all the properties that are members of a ship component such as crobby shield. Other times you may want to go through all components that are members of the shield property. Using pointers and hash collections to link, you can squeeze out amazing performance as well in a memory based database.


[Updated on: Fri, 09 April 2004 13:54]

Report message to a moderator

Re: Design Delimma Sat, 10 April 2004 02:00 Go to previous messageGo to next message
Ptolemy is currently offline Ptolemy

 
Commander

Messages: 1008
Registered: September 2003
Location: Finland

We don't need the complexity of a relational database - there simply isn't enough stuff to require it. Look at Stars! now in a maxed out testbed and you don't have all that many components. Also, components are grouped based on type (scanners, egines etc.) An indexed array of structures containing the componet variables works fine. A base identifier is assigned to each component family (i.e. 0 = engine and the first entry (0) is the QJ5 engine).

#define Engine 0

now all I need to do is get the array entry for the Fuelmizer (for exapmle) which is engine 3 so I go get array entry 2 for the engine. It's lightning quick and efficient.

The actual initial values for the components can be stored in a separate file initially so that they can be changed and, that file can be loaded at startup - or, they can all be stored in the .ini file. At some point though, they will need to be encoded into the executables as fixed values so that they can't be changed by players.

Ptolemy


[Updated on: Sat, 10 April 2004 02:01]





Though we often ask how and why, we must also do to get the answers to the questions.

Report message to a moderator

Re: Design Delimma Sat, 10 April 2004 02:07 Go to previous messageGo to next message
Ashlyn is currently offline Ashlyn

 
Lt. Commander

Messages: 834
Registered: November 2002
Location: Pueblo CO USA

its dilemma... Nod

Report message to a moderator

Re: Design Delimma Sat, 10 April 2004 02:10 Go to previous messageGo to next message
Ptolemy is currently offline Ptolemy

 
Commander

Messages: 1008
Registered: September 2003
Location: Finland

Yep Ashlyn - it sure is. I didn't create the thread though. Razz Perhaps you could correct the thread title? Smile



Though we often ask how and why, we must also do to get the answers to the questions.

Report message to a moderator

Re: Design Dilemma Sat, 10 April 2004 02:19 Go to previous messageGo to next message
Ashlyn is currently offline Ashlyn

 
Lt. Commander

Messages: 834
Registered: November 2002
Location: Pueblo CO USA

Ptolemy,

Nah... after I posted, I noticed how old the thread was...

I wasn't correcting you (or anybody else for that matter)... honest Embarassed - just chuckled how many times people spelled it different - so thought i'd chime in with the correct spelling Grin

Wasn't important either - just bugged me.. I don't know why misspelled words bother me, but they do Nod

*we return now to our subject on "design dilemmas"


(my apologies to LEit - for barging in his room)
Edit: fixed title





[Updated on: Sat, 10 April 2004 02:44]

Report message to a moderator

Re: Design Delimma Sat, 10 April 2004 11:15 Go to previous messageGo to next message
multilis is currently offline multilis

 
Lt. Commander

Messages: 789
Registered: October 2003
Location: Edmonton, Canada
Ptolemy wrote on Sat, 10 April 2004 00:00

We don't need the complexity of a relational database



You don't need the 'complexity' of a String class. Simple char* will do. You don't need the complexity of C++, simple assembler will do.

The idea with a relational system is you end up with a simpler rather than more complex design, you don't have to manually setup, link structures, load, etc (which otherwise may get done different ways and then have to go back and figure out how was done). And if you like STL, object oriented, etc., you can layer it on top.

Arrays (such as char* compare to String class) have a way of sometimes leading to stray pointers and crashes after little mistakes.

The reason programs like DBase III and later MS Access became popular for simple jobs was not because people wanted to work harder.

Report message to a moderator

Re: Design Dilemma Sat, 10 April 2004 12:41 Go to previous messageGo to next message
overworked is currently offline overworked

 
Lt. Junior Grade

Messages: 403
Registered: November 2002
Location: Pittsburgh, PA

multilis wrote on Sat, 10 April 2004 11:15

Ptolemy wrote on Sat, 10 April 2004 00:00

We don't need the complexity of a relational database



You don't need the 'complexity' of a String class. Simple char* will do. You don't need the complexity of C++, simple assembler will do.

The idea with a relational system is you end up with a simpler rather than more complex design, you don't have to manually setup, link structures, load, etc (which otherwise may get done different ways and then have to go back and figure out how was done). And if you like STL, object oriented, etc., you can layer it on top.

Arrays (such as char* compare to String class) have a way of sometimes leading to stray pointers and crashes after little mistakes.

The reason programs like DBase III and later MS Access became popular for simple jobs was not because people wanted to work harder.


What relational db product are you suggesting that will work across multiple platforms as a general host?

Or are you suggesting that the initial host be a WINx product only and we'll worry about other operating systems at a later point?

- Kurt


[Updated on: Sat, 10 April 2004 12:42]

Report message to a moderator

Re: Design Dilemma Sat, 10 April 2004 13:38 Go to previous messageGo to next message
multilis is currently offline multilis

 
Lt. Commander

Messages: 789
Registered: October 2003
Location: Edmonton, Canada
Sorry, my answer to Overworked is sort of a ramble, hard to otherwise explain my way of thinking. I present ideas, others may use or discard as they like.


No matter what... NO WINX ONLY! (imo)

Also for all the joys of stuff similar to MS Access, it is a real pain in the butt when using a complex database to have to work around the bugs of others, especially when they add new bugs with each version.

I believe everything for the server should be in non-machine specific c++ to keep life simple. 'Relational' is just a means of organising data, it is possible to do relational with simple collections (groups of objects) with links to other objects.

I normally layer OO code so that everything is idiot resistant from the outside, not possible to have a simple mistake cause a stray pointer or memory leak if following the system. I also now try to keep every reference to an object to one variable, for example printing a name of a component only requires an ID rather than a TYPE and an ID. These ideas are to protect me from my own stupidness. Smile

Code outside an object/system would not do memory allocations with NEW, etc. Instead you work with either global objects or local (on the stack) ones which are really just references to the true objects (and memory management is all automatic and unseen).

...

There are a number of existing open source c++ memory based databases I have been looking at a bit. For my own needs I am mostly trying to build from scratch (simpler, cleaner).

One trick that can be used is to come up with a simple format for database structure that is thrown through a simple c++ translator program that turns it into c++ language objects including both compile time and run time ways to reference fields, eg both t.Id and t("Id") as well as extra objects for linking/indexing/etc.

Loading all the underlying data then becomes a matter of a simple text file describing the structure followed by an extra "#include" and command in your main program to "mydb.load(zPath);" to load the various xml files.


[Updated on: Sat, 10 April 2004 13:39]

Report message to a moderator

Re: Design Dilemma Sun, 11 April 2004 13:15 Go to previous messageGo to next message
Kotk

 
Commander

Messages: 1227
Registered: May 2003
I think there are a bit more levels of abstraction needed for proper OO.

"fleet" consists of "tokens"

"token" is design ID, number of ships, damage level, number of damaged ships

"design" consists of "slots". Slot[0] is hull slot.

"slot" is component ID, number of components

"component" is structure of properties

Now some examples:
You want to know if fleet is radiating. Fleet asks from each token if its radiating, token asks if design is radiating, design asks if any slots are radiating and slots check if the component in them is radiating. Component has radiating property.

You want to know the mass of fleet. Fleet asks the mass of tokens and sums them up, token asks the mass of design and multiplies with number, design asks the mass of slots and sums them up, slot asks the mass of component and multiplies with number. Component has mass property.

Report message to a moderator

Re: Design Delimma Sun, 11 April 2004 14:57 Go to previous messageGo to next message
LEit is currently offline LEit

 
Lt. Commander

Messages: 879
Registered: April 2003
Location: CT
LEit wrote on Fri, 13 June 2003 15:26

Make Component class generic enough to handle every combination (put shield and armor and all other variables into it) and have it return some default value if a particular component isn't defined.


This is what the code has now. They are in an xml file, that is read into an array (deque really).

Kotk described what we have very closly. I'm pretty sure some of the names are different. And slot[0] isn't the hull, a Ship has a Hull and a array (deque) of Slots.

And then the properties know how to aggrigate themselves over a collection (for example: mass is sum over all, scanners are max of some complex forumla, etc).



- LEit

Report message to a moderator

Re: Design Delimma Mon, 12 April 2004 06:29 Go to previous messageGo to next message
Kotk

 
Commander

Messages: 1227
Registered: May 2003
LEit wrote on Sun, 11 April 2004 20:57

Kotk described what we have very closly. I'm pretty sure some of the names are different. And slot[0] isn't the hull, a Ship has a Hull and a array (deque) of Slots.


Okay so "Ship" instead of "design".
Why using deque as slot array instead of vector? push_front/pop_front needed with slots? Just a matter of taste of course ... deque is not significally slower than vector.

I am not sure if the "property" should be a class of its own or just a member variable of component. I have feeling that as member variable there will be less searching from these containers (even if you use map there) and so it will be multiple times quicker.

Report message to a moderator

Re: Design Delimma Tue, 13 April 2004 15:04 Go to previous messageGo to previous message
LEit is currently offline LEit

 
Lt. Commander

Messages: 879
Registered: April 2003
Location: CT
The initial design work that Omni did used deques for most arrays, so I've stuck with that unless I had a good reason to use something else. Vector isn't being used anywhere else, so it's one less thing for the compiler to have to look at.

Component has lots of variables for the different properties.



- LEit

Report message to a moderator

Previous Topic: Just how flexible?
Next Topic: Planet Transfer
Goto Forum:
  


Current Time: Sun Apr 28 04:51:06 EDT 2024