Some CRPL questions

Started by st00pid_n00b, July 08, 2017, 01:28:58 PM

Previous topic - Next topic

st00pid_n00b


  • I'm writing a script with lots of list manipulations, and was wondering about memory leaks. Are the lists deleted when no variable references them anymore? Does using "--list_name" erase the list or just unassigns the variable? For instance if I have a list L and do:
    --L
    CreateList ->L

    What happens to the old list? What if the old list is referenced by another variable? I don't mean a CopyList, but something like <-L ->L2 before the above code, can I still safely use L2 after that?

  • The documentation says that CONST_ISBUILDING can only be set on CRPL towers. I've set it without problems on regular units. Any side effect?

  • It seems the stack is erased between each script call, but not on Delay. Is that correct? I didn't find it in the documentation.

  • I tried to set a custom score that doesn't depend on time. What I ended up with in my game over function is:

    # Setting the time to 1 frame displays 0:00.0 in the score chart, and gives a base score of 9999.
    # (Setting the time to 0 frames doesn't allow to post score.)
    1 SetGameTimeFrames
    # Compensate for the score related to time, so that only the actual game score is shown.
    # (Note: if the score falls to 0 with this method, it won't allow to post,
    # but this script can't give a score of 0.)
    -9999 AddAdditionalScore
    # End the game
    0 DestroyAllEnemyUnits

    I also came across this map that handles it differently: it sets the time to the maximum possible to give a base score of 0 (when I tried it gave me instead a leftover score of 63 or something like that).

    Are the 0 time and 0 score bugs? I guess the 0 time not allowing to post is to prevent autowin maps. Any suggestions?

  • Any tips to prevent complex scripts from turning into spaghetti code? The main problem is the lack of local variables in functions. This is not meant as a criticism of CRPL, I know it's just an ad-hoc small scripting language. What I did so far:

    * Favor stack manipulation hackery over 'local' variables in small helper functions.

    * Split into several scripts when possible.

    * Maybe prefix 'local' variables with the function name.

  • Is someone willing to playtest my map? It's heavily scripted and I mostly want to test for bugs. I don't know if I post it in this thread or make another one in Custom Map Discussion.

Any help is appreciated! I'm mostly concerned about points #1 and #6.

Edit: Save file attached. Use keyboard arrows for control.
It's still work in progress. The functionality and gameplay is there, so that's where I would like feedback / bug reports. It looks bland, I still need to work on the cosmetics and layout, make a proper intro and give info on scoring.

planetfall

Quote from: st00pid_n00b on July 08, 2017, 01:28:58 PM
I'm writing a script with lots of list manipulations, and was wondering about memory leaks. Are the lists deleted when no variable references them anymore? Does using "--list_name" erase the list or just unassigns the variable? For instance if I have a list L and do:
--L
CreateList ->L

What happens to the old list? What if the old list is referenced by another variable? I don't mean a CopyList, but something like <-L ->L2 before the above code, can I still safely use L2 after that?

I did a small test of this the other day (and ran the game out of memory by creating a 9999999 element list - don't do that) - it seems that lists are properly garbage-collected.

--list only removes that reference to the list. Other references to the list will still work.

Note that when you save the game, list variables are stored by value. If you do the following:


"This is a sentence with words in, innit?" " " Split ->List1
<-List1 ->List2


manipulations to List1 will also affect List2 because they are two references to the same list. If you save and reload, List1 and List2 will have the same contents, but will reference two different lists. The solution is to have one version of the list that is the "master" version. Any other references to this list should be NotPersisted and then grabbed from the master using :awake or :gameloaded.

Incidentally - if you need multiple cores to communicate frequently, use lists. G/SetListElement is vastly more efficient than G/SetScriptVar.

QuoteThe documentation says that CONST_ISBUILDING can only be set on CRPL towers. I've set it without problems on regular units. Any side effect?

This is vastly outdated information.
I've had some glitches setting it on air units, in which case the launch pad finishes building but the plane doesn't spawn. This was years ago and I haven't tested again in the interim. Setting CONST_ISBUILDING is fine for the vast majority of units.

QuoteIt seems the stack is erased between each script call, but not on Delay. Is that correct? I didn't find it in the documentation.

This is correct.
If you are using Delay, be aware of its limitations.

QuoteI tried to set a custom score that doesn't depend on time. What I ended up with in my game over function is:

# Setting the time to 1 frame displays 0:00.0 in the score chart, and gives a base score of 9999.
# (Setting the time to 0 frames doesn't allow to post score.)
1 SetGameTimeFrames
# Compensate for the score related to time, so that only the actual game score is shown.
# (Note: if the score falls to 0 with this method, it won't allow to post,
# but this script can't give a score of 0.)
-9999 AddAdditionalScore
# End the game
0 DestroyAllEnemyUnits

I also came across this map that handles it differently: it sets the time to the maximum possible to give a base score of 0 (when I tried it gave me instead a leftover score of 63 or something like that).

Are the 0 time and 0 score bugs? I guess the 0 time not allowing to post is to prevent autowin maps. Any suggestions?


This is an area of CRPL I have very little experience with, and that has seen relatively little use by others to boot. Someone else will have to help with this one.

QuoteAny tips to prevent complex scripts from turning into spaghetti code? The main problem is the lack of local variables in functions. This is not meant as a criticism of CRPL, I know it's just an ad-hoc small scripting language. What I did so far:

* Favor stack manipulation hackery over 'local' variables in small helper functions.

* Split into several scripts when possible.

* Maybe prefix 'local' variables with the function name.


To quote an Ancestor of mine: We are chained here forever, you and I, in Colonial Space. Free yourself, run the script and embrace the ineffable spaghetti coder that lives within us all.

QuoteIs someone willing to playtest my map? It's heavily scripted and I mostly want to test for bugs. I don't know if I post it in this thread or make another one in Custom Map Discussion.

Posting here is fine, especially if it's the scripts that need testing. I can't guarantee I'll get to it soon, but someone will.
Pretty sure I'm supposed to be banned, someone might want to get on that.

Quote from: GoodMorning on December 01, 2016, 05:58:30 PM"Build a ladder to the moon" is simple as a sentence, but actually doing it is not.

st00pid_n00b

Thanks for the quick and thorough answer! I understand much better now.

Quote from: planetfall on July 08, 2017, 02:49:21 PM
If you save and reload, List1 and List2 will have the same contents, but will reference two different lists

Wow, good to know. I haven't tested much with save and reload, so this needs to be addressed.

QuoteIncidentally - if you need multiple cores to communicate frequently, use lists. G/SetListElement is vastly more efficient than G/SetScriptVar.

Nice, I didn't think about that. So you just use SetScriptVar once to copy a reference to a list? I suppose there is the same issue you mentioned about the lists being duplicated on save/reload?

QuoteIf you are using Delay, be aware of its limitations.

Damn, I've definitely used Delay for animations in for loops. Time for more spaghetti... In last resort I can use your hack of defining the awake function in a separate script (wait... is awake per core, not per script?)

I did a small test, and the whole stack is emptied after a save/reload. If inside a loop, it says "loop stack vars not present". Incidentally, if I Trace "I" after the reload (or outside a loop) it just seems to freeze the script with no error message.

QuoteTo quote an Ancestor of mine: We are chained here forever, you and I, in Colonial Space. Free yourself, run the script and embrace the ineffable spaghetti coder that lives within us all.

Haha this was my fourth option :)

The parts I didn't quote were informative too, I just had nothing to add except thanks!

planetfall

Quote from: st00pid_n00b on July 08, 2017, 04:45:17 PM
Nice, I didn't think about that. So you just use SetScriptVar once to copy a reference to a list? I suppose there is the same issue you mentioned about the lists being duplicated on save/reload?

Yes, both your assumptions are correct.

Quote
Damn, I've definitely used Delay for animations in for loops. Time for more spaghetti... In last resort I can use your hack of defining the awake function in a separate script (wait... is awake per core, not per script?)

I'm unsure what you mean here. Each script can have an :awake; on a core with multiple scripts, every script's :awake will run when the core is initialized.
Pretty sure I'm supposed to be banned, someone might want to get on that.

Quote from: GoodMorning on December 01, 2016, 05:58:30 PM"Build a ladder to the moon" is simple as a sentence, but actually doing it is not.

st00pid_n00b

Quote from: planetfall on July 08, 2017, 05:19:37 PM
I'm unsure what you mean here. Each script can have an :awake; on a core with multiple scripts, every script's :awake will run when the core is initialized.

In the thread you linked you wrote:

Quote
What if I put the awake function in a separate script?

planetfall

Quote from: st00pid_n00b on July 08, 2017, 06:08:58 PM
Quote from: planetfall on July 08, 2017, 05:19:37 PM
I'm unsure what you mean here. Each script can have an :awake; on a core with multiple scripts, every script's :awake will run when the core is initialized.

In the thread you linked you wrote:

Quote
What if I put the awake function in a separate script?


This was with regard to OperateWhilePaused. I was unsure whether enabling OWP on one script meant only that script would operate while paused, or the entire core. (It's only the one script.)
Pretty sure I'm supposed to be banned, someone might want to get on that.

Quote from: GoodMorning on December 01, 2016, 05:58:30 PM"Build a ladder to the moon" is simple as a sentence, but actually doing it is not.

GoodMorning

With regard to scoring, I have found it more effective to just let it be - it confuses people otherwise. If you are doing a completely different game, then time-to-win is still usually an effective measure.

Regarding spaghetti, planetfall gives sound advice. Also of note is that stack hackery is required only if you want something recursive. Otherwise, a variable used only in that function is preferable for readability and has no real cost except in the most oft-run code. By which I mean hundreds-of-times-per-frame. If you write, then optimise, things are easier to read, change and debug.

Remember, though, that the natural state of things is chaos. Entropy always rises.
A narrative is a lightly-marked path to another reality.

st00pid_n00b

I updated the original post and attached the map. If someone can test it and see if they can break the script it would be great!

planetfall:
Thanks for the clarification. I got rid of Delays in loops (or anywhere the stack is not empty). Animation speed issues made me realize something strange: 1 Delay is 2 game frames, and n Delay is n+1 frames. It seems like a bug...

GoodMorning:
Yes, it's a different game, in the same vein as the Space Creepers I linked earlier.
I completely agree about readability (premature optimization etc). I was mentioning ways to avoid name clash (unwillingly modify a variable that is used elsewhere). I don't think spaghetti refers to readability, it's about code that's so intermingled that when you try to change something there are side effects everywhere.
Entropy might rise, but not always at the same rate. I'll do what I can to delay the heat death of the universe :)

planetfall

Some thoughts:
-No bugs found.
-Lack of a hard drop is annoying, holding the down arrow means I usually end up moving the next piece a bit as well.
-Early speed gains seem a bit too small, later they seem a bit fast.
-Harder than normal to line up crops because units aren't visually square
-Since when are combos a thing? Also, most versions have points for drops but not for placement.
-Pausing doesn't seem to be in the spirit of the game.
-Either the pieces are rotating the wrong way, or I'm very rusty.
- http://knucklecracker.com/wiki/doku.php?id=crpl:docs:enablenormalzoomcontrol
Pretty sure I'm supposed to be banned, someone might want to get on that.

Quote from: GoodMorning on December 01, 2016, 05:58:30 PM"Build a ladder to the moon" is simple as a sentence, but actually doing it is not.

st00pid_n00b

Quote from: planetfall on July 10, 2017, 11:14:00 PM
-No bugs found.
Ok, nice!

Quote
-Lack of a hard drop is annoying, holding the down arrow means I usually end up moving the next piece a bit as well.
Ok I can add that. I prefer soft drop but why not both.

Quote
-Early speed gains seem a bit too small, later they seem a bit fast.
I notice them more around level 7-8 but it probably depends on the player ability: each level decreases the vertical timer by 20%. The timers are:
30, 24, 19, 15, 12, 10, 8, 6, 5, 4. Since the last changes are only -1 I don't really see a way around that...

Quote
-Harder than normal to line up crops because units aren't visually square
Yep, and also without a background grid. But visually, a background void looks better than some checkerboard pattern. That's also why I hadn't included hard drop.

Quote
-Since when are combos a thing? Also, most versions have points for drops but not for placement.
I didn't know combos either but found about them while searching for scoring methods. See here for example. It's interesting because it allows for different strategies, setting up for combos is nor the same as for a tetris.

I see points for both drops and placement in other versions. I didn't find how to score drops, should it depend on the height?

Quote
-Pausing doesn't seem to be in the spirit of the game.
Hmm I think most versions allow pause but hide the screen. The pause button isn't disabled with EnableAlternateControlMode anyway, so I put back the P key as well.

There was a funny story with a machine learning algorithm that learned how to play games without prior knowledge. With tetris it didn't figure out it should make lines, so it dropped the pieces as fast as possible to increase the score and when they reached the top paused the game to avoid losing...

A related game spirit concern is save/load: I decided against changing the random seed on load, so the player can know in advance the pieces but he can't reload until he gets the piece he's waiting for.

Quote
-Either the pieces are rotating the wrong way, or I'm very rusty.
Ok, will fix.

Quote
- http://knucklecracker.com/wiki/doku.php?id=crpl:docs:enablenormalzoomcontrol
I was reluctant to use this because the result depends (I guess?) on the screen definition.

A couple more gameplay thoughts:

- I'm not sure about the starting orientation and center of rotation of the pieces.
- Are the sounds annoying?
- Level 10 speed is not indefinitely sustainable for me, but some tetris players are scary, I wonder if the difficulty should keep increasing somehow.

GoodMorning

#10
Notes: You don't have to drop after a fixed delay. Keeping a float timer allows you to drop after a number of frames on average.


once
    0 @AdjustSpeed
    0 ->DropTimer
    1 ->Threshold
endonce

<-DropTimer <-Speed add ->DropTimer
<-DropTimer <-Threshold gt if
    <-DropTimer <-Threshold sub ->DropTimer
else
    return
endif

#Drop

:AdjustSpeed #[ Level - ]
    1.05 swap pow 0.05 mul ->Speed


Also see Meso for an example of adding Cores to a map for the sole purpose of carrying images. You could apply this to make a background grid.

Edit:
I didn't think I had played Tetris of any form enough to be bothered by a reversed rotation direction - but once the speed was sufficiently high, I more than noticed it.
A narrative is a lightly-marked path to another reality.

st00pid_n00b

Yes, I can use floats, I will test and see if it doesn't make a jerky movement.

About the image grid, I'll test if it's not too ugly. Kinda defeats the purpose of making tetris with CW3 graphics though.

st00pid_n00b

Updated map attached. Changes:

_ nicer looking map
_ hard drop
_ soft drop stops at new tetromino
_ background grid
_ clockwise rotation
_ display combo points
_ display line points
_ info button
_ sound on/off button
_ grid on/off button
_ points for drop
_ float timer and slower speed increase
_ set starting zoom but not disable zoom control (didn't look right when I changed my screen resolution)

I'll upload in a couple of days if there's no other suggestion or bug.

Builder17

Not bad, but how Planetfall and GoodMorning have so big scores? :O

st00pid_n00b

Oooh I didn't know the scores were shared for maps outside of colonial space! This mean I "polluted" the server when I did many tests for the score...

There, I beat them, the question is rather why is yours so low?  ;)

I don't know if the scoreboard is also the same when the map is uploaded, but either way don't bother about it because I've got some cleanup to do and there's debug code lying around so the map id won't be the same.