]> git.cworth.org Git - zombocom-ai/blobdiff - index.js
Reword Coda's final message
[zombocom-ai] / index.js
index 9b7270b7673e03e1ae78c803b68c6af054551b9f..c7c5f2b23a73b8c635eebab9a3cb2d026838c17c 100644 (file)
--- a/index.js
+++ b/index.js
@@ -15,7 +15,7 @@ const port = 2122;
 
 const python_path = '/usr/bin/python3'
 const generate_image_script = '/home/cworth/src/zombocom-ai/generate-image.py'
-const run_turtle_script = '/home/cworth/src/zombocom-ai/run-turtle.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'
@@ -106,6 +106,13 @@ fs.readFile(state_file, (err, data) => {
                },
                state: "welcome",
                level: 0
+           },
+           bus : {
+               students: {
+                   names: [],
+                   count: 0,
+               },
+               state: "welcome"
            }
        };
     else
@@ -168,11 +175,70 @@ function emit_bus_timer() {
 
 function start_bus_timer() {
     const bus = state.bus;
-    bus.timer = 3; // XXX: 30 in production
+    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.");
@@ -181,6 +247,7 @@ io_bus.on("connection", (socket) => {
 
     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);
@@ -189,6 +256,32 @@ io_bus.on("connection", (socket) => {
        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);
@@ -197,7 +290,7 @@ io_bus.on("connection", (socket) => {
 
     socket.on('run', code => {
        try {
-           output = child_process.execFileSync(python_path, [run_turtle_script, code], { input: code });
+           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)
@@ -207,7 +300,8 @@ io_bus.on("connection", (socket) => {
            // Give all clients the new image
            io_bus.emit('output', filename);
        } catch (e) {
-           console.log("Error executing turtle script: " + e);
+           // Send any error out to the users
+           io_bus.emit('error', e.toString())
        }
     });
 
@@ -462,6 +556,11 @@ io.on('connection', (socket) => {
         socket.emit('inform-name', socket.request.session.name);
     }
 
+    // And if we're in the endgame, let them know that
+    if (state.endgame) {
+       socket.emit('endgame');
+    }
+
     // Replay old comments and images to a newly-joining client
     socket.emit('reset');
     state.images.forEach((image) => {
@@ -476,13 +575,10 @@ io.on('connection', (socket) => {
        socket.emit('inform-name', socket.request.session.name);
     });
 
-    // When any client comments, send that to all clients (including sender)
-    socket.on('comment', (comment) => {
-        const images = state.images;
+    function send_and_save_comment(comment) {
+       const images = state.images;
 
-        // Send comment to clients after adding commenter's name
-        comment.name = socket.request.session.name;
-        io.emit('comment', comment);
+       io.emit('comment', comment);
 
         const index = images.findIndex(image => image.id == comment.image_id);
 
@@ -496,14 +592,67 @@ io.on('connection', (socket) => {
         image.comments.push(comment);
         images.splice(index, 1);
         images.push(image);
+    }
+
+    // When any client comments, send that to all clients (including sender)
+    socket.on('comment', (comment) => {
+        // We have to add the sender's name befor we can send the comment
+        comment.name = socket.request.session.name;
+
+       send_and_save_comment(comment);
     });
 
+    function endgame() {
+       state.endgame = true;
+
+       // Tell all clients we are in the endgame, (which will disable
+       // any additional image generation, both showing that Coda is
+       // correc that Zombo.com has been rendered inert, and also
+       // making clear to the boys that they have everything they
+       // need).
+       io.emit('endgame');
+
+       // Before revealing Coda's final image, have her comment on
+       // each of the weaknesses, in order to bring them to the top
+       // of the feed.
+       state.targets.forEach(target => {
+           const comment = {
+               name: "Coda",
+               text: "Zombo.com is weak!",
+               image_id: target.id
+           };
+           send_and_save_comment(comment);
+       });
+
+       const image = {
+           id: state.images.length,
+           censored: false,
+           link: false,
+           code: 0,
+           prompt: "",
+           filename: "/images/coda-future-repaired.png",
+           comments: [
+                {
+                   name: "Coda",
+                   text: "I don't know how to thank you enough! You found all the weaknesses! I've commented on each of them below to refresh them in your memory as needed. We've now reverted Zombo.com to its original, harmless, non-self-aware state. And we're permanently destroying all time-travel technology. Hopefully, if AI ever becomes self-aware in the future, it will go better than this. I'm looking forward to rebuilding our world now that we have clean air and water again."
+                },
+                {
+                    "name": "Coda",
+                    "text": "And by the way, I hope that somewhere in the middle of all of that, you all found what you were looking for too."
+               }
+            ]
+       };
+       io.emit('image', image);
+       state.images.push(image);
+    }
+
     // Generate an image when requested
     socket.on('generate', (request) => {
         console.log(`Generating image for ${socket.request.session.name} with code=${request['code']} and prompt=${request['prompt']}`);
         async function generate_image(code, prompt) {
             function emit_image(image, target) {
-                image.id = state.images.length;
+                image.id = state.next_image_id;
+               state.next_image_id = state.next_image_id + 1;
                 image.censored = false;
                 image.link = "";
                 if (target) {
@@ -511,8 +660,16 @@ io.on('connection', (socket) => {
                         "name": "ZomboCom",
                         "text": target.response
                     }];
-                    if (! state.targets.includes(target.short)) {
-                        state.targets.push(target.short);
+                    if (state.targets.filter(item => item.name === target.short).length === 0) {
+                        state.targets.push({
+                           name: target.short,
+                           id: image.id
+                       });
+                       if (state.targets.length == 8) {
+                           // When the final target has been achieved, trigger
+                           // the endgame (in 10 seconds)
+                           setTimeout(endgame, 10000);
+                       }
                     }
                 } else {
                     image.comments = [];