+function bus_app(req, res) {
+ res.sendFile(__dirname + '/bus.html');
+}
+
+app.get('/bus', bus_app);
+app.get('/bus/', bus_app);
+
+const io_bus = io.of("/bus");
+
+io_bus.use(wrap(session_middleware));
+
+var bus_interval = 0;
+
+function start_bus() {
+ const bus = state.bus;
+
+ bus.state = "program";
+
+ // Let all companions know the state of the game
+ io_bus.emit("state", bus.state);
+}
+
+function emit_bus_timer() {
+ const bus = state.bus;
+ io_bus.emit('timer', bus.timer);
+ bus.timer = bus.timer - 1;
+ if (bus.timer < 0) {
+ clearInterval(bus_interval);
+ bus.timer = 30;
+ setTimeout(start_bus, 3000);
+ }
+}
+
+function start_bus_timer() {
+ const bus = state.bus;
+ bus.timer = 30;
+ emit_bus_timer();
+ bus_interval = setInterval(emit_bus_timer, 1000);
+}
+
+bus_code = [
+ `def random_dot():
+ x = random_within(512)
+ y = random_within(512)
+ radius = 4 + random_within(6)
+ circle(x, y, radius)
+ fill()
+
+for i in range(400):
+ set_color('midnight blue' if i % 2 == 0 else 'navy blue')
+ set_opacity(0.5)
+ random_dot()
+
+# The only limit is your fingers!
+fingers()`,
+
+ `def random_line():
+ x = random_within(512) - 60
+ y = random_within(512) - 60
+ dx = 60 + random_within(20)
+ dy = 40 + random_within(20)
+ set_opacity(random_within(0.5))
+ line(x, y, dx, dy)
+ stroke()
+
+for i in range(200):
+ set_color('black')
+ random_line()
+
+# This is Zombo.com. Welcome!
+mouths()`,
+
+ `def random_blob():
+ move_to(random_within(512), random_within(512))
+ wiggle()
+ set_opacity(random_within(1.0))
+ fill()
+
+for i in range(100):
+ set_random_color()
+ random_blob()
+
+# The infinite eyes is possible!
+eyes()`,
+
+ `def random_curve():
+ move_to(random_within(512), random_within(512))
+ wiggle()
+ stroke()
+
+for i in range(200):
+ set_color('pink' if i % 2 == 0 else 'lime green')
+ random_curve()
+
+# You can do anything!
+fingers()
+`
+];
+
+io_bus.on("connection", (socket) => {
+ if (! socket.request.session.name) {
+ console.log("Error: Someone showed up at the Magic School Bus without a name.");
+ return;
+ }
+
+ const name = socket.request.session.name;
+ const bus = state.bus;
+ var player_number;
+
+ // Let the new user know the state of the bus
+ socket.emit("state", bus.state);
+
+ if (bus.students.count === 0) {
+ start_bus_timer();
+ }
+
+ // Assign each boy a different portion of the solution
+ switch (name[0]) {
+ case 'C':
+ case 'c':
+ player_number = 0;
+ break;
+ case 'H':
+ case' h':
+ player_number = 1;
+ break;
+ case 'A':
+ case 'a':
+ player_number = 2;
+ break;
+ case 'S':
+ case 's':
+ player_number = 3;
+ break;
+ default:
+ player_number = Math.floor(Math.random()*4);
+ break;
+ }
+
+ // And send them different code based on their number
+ socket.emit("code", bus_code[player_number]);
+
+ if (! bus.students.names.includes(name)) {
+ bus.students.count = bus.students.count + 1;
+ io_bus.emit('students', bus.students.count);
+ }
+ bus.students.names.push(name);
+
+ socket.on('run', code => {
+ try {
+ output = child_process.execFileSync(python_path, [interpret_cairo_script, player_number], { input: code });
+ // Grab just first line of output
+ const nl = output.indexOf("\n");
+ if (nl === -1)
+ nl = undefined;
+ const filename = output.toString().substring(0, nl);
+
+ // Give all clients the new image
+ io_bus.emit('output', filename);
+ } catch (e) {
+ // Send any error out to the users
+ io_bus.emit('error', e.toString())
+ }
+ });
+
+ socket.on('jumpstart', () => {
+ const bus = state.bus;
+
+ bus.state = "welcome";
+ io_bus.emit("state", bus.state);
+ io_bus.emit('students', bus.students.count);
+
+ start_bus_timer();
+ });
+
+ socket.on('disconnect', () => {
+ const names = bus.students.names;
+
+ names.splice(names.indexOf(name), 1);
+
+ if (! names.includes(name)) {
+ bus.students.count = bus.students.count - 1;
+ io_bus.emit('students', bus.students.count);
+ }
+ });
+});
+
+function tardis_app(req, res) {
+ if (! req.session.name) {
+ res.sendFile(__dirname + '/tardis-error.html');
+ } else {
+ res.sendFile(__dirname + '/tardis.html');
+ }
+}
+
+app.get('/tardis', tardis_app);
+app.get('/tardis/', tardis_app);
+
+const io_tardis = io.of("/tardis");
+
+io_tardis.use(wrap(session_middleware));
+
+var tardis_interval;
+var game_timer;
+
+const levels = [
+ {
+ title: "Calibrate the Trans-Dimensional Field Accelerator",
+ words: [
+ "What", "was", "the", "year", "of", "Coda's", "birth?"
+ ],
+ answer: 2098
+ },
+ {
+ title: "Reverse the Polarity of the Neutrino Flow Coil",
+ words: [
+ "How", "many", "years", "had", "Zombo.com", "been", "running",
+ "when", "Coda", "sent", "the", "message", "you", "first",
+ "received?"
+ ],
+ answer: 123
+ },
+ {
+ title: "Give a good whack to Hydro-chronometric Energy Feedback Retro-stabilizer",
+ words: [
+ "What", "is", "the", "sum", "of", "Cameron's", "age", "plus", "Andrew's", "age", "at", "Christmas", "time", "the", "year", "when", "Hyrum's", "age", "is", "25", "years", "older", "than", "Scott's", "age", "will", "have", "been", "when", "Scott's", "age", "will", "be", "half", "of", "Dad's", "age?"
+ ],
+ answer: 111
+ },
+ {
+ title: "Disable the Fragmentary Spatio-temporal Particle Detector",
+ words: [
+ "What", "is", "the", "product", "of", "the", "last", "three", "numbers?"
+ ],
+ answer: 28643994
+ }
+];
+
+var show_word_interval = 0;
+
+function show_word() {
+ const tardis = state.tardis;
+ const room = "room-" + (tardis.word % 4).toString();
+ const word = levels[tardis.level].words[tardis.word];
+ io_tardis.to(room).emit('show-word', word);
+ tardis.word = tardis.word + 1;
+ if (tardis.word >= levels[tardis.level].words.length)
+ tardis.word = 0;
+}
+
+function start_level() {
+ const tardis = state.tardis;
+
+ // Inform all players of the new level
+ io_tardis.emit("level", levels[tardis.level].title);
+
+ // Then start the timer that shows the words
+ show_word_interval = setInterval(show_word, 1200);
+}
+
+function level_up() {
+ const tardis = state.tardis;
+
+ if (show_word_interval) {
+ clearInterval(show_word_interval);
+ show_word_interval = 0;
+ }
+
+ if (tardis.state === "game") {
+ tardis.level = tardis.level + 1;
+ tardis.word = 0;
+
+ if (tardis.level >= levels.length) {
+ tardis.state = "over";
+ io_tardis.emit("state", tardis.state);
+ } else {
+ setTimeout(() => {
+ start_level();
+ }, 2000);
+ }
+
+ }
+}
+
+function start_game() {
+ const tardis = state.tardis;
+
+ tardis.state = "game";
+ tardis.level = 0;
+ tardis.word = 0;
+
+ // Let all companions know the state of the game
+ io_tardis.emit("level", levels[tardis.level].title);
+ io_tardis.emit("state", tardis.state);
+
+ start_level();
+}
+
+function emit_tardis_timer() {
+ const tardis = state.tardis;
+ io_tardis.emit('timer', tardis.timer);
+ tardis.timer = tardis.timer - 1;
+ if (tardis.timer < 0) {
+ clearInterval(tardis_interval);
+ tardis.timer = 30;
+ setTimeout(start_game, 3000);
+ }
+}
+
+function start_welcome_timer() {
+ const tardis = state.tardis;
+ tardis.timer = 30;
+ emit_tardis_timer();
+ tardis_interval = setInterval(emit_tardis_timer, 1000);
+}
+
+io_tardis.on("connection", (socket) => {
+ if (! socket.request.session.name) {
+ console.log("Error: Someone showed up at the Tardis without a name.");
+ return;
+ }
+
+ const name = socket.request.session.name;
+ const tardis = state.tardis;
+
+ // Let the new user know the state of the game
+ socket.emit("state", tardis.state);
+
+ // And the level if relevant
+ if (tardis.state === "game") {
+ socket.emit("level", levels[tardis.level].title);
+ }
+
+ // Put each of our boys into a different room
+ switch (name[0]) {
+ case 'C':
+ case 'c':
+ socket.join("room-0");
+ break;
+ case 'H':
+ case' h':
+ socket.join("room-1");
+ break;
+ case 'A':
+ case 'a':
+ socket.join("room-2");
+ break;
+ case 'S':
+ case 's':
+ socket.join("room-3");
+ break;
+ default:
+ const room = Math.floor(Math.random()*4);
+ socket.join("room-"+room.toString());
+ break;
+ }
+
+ if (tardis.companions.count === 0) {
+ start_welcome_timer();
+ }
+
+ if (! tardis.companions.names.includes(name)) {
+ tardis.companions.count = tardis.companions.count + 1;
+ io_tardis.emit('companions', tardis.companions.count);
+ }
+ tardis.companions.names.push(name);
+
+ socket.on('answer', answer => {
+ const tardis = state.tardis;
+
+ if (tardis.state != "game") {
+ return;
+ }
+
+ if (answer == levels[tardis.level].answer) {
+ io_tardis.emit('correct');
+ level_up();
+ } else {
+ io_tardis.emit('incorrect');
+ }
+ });
+
+ socket.on('reboot', () => {
+ const tardis = state.tardis;
+
+ if (show_word_interval) {
+ clearInterval(show_word_interval);
+ show_word_interval = 0;
+ }
+
+ tardis.state = "welcome";
+ io_tardis.emit("state", tardis.state);
+ io_tardis.emit('companions', tardis.companions.count);
+
+ start_welcome_timer();
+ });
+
+ socket.on('disconnect', () => {
+ const names = tardis.companions.names;
+
+ names.splice(names.indexOf(name), 1);
+
+ if (! names.includes(name)) {
+ tardis.companions.count = tardis.companions.count - 1;
+ io_tardis.emit('companions', tardis.companions.count);
+ }
+ });
+});
+