@dfabulich@kgold I was interested in understanding the difference between *gosub and *gosub_scene that supports the Fibonacci implementation. *gosub doesn’t push the variables to the stack and recursive calls would overwrite the same variables. The *gosub_scene implementation is closer to what I would expect from a function call in other languages, while *gosub maintains a simpler model and uses the global status.
May I ask why did you choose a different implementation? Was it for performance? Or did you want to maintain the model simpler for *gosub, as the recursive stack may be a complex concept for some developers?
I believe that’s because of what Dan mentioned; he only wanted to use preexisting features of the language in implementing *params. CS has only two levels of scope: 1) global scope, with *create; and 2) per-scene scope, with *temp. So it was a learning complexity concern.
As you mentioned, *gosub_scene provides local scope, pushing variables onto the stack, so the functionality is still there for programmers who want to use it.
The global scope is risky. This example will swap the two variables a and b. If you reuse a name for your params, you may overwrite your data.
This is different with gosub_scene - I may decide to always use gosub_scene in my code.
*create a 100
*create b 10
${a} ${b} # Prints 100 10
*gosub swap a b
${a} ${b} # Swapped, prints 10 100
gosub_scene startup swap a b
${a} ${b} # No second swap, vars unchanged, 10 100
------
*label swap
*params b a
*return
More comments on this, after doing some experiments with recursion.
On complex projects, scoping is making things tricky. If you use *gosub, you may pollute the scene’s scope. With *gosub_scene instead, you don’t have any access to the local scope - all data you need there must be passed as params or global (in particular, simulated arrays).
On the other side, if you use CS as a target language (with tooling), you can use only local names (generated automatically) for *gosub and get a lot of stuff done. For instance, I’ve used strings to simulate growing data structures. This works in a very robust way (go CS engine!), but it’s very slow due to limited tools to manipulate strings – to modify a string you’d have to make a copy with a loop (which you must simulate with a goto). I’ve a version of the maze that can generate unbounded maps, but it’s at least 3x slower than the one using simulated arrays (map_0_0 map_1_1 …).
So, can we get scopes? Or at least, can we get better string manipulation?
@Chris_Conley I wrote a version of my maze (Advanced programming - randomized maze) where the map is stored with one long string variable per row (instead of one variable per cell). This way, I was able to create maps of unbounded size.
It’s very easy to read the data with the new setup.
Before (cell based): grid[ x][y] After (row based): grid[ x]#(y+1)
But setting the data is hard, there is no equivalent to:
*set grid[ x ][y] value
To update the string, you have to make a copy, character by character, and change what you need as you scan the original data.
EDIT:
Ok, I bit the bullet, dug a hole through the abstraction layer and started using native Javascript arrays in CS. Oh, my, it’s fast!! It’s like when, back in the days, you would write a small ASM function to speed up some data crunching in C or Python.
Now, in my preprocessor, I have a few new commands *js_array *js_set *js_get that help me do that. Back to the maze, I can have very large maps without a million variables grid_x_y, and it’s several times faster. Note that the result is still valid CS with some *script.
I think I’m now officially off topic. If anybody is interested we can discuss in a new topic.
I am not very good at reading code but to my understanding this “*create implicit_control_flow true” sets… all bugs hiding in choices invisible? I thought the point of quick test was to find bugs. Or is there another command where it just skips a specific choice? I know my choice works I’ve run it 1000 times but I can’t finagle it any more.
No. What this does is makes *choice behave exactly like *fake_choice, and lets you write *if/*else and *if/*elseif/*else blocks that just fall through to the next line.
With or without ICF, the following is valid code:
*fake_choice
#Yes.
#Maybe.
#No.
OK.
*if met_bob
This might be displayed.
This will always be displayed.
The following will only be valid if ICF is true:
*choice
#Yes.
#Maybe.
#No.
OK.
*if met_bob
This might be displayed.
*else
This might be displayed instead.
This will always be displayed.
Otherwise, without ICF, you’ll need to end every #option under a *choice, and each indented conditional block (that’s not just a standalone *if block), with a *goto or a *goto_scene or a *finish or an *ending.