Cycle 1 – DroidCo. Backend Development

For cycle 1, I built the backend system for a game I’ve been referring to as “DroidCo.” The goal of the game is to give non-programmers the opportunity to program their own AI in a head-to-head “code-off.”

The game starts with both players in front of sets of buttons (either digital or physical via makey makey). The buttons have keywords for the DroidCo. programming language. By pressing these buttons, both players build a program that each of their team’s droid will run. Here’s what a program might look like:

BEGIN
   IF-next-is-enemy
      hack
   END
   WHILE-next-is-empty
      move
   END
END

In addition to BEGIN and END, here are the keywords for the DroidCo. language:

After both players have completed their code, the second phase of the game begins. In this phase, a grid of spaces is populated by droids, half of which belong to one player and the other half belonging to the other player. Each second a “turn” goes by in which every droid takes an action determined by the code written by their owner. One of these actions could be “hack” which causes a droid to convert an enemy droid in the space it is facing into an ally droid. The goal is to create droids that “hack” all of your opponent’s droids, putting them all under your control.

The backend development is sorta an involved process to explain, so I may put the gritty details on my personal WordPress later, but here’s a boiled down version. We start with a long string that represents the program. For the sample program above it would be: “BEGIN IF-next-is-enemy hack END WHILE-next-is-empty move END END “. We give this string to the tokenizer that splits it up into an array of individual words. Here’s the tokenizer:

function tokenizer(tokenString){
	var tokens = [];
	var index = 0;
	var nextSpace = 0;
	while(index < tokenString.length){
		nextSpace = tokenString.indexOf(" ", index);
		tokens[tokens.length] = tokenString.substring(index, nextSpace);
		index = nextSpace+1;
	}
	return tokens;
}

This array is then given to the code generator, which converts it to a code “roadmap” in the form of an array of integers. Here’s the code generator:

function codeGenerator(code, tokens){
	var startIndex = code.length;
	var nextToken = tokens.shift();
	var dummy;
	var whileloop;
	while (tokens.length > 0 && nextToken != "END"){
		switch (nextToken){
			case "BEGIN":
				break;
			case "move":
				code[code.length] = -1;
				break;
			case "turn-right":
				code[code.length] = -2;
				break;
			case "turn-left":
				code[code.length] = -3;
				break;
			case "hack":
				code[code.length] = -4;
				break;
			case "skip":
				code[code.length] = -5;
				break;
			case "IF—next-is-ally":
				code[code.length] = -6;
				dummy = code.length;
				code[code.length] = -100;
				codeGenerator(code, tokens);
				code[dummy] = code.length;
				break;
			case "IF-next-is-not-ally":
				code[code.length] = -7;
				dummy = code.length;
				code[code.length] = -100;
				codeGenerator(code, tokens);
				code[dummy] = code.length;
				break;
			case "IF-next-is-enemy":
				code[code.length] = -8;
				dummy = code.length;
				code[code.length] = -100;
				codeGenerator(code, tokens);
				code[dummy] = code.length;
				break;
			case "IF-next-is-not-enemy":
				code[code.length] = -9;
				dummy = code.length;
				code[code.length] = -100;
				codeGenerator(code, tokens);
				code[dummy] = code.length;
				break;
			case "IF-next-is-wall":
				code[code.length] = -10;
				dummy = code.length;
				code[code.length] = -100;
				codeGenerator(code, tokens);
				code[dummy] = code.length;
				break;
			case "IF-next-is-not-wall":
				code[code.length] = -11;
				dummy = code.length;
				code[code.length] = -100;
				codeGenerator(code, tokens);
				code[dummy] = code.length;
				break;
			case "IF-next-is-empty":
				code[code.length] = -12;
				dummy = code.length;
				code[code.length] = -100;
				codeGenerator(code, tokens);
				code[dummy] = code.length;
				break;
			case "IF-next-is-not-empty":
				code[code.length] = -13;
				dummy = code.length;
				code[code.length] = -100;
				codeGenerator(code, tokens);
				code[dummy] = code.length;
				break;
			case "WHILE-next-is-ally":
				whileloop = code.length;
				code[code.length] = -6;
				dummy = code.length;
				code[code.length] = -100;
				codeGenerator(code, tokens);
				code[code.length] = whileloop;
				code[dummy] = code.length;
				break;
			case "WHILE-next-is-not-ally":
				whileloop = code.length;
				code[code.length] = -7;
				dummy = code.length;
				code[code.length] = -100;
				codeGenerator(code, tokens);
				code[code.length] = whileloop;
				code[dummy] = code.length;
				break;
			case "WHILE-next-is-enemy":
				whileloop = code.length;
				code[code.length] = -8;
				dummy = code.length;
				code[code.length] = -100;
				codeGenerator(code, tokens);
				code[code.length] = whileloop;
				code[dummy] = code.length;
				break;
			case "WHILE-next-is-not-enemy":
				whileloop = code.length;
				code[code.length] = -9;
				dummy = code.length;
				code[code.length] = -100;
				codeGenerator(code, tokens);
				code[code.length] = whileloop;
				code[dummy] = code.length;
				break;
			case "WHILE-next-is-wall":
				whileloop = code.length;
				code[code.length] = -10;
				dummy = code.length;
				code[code.length] = -100;
				codeGenerator(code, tokens);
				code[code.length] = whileloop;
				code[dummy] = code.length;
				break;
			case "WHILE-next-is-not-wall":
				whileloop = code.length;
				code[code.length] = -11;
				dummy = code.length;
				code[code.length] = -100;
				codeGenerator(code, tokens);
				code[code.length] = whileloop;
				code[dummy] = code.length;
				break;
			case "WHILE-next-is-empty":
				whileloop = code.length;
				code[code.length] = -12;
				dummy = code.length;
				code[code.length] = -100;
				codeGenerator(code, tokens);
				code[code.length] = whileloop;
				code[dummy] = code.length;
				break;
			case "WHILE-next-is-not-empty":
				whileloop = code.length;
				code[code.length] = -13;
				dummy = code.length;
				code[code.length] = -100;
				codeGenerator(code, tokens);
				code[code.length] = whileloop;
				code[dummy] = code.length;
				break;
		}
		nextToken = tokens.shift();
	}
}

This code is then given to the server, which stores both players’ codes and controls the game-state. Since the server is over 300 lines of code, I wont be posting it here. Nonetheless, each turn the server will run through the droids, allowing each of them to take an action to modify the game-state. Once they have all acted, it outputs the new game-state to both the display actors and back into itself so it can run the next round.



Leave a Reply