Archives November 2020

Quick Play: Warped Logo Spirals

One of the best things about turtleSpaces is its capacity for discovery through play.

Today I was playing with spirals:

I started off by creating a simple spiral using the RARC primitive and a simple REPEAT loop. This loop uses REPCOUNT (the current repeat loop iteration) to define the radius of the arc, resulting in an ever-growing spiral.

I clicked and dragged on the window to rotate the camera around the spiral. But let’s make the spiral itself three-dimensional:

I’ve ‘warped’ the spiral by placing three rarcs inside of the repeat loop, along with a ROLLLEFT (rl) and a ROLLRIGHT (rr). With the twisting and turning, the turtle ends up roughly halfway around a somewhat distorted circle, which you can see if you type:

cs rarc 90 20 rr 90 rarc 90 20 rl 90 rarc 90 20

cs repeat 2 [rarc 90 20 rr 90 rarc 90 20 rl 90 rarc 90 20]

If we orbit the camera (by clicking and dragging) around our spiral, we can see just how warped it is!

What else can we do with it?

Whoa! What happened there? I added a dn (down) 10 to the end of the loop. Let’s take a look from another vantage point:

That’s pretty distorted. Let’s try a smaller value, say 2:

And what about 5?

What about down (dn) instead of up?

Okay, pretty neat. How about if at the end of each loop we also roll left (rl) a little?

Another angle:

Let’s play around with the values of down (dn) and rollleft (rl) a bit:

What if we do a right turn (rt) instead of a roll right (rr)?

And what if we add the roll back in?

Let’s play around with the values a bit:

All right, that’s enough from me! But you can play around with the different values and see what happens. That’s all part of the fun of turtleSpaces!


Make it Rain!

TO rain


  penup hideturtle
  setbackgroundcolor blue setbackgroundshade 12
  make "count 0

  forever [

    inc "count
    setposition {-300 + random 600 300 -300 + random 600}
    make "size 0.25 + ((1 + random 20) / 20)
    begintag "teardrop
    setfillcolor pick [2 3 6 7 14]
    setfillshade -10 + random 20
    up 90
    cutsphere 10 * :size 20 20 1 13
    lower 4.5 * :size
    cone 8.9 * :size 18 * :size 20
    down 90
    newmodel :count "teardrop
    hatch [
      setmodel :count
      make "speed (5 + random 10) / 10
      while ypos > -300 [
        back :speed rollright 0.25
    wait 5 + random 25


Control turtleSpaces from other applications with its API

You can communicate with turtleSpaces over a socket (telnet) connection to port 1967.

First you need to issue the API command inside of turtleSpaces to enable API connections, or use the -enable-api flag when starting up.

Then connect to turtleSpaces from an external application or terminal. You can issue instructions to affect turtles and the environment just like you would do inside the turtleSpaces application.

The API connection executes as its own worker. You can also redirect input and output to the API instance by using setread “api and setwrite “api – but these must be issued by the API worker itself to redirect to that worker (if you use another API connection, reading and writing will be redirected to that API instance, for example).

Using the API, you can use turtleSpaces as a visualiser for another application, and have it act as a light organ, or create charts and graphs.


‘Snake’ written in turtleSpaces Logo

TO snake
  ;empties (erases) all containers (variables),
  ;returns the turtles to their default positions,
  ;restores their states to defaults
  ;sets all the turtles, except for the default ones
  ;(myrtle, snappy, libby) 'free' - erasing them
  ;don't draw
  setbackgroundcolor lightblue
  ;don't show the executing turtle (typically myrtle)
  ;don't show up in output from the near primitive
  ;don't delay when playing sounds (the default)
  print |Use keys J and K to turn snake, eat apples!|
  ;print instructions to the screen
  ;create the 'ground'
  setfillcolor brown
  setpos [-185 -115]
  lower 5
  quad 185 * 2 115 * 2
  make "score 0
  make "length 8
  ;the default snake length, in segments
  make "queue empty
  ;this snake implementation uses a push / pull queue
  ;to keep track of the snake's turns
  repeat :length [push "fd "queue]
  ;all segments start moving forward
  newmodel "ball [setfillcolor 12 icosphere 5]
  newmodel "head [setfillcolor 4 icosphere 6]
  newmodel "apple [setfillcolor red icosphere 4]
  ;create turtle models, where ball is a non-head segment
  ;create the turtles that make up the starting snake:
  repeat :length [
    nameturtle repcount
    ;create a new turtle
    ;each segment has the number of its segment as its name
    control repcount {"wrap "penup "back repcount "* 10 "showturtle}
    ;initialize the new turtle and move it into position
    ;{} denotes a 'softlist', a list whose contents are evaluated
    ;at execution
    control repcount [
      setbounds [-185 -115 185 115]
    ;sets the bounds used for wrapping. [] denotes a 'hardlist', a
    ;list whose contents are fixed and not evaluated
    if repcount = 1 [control 1 [setmodel "head]]
    ;if this is the first segment, give it the 'head' model
    else [control repcount [setmodel "ball]]
    ;otherwise, give it the 'ball' (segment) model
  ;set up the 'apple' turtle:
  nameturtle "apple
  control "apple [
    penup setmodel "apple showturtle
    dountil (count near position 5) = 1 [
      setpos {-180 + (10 * random 36) -100 + (10 * random 20)}
  ;set a random position until such times as the apple is not near
  ;any snake segments (turtles)
  ;leading parameters used in comparison operators (=, >, < etc)
  ;must be enclosed in {}'s if more than a single value or primitive
  ;because the parser evaluates right to left
  ;the main loop:
  forever [
    ;don't render while we shuffle the snake forward
    for {"i 1 :length} [
      make "move item :i :queue
      ;set the move variable to the :i item in the :queue
      switch "move
      case "fd [ask :i [forward 10]]
      case "rt [ask :i [right 90 forward 10]]
      case "lt [ask :i [left 90 forward 10]]
      ;switch / case statements execute instructions based on the
      ;contents of the container (variable) provided to switch
      ;ask creates a new worker that executes the commands using
      ;the specified turtle (the turtle named by :i)
      ;so, in this for loop we are iterating through the :queue,
      ;moving the turtle segments based on the item present in
      ;each space in the queue (fd, rt or lt)
    while workers != [] [sleep 1]
    ;sleep execution until the ask workers finish moving their
    ;turtles (the snake). This way, when we enable the render,
    ;we won't catch the snake mid-move.
    ;enable rendering again
    wait 40 - :score
    ;wait units are 60ths of a second. The higher the score, the
    ;shorter the wait will be
    ;user control:
    if keyp [
      ;did the user press a key? If so, do this stuff:
      make "input readchar
      ;take the output from the readchar primitive and place it
      ;into the input container. keyp indicates if there is a character
      ;already in the keyboard buffer. If readchar is called when there
      ;is no character in the buffer, it waits.
      switch "input
      case "j [push "lt "queue]
      ;if the user pressed the j key, push lt to the front of the queue
      case "k [push "rt "queue]
      ;if the user pressed the k key, push rt to the front of the queue
      otherwise [push "fd "queue]
      ;if no case has been satisfied since switch (the user pressed a key
      ;other than j or k), push fd to the front of the queue
    else [push "fd "queue]
    ;similarly, if the user hasn't pressed any key, push fd to the front
    ;of the queue
    ignore dequeue "queue
    ;ignore (drop) the last value off the end of the :queue
    ;dequeue would ordinarily return this value, ignore sends it into
    ;the ether
    ;is there something occupying the same space as the head of the snake?
    if (count (near 1:position 5)) > 1 [
      ;near returns the names of the turtles within the specified distance (5)
      ;count returns the count of the number of turtles returned by near
      ;if there is more than one (the head itself), we will execute the following:
      if memberp "apple near 1:position 5 [
        ;is it an apple?
        playsound "chomp
        inc "score
        ;increment the value in the score container by one
        (print |Yum! Score:| :score)
        ;to pass more than the default number of parameters to a primitive, wrap
        ;it in parentheses (round brackets)
        dountil (count near apple:position 5) = 1 [
          control "apple [setpos {-180 + (10 * random 36) -100 + (10 * random 20)}]
        ;place a new apple
        repeat 2 + int (:score / 10) [
          queue "pa "queue
          ;a pa or 'pass' will cause these new segments not to move on the next
          ;iteration, until movement commands are 'shuffled' on to their places
          ;in the queue
          inc "length
          ;increment the value of the length container by one
          nameturtle :length
          ;create a new turtle with the name contained in :length (the new length)
          (control :length {"hideturtle "setmodel ""ball "wrap "penup "setpos
          "query :length - 1 leftbracket "position rightbracket "setvectors
          "query :length - 1 leftbracket "vectors rightbracket})
          ;set up the new turtle and make sure it's positioned next to the
          ;previously last turtle, and is facing in the same direction
          control :length [setbounds [-185 -115 185 115] showturtle]
          ;set the bounds of the new turtle and show it
          wait 10
        ;add new segment to the snake
      ;and if it's not an apple?
      else [
        print "crash! playsound "crash finish
      ;game over, man! game over!