Discuss Scratch

Tutor-42
Scratcher
100+ posts

Please ignore this topic as it is under construction. I will rename it when it is ready.

Reserved Post
Tutor-42
Scratcher
100+ posts

Please ignore this topic as it is under construction. I will rename it when it is ready.

How does Scratch run a project's scripts

Do scripts run at the same time

What are “yield points” for scripts in a project


Scratch makes a list of the scripts/threads that it has to run.

Scratch only runs one script/thread at a time!

For example, when a Scratch project starts, Scratch will list all the “when Green Flag clicked” scripts that it has to run.
The “when Green Flag clicked” scripts are not all started simultaneously but are started one after the other.


If the current script always had to finish running before another script could be started, there would be big problems.
If the script below had to finish before another script could run, then we would get stuck forever in the forever loop.
No other script would get a chance to run to change the variable Score.

when green flag clicked
set [Score v] to [0]
forever
if <(Score) = [10]> then
broadcast [End Game v]
stop [this script v]
end
end


Scratch lets scripts take turns when running. This is done using “yield points”.

A yield point is a place in a script where the script pauses and yields to let the next script run.
Scratch remembers that the current script is unfinished, and will resume the script (after the yield point) when it has its next turn to run.


Scratch yield points are:

- the end of any loop, ie. the end of any Repeat, Repeat until, or Forever loop.
The script yields each time the blocks inside the loop have been executed.

- any type of wait ie. wait secs, wait until

- other blocks where the script waits until the action is complete before executing the next line
eg. broadcast and wait, play sound until done, glide … secs to, say/think … for secs

- end of a script
(also the stop this script block)


When Scratch finishes running the list of scripts up to their yield points, it refreshes the screen (if the screen has been updated).
Scratch only updates the screen every 1/30th second and will wait until 1/30th second has elapsed since the previous screen refresh.
The screen needs updating if any sprite moved or changed colour, size etc, or if the backdrop changed, or if any pen drawing was done.

Scratch then starts running the new list of scripts/threads that need to be executed.
This list of scripts/threads to be executed includes all the scripts that were running but paused at yield points.
The list also includes any new scripts that have been added in response to an event.

Events that may have happened include:
key presses, mouse clicks on sprites or the backdrop, broadcasts, timers, new clones, new backdrop etc.

These events all have corresponding event blocks in Scratch:
“when … key pressed”, “when this sprite clicked”, “when stage clicked”, “when I receive broadcast message”, “when timer > …”, “ when I start as a clone”, “when backdrop switches to …”, etc

Next we will look at some examples of “yields” in projects. There are some surprising and interesting behaviours!

The first example explains exactly how our project “Scratch Can't Count” played a sound 4 times or 0 times instead of 3 times.
It is not as interesting as the later examples but clearly illustrates “yield points” in a project.

The second example project “The Tortoise and the Hare” is interesting and surprising!
We will look at ways to fix it.

The third and fourth example projects, Space Invaders and Spiralizer, create lots of clones.
But if there is a delay between successive clones being created then they may start moving at different times.
Again we look at ways to fix the problem.

Last edited by Tutor-42 (Sept. 12, 2019 04:20:57)

Tutor-42
Scratcher
100+ posts

Please ignore this topic as it is under construction. I will rename it when it is ready.

Scratch Can't Count - An Example of “yields” and How Scratch Runs

We are going to look at our example project Scratch Can't Count

This project with two “when Green Flag clicked” scripts never works properly as the bottom script always runs first.
The project was supposed to play the “A Bass” sound 3 times but it either plays 4 times or not at all.

The problem was described earlier for projects with two “when Green Flag clicked” scripts that run in the wrong order:
Problems that occur when a project has two when Green Flag clicked scripts

Remember that this project would always works correctly playing the sound 3 times exactly if the top script ran first!
We looked at ways to rewrite the project to make sure it always worked correctly.

The following explanation describes exactly how this badly written project runs (bottom script first) to play the sound 4 times or 0 times.




We will first consider the case where the project starts with Count = 0.
Let us look at exactly how the project runs!


First the bottom script runs and Count is incremented to 1 inside the loop.

Then the “play sound A Bass until done” block is executed.
This starts the sound for the first time.
But “play sound … until done” is a type of wait block as the script does not continue until the sound has finished playing.
So execution is yielded to the top script.

The top script resets Count to 0.
The top script has now finished executing and yields control back to the bottom script.

You can see that when the sound finishes playing and Scratch reaches the end of the repeat loop for the first time, Count is 0 but the sound has played once.

The repeat loop is run 3 more times (and the sound plays 3 more times):
Count is incremented to 1, play sound play sound A Bass until done
Count is incremented to 2, play sound play sound A Bass until done
Count is incremented to 3, play sound play sound A Bass until done

(Scratch yields execution at each “play sound … until done” block and each time it reaches the end of the repeat loop but there are no other scripts yet to run).

Scratch checks each time before executing the repeat loop if Count = 3.
Count is now 3 so the repeat loop is finished.
We reach the end of the bottom script and there are no more scripts queued to run.

The sound has been played 4 times instead of 3!


Now we will consider the case where the project starts with Count = 3.
Let us look at exactly how the project runs!


As described above, if the project was started with Count = 0 then when the project finishes Count = 3.

The bottom script runs first.
Scratch checks before executing the repeat loop if Count = 3.
Count is 3 already! So the repeat loop is not executed.

The bottom script has now finished executing and yields control to the top script.
The top script resets Count to 0.
We reach the end of the top script and there are no more scripts queued to run.

The sound has been played 0 times instead of 3!

Count is now 0 and if the project is started again the sound will play 4 times as described above.

Last edited by Tutor-42 (Sept. 12, 2019 04:26:55)

Tutor-42
Scratcher
100+ posts

Please ignore this topic as it is under construction. I will rename it when it is ready.

The Tortoise and the Hare - A Crazy Example showing How Scratch Runs



Who will win the race - The Tortoise or the Hare?

Look at their code in the picture above, then choose your answer.
(The code in the project actually has a “Repeat until touching edge” loop rather than a “forever” loop)

A. The Tortoise (who will move 3x faster than the Hare)
B. It will be a tie. (they will move at the same speed)
C. The Hare (who will move 3x faster than the Tortoise)
D. The Hare (who will move 4x faster than the Tortoise)

You can click on the project picture or the link to run the project: The Tortoise and the Hare
You can even run the project in Turbo Mode if you don't believe the result the first time.

Answer and Explanation:

Let us look at the bottom script for the Hare first.
The Hare moves 3 steps, then the script will yield to let other scripts have their turn at the end of the forever loop.

Now look at the top script for the Tortoise.
The Tortoise will move 1 step, then the script will yield to let other scripts have their turn at the end of the repeat loop.
When the Tortoise script has its next turn, the Tortoise will move 1 step, then the script will yield again at the end of the repeat loop.
And when the Tortoise script has its next turn, the Tortoise will move 1 step, then the script will yield again at the end of the repeat loop.
But when the Tortoise script has its next turn, the Tortoise will not move but the script will yield one more time at the end of the forever loop.

So the Hare script yields once each time it moves 3 steps but the Tortoise script yields four times each time it moves 3 steps.
In other words, the Tortoise script takes four times as long to run as it yields four times compared to once for the Hare script.
The surprising answer is D. The Hare runs 4x faster and wins.

There are no tricks in the project. This is just the way Scratch works.
We can rewrite our projects, however, to overcome this feature of Scratch.


Before “fixing” The Tortoise and the Hare, we will next look at custom blocks defined with “run without screen refresh” as they are part of the solution.

Last edited by Tutor-42 (Sept. 12, 2019 04:29:49)

Tutor-42
Scratcher
100+ posts

Please ignore this topic as it is under construction. I will rename it when it is ready.

“run without screen refresh” Custom Blocks

When you create a custom block, you can tick a box marked “run without screen refresh”.
(You can edit an existing custom block by right-clicking its header/hat block).


“run without screen refresh” custom blocks do not yield execution until the whole custom block script has been run!

“run without screen refresh” custom blocks do not yield execution at the end of loops to let other scripts run.
There are no screen updates (refreshes) while a “run without screen refresh” custom block runs.

Note: You should not put any type of wait block in a “run without screen refresh” custom block as the wait block will override the “run without screen refresh” and will yield execution to other scripts. This includes blocks such as broadcast and wait, play sound until done, glide, and say/think … for secs.


If “run without screen refresh” is not ticked when creating a custom block, the custom block runs like normal Scratch scripts and will yield execution each time it reaches the end of the repeat loop and update the screen if required.


You should use “run without screen refresh” custom blocks whenever you want a loop to run without yielding execution each time it reaches the end of the loop.

Their most common use in Scratch is in Pen projects to stop the screen being updated while a script runs and only show a completed drawing.
(We will also look at other examples to fix some problem projects).


Here is a custom block that draws a square with sides of size “side length”.

define Draw Square (side length)
pen down
repeat (4)
move (side length) steps
turn cw (90) degrees
end
pen up

You could write a script to run the custom block as below:

when green flag clicked
Draw Square (100) :: custom


If Draw Square was defined as a “run without screen refresh” custom block, all 4 sides of the square are drawn and execution is not yielded while the custom block is running. The script does not yield each time it reaches the end of the repeat loop. The screen is not updated while the custom block is running.
So the user does not see the 4 sides of the square being drawn separately.
The user only sees the completed square when the screen is updated after 1/30 second has elapsed.

If Draw Square was not defined as a “run without screen refresh” custom block, the custom block yields execution each time it reaches the end of the repeat loop. We see each side of the square being drawn separately as the screen is updated each time the custom block yields.
In this example the 4 sides of the square will be drawn at 1/30 second intervals, a total of 4/30 second.
Complex drawings would take longer to draw.

Here is an example project showing custom blocks with and without “run without screen refresh”.
Drawing time is over 1 second for each pattern unless we use a “run without screen refresh” custom block.

Note that the scripts are identical in the custom blocks.
The custom blocks run differently depending whether they are defined with “run without screen refresh” or not.



Notes:

- When Scratch calls a custom block, all code in the custom block is run before execution returns to the calling script.
(this is similar to a “broadcast and wait” block).

- A custom block with “run without screen refresh” may call other custom blocks from its script.
Then Scratch will make those custom block also run without screen refresh!

- There is a time limit of 1/2 second for scripts after which they are forced to yield execution and the screen is updated.
Even a “run without screen refresh” custom block will yield after running for 1/2 second.

- In most projects“run without screen refresh” custom blocks are better than Turbo Mode.
The programmer can then control how much code should be run before the screen is updated.

In Turbo Mode Scratch runs scripts (including custom blocks) as many times as it can within 1/30th second before updating the screen.
This would not be suitable for most game projects where the programmer wants to display each frame of action.

Last edited by Tutor-42 (Sept. 12, 2019 04:43:36)

Tutor-42
Scratcher
100+ posts

Please ignore this topic as it is under construction. I will rename it when it is ready.

Fixing The Tortoise and the Hare



In the original Tortoise and the Hare project above, the Tortoise moved slower as the its Move script yielded execution each time it reached the end of the repeat loop. The Tortoise moved only 1 step between screen updates.


We fix this problem by shifting the Move script for the Tortoise inside a “run without screen refresh” custom block as below:



The Move Tortoise “run without screen refresh” custom block does not yield execution at the end of each repeat loop but runs completely before yielding. The screen is not updated while the custom block runs. So the Turtle now moves at the same speed as the Hare.


However, there was another problem with the original Tortoise and the Hare project:
The original Tortoise and the Hare project had 2 separate “when Green Flag clicked” scripts for the Tortoise and the Hare.
As one script has to run first, the project would check if one animal had reached the end first, then stop the project.


It is better structure to write the project with just one Green Flag script as below:

when green flag clicked
set [Tortoise Wins v] to [false]
set [Hare Wins v] to [false]
broadcast [Go to Start Positions v] and wait
wait (1) secs
repeat until <<(Tortoise Wins) = [true]> or <(Hare Wins) = [true]>>
broadcast [Move v] and wait
broadcast [Check if Race finished v] and wait
end


There is just one “wait 1 second” block for both the Tortoise and the Hare before the race starts.
The main project loop uses “broadcast Move and wait” to move the Tortoise and Hare together.
The code checks if the Tortoise or the Hare has reached the edge after both animals have been moved.
(The “when I receive Check if Race finished” scripts for the Tortoise and the Hare set Tortoise Wins = True or Hare Wins = True respectively if the animal has reached the finish line).
We could easily add code after the repeat loop above if the race is a tie. (both Tortoise Wins = True and Hare Wins = True).

Last edited by Tutor-42 (Sept. 12, 2019 04:48:06)

Tutor-42
Scratcher
100+ posts

Please ignore this topic as it is under construction. I will rename it when it is ready.

Space Invaders - A Problem with Clones

We will look at setting up 5 rows of Invader clones as another example of how yield points at the end of repeat loops affect projects.

The first script below has two nested loops that produce 5 evenly spaced horizontal rows each with 10 Invader clones.

The “when I start as a clone” script moves each clone down the screen in a forever loop.
When a clone reaches the bottom edge of the screen, it jumps back to the top of the screen.

when green flag clicked
set y to (165)
repeat (5)
set x to (-180)
repeat (10)
create clone of [myself v]
change x by (40)
end
change y by (-50)
end


when I start as a clone
show
forever
repeat until <touching [edge v] ?>
change y by (-3)
end
set y to (165)
end

This is how the project looks when running:



Why do the Invader clones not move down the screen in 5 horizontal lines?

The Invader clones are created in a repeat loop.
Each Invader clone starts moving downwards when it is created rather than waiting until all Invader clones have been created.

The Invader clones are created from left to right, so the Invader clones at the left of each row start moving down first.
This means that each row moves as a diagonal rather than a horizontal line.

The rows of Invader clones are created from top to bottom, so the Invader clones in the higher rows start moving down first.
The clones move down fast enough that they overlap with the clones still being created in the row below.
If the Invader clones all started moving at the same time, the rows would not overlap.

We will now look at a few alternative fixes to our project so that all Invader clones are created before they are all moved down together.

The first fix-it project is interesting, but not my favoured fix.

The only change is to put the entire repeat loop in the Green Flag script inside a “run without screen refresh” custom block.
Then all the Invader Clones are created wihout the script yielding, so their “when I start as a clone” scripts are queued to be run later.
After the custom block has created all the Invader Clones, the queued scripts are run which starts them all moving down together.

when green flag clicked
Create 50 Invaders ::custom


define Create 50 Invaders
set y to (165)
repeat (5)
set x to (-180)
repeat (10)
create clone of [myself v]
change x by (40)
end
change y by (-50)
end


when I start as a clone
show
forever
repeat until <touching [edge v] ?>
change y by (-3)
end
set y to (165)
end



Note that this fix has 50 loops running, one to move each Invader clone downwards.
I prefer the next fixes which have a single loop and use a broadcast to move the clones.

The second and third fix-it projects have my favoured project structure.

First we create the 50 Invader clones.
The clones are not started moving immediately with a loop in the “when I start as a clone script”
Instead, after the Invader clones have been created in the Green Flag script, there is a single loop which broadcasts “Move Invader Clones”.

The first of these two fix-it projects has an interesting deliberate effect.
The “when I start as a clone” script has a single Scratch block, Show.
Thus the Invader clones are displayed one at a time at 1/30th second intervals until the 5 rows are complete. It looks great!

when green flag clicked
broadcast [Create 50 Invader Clones v] and wait
forever
broadcast [Move Invader Clones v] and wait
end


when I receive [Create 50 Invader Clones v]
set y to (165)
repeat (5)
set x to (-180)
repeat (10)
create clone of [myself v]
change x by (40)
end
change y by (-50)
end


when I start as a clone
show

when I receive [Move Invader Clones v]
change y by (-3)
if <touching [edge v] ?> then
set y to (165)
end



The second of these two fix-it projects creates all the clones “instantly” without using a “run without screen refresh” custom block.
The Invader clones are created one by one but there is no Show block in the “when I start as a clone” script.
Scratch yields execution each time it reaches the end of the repeat loop that creates the Invader clones.
But the Invader clones are hidden. So no screen update is needed!
Without the screen being updated, there is no wait of 1/30 second between creating each clone.
All the clones are created “instantly”.

The project then broadcasts “Show Invader Clones”. This broadcast is received by all the clones.
Their queued “when I receive Show Invader Clones” scripts (which include the Show block) are all run, then the screen is updated.
The clones all show together after 1/30 second.
Then the loop in the Green Flag script moves all the Invader clones down together using a broadcast.


Last edited by Tutor-42 (Sept. 16, 2019 07:49:56)

Tutor-42
Scratcher
100+ posts

Please ignore this topic as it is under construction. I will rename it when it is ready.

Reserved Post
Tutor-42
Scratcher
100+ posts

Please ignore this topic as it is under construction. I will rename it when it is ready.

Reserved Post
Tutor-42
Scratcher
100+ posts

Please ignore this topic as it is under construction. I will rename it when it is ready.

Reserved Post
Tutor-42
Scratcher
100+ posts

Please ignore this topic as it is under construction. I will rename it when it is ready.

Reserved Post
Tutor-42
Scratcher
100+ posts

Please ignore this topic as it is under construction. I will rename it when it is ready.

Reserved Post
Tutor-42
Scratcher
100+ posts

Please ignore this topic as it is under construction. I will rename it when it is ready.

Reserved Post
Tutor-42
Scratcher
100+ posts

Please ignore this topic as it is under construction. I will rename it when it is ready.

Reserved Post
Tutor-42
Scratcher
100+ posts

Please ignore this topic as it is under construction. I will rename it when it is ready.

Reserved Post
Tutor-42
Scratcher
100+ posts

Please ignore this topic as it is under construction. I will rename it when it is ready.

Reserved Post
Tutor-42
Scratcher
100+ posts

Please ignore this topic as it is under construction. I will rename it when it is ready.

Reserved Post
Tutor-42
Scratcher
100+ posts

Please ignore this topic as it is under construction. I will rename it when it is ready.

Reserved Post
Tutor-42
Scratcher
100+ posts

Please ignore this topic as it is under construction. I will rename it when it is ready.

Reserved Post
Tutor-42
Scratcher
100+ posts

Please ignore this topic as it is under construction. I will rename it when it is ready.

Reserved Post

Powered by DjangoBB