Arrays in AGI

sonneveld For people who have done some programming in AGI, they would have noticed the lack of arrays.  I've been thinking of some reasons why arrays would be useful, like mazes or maybe the position of items on the screen.

I've come up with one way for making an array in logic code..  It's readonly but you could store a lot of information in it.  Basically, you set up a comma separated excel file full of data like this:

1, 10, 50
2, 30, 100

where the first digit is the index, or the number that identifies the item.

secondarly, you setup another file, a sort of template file like this:

var1 = %d;
var2 = %d;

when you run this converter I'm working on.. you'll get this as output:

// item #1
if (index == 1)
 var1 = 10;
 var2 = 50;

// item #2
if (index == 2)
 var1 = 30;
 var2 = 100;

// remove this error message if you don't want to handle it
print("ERROR!  Data not found!");

Now, I haven't finished any maze code yet.. but I'm going to start working on another program that will create maze data (ie, rooms that connect to each other in a random way.. but still solvable as a maze)..  The way it compares the index is pretty simple/slow at the moment too.. but I was thinking about using a pseudo binary search as a secondary option.

Then people could add a maze into their game without having to generate a different logic file for each room.. just a few random pics for each room.

I hope that description made sense..  The reason why I posted was to ask other people what they thought... Any ideas on what to add?

- Nick
Joel There are dereferencing commands in AGI, right? If so, I guess all you need to keep track of for an array is two variables -- one to store the base var number of the array and another to store the access index. For example,

v200 = 225;
// to set element i to 5
v201 = v200 + i;
*v201 = 5;

It's not pretty, but it works fairly efficiently. This solution you're proposing could still be useful for read-only applications with lots of data, though.
sonneveld If I was going to implement writable arrays.. that would be the way to do it.  I was thinking more along 15x15 mazes that have a few fields for each cel. Filling up all the spare variables for data that remains constant would be wasteful.

I've written separate tools.. but this would be nice to include into the agi compiler.

I'm in the process of writing my own small game based around a maze.. just to show what you can do with it.  I wish I could think of some more interesting applications for arrays in adventure games... path finding?  (set some waypoints)

- Nick
Andrew_Baker Well, there's the example of Apocalyptic Quest where he wants to have a bunch of strategy combat.  If y'all were able to store the enemy's stats in a 2D writeable array, that would simplify things to no end.  You could initialize and rewrite everything in the array in some loops.  I haven't messed around with dereferencing in AGI, so...

Also, arrays are good for AI.  You can set each coordinate on a 2d array to an exclusive condition, and by comparing two conditions, determine the behavior of the AI agent.

          Happy |  Sad  |  Angry
Drunk| act goofy| cry  |  break stuff
Sober| smile    |  frown | yell
Stoned| do nothing | do nothing | do nothing

Note this last example doesn't necessarily need a writeable array, but anything that could simplify writing the if-then statements for a reasonably complex AI agent would be a big help.

Joel Here's an example of a writeable AGI array. Put this into the new_room section of a logic if you want to see it happen. Dereferencing in AGI is pretty easy to do. However, it is not particularly easy to use. As far as I know, you can't use indirection to print out the value of a variable, and it seems you can't even say something like *v200 *= 2; Anyway, here's the example:

#define vArrayBase   v200
#define vArrayIndex  v201
#define vLoopCounter v202
#define vTemp        v203
#define ARRAY_SIZE  10

 vArrayBase = 225;
 v225 = 2;
 v226 = 7;
 v227 = 8;
 v228 = 12;
 v229 = 49;
 v230 = 50;
 v231 = 100;
 v232 = 30;
 v233 = 40;
 v234 = 78;

// start of first loop

// for (vLoopCounter = 0, vArrayIndex = vArrayBase;
//      vLoopCounter < ARRAY_SIZE;
//      vLoopCounter++, vArrayIndex++)

 vLoopCounter = 0;
 vArrayIndex = vArrayBase;

 // save the value of the current element in a temp variable
 // so that it can be printed out (AGI syntax really needs
 // to be more robust for printing out var values -- I
 // shouldn't have to use var numbers after #define'ing
 // a name for these things)
 vTemp = *vArrayIndex;
 print("array[%v202] = %v203");


 if (vLoopCounter < ARRAY_SIZE)

// start of second loop

// for (vLoopCounter = 0, vArrayIndex = vArrayBase;
//      vLoopCounter < ARRAY_SIZE;
//      vLoopCounter++, vArrayIndex++)

 vLoopCounter = 0;
 vArrayIndex = vArrayBase;

 vTemp = vLoopCounter;
 vTemp *= 2;
 *vArrayIndex = vTemp;

 vTemp = *vArrayIndex;
 print("After assignment, array[%v202] = %v203");


 if (vLoopCounter < ARRAY_SIZE)
Joel I guess, technically, you wouldn't have to use a var to store the base address of the array. You could just use a #define constant. In my example,

#define vArrayBase v200

vArrayBase = 225;

and all references to vArrayBase could be changed to use

#define ARRAY_BASE 225
Andrew_Baker So, if I wanted the value of a specific cell, X, then:

Joel Actually, the loops are unnecessary. If you wanted to print out element 5 of the array, all you'd have to do is something like this:

vArrayIndex = ARRAY_BASE;
vArrayIndex += 5;

v203 = *vArrayIndex;
print("array[5] = %v203");

Two dimensional arrays are a bit more complicated. You'd need ARRAY_BASE defined, ARRAY_SIZEX defined and ARRAY_SIZEY defined, like you did. To print element [x][y], however, you'd need to do some kind of multiplicative calculation, such as:

vArrayIndex = ARRAY_BASE;
vTemp = x;
vArrayIndex += vTemp;
vArrayIndex += y;

v203 = *vArrayIndex;
print("array[x][y] = %v203");

Unfortunately, this is practically like coding in assembly language, because AGI syntax doesn't let you say something like vArrayIndex = ARRAY_BASE + (x * ARRAY_SIZEY) + y;

What you did is fine for looping, but you don't want to have to loop just to access an array element. That's really inefficient.
Andrew_Baker Or, on a sort of sidetrack, you could use a very large array for initialization and break your game up into chapters.  In effect, if you were to use the same views and pics, you could probably make an insanely long game (even within the memory restraints of AGI) by reinitializing the flags and vars at the beginning of every chapter.  This would take some good writing so that the player wouldn't get stuck in any dead ends or so that there wouldn't be any continuity errors.  Also, I'm not sure how long room scripts can be, but each Logic can be broken up into segments:

if (chapter == 1) {
   //room logic for chapter 1

if (chapter == 3) {
   //room logic for chapter 3

else {
   //room logic for every other chapter

I think this would work...  any comments?
Nicholas Sonneveld well, you'd probably share code between chapters too (like pic and view code).. but I'm sure other games use it.. Look at Police Quest, the meetings are different at different times along with other time based stuff.

- Nick
Andrew_Baker Yeah, all the views and pics would remain about the same from chapter to chapter (Some scenes would have somewhat altered pics to represent changes), but on top of that I think this technique could be used to make games much more robust and complex.  Plus, I could probably make a game that didn't suck like Police Quest.

Okay, harsh judgment on Police Quest, but that game really infuriated me.  But, anyway, when you have that array utility thingie worked out, I'd like to try using it in a game I have in mind.
Joel Actually, I had in mind using something like that chapters idea in Jen's Quest. Except I was thinking about using days instead but the basic idea is the same. Really, you can recycle even global flags and variables any time that they're no longer relevant. For example, at the beginning of Jen's Quest, there's a global flag to keep track of whether the player has taken a shower yet. Once the beginning sequence of the game is over, that flag is really no longer relevant to the game. Because you can't proceed past the beginning of the game without taking a shower, being at a point in the game that is past the beginning implies that a shower was taken, and so I no longer need to keep track of the global "shower taken" flag.