middleware should now handle multiple turtles
This commit is contained in:
parent
369f8a54f4
commit
ff6aaf5cdf
|
@ -1233,9 +1233,21 @@ Anyway, here's the tl;dr for basic process linking:
|
||||||
* `monitor/1` means we can monitor exit messages, these will be `(:exit, pid, (:ok, value))` in the case of successful return, or `(:exit, pid, (:err, msg))` in the case of a panic. These are sent as normal messages to the monitoring process.
|
* `monitor/1` means we can monitor exit messages, these will be `(:exit, pid, (:ok, value))` in the case of successful return, or `(:exit, pid, (:err, msg))` in the case of a panic. These are sent as normal messages to the monitoring process.
|
||||||
|
|
||||||
Erlang/Elixir has a lot more, but that's basically it for Ludus for now.
|
Erlang/Elixir has a lot more, but that's basically it for Ludus for now.
|
||||||
|
I don't think we need more than the binary: `:ok`/`:err`, or return/panic as how processes exit.
|
||||||
`monitor/1` makes it easy to write an `await`.
|
`monitor/1` makes it easy to write an `await`.
|
||||||
|
We would need a way of killing processes remotely if indeed we wanted to write robust distributed applications.
|
||||||
|
But I'm less interested in that than in the cognitive modelling here.
|
||||||
|
|
||||||
As I'm thinking about this, I actually want to roll back `spawn` as a special form.
|
As I'm thinking about this, I actually want to roll back `spawn` as a special form.
|
||||||
I think it's actually a good cognitive shift to understand that functions are reified, deferred computations.
|
I think it's actually a good cognitive shift to understand that functions are reified, deferred computations.
|
||||||
And that processes take this a long way.
|
And that processes take this a long way.
|
||||||
We can revisit the idea of a special spawn form later.
|
We can revisit the idea of a special spawn form later.
|
||||||
|
|
||||||
|
=> done
|
||||||
|
|
||||||
|
And now, we build capacity for building projects.
|
||||||
|
|
||||||
|
* [ ] allow for multiple turtles
|
||||||
|
- [ ] rework p5 function in `ludus.js` to properly parse commands targeted at different turtles
|
||||||
|
- [ ] write ludus code to spawn & manipulate turtle actors
|
||||||
|
* [ ] do some sample multiturtle sketches
|
||||||
|
|
66
pkg/ludus.js
66
pkg/ludus.js
|
@ -1,4 +1,4 @@
|
||||||
if (window) window.ludus = {run, kill, flush_stdout, stdout, p5, svg, flush_commands, commands, result, flush_result, input, is_running, key_down, key_up, is_starting_up}
|
if (window) window.ludus = {run, kill, flush_stdout, stdout, p5, new_p5, svg, flush_commands, commands, result, flush_result, input, is_running, key_down, key_up, is_starting_up}
|
||||||
|
|
||||||
const worker_url = new URL("worker.js", import.meta.url)
|
const worker_url = new URL("worker.js", import.meta.url)
|
||||||
const worker = new Worker(worker_url, {type: "module"})
|
const worker = new Worker(worker_url, {type: "module"})
|
||||||
|
@ -51,7 +51,7 @@ async function handle_messages (e) {
|
||||||
case "Commands": {
|
case "Commands": {
|
||||||
console.log("Main: ludus commands => ", msg.data)
|
console.log("Main: ludus commands => ", msg.data)
|
||||||
for (const command of msg.data) {
|
for (const command of msg.data) {
|
||||||
// attempt to solve out-of-order command bug
|
// commands will arrive asynchronously; ensure correct ordering
|
||||||
ludus_commands[command[1]] = command
|
ludus_commands[command[1]] = command
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
|
@ -236,69 +236,71 @@ function unit_of (heading) {
|
||||||
return [Math.cos(radians), Math.sin(radians)]
|
return [Math.cos(radians), Math.sin(radians)]
|
||||||
}
|
}
|
||||||
|
|
||||||
function command_to_state (prev_state, command) {
|
function command_to_state (state, command) {
|
||||||
const [_target, _id, curr_command] = command
|
const [target, _id, curr_command] = command
|
||||||
|
const state_stack = state[target] ?? [turtle_init]
|
||||||
|
const prev_state = state_stack[state_stack.length - 1]
|
||||||
const [verb] = curr_command
|
const [verb] = curr_command
|
||||||
switch (verb) {
|
switch (verb) {
|
||||||
case "goto": {
|
case "goto": {
|
||||||
const [_, x, y] = curr_command
|
const [_, x, y] = curr_command
|
||||||
return {...prev_state, position: [x, y]}
|
return [target, {...prev_state, position: [x, y]}]
|
||||||
}
|
}
|
||||||
case "home": {
|
case "home": {
|
||||||
return {...prev_state, position: [0, 0], heading: 0}
|
return [target, {...prev_state, position: [0, 0], heading: 0}]
|
||||||
}
|
}
|
||||||
case "right": {
|
case "right": {
|
||||||
const [_, angle] = curr_command
|
const [_, angle] = curr_command
|
||||||
const {heading} = prev_state
|
const {heading} = prev_state
|
||||||
return {...prev_state, heading: heading + angle}
|
return [target, {...prev_state, heading: heading + angle}]
|
||||||
}
|
}
|
||||||
case "left": {
|
case "left": {
|
||||||
const [_, angle] = curr_command
|
const [_, angle] = curr_command
|
||||||
const {heading} = prev_state
|
const {heading} = prev_state
|
||||||
return {...prev_state, heading: heading - angle}
|
return [target, {...prev_state, heading: heading - angle}]
|
||||||
}
|
}
|
||||||
case "forward": {
|
case "forward": {
|
||||||
const [_, steps] = curr_command
|
const [_, steps] = curr_command
|
||||||
const {heading, position} = prev_state
|
const {heading, position} = prev_state
|
||||||
const unit = unit_of(heading)
|
const unit = unit_of(heading)
|
||||||
const move = mult(unit, steps)
|
const move = mult(unit, steps)
|
||||||
return {...prev_state, position: add(position, move)}
|
return [target, {...prev_state, position: add(position, move)}]
|
||||||
}
|
}
|
||||||
case "back": {
|
case "back": {
|
||||||
const [_, steps] = curr_command
|
const [_, steps] = curr_command
|
||||||
const {heading, position} = prev_state
|
const {heading, position} = prev_state
|
||||||
const unit = unit_of(heading)
|
const unit = unit_of(heading)
|
||||||
const move = mult(unit, -steps)
|
const move = mult(unit, -steps)
|
||||||
return {...prev_state, position: add(position, move)}
|
return [target, {...prev_state, position: add(position, move)}]
|
||||||
}
|
}
|
||||||
case "penup": {
|
case "penup": {
|
||||||
return {...prev_state, pendown: false}
|
return [target, {...prev_state, pendown: false}]
|
||||||
}
|
}
|
||||||
case "pendown": {
|
case "pendown": {
|
||||||
return {...prev_state, pendown: true}
|
return [target, {...prev_state, pendown: true}]
|
||||||
}
|
}
|
||||||
case "penwidth": {
|
case "penwidth": {
|
||||||
const [_, width] = curr_command
|
const [_, width] = curr_command
|
||||||
return {...prev_state, penwidth: width}
|
return [target, {...prev_state, penwidth: width}]
|
||||||
}
|
}
|
||||||
case "pencolor": {
|
case "pencolor": {
|
||||||
const [_, color] = curr_command
|
const [_, color] = curr_command
|
||||||
return {...prev_state, pencolor: color}
|
return [target, {...prev_state, pencolor: color}]
|
||||||
}
|
}
|
||||||
case "setheading": {
|
case "setheading": {
|
||||||
const [_, heading] = curr_command
|
const [_, heading] = curr_command
|
||||||
return {...prev_state, heading: heading}
|
return [target, {...prev_state, heading: heading}]
|
||||||
}
|
}
|
||||||
case "loadstate": {
|
case "loadstate": {
|
||||||
// console.log("LOADSTATE: ", curr_command)
|
// console.log("LOADSTATE: ", curr_command)
|
||||||
const [_, [x, y], heading, visible, pendown, penwidth, pencolor] = curr_command
|
const [_, [x, y], heading, visible, pendown, penwidth, pencolor] = curr_command
|
||||||
return {position: [x, y], heading, visible, pendown, penwidth, pencolor}
|
return [target, {position: [x, y], heading, visible, pendown, penwidth, pencolor}]
|
||||||
}
|
}
|
||||||
case "show": {
|
case "show": {
|
||||||
return {...prev_state, visible: true}
|
return [target, {...prev_state, visible: true}]
|
||||||
}
|
}
|
||||||
case "hide": {
|
case "hide": {
|
||||||
return {...prev_state, visible: false}
|
return [target, {...prev_state, visible: false}]
|
||||||
}
|
}
|
||||||
case "background": {
|
case "background": {
|
||||||
background_color = curr_command[1]
|
background_color = curr_command[1]
|
||||||
|
@ -532,4 +534,32 @@ export function p5 (commands) {
|
||||||
return p5_calls
|
return p5_calls
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function new_p5 (commands) {
|
||||||
|
const all_states = {}
|
||||||
|
commands.reduce((prev_state, command) => {
|
||||||
|
const [turtle_id, new_state] = command_to_state(prev_state, command)
|
||||||
|
if (!all_states[turtle_id]) all_states[turtle_id] = [turtle_init]
|
||||||
|
all_states[turtle_id].push(new_state)
|
||||||
|
return new_state
|
||||||
|
}, all_states)
|
||||||
|
const [r, g, b, _] = resolve_color(background_color)
|
||||||
|
if ((r + g + b)/3 > 128) turtle_color = [0, 0, 0, 150]
|
||||||
|
const p5_calls = [...p5_call_root()]
|
||||||
|
for (const states of Object.values(all_states)) {
|
||||||
|
console.log(states)
|
||||||
|
for (let i = 1; i < states.length; ++i) {
|
||||||
|
const prev = states[i - 1]
|
||||||
|
const curr = states[i]
|
||||||
|
const calls = states_to_call(prev, curr)
|
||||||
|
for (const call of calls) {
|
||||||
|
p5_calls.push(call)
|
||||||
|
}
|
||||||
|
p5_calls[0] = ["background", ...resolve_color(background_color)]
|
||||||
|
p5_render_turtle(states[states.length - 1], p5_calls)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
p5_calls.push(["pop"])
|
||||||
|
return p5_calls
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user