]> git.cworth.org Git - zombocom-ai/blobdiff - index.js
Set bus timer to 30 seconds
[zombocom-ai] / index.js
index 1fa214bdb28ef79e512980de989f12d40f75e94a..4ed609917e9d53d7c749a8b9faf0dde00c2d8a53 100644 (file)
--- a/index.js
+++ b/index.js
@@ -1,8 +1,8 @@
 const fs = require('fs');
 
 const util = require('util');
-const execFile = util.promisify(require('child_process').execFile);
-
+const child_process = require('child_process');
+const execFile = util.promisify(child_process.execFile);
 const express = require('express');
 const app = express();
 const session = require('express-session');
@@ -15,6 +15,7 @@ const port = 2122;
 
 const python_path = '/usr/bin/python3'
 const generate_image_script = '/home/cworth/src/zombocom-ai/generate-image.py'
+const interpret_cairo_script = '/home/cworth/src/zombocom-ai/interpret-cairo-to-svg.py'
 const state_file = 'zombocom-state.json'
 const targets_dir = '/srv/cworth.org/zombocom/targets'
 const images_dir = '/srv/cworth.org/zombocom/images'
@@ -102,7 +103,16 @@ fs.readFile(state_file, (err, data) => {
                companions: {
                    names: [],
                    count: 0
-               }
+               },
+               state: "welcome",
+               level: 0
+           },
+           bus : {
+               students: {
+                   names: [],
+                   count: 0,
+               },
+               state: "welcome"
            }
        };
     else
@@ -130,8 +140,199 @@ app.get('/index.html', (req, res) => {
     res.sendFile(__dirname + '/index.html');
 });
 
+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) {
-    res.sendFile(__dirname + '/tardis.html');
+    if (! req.session.name) {
+       res.sendFile(__dirname + '/tardis-error.html');
+    } else {
+       res.sendFile(__dirname + '/tardis.html');
+    }
 }
 
 app.get('/tardis', tardis_app);
@@ -142,20 +343,120 @@ 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;
-    console.log("Emitting timer at " + tardis.timer);
     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) => {
-    console.log("In connection handler.");
     if (! socket.request.session.name) {
        console.log("Error: Someone showed up at the Tardis without a name.");
        return;
@@ -164,25 +465,84 @@ io_tardis.on("connection", (socket) => {
     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) {
-       tardis.timer = 30;
-       emit_tardis_timer();
-       tardis_interval = setInterval(emit_tardis_timer, 1000);
+       start_welcome_timer();
     }
 
     if (! tardis.companions.names.includes(name)) {
        tardis.companions.count = tardis.companions.count + 1;
-       console.log("Adding " + name + " for " + tardis.companions.count + " companions");
        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 (! tardis.companions.includes(name)) {
+       if (! names.includes(name)) {
            tardis.companions.count = tardis.companions.count - 1;
            io_tardis.emit('companions', tardis.companions.count);
        }