PDA

View Full Version : loading set from file



laggyluk
15-07-2013, 05:09 PM
I have lot's of different sets that group 'minerals' like that:


sMinerals = set of TBlockType;

mineralsWalkable, mineralsNonBlocking, mineralsRare.. : sMinerals;

then I have a game object with one of felds being 'sMinerals' and would like to be able to load it's properties from file.
I know that it's possible to convert string to enum with typeinfo and load set elements that way but is it possible to load a 'set name' instead and convert it somehow?
loading only elements would mean I have to update the file manually each time I add new element to set definition

Delphius
16-07-2013, 07:50 PM
Hello,


Not sure if I understood well.
You have on one hand an enumerated type plus a set of this type. That is:



TBlockType = (mineralsWalkable, mineralsNonBlocking, mineralsRare, etc);
stMinerals = set of TBlockType;



Even if we were to be more accurate with correct nomenclature guide would be this:



TBlockType = (btWalkable, btNonBlocking, etc);
TMinerals = set of TBlockType;



And you're looking for a way to "automate" the saved and read from a file of this set. And also, you do not look limited to a fixed amount of items, but you can add more types of minerals and not have to alter the file.


If I am right, then I think the technique you're using is not entirely correct.
I think that if you are not fully aware of the amount, and cardinality of items that will TBlockType and / or if you expect this to be a "listed open" then not it is the right kind.
I recommend you to think more in design a list to it so that you can add as many types of minerals as you want.
Then:
1. Design an abstract class called TBlockType
2. Desing as many classes as types descend from this need.
3. Design a class TMineralsList that is responsible for maintaining the list (set) of minerals.


What remains to be implemented, and for reading and saving the file based on the listing. I can suggest you to do based on the Factory pattern.


To keep the set in the file, you could do something like XML:
<set>
NameOfClass1;
NameOfClass2;
...
NameOfClassN;
</set>


Then to read the file just read the name of the classes and proceed with a "materialization" of this and create an instance.
The other way around (save), we scan the list of objects (minerals), determine the name of the class and stored on disk.

Regards,
P.S: Sorry for my bad English.

laggyluk
17-07-2013, 04:12 AM
I didn't explained it well enough, sorry. It turns out it's not possible so I used a map
http://stackoverflow.com/questions/17666864/convert-string-to-set-type

SilverWarior
17-07-2013, 06:03 AM
I know that using strings for naming block types might look easier but in the end it only complicates things not to mention that operating with strings is ususally slower.
So I advice you to use numbers for defining your block type. Why? Becouse with numbers you can quickly kinda divide all of your block types into sets.
Take a look at next example of block definition:


//Pseudocode

//Passable blocks: 0-100
begin
0 = air
1 = water
2 = background wall
3 = doors
....
end

//Unpassable blocks 100-900
begin
//Destructable blocks
begin
//Ground 100-199
begin
100 = dirt
101 = grass
102 = stone
...
end
//Minerals 200-300
begin
200 = iron
201 = coal
202 = coper
...
end
//Undestructable blocks 900-1000
begin
901 = bedrock
end
end


In the end you can also define numeric sets in form of btMinerals: 201..300. This alows you to change the block definition types at any time without the need to recompile your program and still use sets to quickly determine block type.

laggyluk
17-07-2013, 06:42 AM
it's a byte not a string. I just let the compiler number them

type
{$PACKENUM 1}
eBlockTypes = (btNone,btUndefined,btStone, btYellowFlower, btWoodBrown);

...

mineralsNonBlocking:=[btNone]+mineralsOxygen;

Super Vegeta
17-07-2013, 06:57 AM
Hm, can't you just typecast the set to an integer when saving, and then do it the other way when loading?

Btw, doing a writeln(enum_type) will print the name of the identifier, as it appears in code. Have you checked if readln(enum_type) works?

laggyluk
17-07-2013, 07:39 AM
I can't apparently. isn't writeln/readln console program thing? anyways it says 'Error: Can't read or write variables of this type'

SilverWarior
17-07-2013, 07:58 AM
it's a byte not a string. I just let the compiler number them

Automatic numbering for such things isn't verry suitable as you can quickly change the numbering order by changing the set itself. This means that after you change the set your program will no longer be able to load older savegame.
Using my aproach also has advantage of you being albe to leave some identifier numbers unused so you can use them in the future and therefore avoid changing existing ones when adding new ones.

EDIT: As for sets their primarry use is to add predefined identifiers for some properties. And yes in memory set elements are saved just as Indexes of set elements. You can consider sets as bunch od Constants of same type grouped together.

laggyluk
17-07-2013, 08:16 AM
Automatic numbering for such things isn't verry suitable as you can quickly change the numbering order by changing the set itself. This means that after you change the set your program will no longer be able to load older savegame.
Using my aproach also has advantage of you being albe to leave some identifier numbers unused so you can use them in the future and therefore avoid changing existing ones when adding new ones.I know what you mean but so far so good, I just add new elements to end of set :P

Delphius
17-07-2013, 03:23 PM
Although there are other possible ways to address the problem If the intention is to add more elements to the enumerated type seems just a design flaw and is making use of the wrong type.
If you have not yet defined the number of items and you'll be adding more and more, it is clear that go on your design is counterproductive.
It is better to redirect this design to one based on classes as I mentioned in a previous post.
You design The class TMineralList with methods to add and remove items in an internal list. And until it can be designed to do set operations such as union and intersection if you want.
TMineralList assumes responsibility for store / read the file and materialization. If you insist to make the correspondence by string, you be can avail the name of the class. Something like this:



type
TBlockTypeClass = class of TBlockType; //TBlockType is the abstract base class

var
BlockType: TBlockType;
BlockTypeClass: TBlockTypeClass;
...

Index := BlockLists.IndexOf(TheTextReadFromFile);
BlockTypeClass := TBlockType(BlockLists.Object[Index]);
BlockType := BlockTypeClass.Create;


BlockLists is a TStringList.
To "register" each subclass of TBlockType:



BlockLists.AddObject(ConcreteBlockType.ClassName, TObject(ConcreteBlockType));



Naturally this design not is "the solution", because it involves add new classes, but when trying to be adding elements to an enumerated type in the first thing you should think about is a lack of design and/or are not using the right path.
I say this with all respect.

Regards,
P.S: Soory for my bad English.

User137
17-07-2013, 05:27 PM
I don't think it's as much a type problem, but more of a file-format problem. If you have old version file where is

[someobject1]
type=10
(where type 10 meant grass)
And new file

[someobject1]
type=11
(where type 11 means grass)
Loading old file with new loader, would consider that type 10 object something else than it was meant to be.

You need a way to distinct the 2 files. I'm not one to give you right answer to that. Clumsiest of all ways is to have version number on top, and different loading routine for each version. You can forget to change the file version from the source code, and override your work file with garbage data.

One other way is to explain all the types at beginning of file. For example:

[type10]
texture=grass.png
burnable=true
That might also mean new list structure for program to store this information.
(In essence, new types will be added to the end of the list.)

laggyluk
18-07-2013, 01:19 AM
enum range is 0-255 thanks to {$PACKENUM 1} and if I made it a class then I couldn't create hundreds of milions of them like i do now. I can't add new elements to middle of enum definition but I don't have a problem with adding them to the end :P

and In file I can save enum name instead of int

[requires]
3 btStone

Delphius
18-07-2013, 03:25 AM
enum range is 0-255 thanks to {$PACKENUM 1} and if I made it a class then I couldn't create hundreds of milions of them like i do now. I can't add new elements to middle of enum definition but I don't have a problem with adding them to the end :P

and In file I can save enum name instead of int

[requires]
3 btStone

Sorry, but I'm curious as how you're doing it. Could you be a little more expressive with the solution (at least partially) to which you arrived?
What does the {$PACKENUM 1} and who implications you have in your design?

Regards,
P.S: Sorry for mi poor English.

laggyluk
18-07-2013, 06:13 AM
{$PACKENUM 1} tells the compiler to store enum in 1 byte instead of whatever default data size would be