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.
|
||||
|
||||
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`.
|
||||
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.
|
||||
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.
|
||||
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 = new Worker(worker_url, {type: "module"})
|
||||
|
@ -51,7 +51,7 @@ async function handle_messages (e) {
|
|||
case "Commands": {
|
||||
console.log("Main: ludus commands => ", 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
|
||||
}
|
||||
break
|
||||
|
@ -236,69 +236,71 @@ function unit_of (heading) {
|
|||
return [Math.cos(radians), Math.sin(radians)]
|
||||
}
|
||||
|
||||
function command_to_state (prev_state, command) {
|
||||
const [_target, _id, curr_command] = command
|
||||
function command_to_state (state, 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
|
||||
switch (verb) {
|
||||
case "goto": {
|
||||
const [_, x, y] = curr_command
|
||||
return {...prev_state, position: [x, y]}
|
||||
return [target, {...prev_state, position: [x, y]}]
|
||||
}
|
||||
case "home": {
|
||||
return {...prev_state, position: [0, 0], heading: 0}
|
||||
return [target, {...prev_state, position: [0, 0], heading: 0}]
|
||||
}
|
||||
case "right": {
|
||||
const [_, angle] = curr_command
|
||||
const {heading} = prev_state
|
||||
return {...prev_state, heading: heading + angle}
|
||||
return [target, {...prev_state, heading: heading + angle}]
|
||||
}
|
||||
case "left": {
|
||||
const [_, angle] = curr_command
|
||||
const {heading} = prev_state
|
||||
return {...prev_state, heading: heading - angle}
|
||||
return [target, {...prev_state, heading: heading - angle}]
|
||||
}
|
||||
case "forward": {
|
||||
const [_, steps] = curr_command
|
||||
const {heading, position} = prev_state
|
||||
const unit = unit_of(heading)
|
||||
const move = mult(unit, steps)
|
||||
return {...prev_state, position: add(position, move)}
|
||||
return [target, {...prev_state, position: add(position, move)}]
|
||||
}
|
||||
case "back": {
|
||||
const [_, steps] = curr_command
|
||||
const {heading, position} = prev_state
|
||||
const unit = unit_of(heading)
|
||||
const move = mult(unit, -steps)
|
||||
return {...prev_state, position: add(position, move)}
|
||||
return [target, {...prev_state, position: add(position, move)}]
|
||||
}
|
||||
case "penup": {
|
||||
return {...prev_state, pendown: false}
|
||||
return [target, {...prev_state, pendown: false}]
|
||||
}
|
||||
case "pendown": {
|
||||
return {...prev_state, pendown: true}
|
||||
return [target, {...prev_state, pendown: true}]
|
||||
}
|
||||
case "penwidth": {
|
||||
const [_, width] = curr_command
|
||||
return {...prev_state, penwidth: width}
|
||||
return [target, {...prev_state, penwidth: width}]
|
||||
}
|
||||
case "pencolor": {
|
||||
const [_, color] = curr_command
|
||||
return {...prev_state, pencolor: color}
|
||||
return [target, {...prev_state, pencolor: color}]
|
||||
}
|
||||
case "setheading": {
|
||||
const [_, heading] = curr_command
|
||||
return {...prev_state, heading: heading}
|
||||
return [target, {...prev_state, heading: heading}]
|
||||
}
|
||||
case "loadstate": {
|
||||
// console.log("LOADSTATE: ", 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": {
|
||||
return {...prev_state, visible: true}
|
||||
return [target, {...prev_state, visible: true}]
|
||||
}
|
||||
case "hide": {
|
||||
return {...prev_state, visible: false}
|
||||
return [target, {...prev_state, visible: false}]
|
||||
}
|
||||
case "background": {
|
||||
background_color = curr_command[1]
|
||||
|
@ -532,4 +534,32 @@ export function p5 (commands) {
|
|||
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