From: Carl Worth Date: Fri, 23 Dec 2022 01:13:55 +0000 (-0800) Subject: Add initial structure of a Magic School Bus puzzle X-Git-Url: https://git.cworth.org/git?p=zombocom-ai;a=commitdiff_plain;h=f49cdc229d3db14bb00667c9e6d105c1d67274a8 Add initial structure of a Magic School Bus puzzle This has the same sort of introduction as the TARDIS puzzle (zooming into the time machine), but this time it's the Magic School Bus, not the TARDIS, of course. Then, there's an interactive form for making some turtle art, and an image showing the result. Currently, the result is hard-coded inside the script, but it should be simple to make it consume the form input, (which is already being sent to the server at least). --- diff --git a/bus.html b/bus.html new file mode 100644 index 0000000..845b048 --- /dev/null +++ b/bus.html @@ -0,0 +1,217 @@ + + + + + + ZOMBO + + + + + + + + + +
+ + + + + + + +
+ + + + + diff --git a/index.js b/index.js index cb46c20..9b7270b 100644 --- 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 run_turtle_script = '/home/cworth/src/zombocom-ai/run-turtle.py' const state_file = 'zombocom-state.json' const targets_dir = '/srv/cworth.org/zombocom/targets' const images_dir = '/srv/cworth.org/zombocom/images' @@ -132,6 +133,106 @@ 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 = 3; // XXX: 30 in production + emit_bus_timer(); + bus_interval = setInterval(emit_bus_timer, 1000); +} + +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; + + // Let the new user know the state of the bus + socket.emit("state", bus.state); + + if (bus.students.count === 0) { + start_bus_timer(); + } + + 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, [run_turtle_script, code], { 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) { + console.log("Error executing turtle script: " + e); + } + }); + + 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'); diff --git a/run-turtle.py b/run-turtle.py new file mode 100755 index 0000000..fb74b02 --- /dev/null +++ b/run-turtle.py @@ -0,0 +1,34 @@ +#!/usr/bin/env python3 + +from svg_turtle import SvgTurtle +import tempfile +import os + +OUTPUT_DIR_PREFIX='/srv/cworth.org/zombocom' +OUTPUT_DIR="{}/busart".format(OUTPUT_DIR_PREFIX) + +t = SvgTurtle(512,512); + +t.pencolor('red'); + +t.penup(); +t.right(180); +t.forward(200); +t.right(180); +t.pendown(); + +for i in range(50): + t.forward(100); + t.left(123); + +(fd, filename) = tempfile.mkstemp(suffix=".svg", prefix="busart", dir=OUTPUT_DIR); +os.close(fd) + +t.save_as(filename); +os.chmod(filename, 0o644); + +web_file = filename.removeprefix(OUTPUT_DIR_PREFIX); + +print(web_file); + + diff --git a/tardis.html b/tardis.html index 250ec3d..0609192 100644 --- a/tardis.html +++ b/tardis.html @@ -188,7 +188,6 @@ }); socket.on('timer', (value) => { - console.log("Receiving timer value of " + value); timer_div.style.visibility = "visible"; timer.textContent = value.toString();