# All posts by auntiemyrtle

#### 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]
```

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]

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
]

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!

#### Turtle Wordle: A Clone of Wordle in 60 Lines of Logo

Wordle, a cross between Mastermind and Hangman, seems to be the rage these days. Also, implementing it in as few lines as possible. In Logo we get to cheat a little about the second bit, as we can stack multiple commands on the same line. But we still need to keep things within a reasonable line length.

We’re happy to say we succeeded in our efforts, and even expanded on the model somewhat, adding progressively longer words and hints, among other things. This is a pretty good example of string and list management in Logo, which is what it was initially created for, even before the turtle!

Check out the source code below, or open in the web IDE…

```TO wordle
reset hideturtle make [score level stage] [0 1 1] maximize settextsize 1
cleartext overlayscreen settextwindow [0 30 99 47] settextforeground 14 ;short form: settf
print |TurtleWordle: Get one point for each letter in solution, lose one point for each guess.|
print |You have word-length + 1 chances to guess the word. Level acts as score multiplier.|
print |Get 6 4-letter words correct to advance, 5 5-letter words, 4 6-letter words and so-on.|
print |Game ends when you run out of guesses, or guess the 9-letter word! (You brain you!)|
print |Uppercase indicates correct letter and position, lowercase only that letter is in word.|
cursordown playsound "drop ;we use simple encryption not to make this too easy!
make "words1  [bsai dpme ql?n kgli hskn ap?@ jmai qj?k afsk ngli mtcp jmem dgpc @cr? @jsc ctcl]
make "words2  [xc@p? nsnnw kmsqc ?@msr slbcp fmpqc qmslb jmaiq @p?gl ilmai lmprf ?jnf? glbcv]
make "words3  [q?j?kg ngaijc afccqc igrrcl ngejcr rsprjc rmgjcr @mtglc amddcc ecp@gj bctglc]
make "words4  [mar?nsq qg@jgle mnsjclr ejsrrml n?wkclr asqfgml n?p?qmj jcrrcpq gltmjtc @j?licr]
make "words5  [bp?k?rga nclr?eml kmrmpa?p qmjsrgml slbcpqc? mtcpam?r sk@pcjj? l?rrpcqq qslqfglc]
make "words6  [qs@k?pglc u?rcpd?jj qrpccra?p slbcpuc?p bcqrpmwcp ?n?prkclr bmaskclrq pm?bam?af]
label "select ;return here using 'go'
if :level = 7 [settf 3 print |Congratulations! You win!| playsound "applause go "playagain]
make "selection random count thing word "words :level
make "enword uppercase item :selection thing word "words :level
make word "words :level remove :selection thing word "words :level
make "word empty foreach "char :enword [make "word word :word char (ascii :char) + 34]
make "chances 1 + count :word make "tries 0 clearscreen penup setfillcolor 12 ht
settypesize 20 setxy -200 80 typeset "Turtle setxy 80 80 typeset "Wordle settypesize 10 home st
foreach "letter "abcdefghijklmnopqrstuvwxyz [make :letter instances :letter :word]
forever [
foreach "letter "abcdefghijklmnopqrstuvwxyz [make word "p :letter 0]
settf 6 (print |Tries remaining:| :chances - :tries) settf 15
dountil (count :guess) = (count :word) [
(type |Word is | count :word | letters: |) make "guess lowercase readword]
inc "tries make "correct 0 make "hits []
setxy 0 - (5 * count :word) 100 - (:tries * 20)
foreach "letter :guess [
if :letter = item loopcount :word [inc word "p :letter] ]
foreach "letter :guess [
if :letter = item loopcount :word [
settf 4 type uppercase :letter
setfc 4 typeset uppercase :letter
inc "correct playsound "drop queue loopcount "hits
] [if containsp :letter :word [inc word "p :letter
if (thing word "p :letter) <= thing :letter [ settf 13 type lowercase :letter setfc 13 typeset lowercase :letter playsound "drip] [ settf 15 setfc 15 type "- typeset "- playsound "dunk] ] [settf 15 setfc 15 type "- typeset "- playsound "dunk] ] ] print empty slideright 10 if :correct = count :word [ settf 12 type |You got it! | playsound pick [sheep sheep2 sheep3] make "score :score + ((1 + ((count :word) - :tries)) * :level) if (:chances - :tries) > 0 [
inc "stage if :stage = 8 - :level [
make "stage 1 inc "level settf 9 print |Level up!| playsound "please] ] [
settf 1 print |No points. Try again.| playsound "crow]
settf 13 print sentence |Your score is: | :score go "select]
if :tries = :chances [settf 3 (print |Word was:| :word)
settf 7 (print |Too bad! Final score: | :score) playsound "evil go "playagain]
if 0 = remainder :tries 3 [
dountil not containsp :pick :hits [make "pick 1 + random count :word] settf 8
(print |Psst: the letter in position | :pick | is | uppercase item :pick :word |!|)] ]
label "playagain settc 13 question |Play again? (Y/N)|
if "N = uppercase answer [restore resetall finish] [wordle]
END
```

#### Floaty Turtle: A Flappy Bird clone in Logo

Floaty Turtle is a simple clone of Flappy Bird written in the Logo programming language that turtleSpaces uses.

Open in the integrated development environment (IDE)

```NEWTURTLE "myrtle

TO floaty
;here is yet another example of a relatively complex
;game that can be rather simply implemented in Logo:
;a flappy bird clone!

;to make it work we use two turtles, one is the player
;and the other creates, moves and erases the pipes
;these are two seperate 'workers' or threads

;this procedure kicks off the game, displays a title
;screen, and then manages the player, processing
;input and moving the player appropriately

;the pipemaker turtle and its pipes procedure
;manages the pipes and checks for collisions

;the turtle does not move horizontally,

reset
cleartext
setbackgroundcolor pick [6 7 14 5 10 11 14 15]
;select a random background color from the given list

noaudiowait
;don't wait for audio to finish playing

setmodel [penup back 7 stamp "myrtle]
;we need to offset Myrtle's actual position more
;toward her center for the purposes of this game

setmodelscale 5
;make myrtle big!

playsound "doodoo
;startup sound

pipemaker:newworker [pipes]
;'kicks off' the pipemaker turtle, who
;creates and moves the pipes

;create the title screen:

penup
randomfillcolor
slideleft 180 forward 5
settypesize 60
pushturtle
;'push' the turtle's state (position etc) on
;to a stack, from which it can be 'popped'
;off later

typeset "Floaty
popturtle
;restore the previously pushed state

back 100
randfc
;short for randomfillcolor

pushturtle
typeset "Turtle
popturtle
bk 10 sr 70
;back and slideright

settypesize 10
randfc
typeset |Press any key to float!|
;title screen complete!

home
dn 90 rt 90
;down and right

make "raise 0
;the :raise container holds the current
;value remaining to float Myrtle upwards

forever [

if loopcount = 1 [say "Ready!]
if loopcount = 16 [say "Set!]
if loopcount = 31 [say "Go!]
;the loopcount is the number of times the forever
;loop has executed. Based on that count, say the

if divisorp 100 loopcount [
;every 100 loops pick a random background
setbg pick [6 7 14 5 10 11 14 15]
;from the given list
]
;setbg short for setbackground

dosleep [50] [
;try to maintain an average of 50ms to do
;the following:

if loopcount > 30 [
;if the loopcount is greater than 30:

if :raise = 0 [
;if the value inside the :raise container
;is 0, then lower the turtle 2.5 turtle-units:

lower 2.5
]
[
;otherwise raise the turtle 2.5 turtle-units
;and decrease the value inside the :raise
;container by 2.5
raise 2.5
make "raise :raise - 2.5
]

if keyp [
;if a key is pressed, play a sound,
;remove the key from the keyboard buffer
;and increase the value of the :raise
;container by 20:

playsound "air
clearchar
make "raise :raise + 20
]

clean
;remove the turtle's 'track' -- it's not
;drawing or creating anything so we don't
;need to have it piling up

]
]
]
END
```
```NEWTURTLE "pipemaker

TO pipes
;the pipemaker turtle's job is to
;create the pipes, shift them to the left
;and check if they've hit the turtle

clearscreen
noaudiowait
penup
home
slideright 250
back 120
up 90
;position the turtle appropriately for
;creating the pipes offscreen to the right

begintag "move
endtag
;this tag is used to shift the pipes,
;by replacing its contents with an ever-
;increasing slideleft directive

make "height 100
;this is the starting height of the pipes

make "heights [0 0 0 0 0]
;initialize an 'zeroes' list of pipe heights

make "score 0
make "count 0
;initialize the score and the pipe count

settypesize 20
;set the type size. Type is the text you
;create inside the 3D world

forever [
;do this forever:

sleep [50]
;try to do this stuff in an average of
;50ms -- less or more as needed to keep
;that average:

if divisorp 20 loopcount [
;every 20 loops:

inc "count
;increase the contents of the :count container
;by one

if loopcount > 60 [
;if the loopcount is greater than 60:
playsound "clang
inc "score
say :score
;make a sound, increment :score and say it
]

slideright 100

begintag loopcount
;create a new 'tag' for the pipe
;so we can remove it after it passes the
;left side of the screen

make "col 1 + random 5
;select a random number and put it in :col

setfillcolor item :col [12 9 7 6 11 14 10 13]
;set that color based on :col's index in the
;provided list

make "height :height + (-60 + random 120)
;increase or decrease :height based on a
;random number

if :height < 20 [make "height 30 + random 20] if :height > 120 [make "height 110 - random 20]
;if too high or low, pick a new value higher
;or lower as needed

queue :height "heights
;add the new height to the list of heights

;create the lower pipe:
cylinder 20 :height 50
lower :height
setfillcolor item :col [4 8 6 2 1 12 5 3]
;select the complementary color for the pipe

cylinder 25 20 50
lower 50

;type the pipe number:
down 90 right 90 back 10
if :count > 9 [bk 10]
inscribe :count
if :count > 9 [fd 10]
forward 10 left 90 up 90
;if gameplay is too slow, comment out
;the above five lines

;create the upper pipe:
lower 50
;remember, the turtle is upside down!
cylinder 25 20 50
lower 20
setfillcolor item :col [12 9 7 6 11 14 10 13]
cylinder 20 120 - ypos 50
raise :height + 120

endtag
;close the pipe 'tag'

if loopcount > 119 [erasetag loopcount - 100]
;erase the pipe that has left the screen

ignore pop "heights
;remove its height from the :heights container
;and just throw it away (ignore it)
]

if loopcount > 60 [
;check for crash:
if (or
myrtle:ypos > (-120 + (90 + item 3 :heights))
myrtle:ypos < (-120 + (20 + item 3 :heights))
;the third item in the :heights list is the
;height of the pipe around where the turtle
;is, so we can use that to see if we've hit
;anything
) [
(print |Crash! Final Score: | :score)
print |Press flag icon to play again!|

calm "myrtle
myrtle:run [setfillcolor 1 icosphere 30]
;red ball

audiowait
playsound "crack
playsound "aw

myrtle:clean
;gets rid of the red ball

noaudiowait
playsound "fall

myrtle:repeat 90 [
forward 3 lower 3 wait 1
]
;fall to the ground

audiowait
playsound "crash
finish
;that's all folks

]
]

replacetag "move [slideleft loopcount * 5]
;increase the value of the slideleft
;command in the move tag, shifting the
;pipes to the left

]

;and that's it! Not really much code, is it?

END
```

#### 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
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
;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

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.

#### Bale Example: Dodgeball

Dodgeball is a very simple game using a bale wherein the player moves the turtle from the left to right sides of the screen using the keyboard.

This is similar to a popular example used to teach Scratch, and the basic concept should be familiar to most Scratch learners.

In the case of this example, there are multiple balls that are controlled by a ‘bale’, a group of turtles who all execute the same code, first the code in the bale’s init procedure, and then the code in the ‘main’ procedure repeatedly. Each member of the bale executes consecutively, and once all members of a bale have executed (a cycle), the bale’s graphical output (its ‘turtle tracks’) is updated.

The bale’s execution is started by Myrtle, using the startbale command.

Myrtle checks to see if the user has pressed any of the movement keys and moves if required. She also checks to see if she’s been hit by a ball, or if she’s reached the right side of the screen, and if either condition has been met, the game ends.

The balls meanwhile move up and down at different speeds, gradually sliding towards the left side of the screen (to prevent Myrtle from procrastinating!)

```NEWTURTLE "myrtle

TO start
;this is a very simple yet addicting and frustrating game

reset
setmodel [bk 7.5 stamp "myrtle]
;myrtle's position is usually her tail, so we need to move her a bit
;so that her position is center-ish of her shell

startbale "balls 11
pu
setxy -170 0
rt 90
forever [
if keyp [
if :key = "k [sr 10]
if :key = "i [sl 10]
if :key = "j [bk 10]
if :key = "l [fd 10]
]
;pressing keys causes turtle to move

if nearp 15 [pr "ouch! repeat 1000 [rt 1] finish]
;got hit by a ball

if xpos > 180 [pr "win! finish]
;made it to the other side!
]
END

ON start flag queue []
start
END

NEWTURTLE "snappy

NEWTURTLE "libby

NEWBALE "balls

TO init
setmodel {"setfc baleindex "ico 10}
st
penup
setxy -160 + baleindex * 30 100
output {"dir 1 "speed 1 + (0.1 * random (5 * (5 + baleindex)))}

END

TO main
if :dir = 0 [bk :speed] [fd :speed]

slideleft 0.1
;the balls gradually move toward the left side
;of the screen to discourage procrastination!

if ypos < -100 [make "dir 1]
if ypos > 100 [make "dir 0]
END

```

#### Bales, Missiles and Triggers, Oh My!

turtleSpaces is great for creating 3D models and animations, but we also want it to be great for games. Unfortunately, the interpreter is not the fastest thing around — but that’s okay! We can compensate by creating new commands that work ‘under the hood’ to take care of certain game elements.

Bales are groups of turtles that all execute the same code, for example alien attackers in a space battle game. They are declared similarly to turtles, using a NEWBALE declaration.

When each member of a bale is initialized, it executes the code in the bale’s INIT procedure. Then, it repeatedly executes the code in the MAIN procedure in sequence with the other members of the bale, that is each member of the bale executes the code in succession using the same thread. At the end of each cycle, when all bale members have executed, their ‘turtle tracks’ are rendered. This makes it seem like they’ve all moved at the same time.

Take for example this code from a Space Invaders-inspired game (you can try it out here: https://turtlespaces.org/weblogo/?pub=81)

```NEWBALE "ships

TO init
;bales are groups of turtles that execute the same code in sequence,
;one after the other. They are useful for things like groups of
;enemies, to keep them in lock step, where hatchlings can end up
;executing at different rates of speed and fall out of sequence
;with each other.

;new bale members always execute the init procedure, after which
;they repeatedly execute the main procedure until they are
;removed or the entire bale is stopped.

penup
noaudiowait
newmodel "bullet [setfillcolor 15 icosphere 1.5]
setmodelscale 1.5
setposition {
(-150 + 30 * (remainder baleindex - 1 5))
100 - (20 * (int ((baleindex - 1) / 5)))
5
}
;based on the number (index) of the bale member, place
;it in the attacking wave

if 1 = remainder int (baleindex - 1) / 5 2 [setx xpos + 10]
;let's offset the turtles in alternating rows

right 180
;turn to face down

showturtle
;show the turtle

rollright baleindex * 10
;set the default roll rotation

output [dir 1]
;the pairs provided by output become variables in main
;in this case we're setting the default move direction, which
;is the same for all members in this bale
END
```

Each member of the bale is placed based upon its index number in the bale. Once we position the bale member, it moves on to the main execution loop:

```TO main
dosleep 10 [
;take at least 10 milliseconds to do the following:

rollright 10
;roll right ten degrees

if and xpos < 150 :dir = 1 [drift 5 [1 0 0] drift 1 [0 -1 0]] if and xpos = 150 :dir = 1 [make "dir 0 drift 10 [0 0 -1] drift 10 [0 -1 0]] if and xpos > -150 :dir = 0 [drift 5 [-1 0 0] drift 1 [0 -1 0]]
if and xpos = -150 :dir = 0 [make "dir 1 drift 10 [0 0 1] drift 10 [0 -1 0]]
;we use drift to move the ships because we're rolling them for effect

if 1 = random 20 [
newmissile "missile "bullet [0 -1 0] 20 + random 20 [200]
playsound "zap3
]
;fire a missile randomly

if or ypos < -100 containsp "myrtle near position 20 [ make "hit true ] ;if an alien reaches the bottom of the screen it's over if baleindex = last balemembers balename [ if :delay > 0 [dec "delay]
;this increases the speed of the invaders 'music'
;once each time all the bale turtles have executed
;(each loop)
]
]
END
```

Each bale member moves across the screen, randomly firing missiles, until they are destroyed or reach the bottom of the screen, triggering a ‘game over’ condition.

```newmissile "missile "bullet [0 -1 0] 20 + random 20 [200]
```

Missiles are simple elements that are controlled by the game engine. They move through 3D space, and are detected by primitives such as NEARMISSILEP and NEARMISSILES. They can also be detected by the nearmissile trigger. They are much more efficient than using turtles as projectiles.

Similarly, triggers are blocks of code that are executed when a certain condition occurs. These conditions are monitored by the underlying game engine, and are ‘triggered’ when these conditions are detected to be true. Once again, this is much more efficient then having a turtle constantly execute Logo code waiting for the condition to occur!

The following trigger belongs to the previously mentioned alien ships bale:

```ON boom nearmissile block [missile2 10]
hideturtle
playsound "explosion
inc "score
show :score
stopmissile flat list :boom :boomanim
die
;this turtle is no more
END
```

This trigger increments the player’s score, disposes of the missile that triggered it, and then ends the turtle to whom the trigger belongs, in this case a member of the alien ship bale.

As you can see, bales, missiles and triggers all contribute to empowering turtleSpaces coders to create faster-paced, more engaging games.

#### The Art of the Turtle

turtleSpaces isn’t just about games or 3D models, it’s also about art. Although generated by a Logo computer program, some of the visuals created by turtleSpaces can be quite striking! Combined with a digital art program such as Filter Forge, the results can be very artistic indeed.

Check out this gallery of turtle art:

#### Activity Idea: Gone Fishin’

Build this cool island scene using turtleSpaces Logo and a few basic shapes!

First we begin with a tree. We can build a trunk using a repeat loop of cylinders that get narrower in diameter and longer as we go. We can introduce a slight curve as well by tilting the turtle up a bit each cylinder we create:

Next we need to create the leaves, which we can do using the fiso (filled triangle) primitive. We can use a repeat loop to create a series of triangles growing in size, and then a second repeat loop to create a further series of triangles shrinking in size. In both cases, we tilt the turtle down a bit each triangle we create:

Then we can use a further wrapping repeat loop to create 8 of them around the trunk:

Let’s add six coconuts to the top of the tree using another repeat loop and icospheroids:

But one tree is kind of lonely, so let’s create a ring of eight around the edge of the island using the orbitleft primitive. We can also make the island more mound-like using a domoid:

Let’s add a dock Myrtle can fish off of made out of cylinders and voxeloids:

And a hut to shelter in made out of made out of a cutsphereslice and a sphereslice:

Myrtle’s all set, let’s get her fishing! The fishing rod is created using a thick line and a thin line:

The sunset effect is created using an inverted gradient tube:

Good job, Myrtle! You can check out the island yourself at https://turtlespaces.org/weblogo/?pub=64

Just click the flag to create the island. Don’t forget that you can click and drag on the viewport to move the camera, use the scroll wheel to zoom in and out, rotate using the right button and drag, and click both buttons and drag to pan.

Check out the code to see how it’s done! Then try something similar yourself. What will your island look like? Share it so we can see for ourselves!

#### Introducing Environments

Environments are a cool new way to get started with Logo. turtleSpaces now provides a variety of environments in different settings to inspire Logo creation:

Starry Night and Star Platform – Myrtle builds in space!

Winter Ice Pond – Myrtle goes skating!

Pyramid Desert – Build a monument to the Gods!

Under the Sea – Shelly swims with the fishes!

Forest Cabin – Myrtle’s hideaway in the woods.

City Park – Myrtle plays in Central Park!

Parking Lot – Myrtle learns to drive.

The Moon Base – Myrtle on the Moon!

Alien Trees – A ring of growing alien trees.

Environments are available in both the desktop application and the web application. In the web application, you can find them when you start a new project (under the file menu). On the desktop, you open them the way you open examples, in the environments folder.

Environments are written in Logo and are randomly created when they are opened, so some of them may take a minute or two to fully form. You can’t see the environment code in the built-in editors as they are managed by system turtles, which can be used to program the broader Logo environment for purposes such as environments, tutorials and games that use the Logo interpreter to interact with them.

But if you download the procedures, or look at them in an external text editor, you can see the code that makes up the environments, and tweak them, or use them as a template to create your own!

#### turtleSpaces Halloween!

Witches, pumpkins, devils, ghosts and black cats are all haunting turtleSpaces this Hallowe’en! turtleSpaces is great for creating 3D models using code.

You can also find these models in the Published projects section of the webLogo interpreter.

### The Pumpkin:

The pumpkin is created using the spheroidslice primitive, which creates an elongated slice of a sphere. By creating ten of these slices (every 36 degrees) we can easily create the pumpkin’s body:

```TO pumpkin
pu
;penup

rr 180 up 90 lt 18 ra 20
;these were added after in order to position
;the pumpkin to face the camera

pushturtle
;stores the turtle's state on to a stack,
;to be restored later using popturtle

setspheroidaxis false
setfillcolor orange
setpencolor brown

repeat 10 [
spheroidslice 50 10 40 18 23 1.5
rt 36
]
```

Next, we’ll position the turtle and use the domoid primitive to create the eyes, nose and mouth:

```  left 2
raise 37
down 42
right 30
lo 85
setfillcolor 13
setpencolor 8
setspheroidaxis true domoid 15 10 3 0.5
;eye

ra 85
lt 30
UP 42
RT 40
DN 42
RT 30
lo 85
domoid 15 10 3 0.5
;other eye

ra 85
LT 30 UP 42 LT 20 DN 50 RT 30 lo 85
domoid 10 10 3 0.5
;nose

ra 85
LT 30 lo 3 DN 48 FD 40 lo 60
domoid 17 10 10 0.5
;mouth
```

Finally, we’ll create the pumpkins stem:

```  popturtle
;return the turtle to its position, orientation
;and so forth when we did pushturtle
setfc 8
;setfillcolor
cylinder 8 60 10 lo 60
up 90 rt 180 sr 20 lt 72.5
;sr = slideright
cylinderarc 8 20 10 10 3
home ht
END
```

### The Witch:

The witch procedure first stores the pumpkin as a turtle model, then begins with her signature feature: her hat!

First we position the camera and store the pumpkin as a turtle model:

```TO witch
reset cam:pullout 150 cam:orbitleft 20 cam:orbitup 10
begintag "pumpkin
pumpkin
endtag
newmodel "pumpkin "pumpkin
cs
```

Then we create her hat:

```  pu setfs 0
dn 90 ra 80
setoriginvectors vectors
pushturtle
setfc 5
rr 180
cone 40 100 20
cylinder 80 5 20
```

```  rr 180
setfc 1
up 5
cylinderslice 40 50 20 10
lo 50
cutsphereslice 40 20 20 10 20 10
up 90
spot 40
dn 90
ra 50
dn 5
lo 30
setfc 11
rl 90
icospheroid 35 1.5
rr 90
ra 20
lt 30
bk 30
setfc 2
ico 7
fd 30
rt 60
bk 30
ico 7
fd 30
lt 30
lo 30
up 90
rr 180
up 10
setfc 11
cutcone 15 7 60 20
lo 60
setfc 1
dome 7 20 20
popturtle
```

Then her body:

```  lo 100
setfc 2
rr 180
cappeddomoid 30 20 20 2
rr 180
setfc 9
cylindroid 30 50 20 1.2
pushturtle
lo 50
domoid 30 50 20 1.2
repeat 2 [
pushturtle
sr 25
lo 10
dn 30
rr 180 dome 20 20 20
rr 180
cylinder 20 50 20
lo 50
ico 20
up 90
cutcone 20 15 50 20
lo 50
setfc 2
cylinder 20 20 20
lo 20
dn 90
cylindroid 10 20 20 2
lo 20 domoid 10 20 20 2
ra 20 rr 180
cylindroid 10 10 20 2
lo 10
domoid 10 20 20 2
popturtle sl 50]
popturtle
pushturtle
sr 45
dn 20
ra 4
setfc 11
cylinder 13 50 20
lo 50
ico 13
dn 90
cutcone 13 8 50 20
lo 50
ico 12
setmodelscale 0.5
dn 90 rt 160 lo 25 stamp "pumpkin
setmodelscale 1
popturtle
pushturtle
sl 45
dn 20
ra 4
setfc 11
rl 9
cylinder 13 50 20
lo 50
ico 13
dn 20
rl 40
cutcone 13 8 50 20
lo 50
ico 12
```

And finally her broom!

```setvectors originvectors
setx 0
pushturtle
up 80
setfc 8
cylinder 5 100 20
lo 100
lo 5
rr 180
cappeddomoid 13 20 20 3.5
rr 180
cylindroid 13 15 20 3.5
lo 15
sl 35
setfc 13
repeat 15 [
rl -8 + repcount
cylinder 0.1 * (10 + random 20) 70 20
rr -8 + repcount
sr 5
randfs
]
sl 72.5
fd 5
repeat 14 [
randfs
rl -7 + repcount
cylinder 0.1 * (10 + random 20) 70 20
rr -7 + repcount
sr 5
]
bk 10
sl 5
repeat 14 [
randfs
rr -7 + repcount
cylinder 0.1 * (10 + random 20) 70 20
rl -7 + repcount
sl 5
]
popturtle
up 90
rr 180
up 10
setfc 8
cylinder 5 100 20
lo 100
dome 5 20 20
dn 90
ra 5
bk 10
st
END
```

### The Devil:

This unicycling devil robot is a scary creature!

```TO devil
cs
pu
setfc 11
cylinder 10 50 20
bk 10
voxeloid 50 20 50
fd 10
sr 50
cylinder 10 50 20
sl 60
voxeloid 70 50 50
fd 50
sr 10
cylinder 10 50 20
bk 10
voxeloid 50 20 50
fd 10
sr 50
cylinder 10 50 20
rr 180
repeat 2 [
repeat 4 [
dome 10 5 20
rl 90
cylinder 10 50 20
rr 90
sr 50
rt 90
]
up 180
ra 10
voxeloid 50 50 10
lo 60
lt 90
]
lo 10
bk 15
sr 15
lo 0.1
setfc 1
polyoval 10 5 100
bk 20
polyoval 10 5 100
sl 10
rr 180
fd 5
rt 60
rt 30
sl 10
rt 30
lt 30
sr 5
bk 40
pushturtle
repeat 8 [
sr 4
lt 10
]
popturtle
pushturtle
repeat 8 [
sl 4
rt 10
]
popturtle
fd 50
lo 20
sl 20
up 90
pushturtle
repeat 2 [
repeat 15 [
cylinder 16 - repcount 10 10
lo 8
up 10
rr 30 - 20 * repabove 1
]
popturtle
sr 40
pushturtle
]
```

And his body:

```  sl 60
ra 60
bk 15
rr 180
rt 20
setfc 13
cylinder 30 10 20
setfc 1
lo 10
cylindroid 30 20 20 2
sl 40
setfc 1
cylindroid 20 100 20 1
pusht
lo 100
ico 20
up 120
rr 30
cutcone 20 10 100 20
lo 100
up 90
rl 15
ra 20
setfc 10
cylinder 20 40 20
popt
pusht
sr 80
cylinder 20 100 20
lo 100
ico 20
up 60
rl 20
cutcone 20 10 100 20
lo 100
setfc 5
up 90
rl 10
ra 20
cylinder 20 40 20
popt
sr 40
cylinder 40 100 20
lo 160
rr 90
ra 20
setfc 11
cylinder 60 40 20
setfc 13
ra 5
cylinder 10 50 20
lt 90
lo 2.5
up 90
cylinder 2.5 100 10
dn 90
lo 45
up 90
cylinder 2.5 100 10
END
```

### The Ghosts:

These bug-eyed PacMan-style ghosts are a real scream!

The Ghost:

```TO ghost
up 90
make "gc 1 + random 14
make "ec pick [0 1 2 3 4 8 9]
setfc :gc
dome 50 20 18
rr 180
cylinder 50 50 18
lo 50
dn 90
repeat 18 [
tent 9 30 49.2
rl 20
]
setfc 15
up 90
ra 50
fd 50
rr 90
icospheroid 15 1.5
fd 15
setfc :ec
icospheroid 7.5 1.5
rl 90
bk 65
rt 40
fd 50
setfc 15
rr 90
icospheroid 15 1.5
fd 15
setfc :ec
icospheroid 7.5 1.5
rl 90
END
```

The Ghost Circle (this code assumes the above code is in a procedure called ghost:)

```TO ghostcircle
reset
snappy:newworker [
setposition [0 0 0]
dropanchor
forever [
orbitleft 0.5
wait 1
]
]
randbg
setbs 13
setfs 0
dropanchor
up 90
pu
ra 80
randpc
setfc bg
spot 400
lo 80
pullout 300
repeat 18 [
pushturtle
dn 90
rr 20
ghost
popturtle
orbitright 20
]
END
```

### The Cats:

Don’t get in the way of these cats, or it’s bad luck!

The Cat’s Eye:

```TO cateye
setfc 13
pu
rt 90
spot 10.1
fd 4
fiso 9.3 20
bk 8
rt 180
fiso 9.3 20
bk 4
ra 0.1
setfc 0
polyspot 6 40
lt 90
fd 3
fiso 5.2 7
bk 6
rt 180
fiso 5.2 7
bk 3
END
```

The Cat:

```TO cat
sl 30
setfs 0
cateye
sr 60
cateye
sl 30
lo 0.3
bk 10
setfc 5
setfs 12
cylindroid 40 10 50 2
sl 20
lt 30
fd 25
tent 30 70 10
bk 25
rt 30
sr 40
rt 30
fd 25
tent 30 70 10
bk 25
lt 30
sl 20
bk 5
up 180
setfs 0
setfc 11
dome 5 20 20
fd 15
setfc 5
setfs 5
dome 8 20 20
lo 3
dropanchor
tether
pullout 7
rt 180
orbitright 70
setpw 2
repeat 2 [
repeat 5 [
line 80
orbitright 10
]
orbitright 130
]
pullin 7
rr 180
lo 12
rt 20
setfs 12
sr 20
setfs 13
cylindroid 40 100 50 2
lo 90
rr 90
setfs 12
cylinder 10 200 20
lo 200
dome 10 20 20
ra 200
rr 180
up 20
cylinder 10 130 20
lo 130
dome 10 20 20
ra 130
dn 40
cylinder 10 130 20
lo 130
dome 10 20 20
ra 130
sr 80
cylinder 10 130 20
lo 130
dome 10 20 20
ra 130
up 40
cylinder 10 130 20
lo 130
dome 10 20 20
ra 130
END
```

The Cat Ring:

```TO catring
cs
pu
dn 90
dropanchor
pullout 250
pu
repeat 8 [
pushturtle
up 90
cat
popturtle
orbitright 45
]
END
```

Happy Hallowe’en!