diff --git a/assets/test_prelude.ld b/assets/test_prelude.ld index e37673f..579c748 100644 --- a/assets/test_prelude.ld +++ b/assets/test_prelude.ld @@ -1052,125 +1052,126 @@ let turtle_init = #{ & turtle_commands is a list of commands, expressed as tuples box turtle_commands = [] box turtle_state = turtle_init +box turtle_states = #{:turtle_0 turtle_init} box command_id = 0 fn apply_command -fn add_command! (command) -> { +fn add_command! (turtle_id, command) -> { let idx = unbox (command_id) update! (command_id, inc) - update! (turtle_commands, append (_, (:turtle_0, idx, command))) - let prev = unbox (turtle_state) + update! (turtle_commands, append (_, (turtle_id, idx, command))) + let prev = do turtle_states > unbox > turtle_id let curr = apply_command (prev, command) - store! (turtle_state, curr) + update! (turtle_states, assoc (_, turtle_id, curr)) :ok } fn forward! { "Moves the turtle forward by a number of steps. Alias: fd!" - (steps as :number) -> add_command! ((:forward, steps)) + (steps as :number) -> add_command! (:turtle_0, (:forward, steps)) } let fd! = forward! fn back! { "Moves the turtle backward by a number of steps. Alias: bk!" - (steps as :number) -> add_command! ((:back, steps)) + (steps as :number) -> add_command! (:turtle_0, (:back, steps)) } let bk! = back! fn left! { "Rotates the turtle left, measured in turns. Alias: lt!" - (turns as :number) -> add_command! ((:left, turns)) + (turns as :number) -> add_command! (:turtle_0, (:left, turns)) } let lt! = left! fn right! { "Rotates the turtle right, measured in turns. Alias: rt!" - (turns as :number) -> add_command! ((:right, turns)) + (turns as :number) -> add_command! (:turtle_0, (:right, turns)) } let rt! = right! fn penup! { "Lifts the turtle's pen, stopping it from drawing. Alias: pu!" - () -> add_command! ((:penup)) + () -> add_command! (:turtle_0, (:penup)) } let pu! = penup! fn pendown! { "Lowers the turtle's pen, causing it to draw. Alias: pd!" - () -> add_command! ((:pendown)) + () -> add_command! (:turtle_0, (:pendown)) } let pd! = pendown! fn pencolor! { "Changes the turtle's pen color. Takes a single grayscale value, an rgb tuple, or an rgba tuple. Alias: pc!" - (color as :keyword) -> add_command! ((:pencolor, color)) - (gray as :number) -> add_command! ((:pencolor, (gray, gray, gray, 255))) - ((r as :number, g as :number, b as :number)) -> add_command! ((:pencolor, (r, g, b, 255))) - ((r as :number, g as :number, b as :number, a as :number)) -> add_command! ((:pencolor, (r, g, b, a))) + (color as :keyword) -> add_command! (:turtle_0, (:pencolor, color)) + (gray as :number) -> add_command! (:turtle_0, (:pencolor, (gray, gray, gray, 255))) + ((r as :number, g as :number, b as :number)) -> add_command! (:turtle_0, (:pencolor, (r, g, b, 255))) + ((r as :number, g as :number, b as :number, a as :number)) -> add_command! (:turtle_0, (:pencolor, (r, g, b, a))) } let pc! = pencolor! fn penwidth! { "Sets the width of the turtle's pen, measured in pixels. Alias: pw!" - (width as :number) -> add_command! ((:penwidth, width)) + (width as :number) -> add_command! (:turtle_0, (:penwidth, width)) } let pw! = penwidth! fn background! { "Sets the background color behind the turtle and path. Alias: bg!" - (color as :keyword) -> add_command! ((:background, color)) - (gray as :number) -> add_command! ((:background, (gray, gray, gray, 255))) - ((r as :number, g as :number, b as :number)) -> add_command! ((:background, (r, g, b, 255))) - ((r as :number, g as :number, b as :number, a as :number)) -> add_command! ((:background, (r, g, b, a))) + (color as :keyword) -> add_command! (:turtle_0, (:background, color)) + (gray as :number) -> add_command! (:turtle_0, (:background, (gray, gray, gray, 255))) + ((r as :number, g as :number, b as :number)) -> add_command! (:turtle_0, (:background, (r, g, b, 255))) + ((r as :number, g as :number, b as :number, a as :number)) -> add_command! (:turtle_0, (:background, (r, g, b, a))) } let bg! = background! fn home! { "Sends the turtle home: to the centre of the screen, pointing up. If the pen is down, the turtle will draw a path to home." - () -> add_command! ((:home)) + () -> add_command! (:turtle_0, (:home)) } fn clear! { "Clears the canvas and sends the turtle home." - () -> add_command! ((:clear)) + () -> add_command! (:turtle_0, (:clear)) } fn goto! { "Sends the turtle to (x, y) coordinates. If the pen is down, the turtle will draw a path to its new location." - (x as :number, y as :number) -> add_command! ((:goto, (x, y))) + (x as :number, y as :number) -> add_command! (:turtle_0, (:goto, (x, y))) ((x, y)) -> goto! (x, y) } fn setheading! { "Sets the turtle's heading. The angle is specified in turns, with 0 pointing up. Increasing values rotate the turtle counter-clockwise." - (heading as :number) -> add_command! ((:setheading, heading)) + (heading as :number) -> add_command! (:turtle_0, (:setheading, heading)) } fn showturtle! { "If the turtle is hidden, shows the turtle. If the turtle is already visible, does nothing." - () -> add_command! ((:show)) + () -> add_command! (:turtle_0, (:show)) } fn hideturtle! { "If the turtle is visible, hides it. If the turtle is already hidden, does nothing." - () -> add_command! ((:hide)) + () -> add_command! (:turtle_0, (:hide)) } fn loadstate! { "Sets the turtle state to a previously saved state." (state) -> { let #{position, heading, pendown?, pencolor, penwidth, visible?} = state - add_command! ((:loadstate, position, heading, visible?, pendown?, penwidth, pencolor)) + add_command! (:turtle_0, (:loadstate, position, heading, visible?, pendown?, penwidth, pencolor)) } } @@ -1214,27 +1215,27 @@ fn apply_command { & position () -> (x, y) fn position { "Returns the turtle's current position." - () -> do turtle_state > unbox > :position + () -> do turtle_states > unbox > :turtle_0 > :position } fn heading { "Returns the turtle's current heading." - () -> do turtle_state > unbox > :heading + () -> do turtle_states > unbox > :turtle_0 > :heading } fn pendown? { "Returns the turtle's pen state: true if the pen is down." - () -> do turtle_state > unbox > :pendown? + () -> do turtle_states > unbox > :turtle_0 > :pendown? } fn pencolor { "Returns the turtle's pen color as an (r, g, b, a) tuple or keyword." - () -> do turtle_state > unbox > :pencolor + () -> do turtle_states > unbox > :turtle_0 > :pencolor } fn penwidth { "Returns the turtle's pen width in pixels." - () -> do turtle_state > unbox > :penwidth + () -> do turtle_states > unbox > :turtle_0 > :penwidth } &&& fake some lispisms with tuples diff --git a/may_2025_thoughts.md b/may_2025_thoughts.md index be540bb..425fa1e 100644 --- a/may_2025_thoughts.md +++ b/may_2025_thoughts.md @@ -1248,6 +1248,6 @@ We can revisit the idea of a special spawn form later. 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 + - [x] 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 diff --git a/pkg/rudus_bg.wasm b/pkg/rudus_bg.wasm index 9511fd3..af2293c 100644 Binary files a/pkg/rudus_bg.wasm and b/pkg/rudus_bg.wasm differ diff --git a/src/vm.rs b/src/vm.rs index f1d8a09..b2c3300 100644 --- a/src/vm.rs +++ b/src/vm.rs @@ -1117,6 +1117,14 @@ impl Creature { self.call_stack.push(frame); self.ip = 0; } + Value::Keyword(key) => { + let dict = self.pop(); + match dict { + Value::Dict(d) => self + .push(d.get(&Key::Keyword(key)).unwrap_or(&Value::Nil).clone()), + _ => self.push(Value::Nil), + } + } _ => { return self.panic_with(format!("{} is not a function", called.show())) } @@ -1229,6 +1237,15 @@ impl Creature { self.call_stack.push(frame); self.ip = 0; } + Value::Keyword(key) => { + let dict = self.pop(); + match dict { + Value::Dict(d) => self + .push(d.get(&Key::Keyword(key)).unwrap_or(&Value::Nil).clone()), + _ => self.push(Value::Nil), + } + } + _ => { return self.panic_with(format!("{} is not a function", called.show())) }