Skippy the Squirrel's Turtle

redfoot.jpg

When Squirrel loves tree ... :)

Kirilla
Abstract

Designed as a educationnal tool, the Turtle of Logo helps kids discover mathematics and computers in a way that is more entertaining than what is offered in your typical classroom setting. In this short article, we will provide a better overview of recursion and multi-threading by using Squirrel's version of the famous Turtle.

1. Skippy a.k.a the Turtle

The coming release of Squirrel DR4.7 will include a new version of the Squirrel's own Turtle : Skippy. Like the Logo's Turtle, Skippy is more of a tool which teaches children rather than a powerful graphics tool. This new version is a complete rewrite of the previous version shipped since the first release in September 99. I apologize to all those who have already started to use the older versions. There is a lot more usefulness and power in this new version.

For example, the area in which Skippy draws has been replaced by a specific GUI widget instead of using a specialy designed window. This allows one to use Skippy in a wider range of applications.

To play with Skippy we need to create a Sheet widget and a Skippy object. The Sheet is an area where Skippy will draw. Like any other widget the Sheet must be placed in a window:

make "win Window "titled 'Skippy Demo' [100 100] "not.zoomable

make "sheet Sheet [200 200]

Glue :win "top [] :sheet

$win~show

sheet1

A Sheet widget always has a specified size. In our example we set its width and height to 200 pixels.

To draw inside the Sheet we need to create one Skippy object :

make "sk Skippy :sheet
$sk~hop 50

sheet2

A Skippy object is used like any other object in Squirrel. In our example above, we used the method hop to ask Skippy to go forward of 50 pixels. Skippy draw a line when it move. It will have been possible to not draw the line. There are other methods which permit us to direct Skippy backwards, to right or to the left left.

Drawing a square is easily done by the following lines:

$sk~hop 50
$sk~right 90 50
$sk~right 90 50
$sk~right 90 50
square
(Speed of this animated gif is NOT the speed of execution)

The method right accepts for its first input the angle Skippy must rotate right of. The second input (if any) will ask it to go forward of the distance. The second input is optional, it's just a shortcut that avoid to write:

$sk~hop 50
$sk~right 90
$sk~hop 50
$sk~right 90
$sk~hop 50
$sk~right 90
$sk~hop 50

2. Drawing a tree

Drawing a tree is a classic example of recursion. A tree is composed of nodes. Each nodes is the starting point of two segments terminated by a new node. This node is then the starting point of two segments ... and so on.

The function that draw a tree will perform the following:

  • Go left of 15 degrees
  • Go forward
  • Call the function recursively
  • Go backwards
  • Go right of 30 degrees
  • Go forward
  • Call the function recursively
  • Go left of 15 degrees
  • Go backward
One of the citical points of a recursive function is the stop condition. The function must stop when the stop condition is met, otherwise it will go on forever ... well it can't really go on forever since the computer has limited resource.

In our example, we will continuously reduce the size of the segment for each node until the size reachs 0. When the size is 0, the function will stop.

To simulate a real tree, we will add random size segments:

to tree :sk :distance 
  if :distance <= 0 {stop}
  make "distance :distance + (random :distance//2)
  $sk~left 15 :distance 
  thtree :sk :distance//2
  $sk~hback :distance 
  make "distance :distance + (random :distance//2)
  $sk~right 30 :distance 
  thtree :sk :distance//2
  $sk~hback :distance 
  $sk~left 15  
end 
We draw our tree by calling the function with the Skippy object created ealier:
tree :sk 30
tree

3. Going multi-threading

Let's now combine the multi-threading and the recursion to demonstrate how Squirrel handles them. This example illustrates for us an educational purpose.

A Sheet widget accepts more than one Skippy, we so are going to create four Skippies. Each of them will draw a tree, and for a nicer effect, tree will be drawn in different directions. All of the Skippies should draw simultaneously, so we will create 4 threads to execute the tree functions.

To create the four Skippies we begin by using the primitive clone to create a copy of an object (Not all Squirrel's objects can be cloned). When a Skippy is cloned, the clone will be attached to the same Sheet and will inherit the exact configuration:

make "sk1 Skippy :sheet
make "sk2 clone :sk1
make "sk3 clone :sk1
make "sk4 clone :sk1

$sk1~config "color "set :Red
$sk2~config "color "set :Blue
$sk3~config "color "set :Green
$sk4~config "color "set :Yellow
By using the method config, we set the drawing color of all the Skippies. By using the method heading we change the heading of the Skippy to draw some of the tree in other direction:
$sk2~heading 180
$sk3~heading 90
$sk4~heading 270
Now we have to create the four threads using the primitive Thread:
make "th1 Thread "normal "tree :sk1 30
make "th2 Thread "normal "tree :sk2 30
make "th3 Thread "normal "tree :sk3 30
make "th4 Thread "normal "tree :sk4 30
The second input of the primitive is the name of the function to execute. All the other inputs are inputs to this function. Here we specify which Skippy the thread must use and the starting segment size. The primitive Thread outputs a thread id that is used to start the thread:
Thread.hop :th1 :th2 :th3 :th4
And we get (the speed of the animation is NOT the execution speed):

thtree3

This animated GIF show very well how the 4 threads are running concurently and how a tree is draw.

To create this animation, we use the ability of the Sheet widget to generate an image file (in different format) of it contents, to periodically create an image of the Sheet. By using a tool, we then create an animated GIF from the various files.

To create the files, we use a fifth thread that periodically take a snapshot of the widget :

to snap
	make.local "i 1
	while (true) { 
		$sheet~export "Targa string 'tree' :i '.tga'
		incr "i	
		Snooze 50000		
	}
end
We then modify our script:
make "th5 Thread "normal "snap

Thread.hop :th1 :th2 :th3 :th4
Thread.waitfor "all :th1 :th2 :th3 :th4
Thread.kill :th5
Using the primitive Thread.waitfor will allow us to stop taking snapshots of the Sheet widget when all the drawing threads are done.