Discuss Scratch

griffithsrock1
Scratcher
39 posts

Advanced list checker script

Hello, I have a game I'm working on with a pretty advanced script that is supposed to do some list comparison.
Here is the project, the script is labeled “non-functional”. Anyone want to take a look?
https://scratch.mit.edu/projects/905419145/editor/

Last edited by griffithsrock1 (Oct. 12, 2023 00:09:58)

geek62
Scratcher
100+ posts

Advanced list checker script

I think you forgot to increment i2 in the loop.
griffithsrock1
Scratcher
39 posts

Advanced list checker script

geek62 wrote:

I think you forgot to increment i2 in the loop.
Yes, I did forget that. However, it still doesn't work.
Expected behavior is every clone to create a new entry in targets and each one should iterate through the loop until it finds its id in the list then update the values.
Absolutely no updating can be observed.

Last edited by griffithsrock1 (Oct. 13, 2023 20:26:22)

geek62
Scratcher
100+ posts

Advanced list checker script

griffithsrock1 wrote:

geek62 wrote:

I think you forgot to increment i2 in the loop.
Yes, I did forget that. However, it still doesn't work.
Expected behavior is every clone to create a new entry in targets and each one should iterate through the loop until it finds its id in the list then update the values.
Absolutely no updating can be observed.


Okay, fine, I'll look again, and I'll use my brain this time. :-)


In your RequestEnemyData event handler, you use variable i as the character index when building the checkID string. After that loop, you use i-1 as the index into the target array where you probably wanted to use i2.

Aside from that, the logic isn't quite right. Every clone which is touching that tower is going to run through this loop, looking at every element in Targets. If the element matches its own id, it updates it. But if the element does not match its own id, then it adds its own data to the end. So imagine there were three enemies touching the tower. Each one will run through this event handler, see its own id once and update it, and see two other ids and add its own data to the end twice. That would make the Targets array so much longer, and the next time through the problem would be much worse.

It might simplify things to make a utility method that finds this clone's id in that array, or returns -1 if it is not found. So each enemy would call that method, look at the result just once, and either update its existing entry or add a new one - it would never do both.

Another issue is RWSR - “Run without screen refresh”. You probably want to put all of this logic into a RWSR script, and have the “when I receive” block just call that script. Otherwise the program is going to pause and do a screen refresh at the end of every loop iteration, which includes the inner loop for building up the id and such. With a handful of enemies in the list and a few characters in each id, it could take dozens of frames for this existing script to finish.

geek62
Scratcher
100+ posts

Advanced list checker script

geek62 wrote:

griffithsrock1 wrote:

geek62 wrote:

I think you forgot to increment i2 in the loop.
Yes, I did forget that. However, it still doesn't work.
Expected behavior is every clone to create a new entry in targets and each one should iterate through the loop until it finds its id in the list then update the values.
Absolutely no updating can be observed.


Okay, fine, I'll look again, and I'll use my brain this time. :-)

blahblahblah deleted


I just realized that I used some words from programming languages other than Scratch: method, array, etc. But I'm guessing you've done some coding outside of Scratch? (If not: method = script; array = list; return = set the variable that this script uses to give its result; string = data value that is not a number, … are there any others?)
griffithsrock1
Scratcher
39 posts

Advanced list checker script

geek62 wrote:

geek62 wrote:

griffithsrock1 wrote:

geek62 wrote:

I think you forgot to increment i2 in the loop.
Yes, I did forget that. However, it still doesn't work.
Expected behavior is every clone to create a new entry in targets and each one should iterate through the loop until it finds its id in the list then update the values.
Absolutely no updating can be observed.


Okay, fine, I'll look again, and I'll use my brain this time. :-)

blahblahblah deleted


I just realized that I used some words from programming languages other than Scratch: method, array, etc. But I'm guessing you've done some coding outside of Scratch? (If not: method = script; array = list; return = set the variable that this script uses to give its result; string = data value that is not a number, … are there any others?)
Don't worry, I know javascript as well as C#. I thought I made it so clones would only do one or the other but I'll check again.
Seems like I used two if statements instead of an if-else. That is fixed now, the problem is still that nothing happens after the very first list update.

Last edited by griffithsrock1 (Oct. 20, 2023 18:06:54)

geek62
Scratcher
100+ posts

Advanced list checker script

At least part of the problem is that it is still using (i-1) where it should be using i2 in that loop.

I've remixed the project and added some debugging code that I use to figure out tricky clone problems like this.
So now it has a “global” list (ie, for all sprites) called Messages, and a “log” method to add messages onto that list.
You can make that list show, hide, and clear by typing m+s, m+h, and m+x (pressing “m” and holding it while you press the other letter).
And I've put in a ton of log statements to that loop, so you can look at the log and see exactly what is happening.
Go ahead and take a look at that, and let me know if you need help interpreting the results.
(I've already looked at that and can see the problem, but you should get the joy of figuring it out too.)

BTW, it adds so many messages to that list that it will affect how long it takes for the project to save.
If you run it even for ten or fifteen seconds, it will already have over a thousand messages in there.
So I recommend clearing that list (m+x) as soon as you don't need it any more.
Also maybe also disable the “log” method to make sure that list doesn't accidentally get huge again.
(Just detaching the “add” block from the script definition header should do the trick.)

griffithsrock1
Scratcher
39 posts

Advanced list checker script

geek62 wrote:

At least part of the problem is that it is still using (i-1) where it should be using i2 in that loop.

I've remixed the project and added some debugging code that I use to figure out tricky clone problems like this.
So now it has a “global” list (ie, for all sprites) called Messages, and a “log” method to add messages onto that list.
You can make that list show, hide, and clear by typing m+s, m+h, and m+x (pressing “m” and holding it while you press the other letter).
And I've put in a ton of log statements to that loop, so you can look at the log and see exactly what is happening.
Go ahead and take a look at that, and let me know if you need help interpreting the results.
(I've already looked at that and can see the problem, but you should get the joy of figuring it out too.)

BTW, it adds so many messages to that list that it will affect how long it takes for the project to save.
If you run it even for ten or fifteen seconds, it will already have over a thousand messages in there.
So I recommend clearing that list (m+x) as soon as you don't need it any more.
Also maybe also disable the “log” method to make sure that list doesn't accidentally get huge again.
(Just detaching the “add” block from the script definition header should do the trick.)

I can't seem to find the source of the problem. I did remark that “ID matched” never logs in the debugger. I actually found something very odd, I used some debugging stuff to my version and found that the clones do enter the list loop but somehow don't make it to the end of the loop.
whenIreceiveifmainthenrepeatlengthoflistsaytestfor0.1secsifstuffthensaytestfor0.1secs

So this is a representation of the script, the say block right after the repeat loop starts executes but the one at the end does not.
My thinking is that the script is getting interrupted every time the script is called.

-Edit
I got some results when using a variable to track when a loop starts and when it finishes, and having the broadcast wait until one script finishes.
This does support my theory. So then, the question is how can I call this script for each clone without it interrupting the running scripts?

-Edit 1
Results!
I added a slight cooldown the the broadcast and it almost works! I'm seeing obvious bugs in the script but I was right that the script was getting interrupted.

-Edit 2
Seems like if two clones are both touching the tower then they can cause each other to execute the script out of order. I'll see if I can fix this.

Last edited by griffithsrock1 (Oct. 23, 2023 16:07:13)

griffithsrock1
Scratcher
39 posts

Advanced list checker script

OK! I've fixed everything. I did have to tweak the script quite a lot. I'll leave it shared for a day or two so you can see the fixes.
griffithsrock1
Scratcher
39 posts

Advanced list checker script

So how would you make it so each tower clone has it's own list with the enemies putting values in the right list(s)?
geek62
Scratcher
100+ posts

Advanced list checker script

I'll have to think about multiple towers…

The two big clues about your existing code:
1. Scratch pauses every running script when it gets to the end of any loop, in order to do a screen refresh. After the refresh, those scripts are all given a chance to run again. (You can turn off this pausing for screen refresh by a checkbox in the script header, but it is a good idea only to do that on scripts which compute stuff, not in scripts that interact with the user or other scripts. It can be tricky.)
2. Event handlers are pre-emptive. If you broadcast an event and the “when I receive” script has a loop, it will happily keep chugging through that loop as long as it takes, until someone broadcasts that event again. When that event is received again, the event handler that was running is dropped like a hot potato, and the script is restarted at the top again to handle the new event. Effectively there is only one thread for each event handler. (I actually made a project about this way back when: https://scratch.mit.edu/projects/92382514/)

So, when enemies are touching it, your tower keeps broadcasting those events, one per frame. Which means the receiver never gets through the checkID loop, it pauses for screen refresh at the end of the first iteration, and then gets another event from the tower before it can do the second iteration.
geek62
Scratcher
100+ posts

Advanced list checker script

Oh, I see you have turned on the RWSR flag for that script. Excellent!
geek62
Scratcher
100+ posts

Advanced list checker script

I guess the core problem is that Scratch gives us a way to tell if this sprite A is touching some other sprite B, but it can't tell the difference between B and B's clones - if it is touching any of them, it counts as touching B. This was fine for my Asteroids game, because it didn't matter which missile hit which rock: if a rock is touching any missile it has to blow up, and if a missile is touching any rock it has to end.
But if you need to know which one hit which, Scratch doesn't give such a simple way to check it.

I think you'll have to make a list of all tower positions, and a list of all enemy positions. Enemies move, so they will have to keep their entries in that list up to date. With those lists, any sprite could do the math to decide if some enemy was touching some tower. If they are all circles or squares, the calculations are easy. If the shapes are funny, you might still pretend they are circles just for deciding if touching. Especially because I don't think you will ever have enemies actually touching a tower, right? You were just using that to figure out if the enemy was close enough to get shot at? So the distance calculation is really the perfect thing to check.
geek62
Scratcher
100+ posts

Advanced list checker script

Actually, maybe you don't need the list of tower positions. If each tower is going to do its own work of figuring out which enemy to shoot at, it needs the enemy list but it already knows its own position.

Powered by DjangoBB