Color Guide:
Red indicates a command (or commands) you enter into the MUCK.
Navy indicates text returned by the MUCK, usually a result of a command you typed.


MUF: Hyacinth's Amulet-- Arrays on Fuzzball 5.x

Fairies have magical powers, and teleporting themselves around in true fairy-fashion is often one of them. Hyacinth the fairy-ant is no exception, and here's the magic behind how she does it. This program is essentially a glorified teleporter. It can give an object special teleport functions through an action set on that object. For example, using an action called 'wish' linked to the program, the user can say 'wish Walter, Tara and me to Gem', and Walter, Tara and the user will be moved to Gem's location, with a few messaging effects thrown in.

This program illustrates an implementation of an array in Fuzzball 5.x. I code in a FB 5.x environment, and arrays are not implemented on versions of Fuzzball less than 6.0. so being a bit contrary I made one anyway, and here's how you can too. This array solution makes use of MUF's variable space which limits you to approximately 50 array elements, but it's still handy.

First, here's the complete amulet program:
The Basic Trick

If you poke around with MUF long enough, you'll notice that the pre-defined variables me, loc and trigger are the same thing as 0 variable, 1 variable and 2 variable respectively. So if I want the user's dbref, I can say 'me @' or I can say '0 variable @'. Notice their integer sequencing: 0, 1, 2. If I make my own variables using lvar, the first one I make will occupy the next available interger position, 3. Here's a variable list where I've defined two custom local variables:
Me, loc and trigger are represented by comments in this example. We don't declare them (they're pre-declared for you), but they're there all the same. You can get at the value of myvar_1 by saying 'myvar_1 @', or by saying '3 variable @'. That was a case with 2 local variables, but I could just as easily have declared n local variables:
If I store something in myvar_2, I can fetch its value with 'myvar_2 @' or with '4 variable @'. If we had n custom variables declared, then you could get to myvar_n by saying n+2 variable @. For example, if the number of custom local variables was stored in a variable called max_vars, we could say 'max_vars @ 2 + variable @' We need the +2 to skip over the standard MUF variables, me, loc and trigger. The +2 can be thought of as an 'offset'.

Now here's the cool thing: The variable space still exists even if you dispense with the declaring of variable names. For example, if we declare no local variables at all, you can still do this: '3 variable !' to store a value in a variable slot just above trigger, then retrieve the value at some later time with '3 variable @'. Why declare variables at all? Because remembering a name is easier than remembering some dumb number. But, it's not essential.

Put all this together, and all the components are there to make an array, and better yet, an array that you don't have to pre-define its maximum size. The array can grow as large as we need at run-time. This type of array is called a 'dynamic array' as opposed to a 'static array' where you have to declare its size. Essentially you have a second stack at your disposal!

Hyacinth's amulet uses this happy situation to store the dbrefs of those characters who are being teleported. It's easy, and a lot handier than kludges like property-arrays or string-based arrays. It's also faster. All you have to do is determine the offset to your array in the variable list, and make a few small functions to handle reading and writing to your array. These functions can be standardized and used in all of your programs where an array would be useful.

A Second Stack

To make a dynamic array from the MUF local variable space, you have to track two things: the initial position of the array, and the last position occupied by the array. Look at the following example:
Here we declared one local variable and we have an array currently with 4 elements. The index of the first position in the array is 4. That is, we can get to it by '4 variable @'. The index of the last position is 7. We can also find the last position if we know the number of elements in the array. In this array the number of elements is 4, so the last position can be found by: first position + number of elements - 1, in this case 4 + 4 - 1 = 7. Because we're implementing a dynamic array (one that grows or shrinks in size like a stack), tracking the number of elements is handier than tracking the last position.

So we need to know the first position of the array, and the size of the array. We can do this by adding one local variable associated with the array called an 'offset' that we can always use to find the first position in the array. Remember we aren't using names for the array elements, we're using the variable sequence which is just a number, so we need a fixed number (the offset) that we always add to the index of an array element to skip over any regular named local variables and get to the variable representing the array element. This will make more sense in a moment. Next we need a way to track the number of elements in the array. I do this by making the first position of the array a kind of counter and referring to it as element 0, or array[0]. Given the last example with an array containing 4 values, here's what it now looks like:
Using the above example of an array with 4 elements, say we want the 2nd element of the array, written as array[2]. We can't say '2 variable @', that would give us the trigger. We have to add the offset to the element we're looking for. The offset in this case is 5, so we can say '2 offset @ + variable @'. If we want to know the current size of the array (stored in array[0]) we can say '0 offset @ + variable @'. This translates to '5 variable @', which if you look at the above table, is retrieving the value stored at position 5, the number of elements in the array. Obviously the addition of 0 is unnecessary, but I put it there to illustrate that we're retrieving the element array[0].

Here's a test to see if you're following. Given that the number of elements in the array is stored in array[0]. What's the MUF code to retrieve the value stored in the last element of the array no matter what the number of elements is? Find the answer at the bottom of the page.

How To Do It

Despite all that explanation, implementing this stuff is pretty easy. Remember our array works just like the MUF stack. If we want to add something to the array, we'd increment the size of the array by 1 and then save whatever it is at the maximum array position:
Dropping the last element of the array is easier:
All we did was decrement the array counter. Whatever was in the former last position is still there, but we don't care about it. MUF's 'pop' primitive operates on the stack in the same way.

Two more tools and our second-stack implementation is complete. We need a way to retrieve elements from our array, and replace the value of an existing element:
I've coded up some general-use versions of the above four functions plus a few more that might be useful and packaged them as a library:
If you don't want to fool with adding a library, you can just select the functions you want and copy and paste them into your program.

If you use this stuff to make an array in your programs, remember three basic things:
Multiple Arrays

Can you make more than one array? Sure! Remember I mentioned static arrays, whose number of elements is pre-determined, and dynamic arrays, whose number of elements grows or shrinks like a stack. You can have any number of static arrays, but only one dynamic array using these methods. Let's set up an example and I'll show you why.
Array 1 is a static array that has space reserved for 4 elements. Array 2 is a dynamic array, just like our previous examples. The variables array1 and array2 are the offsets for the arrays. If you look at the example, you can see that if you tried to push 5 elements onto array 1, you'd start over-writing the positions of array 2, and things would quickly get messed-up. Array 2 can grow without restrictions, since there's no reserved space for an array beneath it. Most programming languages that support arrays have 'range checking' to throw an error if you try to do this sort of thing. You can implement your own range checking in your MUF functions, or just be careful in what you do.

To work with a static array you need only two of the functions I've coded for you, array_put and array_pick. You set the size of the array (in array[0]) for your static arrays when your program starts, and never touch it again. For example, 4 0 array1 array_put. Never use the array_push or array_pop functions on static arrays, because they will change the number of elements in the array, a Bad Thing for static arrays.

In the above example, you could do the following:

When using static arrays, it's often a good idea to initialize your array elements with some value, like 0 or a null string, "". You can do this with the array_init function. Normally you wouldn't do this with a dynamic array.

That's pretty much all there is to my solution for arrays in FB 5. I hope this wasn't too confusing and gives you the confidence to use arrays in your programs.



Answer: offset @ variable @ offset @ + variable @
First get the value of the location pointed to by the offset, which is array[0]. This will be the number of elements in the array. Then add the number of elements to the offset to get the location of the last element, and fetch it.