View this PageEdit this PageUploads to this PageVersions of this PageHomeRecent ChangesSearchHelp Guide

Control

Here we will discuss control within a script, not the overall design
considerations of a larger project.
Apologies for a very cursory treatment.... I wanted to just get some
quick notes up.

  • Straight-line code


    This is a sequence of commands that is executed one at a time, in
    sequence from start to finish. It does not repeat, and there are no
    conditional branches ("TEST"). In short, these are things you want to
    happen once, and to happen in the order you list them.


    Straight-line code is useful for doing
    simple arithmetic computation
    for example.


    Example uses in Etoys include scripts for starting everything going,
    scripts for resetting various objects and changing their ongoing behavior
    at various points in a game. For example, in a pong game,
    when the ball hits the wall you may want the following to happen:
    score changes, ball disappears, ball gets placed back in center, ball
    reappears, ball gets served (which starts a "ticking" script, as
    described below).

  • Tests: conditional execution


    You've already seen how you can "tear off" a TEST panel, and use it to
    control whether or not a particular command is executed. For
    event-driven programming this is indispensible.
    What types of tests are there? For each object, look in its viewer
    under "tests". These give some basic tests that you can use - whether
    an object touches another, whether it overlaps another, whether a
    particular color of your sketch is above a particular color of some
    other sketch or the background.



    Any expression that has a value of "true" or "false" can be used as a
    test. For example, "counter > 100" will test if the variable counter,
    which presumably has been incremented, is now greater than 100.
    "Health = 0" might be a condition you'd want to check in a game.
    Any
    Boolean variable (true-false valued) that you create can be put in the
    TEST line. For example, in a search/puzzle game, your character may
    need to collect items. "hasKey" might be a variable that is true if
    the Key object has been found. The behavior of a door can then depend
    on a test of whether hasKey is true or false:


    TEST hasKey
    YES door open
    NO door makeBadNoise


    Sometimes it is useful to have compound conditions -
    conditions that depend on more than one test. True/false conditions
    can be combined with logical operators "AND", "OR", and "NOT", to
    create more complex, and useful, expressions. At present Etoys does
    not allow this, but let's examine why these might be useful....


    some examples here


    So, what to do with Etoys if you want to test whether hasKey AND
    hasCoin? You can simply nest tests:


    TEST hasKey
    YES TEST hasCoin
    YES door open
    NO door makeBadNoise
    NO door makeBadNoise

    Thus, provided the first condition holds, a test for the second is
    initiated.


    How about if you wanted to the door to open if either the Key, or the
    Coin, were in possession? (Or both.)
    There are two solutions to this. The first uses nested tests again,
    but in a slightly different way:


    TEST hasKey
    YES door open
    NO TEST hasCoin
    YES door open
    NO door makeBadNoise

    Notice the difference between this one and the one for "AND" above.
    An alternative, is to simply have two separate tests:

    TEST hasKey
    YES door open
    NO door makeBadNoise
    TEST hasCoin
    YES door open
    NO door makeBadNoise.

    Hopefully, you see a problem with this approach, at least in this
    example: What will happen if both hasKey and hasCoin are true? Or
    one true while the other false, or both false? The door may open
    while making a bad noise... or the door may open twice. So, this was
    a bad example. But there are situations when independent tests like
    this would be fine.


    Nesting tests can get ugly if the nesting goes deep, and it is
    unfortunate that at present there is not a tile for "AND" and "OR".



  • Iteration


    There are two types of scripts that you write in Etoys:
    Those meant to execute once through, and those that are meant to start
    and execute over and over until they are stopped. Repeated execution
    is called iteration. Most programming languages have various mechanisms for
    bounded iteration: Repeat a block of code 15 times, or as many
    times as is given by the value of the variable "numTimes".

    Other useful constructs include


    while (condition) do [block-of-code]

    which repeatedly executes the block-of-code as long as the condition
    is true. A simple useful example:

    while (timer > 0) do [timer decrease by 1]


    Unfortunately, there is no such statement in Etoys that allows bounded
    iteration. Our choice is either to do something once as above, or to do it
    forever (a "ticker"). However, if we know how to have scripts start and stop
    eachother, we can find a work-around and implement bounded iteration
    using the provided Etoys primitive operations.



  • How one script can start another


    There are three ways a script can execute another script.
    Suppose World has a script "reset". Perhaps each character needs
    to be placed back in in their starting locations, and we have a script
    joe goHome for character joe.


    1. We can simply include the tile "joe goHome" as a line in the
      World "reset" script.
    2. We can include "joe do goHome" as a line in the reset script.
      This tile is found on the scripting pane.
    3. We can include "joe start goHome", also found on the scripting pane.


    It is important to understand the difference between
    what happens in each of these cases.


    To help understand how and when Etoys decides to execute commands, it will
    help to think of three things: A ticking clock, a list of active
    ticking scripts
    and a list of active fire-once scripts.
    (Note: these lists are not necessarily the way Etoys actually does
    things - but thinking this way will give us a reasonably accurate
    understanding of how things happen.)


    At each tick of its clock, Etoys looks at its lists, and runs through
    each script exactly once. An entire script will
    execute, then another, etc, until all active scripts (of either kind)
    have executed once.
    If a script's name is called within another script (as in joe goHome
    in the first example above), the called script will execute during
    that same tick, and exactly in place where it was called.
    Thus, in the first case, joe goHome
    the script "goHome" is executed exactly once
    through and it is as if all of the single
    commands from the goHome script were substituted in place within the
    reset script.


    At this point, and before the next clock tick, Etoys will update the
    lists of active tickers and active fire-once scripts:


    • All fire-once scripts are removed from the list (because they have already
      fired).
    • If during the last tick there was a command to start a script
      (such as "joe start script goHome"), then the script is added to the
      list of active tickers.
    • If during the last tick there was a command to stop a script
      (such as "joe stop script goHome"), then that script is removed from
      the list of active tickers.
    • If there was a command using "do", such as "joe do goHome",
      then the script is added to the "fire-once" list for the next tick.



    Try the following experiment:


    Create a variable called "count" and set it to 0.
    Create a script add with 10 copies of the statement "count increase by 1".
    Create a script mult with 4 copies of the statement "count multiply by 2".


    • Experiment 1: Insert the scriptname "mult" (not "do mult")
      at various points between the 10 statements of the add script, and
      execute the add script once. Notice what happens.
    • Experiment 2: Do the same, but use "do mult" instead.


    Note that because Etoys works on clock ticks as described above, and
    all start-script and stop-script commands do not take effect until the
    next clock tick, A script cannot stop itself from executing all the
    way through. That is, if you have something like:


    script example
    ...
    TEST (condition)
    YES stop script example
    another command

    NO yet another command

    then if the condition is satisfied, "another command" will be
    executed, despite the fact that the script that is running is
    scheduled to be terminated. (It won't terminate until after running
    through once.)


    As another example,


    script A
    count increase by 1
    stop script A
    count increase by 1

    results in variable count increasing by 2.


    One more thing you should know: it is possible to alter the number of
    times a script is run through at each tick. (Thus, what was said
    above, that at each tick a script is run through exactly once, is not
    correct.) You can set this "fires per tick" by bringing holding the
    cursor over the object's name in the script title, and click when it
    changes into a menu. Within the menu, select "fires per tick".


    It should also be noted that the time between clock "ticks" is not
    consistent. It depends on how many fires per tick each script has,
    and how long it takes to do these.


    A consequence of the above behavior is that there appears to be no
    difference between "pause script" and "stop script", except that the
    scripting buttons widget can be used to start some, but not all,
    scripts ticking: Only the scripts that are "paused" will be restarted when
    the go button is hit.
    (I had imagined
    that pause might stop in place and pick up where it left off... but
    instead it still runs through to the end of the script and then
    starts at the top again if restarted.)


    Example

    Consider the following scripts


    script A
    statement-1
    script B
    stop script A
    statement-2
    do script C
    statement-3

    script B
    script D
    do script E
    statement-4

    script C
    statement-5

    script D
    statement-6

    script E
    statement-7


    Then the following will happen if you run A once (I verified this):

    tick 1:
    statement-1 executed
    (transfer control to script B)
    (transfer control to script D)
    statement-6 executed
    (transfer control back to script B)
    script E scheduled to run through on next tick
    statement-4 executed
    (transfer control back to script A)
    script A scheduled to stop AFTER THIS TICK COMPLETES
    statement-2 executed
    script C scheduled to run through on next tick
    statement-3 executed

    tick 2
    A stops
    E starts
    statement-7 executes
    C starts
    statement-5 executes

    I believe E would start before C, because the "do" for it was executed
    first... it was first in line.


    Strange as this may seem, there is a general principle:
    the set of statements
    in each script is atomic, meaning that if one executes, they
    all do, and the execution is not stopped or interrupted by another
    script. Thus, likely each ticking script is run through entirely once
    (assuming 1 fire per tick) in a round-robin fashion, and script starts
    and stops don't have effect until the next tick.
    Squeak How-To (sequential) is worth reading, and gives the final word


  • Bounded iteration


    One thing that most programming languages provide is a mechanism for
    a block of statements to be executed a certain number of times.
    Etoys scripting does not make this easy at present, so we need to
    implement this feature ourselves.
    The basic idea is easy: Suppose we have a collection of
    statements that we'd like to execute 15 times. We create
    a variable responsible for counting the number of times a simple script
    containing the statements has executed, and stop the script from ticking
    when the counter reaches 15. This will require two scripts actually,
    one which executes once, initializing the counter to 0 and starting
    the second script "ticking", and the second, which executes the
    statements in question, increments and tests the counter, until 15 is
    reached.


    Here is what we would create:


    Ellipse's initialize
    Ellipse's counter -- 0
    Ellipse start script stuff


    Ellipse's stuff
    TEST Ellipse's counter > 15
    YES Ellipse stop script stuff
    NO
    Ellipse's counter increment by 1


    A final comment: Likely, we would not want to just execute
    statements 15 times, or any particular number of times. Rather, the
    number of times we'd want to execute a block of code might depend on
    another variable. In this case, instead of "15", we'd have a tile for
    that variable.


  • WARNING
    Having scripts start and stop eachother, and having scripts running
    all at the same time, can often lead to great confusion when things do
    not happen as expected. Various conditions may be true
    simultaneously, which trigger conflicting actions of a given object.
    As an example, suppose you had a car with black tires, and you used
    "color sees" test to control its heading. If the black was above the
    green grass on the side of the road, you instruct the car to turn
    left. If above the white roadway, you instruct the car to turn right.
    What happens if one black tire is on the grass, while the
    other is on the road?




Link to this Page

  • Squeak How-To last edited on 1 July 2005 at 5:47:09 pm by 12-221-97-163.client.insightBB.com