Category Logo Coding Tutorials

Desert Mountain Lake — An Introduction to Fill

In a recent update to turtleSpaces, we added the ability to create arbitrary shapes, using the beginfill and endfill primitives.

To create a shape, you simply declare beginfill, draw out its borders and then declare endfill. If the lines do not cross each other, then the shape should be created.

For example:

beginfill forward 50 right 90 forward 50 endfill

will create a triangle. But we already have plenty of ways to create triangles, right?

If you add right 60 forward 20 to just before endfill, you’ll understand. You can create whatever shape you want, you just need to make sure your lines don’t cross!

So what can we do with this? Let’s start with a simple example. We could create randomly-generated mountains. Generative art is becoming popular, so let’s see what all the fuss is about.

Okay, so we’re going to draw out the shape of the mountains by starting at the far left, then angling the turtle 30 to 60 degrees to the right.

penup setx -250 beginfill right 30 + random 30

Next we’ll move forward 50 turtle units plus a random number made up of 100 minus the turtle’s current Y position. This will keep us (mostly) from going off the top of the viewable area.

dountil xpos > 220 [
  right 30 + random 30
  forward 50 + random 100 - ypos

Then we’ll turn right between 70 and 110 degrees, for the downslope, and move forward the value of the current y (vertical) position. This will keep us from going below the centerline of the viewable area.

  right 70 + random 40
  forward ypos

Then we’ll return the turtle to the upright position, and then keep creating new mountains until we end up off the right side of the viewable area.

  setheading 0
]

Then we set the turtle’s y position to 0 and declare the endfill:

sety 0
endfill

So that’s not too bad, but we want more! (We always want more.) We’ve made the background blue (using setbackgroundcolor blue) which is going to be our lake, but maybe we want the sky to be a different color. So let’s add a quad before we start drawing the mountains.

This is pretty simple, we’re just going to set the fill color to red and then create the quad at the point we’re going to start drawing the mountains:

setx -250
setfillcolor red
quad 500 150
raise 1/5

Note that we need to raise the turtle a little bit so our sky and the mountains don’t ‘Z-fight’ with each other.

Okay so what about that cool reflection effect? How do we do that? Well, it’s actually pretty simple.

All we’re going to do is log the positions of the turtle as we create the mountains, then ‘replay’ her movements, but with the y position reversed.

To do that, we’re first going to create an empty list, using make.

make "points []
setx -250

Next we’re going to queue our positions into the list. We need to do this in two places in our mountain creation loop, at the peaks and the troughs.

dountil xpos > 220 [
  queue position "points
  rt 30 + random 30
  fd 50 + random 100 - ypos
  queue position "points
  ...
  sety 0 queue position "points
  ;we need to make sure to 'log' the final position

Once the mountains are drawn, we simply declare beginfill again, and use foreach and setposition to move the turtle to each point we logged, but we’re going to use the list primitive to change the y value from a positive to a negative.

setfillshade 5
beginfill
foreach "i :points [
  setposition (list first :i negative second :i third :i)
]
endfill

In the full example we use definecolor to make our brown color a little more blue, for creating the reflection.

The sun is the proverbial icing on the cake. We’re going to make the sun and its reflection using the pie primitive to make half a circle. We’ll generate a number between 80 and 119 and put it into a container, representing the sun’s size.

make "sun 80 + random 40

We’ll position the turtle either at the left-third, center or right-third of the screen:

setxc pick [-71 0 71]

We’ll create the sun:

setfillcolor yellow
pie 180 :sun

Then we’ll redefine the yellow color to make it a bit more blue and subdued.

definecolor yellow [50 50 20]

Finally we’ll turn 180 degrees, create the reflection, then turn back.

right 180
pie 180 :sun
right 180
lower 1/10

We place all of this before we create the quad that draws the red background. So we need to lower the turtle to ensure the sun and the background don’t z-fight.

Here is the full procedure listing:

TO desertmountainlake
  reset hideturtle penup
  setbackgroundcolor blue
  definecolor red [100 0 0]
  definecolor brown [50 31 15]
  ;redefine the colors
  
  setx pick [-71 0 71]
  make "sun 80 + random 40
  setfillcolor yellow pie 180 :sun
  definecolor 13 [50 50 20]
  right 180 pie 180 :sun right 180
  lower 1/10
  ;draw the sun
  
  make "points []
  setx -250
  setfc red quad 500 150
  ;create the red sky quad
  
  raise 2 / 10 setfc orange
  beginfill
  ;start drawing the mountains
  dountil xpos > 220 [
    queue position "points
    right 30 + random 30
    forward 50 + random 100 - ypos
    queue position "points
    rt 70 + random 40
    ;rt is short for right, lt left etc
    fd ypos setheading 0
    ;fd is short for forward
  ]
  sety 0 queue position "points
  ;we need to queue the final position
  endfill
  ;close the fill
  
  ;the reflection:
  setfc brown setfs 5 beginfill
  foreach "i :points [
    setposition (list first :i negative second :i third :i)
  ]
  endfill
  
END

Logo and the Language Microworld

Logo isn’t just all about the turtles! When Logo was first being developed, there were no graphical displays — output was simple text on teletypes, which printed out interactions with the computer on paper. When the developers of Logo went into classrooms to test out their invention on students, all they had were teletypes.

As a result, the first Logo ‘microworld’ was the language microworld, not the geometry microworld which is more often associated with Logo. Logo was designed with many powerful commands used to manipulate strings and lists. Logo is based on Lisp, which stands for List Processing, a programming language in which data and code are the same, and are interchangeable and manipulatable by the running program. In Lisp, as in Logo, programs can modify, create and run themselves.

Some of these primitives (commands) include:

word – join two pieces of text together, eg word “dog “cat returns “dogcat

list – take two words and form a list, eg list “dog “cat returns [dog cat]

first, second, third, last – returns the appropriate character (word0 or item (list)

item, setitem – retrieve or change an item in a list or word based on its numerical index

fput, lput – returns a copy of a list with an additional item inserted in the front or back

butfirst, butlast – returns a list without the first or last item

leading, trailing – returns the front and back of words, respectively

push, pop, queue, dequeue – add or remove items on to the front or back of lists

There are also primitives that sort and search, retrieve words from a built-in English dictionary, convert characters to ASCII and back, and much more. You can browse them all in the Words and Lists reference.

So what can we do with all of these wonderful, powerful tools? Here’s a few examples:

Output

While we can manipulate strings and lists internally we still need to output the result to the user. In Logo, this can be done using the show, print and type primitives, and in turtleSpaces you can additionally typeset (create graphical text) or say (text-to-speech) them.

This example uses the print and the pick commands, the former types text into the console (text) area, following it with a line feed (carriage return) while the latter chooses (picks) a random item from a supplied list. In the example, this is used to generate a randomized simple story:

TO simplestory
  ;this language module contains a number of
  ;different procedures that manipulate language
  ;in fun ways!
  
  ;For example, this procedure uses the print
  ;and pick primitives to choose randomly from
  ;a series of lists, creating a simple one-line
  ;story:
  
  (
    ;encompassing the print command
    ;in round brackets allows us to supply it
    ;more parameters than normal. This also
    ;works with commands like say and functions
    ;like word and sentence
    
    print "The
    pick [
      red green blue
    ]
    pick [
      frog cow cat
    ]
    pick [
      jumped ran walked
    ]
    pick [
      |over to| over underneath
    ]
    ;to create a "word with spaces" (a phrase) in a list,
    ;a single list item containing spaces,
    ;enclose it with pipe symbols.
    
    "the
    word pick [
      overpass mountain |shopping mall|
    ] ".
    ;word is used here to create a single word
    ;made up of the final 'pick' and a period.
  )
END

Note that a ‘word’ in Logo is a single word, usually represented with a single double quote, eg “frog

Multiple words can either be represented in a list, eg [dog cat pig], or as a phrase |fat dog|, or both [|fat dog| |lazy cat| |pink pig|]

In the case of the last example, the second item in that list is |lazy cat|. You can turn |lazy cat| into a list of two items using the parse primitive.

A one-line version:

TO oneline
  ; a simple single line 'story':
  print (sentence pick [|The duck| |Old John| |A tabby cat| |Little Susie|] pick [ran walked surfed jumped danced] pick [|away from| towards] pick [|the city| |the mall| |their house|])
END

Questions and Answers, Repeats and Repcounts

The next example takes a user-supplied string and turns it into a word pyramid, by repeatedly displaying more and more of the word on successive lines.

It does this first by retrieving the desired word from the user using the question primitive, which prompts the user for input and then ‘stores’ the result in the ‘answer’ primitive, a function that returns the user’s input.

Then, it determines the length of the word using the count primitive, and employs repeat and repcount to generate the pyramid. repcount returns the current iteration of a repeat loop and is a very useful primitive.

reverse is also used in this example, which reverses a word or list.

TO wordpyramid
  ;this procedure creates a text 'pyramid'
  ;out of a string provided by the user:
  
  question |word| make "word answer
  ;the question primitive prompts the user
  ;for input, which is retrieved using the
  ;answer function
  
  ;the make primitive places the output from
  ;answer (the input to question) in a container
  ;called "word, which is read using :word
  
  repeat count :word [
    ;count is a function that returns the number
    ;of characters in the provided word, which is
    ;in this case the contents of the "word container
    ;symbolized using :word (which is the equivalent
    ;of 'thing "word', the primitive 'thing' able
    ;to retrieve and return the contents of containers.)
    
    repeat (count :word) - repcount [
      ;expressions are resolved right to left. What
      ;this means is that without the round brackets,
      ;Logo would try to subtract repcount (which returns
      ;the current number of repeat loop iterations)
      ;from the contents of the :word container, a string,
      ;which would result in an error. We solve this by
      ;placing round brackets around (count :word) to
      ;ensure that repcount is subtracted from the
      ;result of count :word instead
      
      type spacebar
      ;'type' prints a character(s) without adding a
      ;carriage return at the end, allowing one to type
      ;more on the same line. spacebar is a function that
      ;returns the space character
      
    ]
    if repcount = 1 [
      ;if the current repeat iteration is 1 (the first):
      print first :word
      ;print the first character of the contents of the :word container
    ] [
      ;otherwise:
      print (word
        ;print a word made up of:
        reverse leading repcount :word
        ;the leading (first) repeat loop iterations characters
        ;in the :word container, but in reverse.
        
        ;Remember, Logo evaluates expressions from right to left,
        ;so it first slices off the first however many 'leading'
        ;characters from the front of :word and then reverses it.
        
        butfirst leading repcount :word
        ;the second part of our combined word is the leading
        ;repcount number of characters, excluding (butfirst)
        ;the first character.
      )
    ]
  ]
  ;don't forget you can hover the mouse over keywords to
  ;get a popup that explains what they do!
END

Word(s) around the world!

The turtle is starting to get a bit bored, so let’s give her a bit of a workout! The next example uses the typeset and orbit primitives to create a ‘word circle’ out of user-provided input.

The orbit commands in turtleSpaces allow the turtle to orbit around a specific point in turtleSpace. We’re going to use the item primitive and a bit of math to distribute the input into a ring around the home [0 0 0] point:

TO wordcircle
  ;this procedure create a graphical 'word circle'
  ;out of the supplied word:
  
  clearscreen hideturtle
  dropanchor penup question |word|
  ;dropanchor moves the 'anchor point'
  ;used by orbit primitives to the turtle's
  ;current location
  
  pullout 8 * count answer
  ;pullout backs the turtle away from the
  ;anchor point without moving it with
  ;the turtle
  
  ;in this case, we're 'pullling out'
  ;8 turtle-units * the number of characters
  ;in the 'answer' provided to the question
  ;by the user
  
  repeat count answer [
    ;loop for however many characters are in the answer:
    
    randomfillcolor
    typeset item repcount answer
    ;typeset (create a graphical character) for the
    ;character 'item' in the place of the current repeat iteration
    ;eg if the word was 'frog', item 1 is 'f', item 4 is 'g'
    ;and on the third loop iteration it would be 'o'.
    
    slideleft 10
    ;the turtle moves when it typesets the character, but
    ;we're going to 'orbit' to make a circle, so we need to
    ;move the turtle back to where it started
    
    orbitright 360 / count answer
    ;'orbit' the turtle 360 degrees divided by the number of
    ;letters in the answer
  ]
  ;all done creating the circle
  
  forever [
    cam:rollright 1 wait 1
  ]
  ;rotate the camera forever
  
END

Spiral Stories

Similarly, this next example creates a spiral of words, which grow larger over time as the spiral is built. Each word is spoken by the computer’s text-to-speech facility. Rather than prompting for input, the ‘story’ is defined in a container (variable) using the make primitive.

While in the previous example, item was used to pick out each letter in the user’s input, here it is used to pick out each list item (word) in the “message (story) container.

The foreach primitive is used to typeset each letter in each word independently, to create a more flowing effect. This example demonstrates opportunities for creating text-based animations and artworks:

TO wordspiral
  ;creates a spiral made out of words from a given message:
  
  reset
  cam:pullin 100
  ;pulls the camera closer to the turtle
  cam:tandem myrtle
  ;causes the camera to match the turtle's movements
  penup
  ;don't draw lines
  
  make "message [Once upon a time there was a dog named Jim who used to deliver newspapers with his human Susan. One day they came upon an old house occupied by an ornery orange cat.]
  ;sets the message into the :message container.
  ;The message is a list of words,
  ;as opposed to a single word
  
  repeat count :message [
    ;repeat the following the same number of times
    ;as there are words in the message:
    
    cam:pullout 10
    
    repeat 19 [
      right 2 cam:rollright 2 wait 1
    ]
    ;turn in an animated way
    
    settypesize 10 + (repcount / 2)
    ;make the words larger as we progress
    
    if repcount = 32 [
      setfillcolor orange
      ;if this is the 32nd word, make the
      ;'fillcolor' orange
    ] [
      ;otherwise pick one from the given list:
      setfc pick [6 7 5 10 15]
      ;many primitives have shorter 'shortcuts'
    ]
    ;the fillcolor is the color used to create
    ;shapes such as typeset characters
    
    say item repcount :message
    ;speak the current word
    
    foreach "i item repcount :message [
      ;for each letter in the current word:
      typeset :i wait 1
      ;typeset the letter and wait 1/60th of a second
    ]
    
    if repcount != count :message [
      ;if this isn't the last word:
      repeat 12 - count item repcount :message [
        ;repeat 12 subtracted by the number of letters in the word:
        typeset spacebar wait 1
        ;type a 'space' and wait 1/60th of a second
      ]
    ]
  ]
  ;if we're here, we're finished the spiral!
  
  wait 120
  setxy -100 -150
  ;move the turtle to the center
  
  cam:notandem
  repeat 480 [
    cam:pullout 1 cam:rollright 1 wait 1
  ]
  ;spin the camera and pull it back
END

Codes and Cyphers

Whew! Myrtle’s had her workout, so she can take a well-deserved rest now. Let’s look at some fancier text manipulation, starting with a simple cypher.

Cyphers traditionally manipulate each letter in words in a specific, usually secret way, so that one can pass messages without those who intercept them having the ability to read them.

In the case of our simple example, we’re going to advance the ASCII value of each character by one, changing A into B and so-on.

This procedure is also our first encounter (in this article anyhow) with the “functionalisation” of a procedure in Logo.

In Logo, a procedure can either be just a procedure, in the sense that it is executed simply by declaring its name, and nothing more, or it can be a function, which requires input values be provided when it is declared, such as forward 20. It can also be a returner, which returns a result but requires no input, or a functional returner, which takes input and returns a result.

clearscreen – procedure
forward 20 – function
pencolor – returner
sin 20 – functional returner

Our cypher is a functional returner: it requires an input while returning an output. As a result, it needs to be executed by feeding it back into something else, such as print:

print cypher “albatross

This cypher example only encrypts a single word, or phrase if input is provided using the pipe symbols:

print cypher |I wish I was a secret agent!|

Now, as to the code itself, it first uses a boolean (listp) to check if the input is in fact a word / phrase and not a list, and complains if it is not. Booleans (predicates, which is why they typically end with p) return true or false, based on the input they are provided or the state of the turtleSpaces environment. The ‘if’ primitive takes a boolean result and if it is true then executes the supplied list.

See what I mean when I say lists of data and code are the same in Logo? You can manipulate lists of code just like any other list, and attempt to execute any list (although the results may not be that useful!)

Next we create an empty output container, and then foreach character in the input, we convert it to its ASCII value, add one, and then convert it back into a character, inserting it into the output container. Finally, we use the output primitive to return the result back to the calling primitive, for example print.

TO cypher :input
  ;A simple cypher:
  
  ;adding a :parameter to the end of a TO declaration
  ;causes the procedure to require the parameter when it
  ;is called.
  
  ;in this case, we're requiring a parameter called :input
  ;which when provided will place that given value into
  ;a container called :input _that only exists for the
  ;time this procedure is executed_ then vanishes!
  
  if listp :input [print |You can only provide a word to this procedure!| stop]
  ;check to see if the input is a list. If it is, reject it
  ;and stop processing this procedure
  
  make "output "
  ;initialize an empty container called :output
  
  foreach "letter :input [
    ;for each character in the :input container
    ;(represented by :letter) :
    
    make "output word :output char (ascii :letter) + 1
    ;update the :output container to contain the current contents
    ;of that container plus:
    
    ;the character representation of
    ;the ASCII value of
    ;the current :letter
    ;plus one.
    
    ;So, for example, the ASCII value of A is 65. We're
    ;going to add one to that (66) and then add the char(acter)
    ;representation (B) of that ASCII value to the existing :output
    ;container
  ]
  ;We do this for all the letters in the supplied word.
  
  output :output
  ;we 'return' our finished :output via the output command.
  ;This means that the output is passed on to the next command
  ;rather than just being printed out. It makes this procedure
  ;a _function_. Functions take input and return output.
  
  ;So, to show our cypher, we must enter a command such as:
  
  ;print cypher "elephant
  
  ;because if we just type: cypher "elephant
  ;Logo will not know what to do with the output
END

Deciphering the Gibberish

Of course we need to decipher our encrypted messages too:

TO cypherlist :input
  ;this function takes a list rather than a word,
  ;and uses cypher to encrypt each word separately:
  
  local "output
  ;we use :output in 'cypher', so we need our own
  ;'local' copy that is exclusive to this procedure
  ;and will not be modified by 'cypher'
  
  make "output []
  
  foreach "i :input [
    queue cypher :i "output
  ]
  
  output :output
  ;can you write a 'decypherlist'?
END

The Language of Music

Of course, English isn’t the only language Logo can speak — it can speak all sorts of languages! But especially, it can speak the language of music, using the playnotes primitive.

playnotes takes a list of music ‘commands’ and then plays them. These include:

L# – play the next provided note at the given length. L0 = 16th note … L9 = double whole note!

R# – plays a ‘rest’ (no tone) of the given length (similar to L)

C5 – plays a note of the given tone (C) and length (5)

Can we use list primitives to create random music? You bet we can!

This example uses the forever loop to play randomly generated music… forever! Inside the forever loop the code employs the list, word, pick and random primitives to piece together our computer-created composition:

TO randommusic
  ;but hey, all word and list handling isn't necessariy
  ;about text. Check out this random music generator:
  
  forever [
    (
      playnotes (pick
        ;pass to the playnotes primitive (which plays
        ;musical notes) one of the following two things:
        
        ;either:
        list (list word "R random 6)
        ;a list (which is the type of data playnotes
        ;requires) made up of a single word containing the
        ;letter R and a random number between 0 and 5,
        ;the combined word (eg R4) signifying a rest, or:
        
        (list
          word "L random 6
          word pick [C# D# F# G# A#] pick [3 4 5 6]
        )
        ;a list made up of two words, the first containing
        ;the letter L and a number between 0 and 5,
        ;and the second containing a note (eg C#) and
        ;an octave (eg 4)
      )
      ;use round brackets to space out your command statements
      ;across multiple lines, to make them easier to read
      ;and explain!
    )
  ]
END

Note how we can use the () round brackets to spread out a single complete instruction across multiple lines to make it easier to comment and read.

Doyay owknay igpay atinlay?

Pig Latin is a perennial favourite of children across the ages. A series of simple rules are used to create a ‘secret language’ that can only be understood by those who know them.

The rules are:

  1. If the word only has one letter (a) leave it as-is
  2. If the word only has two letters, add ‘yay’ to it
  3. If the world starts with ‘th’, move it to the end and add ‘ay’ to it
  4. If the word starts with a vowel, add ‘nay’ to it
  5. Otherwise, move the first letter to the end and add ‘ay’ to it.

The following example uses if, elsif and else to sort the input words through all of these rules, stitching together a result:

TO piglatin :input
  ;doyay owknay igpay atinlay?
  ;this procedure takes a list as an input
  
  make "output []
  ;initialize the output container as an empty list
  
  foreach "word :input [
    ;for each word in the input string:
    
    if 1 = count :word [
      ;if the word only has one letter (a):
      queue :word "output
      ;put it in the output untouched
    ]
    
    elsif 2 = count :word [
      queue word :word "yay "output
    ]
    ;if the word is two letters, add it to the output
    ;+ 'yay'
    
    elsif "th = leading 2 :word [
      ;otherwise, if the word starts with 'th':
      queue (word trailing (count :word) - 2 :word "thay) "output
      ;take the remaining part of the word minus the 'th' and add 'thay' to it
      ;then 'queue' it (add it to the end of) into the :output list
    ]
    
    elsif not memberp first :word [a e i o u y] [
      ;- 'not' reverses the boolean, making a true false
      ;- 'memberp' returns true if the first value provided to it
      ;is present in the list supplied as the second value
      
      ;and so, if the first letter in the word is NOT
      ;a vowel:
      
      queue (word butfirst :word first :word "ay) "output
      ;take everything except the first letter (butfirst)
      ;and then add the first letter to the end, followed
      ;by "ay, Note the brackets around the word function,
      ;they are needed because we are supplying three
      ;inputs: the end of the word, the first letter and "ay.
      
      ;finally we queue the result into :output
    ]
    
    else [
      ;FINALLY, if all else fails (the word starts with a vowel):
      queue (word :word "nay) "output
      ;we think you can figure this one out for yourself! :)
    ]
  ]
  
  show :output
  ;we're going to show the output rather than outputting it,
  ;but you could change this to output :output if you wanted
  ;to use this as a function.
END

Note the use of the leading and trailing primitives, which return the front and back of input words, respectively.

And now for something (somewhat) completely different…

You know that you can make containers containing values, and that you can use expressions to create those values. Typically these expressions are evaluated before they are put into the container, and so they are fixed at the point in time the make is executed. But what if you could put the expression into the container instead? This is where make! comes in handy:

TO nameexample
  ;this is a brief example of make!
  ;which creates a container whose value can be different
  ;every time you 'look' in it! (hence make!)
  
  ;in this case, when we define our container using
  ;make!, the expression goes into the container, not the result.
  ;This means that every time we show the container's 'contents'
  ;what we get back is an evaluation of the expression. So if
  ;we provide a dynamic expression, then the result will be
  ;varied.
  
  make! "name sentence pick [Big Little Stout Thin Tall Short] pick [Jim Fred Mark Paul Mike]
  show :name
  show :name
  show :name
  make! "time (se hours minutes seconds)
  show :time
END

As you can see from the example, containers created with ‘make!’ are more like magic boxes, whose contents change based on when you look inside them. Logo is magical, indeed!

A Sophisticated Story

Earlier, we told a simple story, now let’s tell a more sophisticated one.

In the following example, a series of list containers are created containing various story elements. These elements are then laid out on the graphical display using the typeset primitive. Note the absolute positioning set using the setxy primitive.

This is actually a fairly simple example, with the opportunity for participation from the entire classroom, in choosing the various words and phrases that make up the story:

TO story
  ;Let's tell a random story. This can be a fun yet simple project for students
  ;since they can pick the various items that go into each list of story elements:
  
  make "protagonist [|an old man| |a young lady| |a small dog| |a white cat| |a red walrus|]
  make "city [Vancouver Seattle |Los Angeles| |New York| Paris Rome]
  make "vehicle [|their car| |their bike| |the bus| |a taxi| |an Uber|]
  make "destination [supermarket mall dentist |computer store| |art gallery|]
  make "badevent [|slipped and fell| |got mugged| |lost their wallet| |got sick|]
  make "goodevent [|found $100| |won the lottery| |met Ryan Reynolds| |ate a burger|]
  ;first we 'make' lists for each element in the story
  ;note that phrases (also called 'long words') have to be surrounded with pipes ||
  
  clearscreen penup setxy -200 100
  ;setxy positions the turtle at the given x and y co-ordinates
  
  settypesize 9
  typeset (sentence |There was once| pick :protagonist "from word pick :city ",)
  ;sentence assembles multiple words into a list (including 'long words' or phrases)
  ;we're using word here to add punctuation to a word (or phrase) taken from the :city list.
  
  setxy -200 80
  typeset (sentence |who took| pick :vehicle |to the| word pick :destination ".)
  setxy -200 60
  typeset (sentence |Sadly, they| word pick :badevent "!)
  setxy -200 40
  typeset (sentence |But luckily, then they| word pick :goodevent "!)
  setxy -200 -50
  typeset |And they lived happily ever after.|
  home
END

And they lived happily ever after.

The next word, and the next word…

This next example is a simple little cracker, which uses the english primitive to retrieve random words from turtleSpaces built-in English dictionary, and then typesets them into the graphical display, rolling and turning the turtle to create a 3D ‘word cloud’.

If you click and drag on the graphical display area you can rotate around the output.

TO wordcloud
  ;creates a 3D word cloud out of random words:
  clearscreen penup
  
  repeat 100 [
    right pick [-90 90]
    typeset pick english 6
    ;'english' returns a list of english words of the given length
    slideright 10 rollright 90
  ]
END

If you are unable…

The next example tries to pick a random word out of the dictionary and turn it into an un-able variation, eg unfixable.

A naive version would look something like:

print (word “un pick english 5 “able)

which while straightforward is going to output a lot of rubbish.

Like piglatin, we’re going to define some rules that we’ll use to process the word and see if we can create something that makes sense. But this time we’re going to do this through exclusion — if the random word doesn’t conform to our rules, we will simply pick another one.

We’ll do this using the dountil primitive, which does something until specific conditions are met:

TO unable
  ;tries to create the 'un-able' version of a random word, eg unstoppable
  
  ;A simple version:
  
  ;print (word "un pick english 5 "able)
  ;stop
  
  ;A better version:
  
  dountil (
    ;we're going to cheat and find a good un-able word:
    ;'dountil' means perform the provided list, THEN
    ;check the conditions, and if they are false, perform
    ;the provided list of instructions AGAIN until the
    ;conditions are all true.
    
    and
    ;do this until all of the following are true:
    "un != leading 2
    ;the first two letters aren't 'un' -- we don't want 'ununable'
    :pick "er != trailing 2
    ;the last two letters aren't 'er' -- that would just be weird
    :pick not memberp last :pick [a e i o u y s d g]
    ;the last letter isn't a vowel or s, d or g
    
    ;note that the opening round bracket beside 'dountil'
    ;has allowed us to spread the above out across multiple lines.
    ;Without the brackets, all of that would have needed to be
    ;on the same line, or Logo would have become confused.
  ) [
    make "pick pick english 6
    ;pick an english word with 6 letters and place it into
    ;the :pick container
  ]
  print (word "un :pick "able)
  ;print the un-able word
END

Is this cheating? We’ll leave that for you to decide.

Check out these and other examples in the following turtleSpaces project:

turtlespaces.org/weblogo/?pub=193

Project Idea: How to Code a Hangman-style game in Logo

You can open this project in the web-based IDE environment by following this link: https://turtlespaces.org/weblogo/?pub=122

Due to Logo’s enhanced string-handling capabilities and built-in dictionary, making Hangman-style games in turtleSpaces is easy-peasy!

The mechanics of a Hangman game are simple: the computer chooses a word, and then the player chooses letters. If the letters they choose are in the chosen word, then those letters are revealed. If they aren’t in the chosen word, the ‘hanged man’ is built — although these days we probably shouldn’t do that due to nasty historical context, and choose something else.

In our example, we’ve decided to reconstitute the Egyptian god Konshu (aka Khonsu) as a shameless reference to the Marvel TV show Moon Knight. Once Konshu is fully-reconstituted then he will block out the sun forever — we don’t want that! Another potential conceit could be a space portal being built by aliens which once complete will deliver an invading armada.

Whichever way you choose to present it, the mechanic is the same — once you run out of ‘misses’ the game is over.

  • Computer chooses a word
  • The number of ‘misses’ is specified
  • We prompt the user for a selection
  • We check to see if the letter is valid (hasn’t been picked before)
  • We search through the target word looking for the letter
  • If we find it, we reveal it by placing it into the ‘solution’
  • We show the player if they’ve uncovered any letters or not
  • If not, we add more on to Konshu (or the space portal, or what-have-you)
  • We repeat until the target word is entirely revealed, or Konshu is built

Here are the ways in which Logo makes building this program simple:

  • We can use the pick and english primitives to choose a word from the built-in dictionary
  • We can use forever to repeat our game loop… forever!
  • We can use question and answer to prompt for and receive input from the user
  • We can queue the user’s selection into the list that logs the user’s choices
  • We can use the boolean containsp to determine if the user’s choice has already been chosen, by passing it the user’s current choice and the list containing the users previous choices
  • We can use count to ensure the user only provides a single letter as input
  • We can use dountil to repeat the question until we get a valid answer
  • We can use foreach to cycle through each letter of the selected word and check for matches.
  • We can use a combination of setitem and loopcount to set the appropriate space in the ‘solution’ container to the guess, should it match. If we initially set the solution container to be a series of dashes, we can then check for dashes using containsp later, to see if we’ve completed the solution
  • We can call another procedure to build our ‘hangman’, passing it the number of tries remaining, so we know which part of the hangman to build. We can use switch and case to build the relevant part of our hangman
  • We can use decrement to decrease a counter by one, and use if to check to see if we are out of tries.

Because we have all of these tools at our disposal in Logo, the game logic does not occupy very many lines of code, and is nearly english-readable:

TO game
 reset hideturtle
  
  (print |You must determine the magic word required to 
   banish Konshu back to the netherrealm.|)
  (print |If Konshu is able to reconstitute himself in 
   this realm and awaken, he will banish the sun!|)
  ;instructions (in brackets for display purposes)
  
  make "attempts 10
  ;this could be made more, or less
  
  make "target pick english 6
  ;similarly, we could pick a different target word length
  
  make "solve "------
  ;this container reflects correct player choices
  ;if we change the word length, we will need to change 
  ;the number of dashes
  
  make "picks []
  ;this holds the players choices so we can discount 
  ;duplicate picks
  
  forever [
    dountil (and
      not containsp answer :picks
      1 = count answer
    ) [question |Pick letter:|]
    ;repeatedly ask which letter until the player enters only
    ;one letter, and it has not already been picked
    
    queue answer "picks
    ;add the selection to the :picks list
    
    make "hit false
    ;set the :hit boolean to false
    
    foreach "i :target [
      ;for each letter in the :target word:
      
      if :i = answer [
        ;if the letter matches the users choice
        
        setitem loopcount "solve answer
        ;update the appropriate character in the :solve
        ;container to reveal the matching letter
        
        make "hit true
        ;set the :hit boolean to true, so we don't
        ;advance the building of Konshu
      ]
    ]
    
    show :solve
    ;show the current state of the :solve container
    
    if not :hit [
      ;if our selection wasn't in the answer:
      
      dec "attempts
      ;decrease the :attempts container by one
      
      konshu :attempts
      ;execute the konshu procedure, passing the
      ;number of remaining attempts to it
      
      (print |Attempts remaining:| :attempts)
      ;placing round brackets around a statement allows more
      ;parameters to be passed to it than its default
      
    ]
    
    if not containsp "- :solve [
      ;if there are no dashes left in the :solve container:
      
      print |You have banished Konshu!|
      finish
    ]
    
    if :attempts = 0 [
      ;if we've no attempts left:
      
      (print |The word was:| :target)
      print |Darkness descends.... forever!|

      finish
    ]
  ]
END

See? That was pretty easy — it only took an hour to write that code! Then comes the building of our ‘hangman’ model:

TO konshu :attempts
  
  ;this procedure builds the relevant part of the Konshu
  ;model.
  
  switch "attempts
  ;switch takes a literal container name
  ;and then subsequent case statements execute
  ;based on the contents of that container:
  
  case 9 [
  ;this is what we build if there are 9 tries remaining
  ]
  case 8 [
  ;...
  ]
  ;etc...
END

You simply put the code to create each stage of construction inside each case block. This could make a great project to tie language / string manipulation to game coding, with a bit of 3D artistry (putting the A in STEAM) in the mix.

Logo is such a powerful, easy-to-use language it can be hard to decide what makes the best introduction for your students. But you know them best, and we hope that one of our examples will work for them, and you!

Thanks for checking out this project!

Logo Game Design Introduction: Downhill Skiing

Downhill Skiing exercises Logo’s list handling capabilities and leverages turtleSpaces 3D functionality to create an engaging yet simple game.

Once loaded, click the flag icon to start.

Click here to open this project in the web IDE.

Herein lies a wonderful introduction to game development in turtleSpaces. The game mechanic behind downhill is simple: the player travels down the ski slope, avoiding randomly-positioned trees while shifting left and right to end in the target zone.

The core game mechanic is similar to simple early text-based ‘action’ games written in languages such as BASIC, where the player needs to avoid asteroids in an asteroid field, for example. It is straightforward and easy to understand.

As such, this program would make an excellent project for students moving from Scratch to text-based coding. It provides an introduction to the turtle, programmatically-generated lists, 3D model creation, user input and various sound primitives, without using any sophisticated math or complex language primitives.

The game logic is as follows:

  • Create initial graphic elements (stars, ground, slope)
  • Create the ‘landing zone’ and store its location in a container (variable)
  • Move the turtle across the ‘slope’ to each spot on a 39 x 40 grid
  • Decide randomly to create a tree or not, create that tree and log that decision in a list
  • Each row has a separate list (trees0 trees1 etc), made up of 39 values (one for each column)
  • Once finished, place the turtle randomly at the top, in a location free of trees
  • Loop through each row of trees (and their respective list containers) in descending order
  • Check the relevant list to see if the player is occupying the same location as a tree, if so crash
  • Check to see if the player has pressed a key, and act accordingly, moving the player
  • Pause between rows but for less time each iteration, so that the player speeds up as they descend
  • When we get to the ‘bottom’, check to see if we’re in the target zone
  • If we’re not in the target zone, retry with the same level
  • If we are in the target zone, create a new level increasing the odds of trees

As you can see, developing a lesson plan is easily achievable. Let’s explore each area of the code in depth. This tutorial assumes some familiarity with text-based coding on the part of the reader, and sometimes speaks in general terms. The fully-commented source code for this game is available at the end of this article, and inside the turtleSpaces environment itself as a published project.

In the beginning…

When the procedure starts, the first thing we need to do is reset the workspace, to ensure we’re working with a clean slate. Next, we need to provide the user with some instructions!

For this, we’ll use the simple text command print. We can change the color of the text using settextforeground. Note that for every set command there is a corresponding function to return the parameter’s status, such as textforeground. settextforeground takes a number as a parameter, which represents a color (type showcolors in the console to see a list of colors and their numeric values). However, turtleSpaces has a number of color primitives (red, orange, yellow etc.) that return their respective numeric values, so that you can say, for example, setttextforeground yellow (which works because yellow is a function that returns the number 13.)

The print command itself takes either a word (a single word preceded with a quote “) a list (a series of words wrapped in square brackets []) or a ‘long word’ (a series of words wrapped in pipes ||). You can use either a list or a long word to display a sentence, eg print [Welcome to turtleSpaces!] or print |This also works.| The advantage to using a list is that you can cherry-pick one word from it easily, eg print first [Welcome to turtleSpaces!], however it can be more complicated when pairing text with numeric values, for example, when using a long word would be preferable.

Note that print “Welcome to turtleSpaces” will throw an error, because the parser will attempt to execute to and turtleSpaces” as commands.

We can use cursordown to put a blank line between sections of text. We can also say text as well, to make things more interesting!

Next, we need to create some default containers (variables). We can create these using the make primitive, which is the traditional method in Logo (eg make “container 5), although turtleSpaces also supports put x in “container and “container := x (note the colon before the equals). A colon before a word in Logo returns the value stored in the container of that name, eg :container. But make needs the container name specified with a quote ” — this is because if you used a colon, make would create a container with the name of the value stored inside that container!

We need to set the initial :level (0). We’re also going to create a container containing the number of available ‘retries’ (we will give the player limited opportunities to generate a new level if the level provided to them is impossible.)

make “level 0 make “retry 3

Notice that we can ‘stack’ multiple commands on the same line! This can sometimes be a blessing, sometimes a curse, so use your discretion when stacking commands.

Finally, we’re going to declare a label, which can be returned to using the go primitive:

label “start

so that we can return here at the start of a new level.

My God, It’s Full of Stars!

It’s time to start building the graphical environment in which the game will take place. First, let’s create a starfield. This is a fairly simple algorithm:

  • set a ‘static’ z (depth) position
  • set a random x and y position
  • set a random ‘fill’ shade and color
  • set a random fill opacity
  • create a spot of varying size
  • do this all 100 times

In the code we use the setxy and setz primitives to accomplish the first two items, and the random function to generate the random values we need to give to each. There’s some turtleSpaces-specific primitives such as randomfillshade and randomfillcolor (the color used to create shapes is the ‘fill’ color, as opposed to the color used to create lines which is the ‘pen’ color.)

We wrap all of this in a repeat loop, which repeats the contents of the list provided to it the given number of times, for example repeat 20 [print repcount] which counts from 1 to 20.

We actually need to create two starfields, one behind the hill, and one in front of it (so that we see stars regardless of which direction we’re facing, either looking at the hill, or from it while we’re playing. We can make the single loop do double-duty by using the oddp primitive which returns true if the value passed to it is odd. As you saw with the above example, repcount returns the current repeat loop iteration, and so if we pass repcount to oddp, then oddp will be true on odd iterations of the repeat loop.

We can then pass oddp to if, which will then execute the first list of instruction if oddp is true, and the second list of instructions if it is false. In this way, we set the z position of the turtle in an alternating fashion.

Note that the back side of a spot is not ‘lit’ and so we need to enable twosided mode before we create the stars to ensure both sides of the spot are lit, since we’re going to be looking at them from both directions.

Making Ground

Next, we’re going to create the ground. This is another spot, like the stars, just a very big one! We use setxy and setz to position the turtle in the lower-centre of the screen, and then tilt it up 90 degrees (so that we see the ‘dark’ back side of the spot), setting random fill and shade colors before making the spot. The value passed to spot signifies its radius, and is given as ‘turtle units’, the same unit of measure used by all shapes and lines.

To choose the fill color we use the pick primitive, which randomly selects an item from a list, eg print pick [1 3 5 7 9]

The landing area is where our skiball will end up assuming the player avoids all of the trees. This is created using the quad primitive, which creates a filled rectangle. A container is created containing a random value signifying the left (from this perspective) side of the target area. It’s width is dependent on the level, but to begin with, it could just be arbitrary (eg 100 ‘turtle units’ wide). We will use the value in this container later, to determine if the player is inside the target zone.

There are, then, three quads, the left side before the target zone, the target zone itself, and the right side after the target zone. We color the target zone dark blue to distinguish it from the rest of the landing area.

Finally, we create the ski hill itself. Its angle does not matter that much, as from now forward the turtle will operate on the plane of the hill. In this example we’ve used the gradient mode to create a gradient fill on the quad that makes up the ski hill. In gradient mode, the pen and fill colors are both used to create the gradient, the pen color being the ‘near’ color and the fill color being the ‘far’ color.

It’s time to populate the hill with trees.

Seeing the forest for the trees

When this game was first developed, the ‘trees’ were simple icospheres. In creating a lesson plan, you can take two approaches here:

  • Create and validate the core game logic first, using a placeholder such as a sphere
  • Create the trees first, and then develop the game logic later

This is really a choice between whether we have vegetables or dessert first, but here we’ll start with dessert.

The trees are made up of firstly a cylinder (for the trunk), then a series of cutcones and then finally an icosphere on the top. We select the color of each trunk and tree using the pick primitive, and select a random fillshade for each layer of the tree.

We need to do a bit of turtle acrobatics to ensure the trees are upright relative to the ground, and not upright relative to the slope! Shapes are created beneath the turtle, and so as a result the turtle needs to be ‘flipped over’ and the trees trees created successively from the trunk ‘downward’ to the icosphere on the top of the tree.

From a default startup state, type cylinder 20 50 20 into the console, then click and drag on the view area to rotate the camera:

As you can see, the cylinder is created beneath the turtle. To create additional shapes at the opposite end of the cylinder, we will need to lower the turtle the length of the cylinder.

cutcones are similarly created under the turtle. However, icospheres are created around the turtle. See the shape guides available on this website for more information about shape orientations relative to the turtle.

And so we sweep left to right, working up the slope, deciding at each ‘stop’ whether or not to build a tree, and then noting in the list for the current row if we have created a tree at the current column.

Here comes the vegetables. At the start of each row we create a list:

make word “trees repcount []

word combines two inputs into a single output. We’re using it here to create a new list container [] based on the current repcount, prefixing it with the word “trees, creating for example :trees11

As we sweep across each row, we use the queue primitive to add either a 0 (no tree) or a 1 (tree) to the end of the list:

queue 1 word “trees repabove 1

repabove returns the iteration of the parent repeat loop, in this case the row repeat loop. Using repcount here would return the current column, as we are iterating through the row.

We will use these lists later to cross-reference with the turtle’s position and reveal if a tree is present at that location.

Introducing the Player

Once we’ve created all the trees, we could spend a bunch of time creating an actual skier model, but I like just using the amigaball, which creates a checkered ball based on the current fill and pen colors. We use setmodel to make it the turtle model. Later on we can use setpremodel to make it ‘roll’ down the hill, which looks pretty cool! Note that we will need to elevate the turtle the radius of the amigaball to ensure it’s ‘sitting’ on the slope.

Skiing the Slopes

So basically now we’re ready to position the turtle at the top of the slope. We pick a random location and then check :trees40 and :trees39 to ensure there isn’t a tree at the random location, or below it, respectively. We do this by using a container called :pos which contains the column number occupied by the player.

Alright, so now we enter the main game loop, which is a repeat loop based on the number of rows (40):

  • Check for keypresses and action them as appropriate
  • Move the ball down one row
  • Check for collisions and act accordingly

We use if to check keyp which returns true if there are any keypresses waiting in the keyboard buffer. If there are, we use peekchar to ‘peek’ at what the first keypress in the buffer is. If the key is j or k we check our :pos and if valid we then move the turtle player and increment or decrement the :pos container as appropriate.

We can also check for a press of the r key and then initiate a ‘retry’ but that is something that we can add in later. We don’t need to deal with it now.

Once we’ve dealt with the keypress, we can remove it and any superfluous keypresses from the key buffer using clearchar

The second thing we do in the loop is move the turtle forward 10 turtle units (one row). This is pretty straightforward.

Third, we check for a collision:

if 1 = item :pos thing word “trees 40 – repcount [do this stuff…]

Whew! That’s a mouthful, isn’t it? The first thing you’ll notice is the meat of our sandwich is on the right — other programming languages would tend to have the sides of the equals reversed, with the complicated bit first followed by the = 1. But Logo ‘parses’ right to left, which means that if we did that, the = comparator would only check 1 against repcount, ignoring all the rest of it.

We can solve that problem by putting round brackets () around the entire expression (the meat) but that adds extra clutter we don’t need if we just put the value first.

  • item returns the nth list item. So print item 3 [1 2 3] will return 3, for example. item :pos returns the list item based on the player’s current position.
  • thing returns the value stored in the container of the given name. It’s the longhand of :container. But it’s useful here because unlike using the colon method, we can construct the name of the thing to be retrieved, as we did with make earlier.
  • word acts similarly here to when we created the lists, with the exception of subtracting repcount from 40 in order to get the current row list in descending order.
  • And so, if the value returned from all of that is 1, we’ve hit a tree! Do the stuff in the list.

Ouch! Well, when all else fails, try, try again… we can use a label go pairing to send the player back up to the top, logically speaking. And we can wrap a tag around the player’s progress down the hill so we can erase it, sending the player turtle back to where it started, turtle-y speaking.

If we make it to the bottom of the hill we need to check to see if we’re in the target zone:

if and xpos > -200 + :zone xpos < -100 + :zone – 10 * :level [do this stuff…]

xpos holds the turtle’s current x position. We can use it along with the value stored in the :zone container to decide if we’re in the zone. This statement assumes we’ve factored the :level into the width of the :zone.

If we’re in the zone, congratulations! Then (if we’re not on the last level) we increase the level and send the player off to try again. If we’re not in the zone, too bad, and we send the player off to try the level again.

Of course, the version available to play has a few more bells and whistles, you can read all about them in the documented source code below.

Note: This was created as a monolith procedure, but you could break bits of it out into separate procedures in order to demonstrate Logo’s abilities in that area. For example, the tree model creation could be its own procedure, as could the contents of the main game loop, the crash logic, the level completion logic and so forth.

TO downhill
  ;myrtle builds a ski slope and then rolls down
  ;it in the guise of a rolling amiga ball.
  
  ;this is a wonderful introduction to game
  ;development in turtlespaces. The game mechanic
  ;is quite simple:
  
  ;the player needs to travel down the ski slope
  ;while avoiding the randomly-placed trees
  ;and finish in the highlighted zone.
  
  ;most of this procedure is window-dressing.
  ;at its core it simply fills a number of lists
  ;with 0s and 1s, and then checks these against
  ;indices inferred by the player's position, eg
  ;the 'row' being the current iteration of the
  ;player's progress down the ski hill, and the
  ;column being the player's horizontal location
  ;on that hill. The row matches against the list
  ;generated with the row number, eg :trees 10
  ;while the column (:pos) matches with the item
  ;number in that list, eg item :pos :trees10
  
  ;this game would be rather pedestrian with a
  ;simple text representation, but in turtlespaces
  ;we can 'jazz it up' and make it more engaging.
  
  ;Let's go!
  
  reset
  cam:pullout 10
  ;the slope doesn't quite all fit in the screen!
  ;so we'll pull out the camera turtle a little bit.
  ;'cam' is shorthand for whichever turtle is the current
  ;view turtle
  
  cleartext settextforeground red
  print |Welcome to Amigaball Backcountry Downhill Skiing!|
  ;bars indicate a 'long word' or a word that contains spaces
  ;while quotes indicate a single word and square brackets
  ;indicate a list. Curly braces indicate a list 'resolved'
  ;when the list is processed during execution. (Don't worry
  ;if you don't know what that means just yet!)
  
  cursordown
  settextforeground yellow
  print |Press J and K to steer your skier (Amigaball)|
  print |Land in the Blue Zone. Don't hit the trees!|
  print |More trees each level. Try to make it to level 10.|
  print |Press R to get a different map (3 retries) Q to quit|
  say |Welcome. Don't hit the trees!|
  make "level 0
  ;'make' creates a container with the given name containing the given value
  
  make "retry 3
  ;although the odds of trees on lower levels is less,
  ;it is still possible to get a very dense level if you are unlucky!
  ;and so we'll give the player three opportunities to regenerate
  ;the level
  
  ;hint: if you open this in the web IDE you can hover
  ;over the primitive names with the mouse and a popup will
  ;appear with their syntaxes
  
  label "start
  ;we will return here at the start of each 'level'
  ;the program gets redirected to labels using 'go'
  
  ;create the 'mountain':
  clearscreen hideturtle penup
  ;you can 'stack' multiple directives on the same line
  
  twosided ; by default the back side of faces is not 'lit'
  
  ;make the background stars:
  repeat 200 [
    if oddp repcount [setz -600] [setz 600]
    setxy -800 + random 1600 -100 + random 600
    randomfillshade ; aka randfs
    randomfillcolor ; aka randfc
    setfillopacity 50 + random 50 ; aka setfo
    ;opacity is the transparency of the shape
    ;it takes a value from 0 to 100 (full)
    spot 2 + random 7
  ]
  
  setfo 100 ; aka setfillopacity
  
  setz 0
  setxy 0 -115
  
  ;create the 'ground spot':
  notwosided
  up 90 raise 1
  setfs -5 + random 15 ; aka setfillshade
  setfc pick [4 5 8 9] ; aka setfillcolor
  spot 1000
  slideleft 200 lower 1
  
  ;pick colors:
  setbg pick [2 6 7] ; aka setbackgroundcolor
  setbs -5 + random 15 ; aka setbackgroundshade
  setpc pick [5 6] ; aka setpencolor
  setps random 10 ; aka setpenshade
  setfc pick [10 15 7 14] ;aka setfillcolor
  setfs -10 + random 10
  gradient
  ;this causes many shapes to transition from the
  ;pen color to the fill color
  
  ;create the landing area:
  make "zone random 300
  ;pick the 'target zone' area
  
  ;containers that hold values are created using 'make'
  ;with the given name and value
  
  twosided
  quad :zone 400 ; creates a quad stretching to the start of the zone
  ;values in containers are retrieved by specifying the container name
  ;prefaced with a colon. You can also use the 'thing' function to retrieve
  ;the value of containers, eg 'quad thing "zone 400'
  slideright :zone ; aka sr
  make "pencol pencolor
  ;we need to store the pencolor and restore it later
  setpc blue ; several color names translate to their color number
  quad 100 - 10 * :level 400 ; create the zone based on the level
  sr 100 - 10 * :level
  setpc :pencol
  ;this is where we restore the pencolor
  quad 300 - :zone + 10 * :level 400 ; create the remainder of the landing area
  sl 100 + :zone - 10 * :level ; return to the start
  
  
  ;create the ski slope:
  notwosided
  down 120
  quad 400 400
  nogradient
  
  
  ;position the turtle for creating trees:
  fd 5 ; aka forward
  sr 5 ; aka slideright
  
  ;place the trees:
  repeat 40 [
    ;we'll have 40 rows of trees
    
    make word "trees repcount []
    ;our strategy here is to make a series of lists, each of which
    ;indicate the presence or absence of a tree in a particular
    ;location on a particular 'line' of the ski slope
    
    repeat 39 [
      ;there are 39 columns on each row of the ski slope
      sr 10
      
      if 1 = random 10 - :level [
        ;the higher the level the more likely a tree will be
        ;placed in a given spot
        
        noise (10 * repcount) + (200 * repabove 1) 1
        ;let's make a little noise based on position
        
        up 150
        ;most shapes are created under the turtle, so we need
        ;to flip it over
        
        make "size 0.5 + 0.1 * random 10
        ;pick a random size
        
        setfc pick [5 8 9]
        cylinder (1 + 0.1 * random 10) * :size 10 * :size 4 + random 10
        ;create the tree trunk. cylinder takes radius, depth and sides
        
        lo 10 * :size
        ;remember, these shapes are created under the turtle
        ;so we have to 'lower' to continue to build 'beneath' them
        
        setfc pick [4 5 6 7 12 14]
        
        ;create the body of the tree:
        repeat 5 [
          randfs
          cutcone (7 - repcount) * :size (5 - repcount) * :size 2 * :size (5 + random 10) * :size
          ;cutcone takes 4 parameters: the starting radius, ending radius, depth and sides
          lo 2 * :size
        ]
        
        ;put a little 'cherry' on the top of each tree:
        randfc ico 1 * :size
        
        ra 20 * :size
        dn 150
        ;return to the base of the tree and reorient back
        
        queue 1 word "trees repabove 1
        ;a 1 in the list indicates a tree
        ;queue adds it to the end of the list
      ]
      [
        ;this second list executes if the if condition is false:
        queue 0 word "trees repabove 1
        ;a 0 indicates no tree
      ]
    ]
    sl 390
    fd 10
    ;move ahead a row and return back to the starting column
    
  ]
  ;return to the start of the repeat loop and loop until finished
  
  ;we're done making trees! Let's place the player:
  
  bk 10
  ;pick a random placement:
  make "pos 5 + random 30
  sr 10 * :pos
  ;and check to make sure there's no tree there or directly in front:
  until (
    and 0 = item :pos :trees39
    0 = item :pos :trees40) [
    sr 10
    inc "pos
    if :pos > 39 [sl 400 make "pos 1
      ;if we hit the end of the row, circle back to the start
    ]
  ]
  rt 180
  ;turn to face down the hill
  ra 5
  ;elevate a little bit off the hill
  
  make "startpos :pos
  ;store the starting position to return back to it later
  
  label "attempt
  ;we will return here if the player's attempt fails
  
  setpremodel []
  ;setpremodel allows for the manipulation of the orientation
  ;and position of the turtle model.
  ;we use setpremodel later and need to clear it if
  ;the player needs another attempt
  
  begintag "route
  ;we're going to place the players 'route' into a 'tag'
  ;so that we can erase it if the player's attempt fails.
  ;this way we can keep the level we've generated and
  ;they can try again
  
  setview "myrtle
  ;view the scene from myrtle's perspective
  myrtle:setlight 1
  snappy:setlight 0
  ;switch the light to myrtle's position
  setviewpoint [0 10 -30]
  ;set the viewpoint offset from myrtle's position
  
  randpc ; aka randompencolor
  setpenwidth 4 ; make the turtle draw fat lines
  
  pendown ; let's draw the route the player takes!
  ;because why not? It'll look interesting later
  
  clearchar
  ;clears the keyboard buffer so the player doesn't
  ;end up with a bunch of buffered keypresses
  ;influencing movement
  
  setmodel [
    setfc red
    ico 5 ; aka icosphere
  ]
  ;sets the turtle 'model' to the given contents
  ;setmodel can also use tags
  
  ;Ready set go:
  showturtle
  type "Ready...
  toot 1000 30
  ;frequency and duration in 60ths of a second
  
  setmodel [
    setfc white
    ico 5
  ]
  type "Set...
  toot 1000 30
  print "Go!
  setmodel [
    setfc red
    setpc white
    amigaball 5 10 20
    ;creates a chequred ball
  ]
  toot 2000 60
  ;and we're off!
  
  repeat 39 [
    setpremodel {"dn repcount * 20}
    ;the premodel processor doesn't know what 'repcount' is, and so we need
    ;to use curly braces to make a 'soft list' that is evaluated when it is
    ;executed -- that is, repcount is resolved into its value, which is then
    ;passed to setpremodel
    
    fd 10
    ;move forward
    
    if keyp [
      ;has a key been pressed?
      
      if and :pos < 39 "j = lowercase peekchar [ ;if it's the j key and the position is less than 39: sl 10 ; aka slideleft inc "pos ; increment the 'pos' container value ] if and :pos > 0 "k = lowercase peekchar [
        ;similarly, if the k key and the position is greater than 0:
        sr 10 ; aka slideright
        dec "pos ; decrement the 'pos' container value
      ]
      
      if and :retry > 0 "r = lowercase peekchar [
        ;if the player presses the r key and has retries remaining:
        dec "retry
        (print :retry |retries remaining...|)
        setview "snappy
        snappy:setlight 1
        setlight 0
        ;switch the light and view back to snappy, the default view turtle
        endtag
        ;close the route tag
        go "start
        ;jump back to the start label
      ]
      
      if "q = lowercase peekchar [finish]
      ;I quit!
      
      clearchar
      ;clear the key buffer
    ]
    
    if 1 = item :pos thing word "trees 40 - repcount [
      ;are we where a tree is?
      
      ; - 'thing' returns the value of the given container.
      ;   we need to use it because we programatically
      ;   created a series of list containers earlier named
      ;   trees0 trees1 trees2 etc. and so we're using thing
      ;   and 'word' to create the appropriate container name
      ;   and then using 'item' to retrieve the value in the
      ;   relevant column (list index)
      
      ; - 'repcount' returns the current iteration of the
      ;   repeat loop. there is a similar function for other
      ;   loops called 'loopcount' and indeed loopcount will
      ;   also return the current repeat loop iteration. But
      ;   repcount is used by a number of different Logos and
      ;   it's a useful distinction
      
      ; so apparently we did:
      
      pr |Ouch! You Crashed! Try again.| ; aka print
      playsound "crash
      say pick [|too bad| |ouch| |that hurt| |dont hit the trees| |wipeout|]
      
      setview "snappy
      snappy:setlight 1
      setlight 0
      ;return the view and light back to 'snappy' the default view turtle
      
      repeat 2 [ht wait 30 st wait 30] ; aka hideturtle and showturtle
      ;wait causes the program to pause for the given number of 60ths of a second
      ;this is due to early computers operating at 60 frames per second
      ;you can also use 'sleep' which takes milliseconds
      
      make "pos :startpos
      ;return the value of the 'pos' container to its starting value
      endtag
      ;close the 'route' tag
      erasetag "route
      ;erase the 'route' tag. This returns the turtle to the position
      ;it was at at the start of the tag, as we have erased part of its
      ;'turtle track'
      go "attempt
      ;return to the 'attempt' label and continue execution from there
    ]
    ;indicates the end of instructions to execute if the player hits a tree
    
    noise repcount * 200 (40 - repcount) * 0.5
    wait (40 - repcount - :level) * 0.5
    ;make a little static noise based on progress down the hill
    ;and similarly wait a little bit based on the progress and the level.
    ;this makes us speed up as we descend the hill
    
  ]
  
  ;we've made it to the bottom!
  up 60
  ;reorient the turtle upward
  
  repeat 100 [
    setpremodel {"fd repcount "dn 10 * repcount}
    ;this causes the ball to move ahead of the camera
    setmodel [
      setfc 1 setpc 15
      setfillopacity 100 - repcount
      setpenopacity 100 - repcount
      amigaball 5 10 20
      ;this causes the ball to fade out
    ]
    setpenopacity 100 - repcount
    ;this causes the line to fade out
    forward 1 noise 8000 - 80 * repcount 1]
  ;do a little victory 'lap'
  
  endtag
  ;close the 'route' tag
  
  setfo 100 setpo 100 ; aka setfillopacity setpenopacity
  ;set the fill and pen opacity back to full
  
  setview "snappy
  snappy:setlight 1
  setlight 0
  ;set the view and light back to snappy, the default view turtle
  
  if and xpos > -200 + :zone xpos < -100 + :zone - 10 * :level [
    ;are we in the zone? if so:
    
    say pick [|great job| |good effort| |way to go|]
    playsound "cheers
    inc "level
    
    if :level < 10 [
      (print |Great Job! Try level| word 1 + :level "!)
      ;round brackets allow more than the default number of
      ;parameters to be 'passed' to a primitive, in this case
      ;print
      
      go "start
      ;return back to the 'start' label to create a new level
    ]
    
    print |You Win! Game Over.|
    say |you win|
    finish
    ;this finish isn't strictly necessary because we will fall out
    ;of execution at this point anyway...
  ] [
    ;we're not in the zone! Let's try this again:
    
    playsound "aw
    print |You missed the Blue Zone! Try Again.|
    say pick [|try again| |go again| |you can do it| |you'll get it this time|]
    wait 120
    make "pos :startpos
    erasetag "route
    go "attempt
  ]
  ;that's all folks!
END

 

Alpine Trees: an Introduction to Terrain

turtleSpaces new terrain functionality provides some great opportunities for simple coding examples with few lines of Logo code, in this case less than 30.

First, we need to introduce the setterrain primitive. setterrain creates ‘terrain’ or a contiguous object of varying elevation.

The above is an example of the object created by setterrain. The setterrain primitive takes a number of parameters, and is a good opportunity to explain the different types of parameters used in turtleSpaces to students.

The setterrain command used to create the above is:

setterrain [-5 -5 5 5] [0 50 10 40] random 1000 [fbm 2] “voxel

As you can see, the command has a number of different parameters.

The first parameter is a list containing the number of quadrants (blocks of ten elevation points, by default spaced one turtle-unit apart) in each direction from [0 0], the center of the turtleSpace, expressed as X Y pairs.

So, in this case, we are creating a grid of 25 quadrants in each of the four directions (forward/left, back/left forward/right back/right) surrounding [0 0], for a total of. 100 quadrants.

The second list contains four values: The elevation (or z value) of the ‘bottom’ of the terrain, the elevation of the highest points of the terrain mathematically generated, the ‘floor’ of the terrain which is the lowest level of the generated terrain rendered (so you can create lakes) and the ‘ceiling’ of the terrain which is the highest elevation of the generated terrain rendered (so you can create plateaus).

You can use the setterraincolors primitive to set the colors of various elevations, but this is beyond the scope of this article.

The third parameter is the ‘seed’ of the terrain generator. In the case of our example, we are using the random function to generate a random number to use as the seed, to create a different terrain each time we execute the command. But you can use an absolute number, eg 433, which will create the same terrain every time. This allows you to build additional objects on the terrain and ensure that terrain is the same every time you execute your program.

The fourth parameter, the algorithm used to generate the terrain, is a chameleon of sorts: it can either take a list or a word value depending on your intent. You can provide just a word, eg “fbm (the only algorithm type currently available) or a list, eg [fbm 2] — the second value in the list provides the ‘magnification’ of the algorithm. The larger the number, the less dense the terrain will be in terms of peaks and troughs. If you use a fraction eg 0.2 then the terrain will become more dense, to the point of looking more like a cityscape then terrain!

The final parameter is a word that specifies the style. There is currently only one style, voxel, hence the parameter in our example is “voxel

And so, as you can see, we have lists, numbers and words all as parameters passed to setterrain.

You can play with these values a bit and see the results. Be aware that you need to either call reset or clearterrain (not clearscreen, which does not affect terrain) before you execute the setterrain command again, because you cannot ‘overwrite’ existing terrain. But keep in mind you can create different blocks of terrain in different areas, so long as they do not overlap!

The voxels in the terrain by default are only one turtle-unit cubed, which are very small, You can increase the size of the voxels used in the terrain by using the setterrainresolution primitive to change that size, eg setterrainresolution 10. This will expand the size of the terrain by a factor of 10, so keep in mind your camera may end up ‘inside’ the terrain and you will need to use the scroll wheel on your mouse to pull the camera to the outside of it. Or you could use commands like:

cam:setorbitdistance 1000

cam:orbitdown 60

which pulls the camera turtle away from the center point and ‘orbits’ it down 60 degrees, cam: denoting that we are talking to the camera turtle, by default ‘snappy’ (if you use the setview primitive to change the camera turtle, then cam: commands that turtle instead.)

And so the start of our program looks like this:

TO trees
  reset hideturtle penup
  setterrainresolution 10
  
  setterrain [-5 -5 5 5] [0 50 10 40] random 1000 [fbm 2] "voxel
  ;create random terrain
  
  cam:setorbitdistance 1000
  cam:orbitdown 60
  ;position camera
END

and results in this:


And so we have our terrain, which is by default all white and suits our purposes. But now we need to put trees on it!

Our trees are made up of cylinders, of varying size and side counts, to provide variety. A random icosphere adorns the top of each tree.

First, we’ll create a repeat loop of 100, to create 100 trees:

repeat 100 [

]

The lines between the square brackets are where we’ll put the lines we want to repeat. Next we want to position the turtle in a random spot on the terrain, but we want the treeline to be at least 200 turtle-units on the positive Z axis.

We’re going to be using the elevation primitive, which returns the elevation at the given x and y co-ordinates expressed as the z co-ordinate of the elevation NOT the voxel count (remember, we’ve set the terrainresolution to 10, which means the actual z co-ordinate will be 10 times the voxel count).

We’re also going to be using dountil, which repeats an action until the condition provided to it is satisfied. And so we’re going to position the turtle in random spots within the area of the terrain and measure the elevation there, only continuing if the elevation is 200 or greater:

    dountil 200 < elevation pos [
      setxy -480 + random 960 -480 + random 960
    ]

Once we’ve found an appropriate spot, we’re going to set our elevation to the top of the terrain and flip the turtle upwards 180 degrees, because cylinders are created beneath the turtle:

    setz elevation pos up 180

We need to pick a color for the trunk (8 is brown and 9 is orange) and a random shade:

    setfillcolor pick [8 9]
    randomfillshade

Next we’ll create containers containing randomly-generated values determining the size of the tree and the number of sides it has:

    make "size 5 + random 20
    make "sides 3 + random 20

We’ll use the values contained in these containers in our cylinder commands to create our tree appropriately. Next we make the trunk based on these values:

    cylinder :size / 2 2.5 * :size :sides

cylinder takes three parameters: the radius of the cylinder, the depth of the cylinder and the number of sides. :size returns the value stored in the “size container, similarly with :sides.

Next we’ll lower the turtle to the end of the cylinder:

    lower 2.5 * :size

Now comes the creation of the tree’s ‘foliage’. First we need to select the tree’s color, then create 20 cylinders making up the foliage, then place a randomly-colored icosphere on the top.

    setfillcolor pick [4 12 14]

4, 12 and 14 are greenish colors.

repeat 20 [

]

We’re going to make 20 cylinders. Inside the repeat loop we place the following:

      setfillshade 15 - 1.5 * repcount
      cylinder 2.5 * :size - ((2.5 * :size) / 20) * repcount :size / 4 :sides
      lower :size / 4
      right 90 / :sides

repcount returns the current repeat ‘loop’ we’re in, and so first we set the fill shade (fill colors are used by shape primitives such as cylinder) based on the repeat loop. Second we create the cylinder, third we lower the turtle to the bottom of the cylinder, and finally we turn the turtle right based on the number of :sides the cylinder has, creating the spiral effect.

    randomfillshade randomfillcolor
    raise :size / 4 lower :size / 5
    icosphere :size / 5 up 180

Next, we set a random fillshade and fillcolor, raise the turtle back up so that we can lower it with a more appropriate value for our icosphere, then create the icosphere based on the :size value. Finally, we create the icosphere. Then we repeat all of this 100 times for 100 trees, ending up with something like this:

Here is the listing in full:

TO trees
  reset hideturtle penup
  setterrainresolution 10
  ;the terrain resolution affects all terrain! If you change it, terrain is regenerated.
  
  setterrain [-5 -5 5 5] [0 50 10 40] random 1000 [fbm 2] "voxel
  ;create random terrain
  
  cam:setorbitdistance 1000
  cam:orbitdown 60
  ;position camera
  
  ;create 100 trees:
  repeat 100 [
    dountil 200 < elevation pos [
      setxy -480 + random 960 -480 + random 960
    ]
    ;position the turtle someplace where the elevation is 200 or greater
    
    setz elevation pos up 180
    ;raise the turtle to that elevation and flip it on its back
    
    setfillcolor pick [8 9]
    randomfillshade
    ;select trunk color / shade
    
    make "size 5 + random 20
    make "sides 3 + random 20
    ;select random values
    
    cylinder :size / 2 2.5 * :size :sides
    ;make the trunk
    
    lower 2.5 * :size
    setfillcolor pick [4 12 14]
    ;select foliage colors
    
    repeat 20 [
      ;create 20 'rings'
      setfillshade 15 - 1.5 * repcount
      cylinder 2.5 * :size - ((2.5 * :size) / 20) * repcount :size / 4 :sides
      lower :size / 4
      right 90 / :sides
      ;this creates a spiral effect in the tree
    ]
    
    randomfillshade randomfillcolor
    raise :size / 4 lower :size / 5
    icosphere :size / 5 up 180
    ;make the 'ball' on top
  ]
END

Don’t forget that you can click and drag the mouse to move the camera around the model, and scroll in and out to zoom!

 

Tutorial: Fancy a game of darts?

Consider the below game of ‘pub darts’:

You can open it in our Javascript-based IDE by clicking here

If you hover over the various primitives in the editor, a popup will tell you what they do.

The game consists of three main parts: the dartboard, the darts and the game itself.

The dartboard is constructed primarily using the ringarc primitive and a number of repeat loops. It uses the oddp boolean to decide which color each segment of each ring should be, allowing us to match the colors of a standard dartboard.

TO board
  ;create the dart board
  
  hideturtle
  rt 9 setfc 1 polyspot 5 20
  ;bullseye
  
  lo 0.1 setfc 4 polyspot 10 20
  ;outer bullseye
  
  repeat 20 [if oddp repcount [setfc 13] [setfc 0] ringarc 25 10 20 1 rt 18]
  repeat 20 [if oddp repcount [setfc 4] [setfc 1] ringarc 5 35 20 1 rt 18]
  repeat 20 [if oddp repcount [setfc 13] [setfc 0] ringarc 20 40 20 1 rt 18]
  repeat 20 [if oddp repcount [setfc 4] [setfc 1] ringarc 5 60 20 1 rt 18]
  ;rings
  
  lo 0.1 setfc 0 cylinder 80 10 20 lt 9 penup setfc 15 rt 180
  ;backboard

The numbers around the outside of the dartboard are created using the inscribe and orbitleft primitives. We offset the numbers as required to center them on their relevant wedges. We also use the orbit / pullin / pullout functionality to create the wire frame overlaid on the dartboard.

 dropanchor pullout 65 ra 2
  foreach "i [20 1 18 4 13 6 10 15 2 17 3 19 7 16 8 11 14 9 12 5] [
    lt 90 if :i > 9 [bk 10] [bk 5] inscribe :i
    if :i > 9 [fd 10] [fd 5] rt 90 orbitleft 18
  ]
  ;numbers
  
  home setpw 1.2 setpc 5 setfc 5
  dropanchor tether pullout 5 orbitleft 9
  repeat 6 [
    pd
    repeat 20 [orbitright 18 ico 0.6]
    pu switch {repcount}
    case 1 [pullout 5] case 2 [pullout 25]
    case 3 [pullout 5] case 4 [pullout 20]
    case 5 [pullout 5]
  ]
  ;metal rings
  
  home pullout 10 orbitleft 9
  repeat 20 [pd pullout 55 pu pullin 55 orbitright 18]
  ;metal lines
  
  home
  
END

The darts are assembled using cylinders, cones, cutcones and poilyspots (for the fletchings).

TO dart :color
  ;create the dart models
  
  lower 50 setfillcolor 10 cone 1 4 20
  raise 20 cylinder 1 20 20
  ra 10 setfc 5 cutcone 3.5 2 10 20
  ra 20 setfc 10 cylinder 3 20 20
  ra 5 setfc 5 cylinder 3.5 5 20
  setfc item :color [11 6]
  ra 20 cutcone 2.5 3 20 20
  ra 5 setfc 10 cutcone 2 2.5 5 20
  setfc 5 ra 5 cutcone 1.5 2 5 20
  ra 10 setfc 0 cutcone 1 1.5 10 20
  ra 20 cutcone 1 1 20 20 up 180
  cone 1 15 20 rr 90
  
  repeat 3 [
    up 60 setfc 10 twosided
    polyspot 13 6 setfc :color
    ring 2 13 6
  ]
  ;fletchings
END

The game itself consists of a setup section and the main game loop. Inside the game loop we have the aiming section and the scoring section. Every three darts we switch between players. If one of the players score exceeds 301 then they are declared the winner and the game ends.

TO game
  ;world's smallest dart game
  
  reset
  clearfrozen
  cleartext
  print |Welcome to darts! Two players take turns until one scores more than 301.|
  cursordown
  print |Try to aim the dart with the mouse and press the mouse button to throw...|

The setup section creates the dart ‘room’, draws and ‘freezes’ the board (breaks it off into its own ‘turtle track’) and creates the dart models using the dart procedure. Default containers (variables) are created using surnames (container classes), one for each player. Surnames enable us to use the same code for each player without needing to use lists or tables.

  pu sl 300 bk 300 ra 600
  setfc pick [3 5 8 9] setfs 10
  setbs fillshade setbg fillcolor
  voxel -607 setfs 0 home
  ;create dart room
  
  board
  ;create board
  
  cam:pullout 180
  freeze "board
  
  newmodel "dart1 [dart 1]
  newmodel "dart2 [dart 2]
  ;create dart models
  
  setmodel "dart1
  setmodelscale 0.7
  setanchor [0 0 0]
  
  foreach "i [red blue] [setsurname :i
    rt 180 make "down forwarddir rt 180
    make "updown random 2 make "leftright random 2
    make "dart 0 make "frame 0 make "total 0
    make "oldx mousex make "oldy mousey
  ]
  setsurname "red
  ;define player containers
  
  print word surname |'s turn...|
  showturtle
  
  setposition [0 0 250]

The main game loop moves the dart according to the mouse position. The dart moves up and down and side to side to simulate shaky hands, in an exaggerated fashion to make the game more challenging. The player pushes the left mouse button to launch the dart. The camera follows the dart in a method dictated by a random number.

  forever [
    ;main game loop
    if or :oldx != mousex :oldy != mousey [
      make "oldx mousex make "oldy mousey
      setposition {-100 + 200 * mousex / 100 50 - 100 * mousey / 100 250}
      cam:sety myrtle:ypos
      ;move the dart with the mouse
    ]
    move 1 random 360

    if :updown = 0 [up 1 if and pitch < 350 pitch > 20 [make "updown 1]]
    if :updown = 1 [dn 1 if and pitch < 350 pitch > 20 [make "updown 0]]
    if :leftright = 0 [rl 0.5 if and roll < 350 roll > 10 [make "leftright 1]]
    if :leftright = 1 [rr 0.5 if and roll < 350 roll > 10 [make "leftright 0]]
    ;shaky hands, maybe try drinking herbal tea?

    wait 1
    ;delay

    if buttonp 0 [
      ;if button clicked:

      make "camdir random 4
      ;pick random camera action

      dountil (item 3 extrapolate position vectorsub updir [0 0 0] 35) < 0 [
        ;until the tip of the dart hits the board (basically):

        setpremodel {"lt loopcount}
        ;spin the dart model
        lo 1 dn 0.1 cam:pullin 1
        ;toward the board and down a little

        if :camdir != 3 [cam:sety myrtle:ypos]
        if :camdir = 1 [cam:orbitleft 0.3]
        if :camdir = 2 [cam:orbitright 0.3]
        if :camdir = 3 [cam:orbitup 0.3]
        ;camera actions
      ]

Once the dart reaches the wall / board, we calculate if the dart hit the board or the wall by checking its distance from the center of the dart board [0 0 0]. If it actually hit the board then we 'stamp' the model in place.

      norender
      ;stop rendering graphics while we deal with things

      ht lo 34
      make "vec vectors
      setvectors originvectors
      updatestate
      ;need to update the state for towards to work
      ;with rendering disabled

      make "dir towards [0 0]
      ;where did we land relative to the center?

      if and :dir >= 171 :dir < 189 [make "score 20]
      if and :dir >= 189 :dir < 207 [make "score 1]
      if and :dir >= 207 :dir < 225 [make "score 18]
      if and :dir >= 225 :dir < 243 [make "score 4]
      if and :dir >= 243 :dir < 261 [make "score 13]
      if and :dir >= 261 :dir < 279 [make "score 6]
      if and :dir >= 279 :dir < 297 [make "score 10]
      if and :dir >= 297 :dir < 315 [make "score 15]
      if and :dir >= 315 :dir < 333 [make "score 2]
      if and :dir >= 333 :dir < 351 [make "score 17]
      if or :dir >= 351 :dir < 9 [make "score 3]
      if and :dir >= 9 :dir < 27 [make "score 19]
      if and :dir >= 27 :dir < 45 [make "score 7]
      if and :dir >= 45 :dir < 63 [make "score 16]
      if and :dir >= 63 :dir < 81 [make "score 8]
      if and :dir >= 81 :dir < 99 [make "score 11]
      if and :dir >= 99 :dir < 117 [make "score 14]
      if and :dir >= 117 :dir < 135 [make "score 9]
      if and :dir >= 135 :dir < 153 [make "score 12]
      if and :dir >= 153 :dir < 171 [make "score 5]
      ;calculate dart position and assign score

      make "dist distance extrapolate position vectorsub updir [0 0 0] zpos [0 0 0]
      ;how far away from the center is the tip of the dart?
      if :dist <= 5 [make "score 50]
      ;bullseye!
      if and :dist > 5 :dist < 10 [make "score 25]
      ;half bullseye
      if and :dist >= 35 :dist <= 40 [make "score :score * 3]
      ;triple ring
      if and :dist >= 60 :dist <= 65 [make "score :score * 2]
      ;double ring
      if :dist > 65 [make "score 0]
      ;missed!


We use the towards primitive (an original Apple Logo II primitive!) to determine the number of degrees the landed dart (which is in reality pointed upwards toward the ceiling, the dart 'descending' towards the board along the Z axis) would have to turn to face [0 0]. This, combined with the distance from the center allows us to calculate the dart's score.

      (print |Dart| word :dart + 1 |:| :score) make "frame :frame + :score
      setvectors :vec
      if :dist < 79 [
        ;'stick' a dart to the board using stamp
        playsound "click2
        ra 34
        run premodel
        if surname = "red [stamp "dart1] [stamp "dart2]
        render wait 120
      ]

      else [
        playsound "knock
        pr "Missed! ra 34 st render
        repeat (ypos + 300) / 4 [drift 4 :down cam:lo 4 wait 1]
      ]
      ;drop the dart to the floor

      cam:cs
      ;reset the camera

      setheading 0 setpitch 0 setroll 0
      setvectors originvectors
      ;reset state

      make "updown random 2
      make "leftright random 2
      ;pick random starting wobble directions
      ;for the next dart

      inc "dart
      ;add one to :dart

      if :dart = 3 [
        ;if we've thrown three darts:

        (print |Frame Score: | :frame)
        make "total :total + :frame
        (print word surname |'s Total Score: | :total)
        make "dart 0 make "frame 0
        cam:pushturtle
        cam:run pick [[repeat 30 [orbitleft 1 wait 1]] [repeat 30 [orbitright 1 wait 1]]]
        wait 120 cs cam:popturtle

        if :total > 301 [(print surname "wins!) finish]
        ;the end

        if surname = "red [setsurname "blue setmodel "dart2]
        else [setsurname "red setmodel "dart1]
        print word surname |'s turn...|
        ;switch players
      ]

      setpremodel [] cam:pullout 180 setposition [0 0 250] showturtle
      ;position camera, reset 'spin', show the next dart

    ]
    ;end of throw

  ]
  ;end of main game loop

END

Logo code is like poetry! It's easy to read and describes what the computer is doing in fairly broad terms. This is why it has always been great as a first text-based coding language.

 

A Starry turtleSpaces Logo Introduction Part Three: Starwarp Improvements

In the previous episode, we created a progressively-generated starfield we moved through with the camera turtle, creating a cool flying-through-space effect.

But it has a few issues we should address. Firstly, it is possible for a star to end up flying through the windscreen of our spaceship! Which is cool, but looks a bit strange. Second, as the program runs it piles up all of these stars behind us, which can slow everything down. We need to get rid of those. Finally, it would be pretty neat if we could have the stars ‘pop’ into view, so we’ll explore how we can do that.

Improvement One: Spaced Out Stars

So, firstly we want to ensure that stars aren’t placed too near to the center, where they could possibly fly through our windshield. Ideally we want to detect if we’re going to place a star within the narrow barrel our ship is flying through, and if so don’t place it there, place it somewhere else.

There are a few new tools we can use to accomplish this:

distance – takes two lists of coordinates, eg [x1 y1 z1] [x2 y2 z2], and returns the distance between them. This will be useful to us, because we are going to provide the prospective new spot position as one list, and {0 0 zpos} as the second list, giving us the distance between the new spot and the XY center of the space at Myrtle’s current Z position.

dountil – repeats a list of commands until it gets the desired results. In this case, we’re going to want to have dountil pick a random position, and then use the distance function to check if that position is outside of our no-go range. dountil executes the list of instructions it is provided before checking the condition it needs to stop executing, while its cousin until checks first.

So, to ensure we don’t place a star within that ‘barrel’, instead of the existing setposition command, we do the following:

dountil 100 < distance position {0 0 zpos} [
  setposition {-1500 + random 3000 -500 + random 1000 zpos}
]

So, until 100 is less than the distance between the turtle’s position and the XY center of the turtle’s current Z position, keep choosing a new position — and choose a new position before doing the first check.

Note: because of the way Logo’s parser works, if you do a comparison operation (<, >, = etc) where one side is a single parameter (eg a number) you’re best to put that FIRST and the complex parameter second.

Why? Because Logo collects things up right to left, and while Logo will evaluate the stuff to the right of the operator correctly, passing that to the operator, the parser will give the operator the first ‘complete’ thing it sees to the left of it, which if you switched things around would be {0 0 zpos} and is not what we want!

So to solve this you would need to put round brackets () around distance position {0 0 zpos} to ensure that Logo evaluated and gave < what we really wanted to give it. It’s easier in this case just to put the single parameter on the left.

This solves our problem! Stars will keep out of our way. However, this method also moves the turtle every time we try a new position, and all of these false jumps will stay in the turtle’s ‘turtle track’ or list of things the turtle has done.

There are a few methods we could use to stop this from happening, but this one is probably the simplest:

dountil 100 > distance :position {0 0 zpos) [
  make "position {-1500 + random 3000 -500 + random 1000 zpos}
]
setposition :position

Rather than set the position every time we try, we make a container (variable) called position containing the prospective coordinates, and test that instead. Then, once we have a good set of coordinates, we set that :position using the setposition command.

A colon before a word indicates to the parser that it is meant to pass the contents of a container with that name to the next command or function in the chain.

The colon is shorthand for thing, which returns the value of the container named passed to it. So, for example, you could have used setposition thing “position instead. Note that the name of the variable is preceded by a quote, not a colon. If you used a colon, thing would return the value of the container with the name CONTAINED inside of the container you referenced with the colon!

The mind boggles, doesn’t it?

This is also why you generally make “container rather than make :container — if you did the second, you would make a container with the name contained inside of the container you referenced, which does have practical applications but can be a bit confusing at first.

For now, just remember you make with a quote, retrieve with a colon.

Okay, moving on…

 

Improvement Two: Galactic Janitorial

This procedure creates a lot of stars. Like, a lot. While you can have a lot of things in turtleSpaces (so many things!) they can start to gum up the works if they get to extreme numbers. A computer can only remember so much you know! And so, we should clean out the stars behind us, because they don’t matter to us anymore anyway.

But the stars are part of Myrtle’s ‘turtle track’, and so we can’t just clean out some of them, can we? There are the clean and clearscreen commands but they get rid of everything!

Never fear, tag is here!

tag allows you to wrap one or more turtle track entries so that you can reference them later, to replace, copy or delete them. We’re going to put tags around stars so we can delete them. We do this with begintag and endtag.

We need to give each tag a name, and so we need to give begintag a name to use. endtag doesn’t take a name, since we can only close the last opened tag. If you create a tag within a tag, that tag has to be closed first, before you can close the tag above it.

What we’re going to do is every 2000 stars, we’re going to close off the existing tag (the stars still  in front of the camera turtle), then create a new tag for the next batch of stars, and erase the tag that came before the one that we just closed off (the stars now behind the camera turtle). It’s a real slight-of-hand magic trick!

To set up our tags, first we have to add the following before the forever loop:

  begintag 0
  endtag
  ;dummy 'first' tag (numbered 0)
  make "tag 1
  ;create tag container
  begintag :tag
  ;create second tag using the tag
  ;container value

Because we’re erasing the tag before the previous tag, we need to create a dummy 0 tag to erase when we get started. Then we create a tag container, which contains the value (number) of the current tag, and then we create a tag with a name of that value (1).

We’re all ready to go! Now, inside of the forever loop, we need to add:

    if divisorp 2000 loopcount [
      ;every 2000 loops:
      endtag
      erasetag :tag - 1
      ;erase the tag BEFORE the tag we just closed
      inc "tag
      ;increment the tag container
      begintag :tag
      ;start a new tag
    ]

if is sort of like dountil, except that it checks if the condition is true first and then executes the list of instructions provided to it, but it only does it once and only if the condition is true.

divisorp is a boolean, it returns only true or false. Because of booleans, unlike in other programming languages if does not require a comparison operator. Comparison operators (=, >, < etc) are themselves boolean operators — they return either true or false to if, until, dountil or whatever other command needs a boolean parameter passed to them.

divisorp returns true if the first value passed to it divides equally into the second value. divisorp  in this case returns true if 2000 divides equally into loopcount, which is the total count of the number of loops executed  — for example, the number of times we’ve been through the forever loop.

So every 2000 times through the forever loop, divisorp returns a true value and that causes if to execute the contents of the list. Which is:

endtag – end the current tag

erasetag :tag – 1 – erase the tag before the tag we just closed

inc “tag – increment the tag counter

begintag :tag – start a new tag

And that’s all there is to it. The janitor will come every so often and sweep out those old stars, keeping things running smoothly!

 

Improvement Three: View Screen On

I’m kind of torn on which is cooler, having the galaxy wink into existence around us or approaching it as if we’ve arrived from the intergalactic void, but if you want to try the winking-into-existence option, here’s how to do it:

First, before the forever loop, we put in a norender command. This stops turtleSpaces from updating the ‘render’, or representation of items in 3D space.

Second, inside the forever loop, before everything else, we need to put the following:

    if loopcount = 1500 [
      render
      print |Engage!|
    ]

which as you may remember from the previous section, after 1500 loops will cause if to execute the render command, which turns rendering back on. This ensures there are stars for us to see before we start to see them.

That’s all there is to it!
Bonus Improvement: Spinning Through Space

As a final bonus improvement, after the cam:forward command, insert the following:

cam:rollright 1/10

This will simulate the gentle roll of the spacecraft as it travels through the stars, causing the stars to spin slightly around the spacecraft.

Congratulations! You’ve graduated from starwarp academy! Good job. Welcome to turtleSpaces.

 

A Starry turtleSpaces Logo Introduction Part Two: Starwarp

In this second part of our introduction to turtleSpaces Logo, we’re going to take the stars we made in the first part, and create a ‘rolling’ starfield we are going to move through using the camera turtle, to create a Star Trek-style warp effect.

To create this effect, the drawing turtle, Myrtle, is going to create stars deep into the space. The camera turtle, Snappy, will move forward (the camera turtle points into the space, towards Myrtle, by default) following Myrtle as she moves deeper, creating stars.

This tutorial is in three parts: First we’ll create the moving starfield, then we’ll cause old stars to vanish (and explain why we need to do that), and finally we’ll set things up so that the starfield ‘pops’ into view, rather than being shown from the beginning.

To begin, we’ll create a new procedure:

TO starwarp
END

and then we’ll start with some setup commands:

TO starwarp

  reset
  hideturtle
  penup
  lower 3000

  setfillshade 12
  setpenshade -12
  gradient

END

We know all of this already from the first part, except for lower, which causes the turtle to descend, from its point of view.

Then we’ll add the main forever loop, which will repeat, well, forever:

  forever [
  ]

Then we’ll populate it with the commands needed to make the rolling starfield, and explain them:

  forever [
    
    lower 10
    setposition {-1500 + random 3000 -500 + random 1000 zpos}
    randomfillcolor
    setpencolor fillcolor
    spot 0.1 * (1 + random 50)
    cam:forward 10

  ]

This is the basic routine, but it can use a lot of work, which we’ll get to in a moment.

lower 10 – lowers the turtle 10 turtle units. The turtle descends from its position and orientation, like an elevator.

setposition {-1500 + random 3000 -500 + random 1000 zpos} – this is a bit complicated. setposition sets the turtle’s position in 3D space. It takes a list of three values, X Y and Z. It does not change the turtle’s orientation.

The center of 3D space is [0 0 0]. From Myrtle’s default position, to her left is negative on the X axis, to her right is positive. To her rear is negative in the Y axis, to her front positive. Below her is negative in the Z axis, while above her is positive.

And so, we’re passing a list to setposition made up of two random calculations (for the X and Y coordinates) and Myrtle’s existing Z co-ordinate, which is expressed by the zpos primitive, which is a function that returns Myrtle’s current Z coordinate.

Why the curly braces? Well, traditional Logo lists such as [pig duck cow] aren’t dynamically generated — if you want to remove, add or change values inside of them, you need to do so using commands that manipulate the list. But this can be a bit tedious and so we created the concept of ‘soft lists’ (as opposed to traditional ‘hard lists’) which are lists whose contents are evaluated (or solidified) at runtime, when the interpreter actually processes and executes the command to which the list is attached.

And so, with soft lists, each item is usually either a function (such as random) or a value (such as 10). If you want to add a string value to a softlist, you need to precede it with a ” eg “duck or surround it with pipes eg |duck|.

So, when the setposition command is evaluated, the parser (the part of Logo that decides what to do next) sees the softlist, and evaluates its contents, turning it into a hard list. So it generates the two random numbers, and gets the zpos, and then creates a hard list of 3 items, passing it back to setposition.

randomfillcolor – sets a random fill color (as in part one)

setpencolor fillcolor – sets the pen color to the fill color (as in part one)

spot 0.1 * (1 + random 50) – creates a spot of a size from 0.1 to 5

cam:forward 10 – moves the camera turtle (cam is a shortcut turtle name for the current view turtle). Prefixing a command with turtle: causes the named turtle to execute that command.

…and that’s it for version one! Run the starwarp procedure and see what happens.

Pretty cool huh? But it has a few shortcomings, which we will address in part three.

TO starwarp

  ;this procedure recreates the classic
  ;'moving starfield' effect
  
  ;the turtle starts deep into the workspace
  ;(by 'lowering' or decreasing its Z-coordinate)
  ;then creating stars (at least a certain distance
  ;away from the center using the distance function)
  ;and continuing to lower. Meanwhile the camera
  ;moves forward (from its perspective), following
  ;the turtle.
  
  ;Like in reality, the stars are not moving, the
  ;turtles are!
  
  ;On to the code:
  
  reset
  ;reset the workspace
  hideturtle
  ;hide me!
  penup
  ;don't draw
  lower 3000
  ;'lower' into the distance
  
  setfillshade 12
  setpenshade -12
  gradient
  ;gradiented stars
  ;gradiented shapes graduate between the
  ;pencolor / penshade and the fillcolor / fillshade
  
  forever [
    
    lower 10
    ;move the 'star turtle' deeper into the scene
      
    setposition {-1500 + random 3000 -500 + random 1000 zpos}
    ;flat-ish galaxy
    
    ;curly braces denote a 'soft list', a list that
    ;is evaluated and created upon execution
    
    randomfillcolor setpencolor fillcolor
    ;spots and other shapes use the fill color
    ;which randomfillcolor randomly chooses
    ;we set the pencolor to the fill color
    ;for the gradient, because we're only
    ;gradienting the shade
    
    spot 0.1 * (1 + random 50)
    ;make a randomly-sized star
    ;between 0.1 and 5 turtle units in size
    
    cam:forward 10
    ;the camera turtle points towards
    ;what it's looking at, so moving
    ;forward decreases its z position
    ;(in its default orientation)
    
  ]
  ;do this forever and ever
  
END


 

A Starry turtleSpaces Logo Introduction Part One: Starfield

Traditional Logo had new users build a house as an introduction, but due to turtleSpaces’ 3D nature, starfields are much more impressive, so we’ll start there.

Click and drag the window above to see all the stars!

Cool huh? First, we’re going to create this simple starfield that wraps around the camera position.

We’ll start by creating the procedure:

TO stars
END

Then we’ll add in some setup stuff:

TO stars

  reset
  hideturtle
  penup

  setfillshade 12
  setpenshade -12
  gradient

END

reset – resets the workspace to its default configuration
hideturtle – hides the turtle
penup – doesn’t draw lines. The turtle draws lines as it moves by default

setfillshade 12 – sets the fill shade to 12. Shades have a range of -15 (light) to +15 (dark) where 0 is normal
setpenshade -12 – sets the pen shade to -12 (light)
gradient – causes shapes that support gradients to use them. They graduate from the pen color / shade to the fill color / shade

We don’t need to use the gradients, but the starfield looks so much better with them enabled!

So, now we’ll carry on and create our main loop:

  repeat 1000 [
  ]

This will create 1000 stars, once we fill in the rest of it. Let’s fill in the loop and then go through each command:

  repeat 1000 [
    randomvectors
    forward 500 + random 1000
    up 90
    randomfillcolor
    setpencolor fillcolor
    spot 1 + random 10
    home
  ]

…and that’s it! Not a lot, is it? Let’s go through it step-by-step.

randomvectors – sets a random three-dimensional orientation for the turtle. This is the equivalent of going left random 360 up random 360 rollright random 360 but in an easier and faster method. Why vectors? Because vectors describe the turtle’s orientation in 3D space, as well as the orientation of all other objects in it. Why build-in a shortcut? Because we wan’t to be easy to use!

forward 500 + random 1000 – move forward 500 turtle-units PLUS 0-999 turtle-units. random returns a random value between 0 and one less than the given value, because 0 is one of the (in this case) possible 1000 choices. This gives our starfield depth while making sure the stars aren’t too close!

random is a function, it doesn’t do anything on its own. Try typing random 30 at the prompt and see, you’ll get:

I don’t know what to do with 10

for example. That value needs to be ‘passed’ to another function or command — its output needs to become someone else’s input, in this case +.

Then +‘s output is passed to forward, which then moves the desired number of turtle-units. This is a big part of how Logo works, and is why commands can be stacked on the same line — they gobble up all of the inputs, and once they do they are ‘complete’ and we know to move on.

up 90 – spots are created around the turtle on its z-plane and so we need to tilt the turtle up 90 degrees so that the stars are facing back towards our view point near the center of the space.

(Note that if you used the lower primitive instead of forward, you wouldn’t need to tilt the turtle up.)

(Note also that different shapes may be positioned in different orientations relative to the turtle. The best way to build things is to progressively build them through the REPL (the command-line interface), using the backtrack command to undo any mistakes.)

randomfillcolor – picks a random fillcolor (the color shapes are colored), a value between 1 and 15 that is NOT the current fillcolor. The alternate form of this is complex: make “oldfillcolor fillcolor dountil fillcolor != :oldfillcolor [setfillcolor 1 + random 15]randomfillcolor is nicer. But you could do it the hard way if you want!

setpencolor fillcolor – because we’re using gradients we need to make the pencolor the new fillcolor so that the stars don’t gradient to a different color. fillcolor is a function that returns the current fillcolor.

spot 1 + random 10 – create a spot graphical primitive between 1 and 10 turtle-units in diameter around the turtle. Remember, random 10 will return a value from 0 to 9. If you just did spot random 10 without adding the 1 you might get 0., which while not an error won’t create anything of substance (literally).

home – return the turtle to the home position, which by default is [0 0 0], the center of the space.

Finally, all of this ‘filling’ is a list, passed to repeat to execute. Logo uses lists for all sorts of things, as you’ll see as you progress in your journey through Logo!

Congratulations, you’ve reached the end of this first (ahem) turtorial! Next in part two, we’re going to make a moving star ‘warp’ effect.

Here’s the full commented listing:

TO stars
  ;TO declares a procedure, in this case one called
  ;'stars'. Procedures can be simply called by name
  ;to execute them. So, at the prompt, you can
  ;type 'stars' (without quotes) to execute this
  ;procedure
  
  ;this is a very simple starfield generator and
  ;a good first project for turtleSpaces. All
  ;we're doing is randomly orienting the turtle,
  ;moving forward a random amount and creating
  ;a randomly-sized spot 1000 times.
  
  ;10 commands, one function. Dead simple!
  
  ;But first a little setup stuff...
  
  reset
  ;reset the workspace
  
  hideturtle
  ;hide the turtle
  
  penup
  ;don't draw lines
  
  ;we could omit this but it looks much better this way:
  setfillshade 12
  setpenshade -12
  gradient
  ;gradiented stars
  ;gradiented shapes graduate between the
  ;pencolor / penshade and the fillcolor / fillshade
  
  ;...and that's it for setup stuff!
  ;Now on to the main event:
  
  repeat 1000 [
    ;this means 'do this 1000 times'.
    ;things between square brackets are lists.
    ;repeat is a command that takes the number
    ;of times it is supposed to execute the contents
    ;of a list, and that list itself. What follows
    ;is the contents of that list:
    
    randomvectors
    ;give the turtle a random 3D orientation.
    ;you could do this yourself using a bunch
    ;of movement commands but we like making
    ;things easy!
    
    forward 500 + random 1000
    ;move forward 500 turtle units
    ;plus 0-999 turtle units
    ;(random returns a value between
    ;0 and the number passed to it
    ;excluding that number)
    
    ;random is a function that does not
    ;'do' anything on its own. Try typing
    ;'random 30' at the prompt to see.
    ;It returns a random value, which is
    ;then passed to another function or
    ;a command. In this case, random's
    ;output is passed to the + function,
    ;which then adds 500 to it and then passes
    ;its output to the forward command
    
    ;this is a big part of how Logo works
    
    up 90
    ;spots are created around the turtle
    ;on the z-plane, and so we need to tilt
    ;the turtle up
    
    ;try creating a spot eg 'spot 100'
    ;after the workspace has been reset
    ;to see how the spot is placed relative
    ;to the turtle
    
    ;different shapes may be places different
    ;ways relative to the turtle
    
    randomfillcolorsp
    ;shapes use the fill color
    ;and randomfillcolor picks a random
    ;fill color. You can pick one arbitrarily
    ;using the setfillcolor command
    
    setpencolor fillcolor
    ;because we're using a shade gradient, we
    ;need to set the pencolor to the fillcolor
    ;otherwise it would gradient to the default
    ;pencolor as well
    
    spot 1 + random 10
    ;make a spot between 1
    ;and 10 turtle-units in diameter
    ;(remember, random 10 returns
    ;a value between 0 and 9)
    
    home
    ;return to the home position
    ;(where the turtle started)
    
  ]
  ;perform the above list 1000 times
  ;as you can see, lists can be spread out
  ;across many lines
  
  ;So, 14 commands, a fillcolor and a couple of
  ;randoms and that's it.
  
  ;Click and drag the mouse over the view window
  ;to rotate the camera and see all the stars!
  
END

How to create and 3D print a chess pawn in turtleSpaces Logo

First, open the weblogo.

Then, click in the bottom right REPL area

Create the ‘head’ of the pawn using the ico primitive

cs penup ico 20


If you start the line with a cs, you can use the up arrow to go back to the line after adding each command (and seeing the result) to edit what you’ve done and add more! Append all of the following instructions on to the same line, then just keep re-executing it.

We’re going to be making a cone next, and cones are created under the turtle. So we need to tiilt the turtle down, and lower it close to the bottom of the ico, in preparation for creating a ‘cut cone’:

dn 90 lo 17

Next we create a cutcone, lower the turtle and create another cutcone. Type help “cutcone to see the parameters…

cutcone 10 20 5 20


Lower the turtle and create the next cone segment

lo 5 cutcone 20 15 5 20


Lower the turtle again and create a larger cone

lo 5 cutcone 10 20 40 20


… smaller cone, but deeper than is visible so that the 3D printer slices the pawn correctly …

lo 40 cutcone 22 28 20 20


… a torus … (type help “torus to see the parameters)

lo 15 torus 5 24 20 20

… another torus …

lo 5 torus 3 28 20 20

… and a cylinder to finish the base! (type help “cylinder to see the parameters)

cylinder 31 5 20


All done! Now you can download the STL file under the File menu, open it up in your slicing program and print it. But don’t forget to type hideturtle first or you might get a surprise!

The pawn sliced in Ultimaker Cura…

The whole line of code should look something like this:

cs ico 20 dn 90 lo 17 cutcone 10 20 5 20 lo 5 
cutcone 20 15 5 20 lo 5 cutcone 10 20 40 20 lo 40 
cutcone 22 28 20 20 lo 15 torus 5 24 20 20 lo 5 
torus 3 28 20 20 cylinder 31 5 20 hideturtle

Easy peasy! Click and hold the left mouse button over the model and drag to rotate it.

Read through the shape guides available under the Docs menu on this website and think about how you could create other chess pieces!

This is the chess pawn as a procedure:

TO pawn
  cs penup
  dn 90 
  ico 20 
  lo 17 
  cutcone 10 20 5 20 
  lo 5 
  cutcone 20 15 5 20 
  lo 5 
  cutcone 10 20 40 20 
  lo 40 
  cutcone 22 28 20 20 
  lo 15 
  torus 5 24 20 20 
  lo 5 
  torus 3 28 20 20 
  cylinder 31 5 20 
  hideturtle
END

You can turn it into a procedure just by typing to pawn in the REPL, pressing the up arrow until you retrieve the pawn code, press enter, and then type end. Then you can save it!