Stop a repeat as soon as it returns false w/o finishing the next iteration

Ok, so I’m trying to make a block which repeats for a certain amount of seconds. It’s supposed to stop as soon as the seconds reaches its target, but because of repeat until <> semantics, it doesn’t stop exactly at 3 seconds, but it also has to wait for the iteration to finish.

Is there any way to make it stop as soon as the condition is false?

repeat until <contition> {
if <condition> {
...
} $>
}

i don’t know for sure if this’ll work but it was the first thing that came to mind, try it if you want

No, it doesn’t :‍(

oh sorry
i might try looking for solutions

You might need to create a custom block so that stop [this block V]can work. But then you could insert an if in between each step of the process, something like this:

repeat until <3 seconds have passed> {
say [Step 1]
if <3 seconds have passed> {
stop [this block V]
}
say [Step 2]
if <3 seconds have passed> {
stop [this block V]
}
say [Step 3]
if <3 seconds have passed> {
stop [this block V]
}
}

Of course, replace the “say Step 1/2/3” blocks with what is actually going to be happening in those steps. And replace the <3 seconds have passed> predicate with the actual check you’re using.

In theory there’s no need for the final if <3 seconds have passed> { stop [this block V] } block after step 3 because the repeat block will do that check. But if you want to add a step 4, then you’d have to remember to add an “if 3 seconds have passed” in between steps 3 and 4. This way, you just have to right-click the say [Step 3] block and choose Duplicate, and the attached “if 3 seconds have passed” check will also be duplicated with it.

This is one way I found out how to get this to work

It works by creating a separate sprite to run the loop, which allows me to stop just that script when the condition is true. The reason there’s two sprites, is because for some reason when sprite 1 tells sprite 2 to run a script, stopping scripts in sprite 2 doesn’t stop the script that sprite 1 told sprite 2 to run. However, stopping sprite 1 will stop that script. In this case, I use (loop caller) as sprite 1, and (loop runner) as sprite 2, so I can stop (loop caller) to stop the loop, but not the rest of the scripts in the main sprite.

P.S. The (condition) input is set to unevaluated, so that’s why I have to call it. If it wasn’t unevaluated, then the condition value will never change.

P.P.S. If you don’t know why the script in the loop is still running on the parent sprite instead of the temporary sprite, then you’ll have to learn about scope context, which I’m sure you can read about in the snap manual.

Can you do this without cloning?

This is the method I came up with. Here’s the thing with the “Turtle sprite” option in that block, instead of creating a clone of the current sprite or any user created sprite, that creates a completely empty sprite.

No because there is a clone limit of ~1,000 in Snap!.

So? Unless you’re actually coming anywhere close to that limit elsewhere, I don’t see a reason why that matters.

nice, although this may mess up with layers.

Nvm the limit got removed lol

cool

True. I’m not entirely sure if there’s a way to get around that.

If you tell to launch the script will be in the clone context.

Huh, I guess I somehow didn’t think to do that. That simplified the block a bit.

Due to the cooperative nature of the Snap’s threading model, there is no point in testing the condition at the loop border.


wait until tests the condition at every loop iteration.

right. but how does the block work at all if the script being run in the clone’s context?

That’s the magic of context.

When a ring is created, it remembers what sprite and script created it. That way no matter where the ring us ran, it will always run in the sprite it was created in, and can also access any variables that were in the script that created it.

Ah! That’s really smart!