Multibrains

WARNING: Do not edit this page as for now, as its content is either perfectly correct, or false but known to be false and therefore will be corrected soon.

Some Background

Multibrains are probably the most complex concept in Spark related to the brains. They provide an enormous amount of flexibility, and with proper use, can simplify your kode while enabling very complex behavior. However they currently have no documentation, and if used incorrectly, can lead to unexpected behavior that can be very hard to track down.

Some Terms

First, let's clarify some terms, as the term 'brain' is starting to get overloaded. Since a single object can have multiple brains (hence multibrains) I'm going to start referring to the full group of brains on an object as an object's  mind. A brain can be thought of as a collection of pages, each page being 0 or more lines of kode and a collection of variables.

Two additional terms related to multibrains that we should define up front are  channels  and  slots . These both relate to the way an object's mind is structured. An object's mind can contain multiple channels, and each channel can contain multiple slots. A slot can only contain one brain. Both channels and slots are specified by name and you can name them whatever you'd like. I'm not sure what the maximum number of channels or slots would be, but I suspect they have a pretty high limit due to the absence of warnings and that any limits for stuff like this would probably be arbitrary.

The final terms I'll frontload are  stacks  and  frames . A stack is a commonly used programming structure, referred to as a "last in, first out" (LIFO) structure. The best analogy to a stack I can think of is a Pez dispenser, where you put in a single candy at a time. In this analogy, each candy is a stack frame. The dispenser has two common operations: you can push a candy into the top of the dispenser, pushing all the other candy one frame down in the stack, or you can pop a candy out of the dispenser, allowing the candy still in the dispenser to rise up one frame.

Some Tiles

Multibrains gives you several tiles to interact with and alter the mind of your object, so let's briefly discuss them now.

[Push brain] and [Pop brain]

The [push brain] and [pop brain] tiles are channel wide operations. This means that they affect all active brains on whichever channel they run against. Pushing a brain to a channel is a reasonably complex operation, it essentially disables all brains currently running on the  channel and creates a new layer (frame) on the channel that contains the brain you pushed. This means that a channel operates like a stack. Each stack frame consists of one or more slots, and you can push a new frame on top of the stack. Only the brains at the top frame of the stack will run. Popping the stack of a channel removes the top frame, causing the frame below it to be the top, and to become active. Despite the [from slot] modifier on [pop brain], all active slots in the specified channel will be removed. I suspect it is a bug that [from slot] is available as a modifier on [pop brain].

[Add brain] and [Remove brain]

The [Add brain] and [Remove brain] tiles allow you to add a brain to a channel without interfering with the brains that are currently active in that channel. When adding a brain, you place the added brain into an active slot in whichever channel you specify. If you specify a slot to add your brain into, you will replace any brain that was already in that slot. Right now, saying to [remove brain] and then specifying a channel will remove all active brains in the specified channel. This is in contrast to [remove brain] without a channel specified, which removes the brain in the first active slot in the default channel. You can have a brain remove itself from its slot, without interfering with any other brains, by saying [remove brain] [this brain].

<span style="color: #333333; font-family: 'Open Sans', sans-serif; font-size: 12.727272033691406px; line-height: 14.536362648010254px; text-decoration: underline;">[get brain] and [this brain]

<span style="color: #333333; font-family: 'Open Sans', sans-serif; font-size: 12.727272033691406px; line-height: 14.536362648010254px;">The [get brain] and [this brain] tiles are there to help you out when you are using multibrains. [get brain] allows you to get a brain from a specific channel. For some reason, you cannot specify the slot you want your brain to come from, so you always get the brain in the first slot in the channel you request. The [this brain] tile provides to you some information about the brain that is currently running. You can find out the name of the slot the brain is in, what page it is on, whether it is enabled, etc...

Some Pictures

<span style="color: #333333; font-family: 'Open Sans', sans-serif; font-size: 12.727272033691406px; line-height: 14.536362648010254px;">Since explanations will only get you so far, let's take a look at some examples. First, here what the mind of a unit that "isn't" using multibrains looks like:

<span style="color: #333333; font-family: 'Open Sans', sans-serif; font-size: 12.727272033691406px; line-height: 14.536362648010254px;">The diagram shows a mind with only the default channel, and the default channel has a brain in its default slot. This is what most of your object's minds will look like before you start messing around with them. This is just for reference. Now let's contrast that with a brain that   <span style="color: #333333; font-family: 'Open Sans', sans-serif; font-size: 12.727272033691406px; line-height: 14.536362648010254px;">is making full use of multibrains.

<span style="color: #333333; font-family: 'Open Sans', sans-serif; font-size: 12.727272033691406px; line-height: 14.536362648010254px;">Now let's take a look at how some operations affect an object's mind. First, we'll say that the above diagram shows the mind of our object before we do any of our operations, then we'll look at a diagram that shows the brain after the operation is executed. The brain is deliberately complex, so that the details of the operations can be seen in it.

<span style="color: #333333; font-family: 'Open Sans', sans-serif; font-size: 12.727272033691406px; line-height: 14.536362648010254px;">First let's take a look at what happens when a brain is pushed to channel "2" and and we specify the slot "A" with kode that would look like this: <span style="color: #333333; font-family: 'Open Sans', sans-serif; font-size: 12.727272033691406px; line-height: 14.536362648010254px;">[push brain] [to channel] [text: Channel 2] [to slot] [text: A]

<span style="color: #333333; font-family: 'Open Sans', sans-serif; font-size: 12.727272033691406px; line-height: 14.536362648010254px;">As you can see, the frames in Channel 2 have all been pushed one layer downwards, with the newly pushed brain at the top. Because the slot name was specified, the slot the brain is placed in is called "A". This doesn't interfere with any existing slots called "A" because they exist on a different frame.

<span style="color: #333333; font-family: 'Open Sans', sans-serif; font-size: 12.727272033691406px; line-height: 14.536362648010254px;">If you were to pop Channel 2, the diagram would go back to the original diagram, essentially removing all brains in the active frame in Channel 2 and moving each other frame in Channel 2 up one frame.

<span style="color: #333333; font-family: 'Open Sans', sans-serif; font-size: 12.727272033691406px; line-height: 14.536362648010254px;">How about Adding a brain then. Here is how the diagram will change should we add a brain "Z", specifying channel "2" and slot "C"

<span style="color: #333333; font-family: 'Open Sans', sans-serif; font-size: 12.727272033691406px; line-height: 14.536362648010254px;">As you can see, our added brain is put into the active frame in Channel 2 in slot "C". The brain that was previously in slot "C" is replaced, but the other brains in Channel 2 are not affected.

<span style="color: #333333; font-family: 'Open Sans', sans-serif; font-size: 12.727272033691406px; line-height: 14.536362648010254px;">There are different behaviors when using remove brain, depending on what modifiers are specified. If you were to say [remove brain] without any modifiers, it would remove the brain in the first slot of the active frame in the default channel. (The top left most slot in the diagram) If you were to say [remove brain] [from channel] [Channel 2] it would remove all the brains in Channel 2's active frame and would leave channel 2 with its active frame empty. If a brain were to say [remove brain] [this brain] then just that brain would be removed, leaving all other brains as they were.

Why use Multibrains?

<span style="color: #333333; font-family: 'Open Sans', sans-serif; font-size: 12.727272033691406px; line-height: 14.536362648010254px;">One of the big things you learn when programming for a while is that repeated code/kode is a bad thing. Any time you can make your kode generic enough that it can be used in multiple places, it is almost always a good idea to do so. One way of sharing your kode between objects is to use templates or push the brain you want onto your object. That however only works if the behavior you want to copy is the whole behavior of your object. If you want a part of something's behavior to be shared, that is when multibrains comes into play.

<span style="color: #333333; font-family: 'Open Sans', sans-serif; font-size: 12.727272033691406px; line-height: 14.536362648010254px;">For example, when making a 2D platformer, you want to lock your player, and probably all your enemies to a single plane. If you put that "plane locking" logic in a brain, then you can have all enemies and your player add that brain and have it running all the time in addition to whatever other brain(s) make them unique.

Do brains run at the same time?

<span style="color: #333333; font-family: 'Open Sans', sans-serif; font-size: 12.727272033691406px; line-height: 14.536362648010254px;">This is a bit tricky, so I want to be clear here. I actually don't think that more than one brain can ever be run in Spark at the same time. Now, this is so far just my belief, though there are some experiments I could do to improve my confidence. I believe the way it works is that all brains for a single object are run in sequence, and then you move onto the next object, and all its brains are run. The order that the brains are run for a single object is not directly under your control, though it does seem to run them in the order they were added. I would recommend making your additional brains so that the exact order they run in doesn't matter.

What is the use and the syntax of get brain?

<span style="color: #333333; font-family: 'Open Sans', sans-serif; font-size: 12.727272033691406px; line-height: 14.536362648010254px;">Honestly, I don't find it terribly useful as is, but there is at least one use I have come up with for it. If you for example wanted to know if an object was already running a brain in a particular channel. Example, I have a channel called "Poisoned" that runs a specific "Poisoned" brain on my character. I can ask if there is currently a brain running in that channel, so I can for example not add another poisoned brain, since one is already running. As for syntax:

<span style="color: #333333; font-family: 'Open Sans', sans-serif; font-size: 12.727272033691406px; line-height: 14.536362648010254px;">W: [get brain] [from channel] [text: Poisoned] D: [pop brain] [sensed brain]

<span style="color: #333333; font-family: 'Open Sans', sans-serif; font-size: 12.727272033691406px; line-height: 14.536362648010254px;">[sensed brain] is an output tile that gives you the brain that was found by [get brain]. If no brain is found, then the do side will not be entered.

Known Limitations and Gotchas

It is important to know that you cannot call a page in an added brain as if it is in the current brain. There is a way to call pages from other brains and objects, but it requires very specific syntax that I won't go into here.

Also be aware that by default variables are not scoped to brains, but instead scoped to objects. That means all brains running on a single object share object scoped variables. This can lead to unexpected behavior if you accidentally re-use an object scoped variable. In order to help avoid this, you can use brain scoped variables. More information about them can be found at http://forums.projectspark.com/yaf_postst102557_Multibrains-and-Variables-scopes.aspx. They are a powerful complement to using multibrains, and can improve your shared brains.