I am the Lore: Extending the Magikcraft API

I’m standing at the front of the staff room, whiteboard marker poised over the board, about to write. Something stops me.

Behind me, ten primary school children wait for me to teach them how to write a loop. They want to launch multiple fireworks at once in the final session of Magikcraft for the term. We’re creating a fireworks display for their parents, in between fighting Zombies and exploring the Magikcraft worlds.

Something stops me. It’s a combination of two things: a blog article or Slack comment I read by a functional programmer deriding loops as a horrible practice — and the certain knowledge, based on experience, that once I show these kids how to write an arbitrary loop, literally millions of iterations of fireworks will grind that server to a halt within minutes.

This is why we can’t have nice things (aka loops)…

One is practical — every time we’ve given arbitrary loops, the server is destroyed. The other is ideological, or is it pedagogical? I have the opportunity to introduce these kids to programming from nothing. I literally can introduce them to recursion first.

So I do, and I do it using the new extension point for the Magikcraft API: magik.Lore.

Lores in Magikcraft allow Master Wizards to create their own School of Magik. Spells in Magikcraft are bound to individual players. Lores are available to everyone on the server.

A Note on code samples:

Code blocks that start with the following are complete spells:

var magik = magikcraft.io

You can put that code in your Magikcraft spellbook and hit save, then follow the onscreen instructions to cast that spell in Magikcraft.

Code blocks that don’t have that at the start are code snippets, to illustrate a point.

Iteration versus Recursion

A short digression. Here’s the firework spell:

var magik = magikcraft.io;
function firework(){
var there = magik.illic(); // Get where I'm looking
magik.stella(there); // Launch a rocket from there
}
// invoco applies arguments passed from Minecraft
magik.invoco(firework);

Here it is with a loop:

var magik = magikcraft.io;
function fireworks(){
var there = magik.illic();

for (var i = 0; i < 5; i++) // loop 5 times
magik.stella(there);
}
magik.invoco(fireworks);

We’ve considered moving to Python for Magikcraft simply because of the C-style loops and curly braces in JavaScript, but JavaScript is the language of the Gods, so we won’t.

Here is the same spell, using recursion:

var magik = magikcraft.io;
function fireworks(n){
if (n < 1) return;
   if (!n) n = 5;            // Default to 5 if called with no args             
   fireworks(n-1);
   var there = magik.illic(); 
return magik.stella(there);
}
magik.invoco(fireworks);     // Apply argument n from Minecraft

I am the Lore

Here’s how you encapsulate the complexity of this for reuse using a Magikcraft Lore.

Here’s a utility function doNTimes that takes a function action and an integer N, and then calls action N times, using recursion:

function doNTimes(action, n){   
if (!action || !n) return; // returns on n=0
doNTimes(action, n-1);
return action();
}

Here’s how I add this to my Lore to allow other users on the server to use it. I create the following spell to create and register doNTimes:

var magik = magikcraft.io;
function registerDoNTimes(){
maxN = 10; // Range limit!
  // named function
var doNTimes = function doNTimes(action, n){
if (n > maxN) n = maxN; // Range limiter
    if (!action || !n) return;   // returns on n=0
doNTimes(action, n-1);
return action();
}
   // Add it to my Lore
magik.perscribo(doNTimes);
}
magik.invoco(registerDoNTimes);

When I now type in Minecraft:

/cast registerDoNTimes

This function executes, and perscribo (Latin for “to register”) registers doNTimes in my Lore. Magikcraft uses my playername — which is guaranteed to be globally unique — as the namespace for my Lore.

So players on this server can now do this:

var magik = magikcraft.io;
function fireworks(){
var there = magik.illic();
magik.stella(there);
}
magik.Lore.sitapati.doNTimes(fireworks, 5);

To cast that spell, a player types in Minecraft:

/cast fireworks

The same spell taking an integer as an argument would be this:

var magik = magikcraft.io;
function fireworks(n){
if (!n) n = 5; // Optional: 5 by default
var there = magik.illic();
   function firework(){         
magik.stella(there);
}
   magik.Lore.sitapati.doNTimes(firework, n);
}
magik.invoco(fireworks);    // Apply args from Minecraft

And it would be cast like this in Minecraft:

/cast fireworks 8

Anything over 10 results in only 10 rockets being sent at once.

There are all kinds of things going on here including closures, but the idea is to have kids using them with the opportunity discover the underlying patterns, rather than explicitly teaching them.

But it could still be a loop, since it’s encapsulated!

Since the implementation of doNTimes is hidden and the inputs can be range limited, the implementation of doNTimes could just as well be:

function doNTimes(action, n){   
if (n > maxN) n = maxN; // Range limit!
    for (var i = 0; i < n; i++)
action(i); // pass in the iterator
}

In this case you could make an iterator available to the function, something that is missing in the recursion implementation — although you could add that in the recursion implementation as well.

Anyhoo, that’s a practical example of extending the Magikcraft API.

StackOverflow has an interesting discussion on recursion vs iteration, but I don’t know which will perform better in the Nashorn JavaScript engine. Probs iteration.

Conclusion

The Magikcraft API is extensible. Using magik.prescribo you can add Spells to your Lore, and your padawans can learn them at your feet as you build the might and the myth of your very own School of Magik!

About me: I’m a coder, a father, and the founder of Magikcraft, the world’s most popular platform for incidental learning of coding.