[solved] looking for something like a data collection

Started by arj0n, Thu 19/06/2014 16:57:39

Previous topic - Next topic

arj0n

So, this is the situation in short:

I have:
1. a set of sprites in the sprite editor.
2. a random selected sprite (int spritenum) (this happens in the global script, in the always_execute function).
3. a gui that shows the random selected sprite for x seconds.
4. a menu where you can activate/dis-activate categories (about 20)

What I'm looking for is this:
for each category I do know the collection of sprites that does fit within each category.
Now, after a random sprite has been selected, I'd like to check if this specific chosen sprite does exist in each category.

if so (the category does contain this random sprite number): show sprite
if not (the category does not contain this random sprite number): choose another random sprite

So, is there something like a data collection I can create where you can store a bunch of, in this case, numbers and check the random sprite (int spritenum) against such data collection?

Joe

#1
What about usign arrays?
Code: ags

//10 sprites per category for example
#define MAX_SPRITES_PER_CATEGORY 10 
//As far as I know bidimensional arrays ar not implemented in AGS so the best option would be:
int sprites[20*MAX_SPRITES_PER_CATEGORY];

//You would have to fill it manually at some point in the script (game_start maybe).
int category=0;
sprites[category*MAX_SPRITES_PER_CATEGORY]=23;//23 would be the fisrt spriteID in category 0
sprites[category*MAX_SPRITES_PER_CATEGORY+1]=45;//45 would be the 2nd spriteID in category 0
sprites[category*MAX_SPRITES_PER_CATEGORY+2]=63;
//...
category=2;
sprites[category*MAX_SPRITES_PER_CATEGORY]=65;
//...

//and then wherever you want to check if the sprite is in the wanted category:
int i=0;
bool found=false;
while(i<MAX_SPRITES_PER_CATEGORY)
{
    if(sprites[selected_category*MAX_SPRITES_PER_CATEGORY+i]==randomly_chosen_sprite)
        found=true;
    i++;
}
if(found==true)
    your_show_sprite_function();//
else
    choose_another_sprite();//or wherever you have scripted
    



EDIT: I posted a better solution below!!
Copinstar © Oficial Site

arj0n

#2
Thanx Joe! I'll check this out tomorrow.

questions:
Any reason spriteID 23 is written out twice for category 0?

EDIT:
After having a good look at it: this seems to be exactly what I was looking for, thanx again!

Joe

#3
No problem!

1Q) The reason for having a 'max sprites...' is that it's the easiest way. You can define it as 100 or 1000... On modern PC's you won't ever have memory overflows issues.
2Q) No sorry, just copied the line and forgot to modify it.

Nevertheless, looking at your 1stQ I've thought an easier way where you don't need to define a max_sprites_per_category:

Code: ags

int sprite_category[MAX_AGS_SPRITES];//this array will store the category number instead of the sprite ID
//NOTE: I don't know if MAX_AGS_SPRITES is already defined in AGS

//at game_start
int i=0;
while(i<MAX_AGS_SPRITES)
{
    sprite_category[i]=-1;//Fill the whole array with -1 which will mean null category
    i++;                  //The reason for doing this is that there will surely be some sprite IDs which will not be used for the category purpose
                          
}
//then fill the array manually:
sprite_category[23]=1;//The sprite whose ID is 23 is in category 1;
sprite_category[31]=1;//The sprite whose ID is 31 is in category 1;
sprite_category[11]=2;//The sprite whose ID is 11 is in category 2; 
//...

//and then whenever you want to check if the sprite is in the wanted category:
if(sprite_category[randomly_chosen_spriteID]==selected_category)
    your_show_sprite_function();
else
    choose_another_sprite();
    
Copinstar © Oficial Site

arj0n


arj0n

Drat, can't seem to implement your code.
Would probably need to rewrite the whole part I already have...

So, the whole thing was already working except for 1 thing:
As you can see in Line 80 and 81, I now have to write out an 'if' statement for each available category. (written out to of them now: exterior & nature)
I guess there's a way better way to do such a check for all categories.
The categories are set from line 182. (again, only exterior & nature)

This is what I already have (sorry for the not so well done coding of me...):

Code: ags

// main global script file
bool menu_is_shown; //if menu is shown, stop running the gui with images
int countdown;
int ran; //randomizer
bool playlist_Exterior = true; //for enable/Disable category Exterior (in the menu)
bool playlist_Nature = true; //for enable/Disable category Nature (in the menu)
int prevsprite;int prevsprite;//to store the previous sprite to avoid showing 1 sprite twice

struct Sprite { //struct with arrays as categories for each sprite
  String name;
  bool cat_Nature;
  bool cat_Interior;
  bool cat_Exterior;
  import void Copy(int spriteID);
};
Sprite sprites[14]; //set number of sprites


function repeatedly_execute_always(){
//set Sprite Info
sprites[0].name = "info0";
sprites[1].name = "info1";
sprites[2].name = "info2";
sprites[3].name = "info3";
sprites[4].name = "info4";
sprites[5].name = "info5";
sprites[6].name = "info6";
sprites[7].name = "info7";
sprites[8].name = "info8";
sprites[9].name = "info9";
sprites[10].name = "info10";
sprites[11].name = "info11";
sprites[12].name = "info12";
sprites[13].name = "info13";

//set multiple categories per sprite on/off:
sprites[0].cat_Exterior = true;
sprites[1].cat_Exterior = true;
sprites[2].cat_Exterior = true;
sprites[3].cat_Exterior = false;
sprites[4].cat_Exterior = false;
sprites[5].cat_Exterior = false;
sprites[6].cat_Exterior = true;
sprites[6].cat_Nature = true;
sprites[7].cat_Exterior = true;
sprites[7].cat_Nature = true;
sprites[8].cat_Exterior = true;
sprites[8].cat_Nature = true;
sprites[9].cat_Exterior = true;
sprites[9].cat_Nature = false;
sprites[10].cat_Exterior = false;
sprites[10].cat_Nature = false;
sprites[11].cat_Exterior = false;
sprites[12].cat_Exterior = false;
sprites[13].cat_Exterior = false;

if (countdown > 0)
  {
  countdown--; // decrease countdown by 1 and reloop
  }
else if (menu_is_shown == true) //do nothing when menu is shown and reloop
  {
  //reloop   
  }  
else if (countdown == 0)
  {
    Bblitz.Visible=false;//set gui button invisible
    Lblitznfo.Visible=false;//set gui label invisible
    ran=Random((12)+1); //pick a random sprite
  
    if ((ran < 6) || (ran == prevsprite))//reloop if random selected sprite_id is 5 or lower or current random sprite is same previous random sprite
      {
        //reloop  
      }
    else if (ran >= 6) //random selected sprite_id is 6 or higher, sprite_id 0 to 6 aren't used
      {
        Bblitz.Visible=false;//set gui button invisible  
        int spritenum = (ran);

        if (((sprites[(spritenum)].cat_Exterior==true) && (playlist_Exterior==true)) && 
            ((sprites[(spritenum)].cat_Nature==true) && (playlist_Nature==true)))  //go if sprite cat exterior is true en playlistexterior is choosen in menu
         {
          prevsprite =(ran);
          Bblitz.NormalGraphic=(ran); //set button normalgrahp with random sprite within range
          Bblitz.X = ((1024 - Game.SpriteWidth[ran])/2);//center gui button that shows sprite
          Bblitz.Visible=true; //set gui buttonvisible 
          Lblitznfo.Visible=true;//set gui label visible 
          Lblitznfo.Text = (sprites[(spritenum)].name); //the sprite information shown on the label on the gui
          countdown=60;//reset countdown (40=1sec)
         }
      } 
  }
}

//These are buttons in the menu to set a category on or of:
function Bcatexterior_OnClick(GUIControl *control, MouseButton button)
{
if (Bcatexterior.Text == "exterior is on"){
     Bcatexterior.Text = "exterior is off";
     playlist_Exterior = false;
} 
else if (Bcatexterior.Text == "exterior is off"){
     Bcatexterior.Text = "exterior is on";
     playlist_Exterior = true;
}
}

function Bnature_OnClick(GUIControl *control, MouseButton button)
{
if (Bnature.Text == "nature is on"){
     Bnature.Text = "nature is off";
     playlist_Nature = false;
} 
else if (Bnature.Text == "nature is off"){
     Bnature.Text = "nature is on";
     playlist_Nature = true;
}
}

Joe

#6
1st of all. Why initalize sprites in rep_ex func?? I think you should initialize them in game_start function.

I could appreciate that 1 sprite can have several categories and you can also have more than one category selected. Am I wrong?

In that case I wouldn't create several properties for handling categories.
I would do the foloowing:
Code: ags

//Declare each category as binary code. Note they can only be powers of 2
#define NATURE   1 //00000001
#define EXTERIOR 2 //00000010
#define INTERIOR 4 //00000100
#define RELAX    8 //00001000

struct Sprite { //struct with arrays as categories for each sprite
  String name;
  int category;
  import void Copy(int spriteID);
};

//Also define only 1 variable for your playlist_category
int playlist_category;

//then at game _start:
sprites[0].category = EXTERIOR;
sprites[1].category = EXTERIOR;
sprites[2].category = EXTERIOR;
sprites[6].category = EXTERIOR | NATURE;//this will result 00000011 which will mean exterior+nature
sprites[6].category = EXTERIOR | NATURE;
//...

//Whenever you select 1 category:
playlist_category=NATURE|INTERIOR;//Or what ever you have selected

//Whenever you want to compare(your 80 and 81 lines):
if(sprites[spritenum].category & playlist_category) //Note it's only one & and not 2 since it's bitwise operation
                                                    //This condition will return true if any of the sprite categories fits with any of the playlist categories
{
//...your stuff
}

 
Copinstar © Oficial Site

arj0n

#7
Code: ags
//Whenever you select 1 category:
playlist_category=NATURE|INTERIOR;//Or what ever you have selected


That's for when you click a category button in the menu, I suppose?
If so, I can never know what other categories has been set, so I can't create a combi like NATURE|INTERIOR.

But it wouldn't surprise me if I completely misunderstoot this specific line of code of yours.

In my case multiple categories can be set on or off.

And yes, I complete misplaced parts of the code in rep_exec instead of the game_start function.
That wasn't a smart move indeed.

Joe

#8
You intrepeted it just right. I'll try to fit that line in your code:

Code: ags

function Bcatexterior_OnClick(GUIControl *control, MouseButton button)
{
  if (Bcatexterior.Text == "exterior is on"){
     Bcatexterior.Text = "exterior is off";
     playlist_category ^= EXTERIOR; //This will toggle EXTERIOR bit ...
  }
  else if (Bcatexterior.Text == "exterior is off"){
     Bcatexterior.Text = "exterior is on";
     playlist_category |= EXTERIOR; //this will add the EXTERIOR bit without modifying other bits so you can have combiantions
  }
}
Copinstar © Oficial Site

arj0n

I think this looks like the solution I was looking for.
But I don't know why line 5 & 9 from your code gives me a parse error:

GlobalScript.asc(635): Error (line 635): PE04: parse error at 'playlist_category'


I'm not really familiar with using operators like XOR this way :(

Joe

#10
Maybe its not supported in AGS, try this mode:

Code: ags

playlist_category = playlist_category ^ EXTERIOR; 

playlist_category = playlist_category | EXTERIOR; 


EDIT: If you dont feel good with using this AND, OR, XOR operators and need an explanation of their operating mode please tell, I think I can explain it in detail.
Copinstar © Oficial Site

arj0n


arj0n

Quote from: Joe on Fri 20/06/2014 22:08:37
If you dont feel good with using this AND, OR, XOR operators and need an explanation of their operating mode please tell, I think I can explain it in detail.
Yeh, I'm not really familiar with these bitwise operators, so it would be quite helpful for me to see an explanation of their operating mode.
Very kind of you.

Gurok

Quote from: Joe on Fri 20/06/2014 21:16:49
You intrepeted it just right. I'll try to fit that line in your code:

Code: ags

function Bcatexterior_OnClick(GUIControl *control, MouseButton button)
{
  if (Bcatexterior.Text == "exterior is on"){
     Bcatexterior.Text = "exterior is off";
     playlist_category ^= EXTERIOR; //This will toggle EXTERIOR bit ...
  }
  else if (Bcatexterior.Text == "exterior is off"){
     Bcatexterior.Text = "exterior is on";
     playlist_category |= EXTERIOR; //this will add the EXTERIOR bit without modifying other bits so you can have combiantions
  }
}


Support for yer' new fangled assignment operators is hopefully coming in the next release. For now, yeah, you need to use the long form.
[img]http://7d4iqnx.gif;rWRLUuw.gi

Joe

Quote from: Gurok on Fri 20/06/2014 23:39:50
Support for yer' new fangled assignment operators is hopefully coming in the next release. For now, yeah, you need to use the long form.
Nice to know.

Quote from: Arj0n on Fri 20/06/2014 22:40:44
Yeh, I'm not really familiar with these bitwise operators, so it would be quite helpful for me to see an explanation of their operating mode.
Very kind of you.

I guess you can understand logic operations, anyway here is a link for reference: http://en.wikibooks.org/wiki/Digital_Circuits/Logic_Operations
Bitwise operations are made bit by bit. If you type:
result=var1|var2;
It will do the OR operation between var1's first bit and var2's first bit and the result will be stored in result's 1st bit and so on with the rest of bits

An example:
00101101 = var1
10101000 = var2
============ OR
10101101 = result

In our case we want to be able to store several categories in 1 variable so if we type:
sprites[10].category = EXTERIOR | NATURE | INTERIOR;

The operation would be the following:

00000010
00000001
00000100
====== OR
00000111

Note im just typing 8 bits but they are actually 32. So you can store your 20 categories.

Also when we are checking if our sprite fits with any of the selected categories:

if(sprites[spritenum].category & playlist_category)

Let's guess spritenum=10 so the category value is 00000111. If the playlist_category value is for example 00001100 (INTERIOR + RELAX) the condition should return true (note any value !=0 is considered as true) since INTERIOR category is selected. The operation would be the following:

00000111
00001100
======= AND
00000100 -------> this is !=0 so it's true as we expected

However if the playlist_category value was 00001000 then the operation should return false:

00000111
00001000
======= AND
00000000 -------> 0 = false since none of the sprite categories fit with the selected categories

The XOR operation used in the selection is for toggling the corresponding bit. Actually you can use the XOR operator in both cases (selection and deselection) since it will always toggle the bit. The only reason I used the OR operator in the selection part was to make sure the bit is set to 1. But I really dont think there would be any problem if you use the XOR operator.

Hope you understand my testament ;)



Copinstar © Oficial Site

arj0n

Wow, thanx for the comprehensive testament Joe!
I'm gonna have a good read now first :)

EDIT:
While reading it I do remember I've actually already been through this bitwise operations material some years ago for software testing techniques.
But it's good to read such a clear explanation like yours.
I'm gonna save your testament in a text file on my hd now for sure :)


SMF spam blocked by CleanTalk