Pulling numbers from a string?

Basically, I was wondering if there is a way to read a long series of numbers by digit. What I mean is… For example:
1234567890

Lets say 12 is tied to your skin tone, 34 is tied to your hair colour, 56 to hair texture, so on. Is there a way to pull those numbers in order to allow for a single number (1234567890) to be split up and then used to set all of these variables? There was a similar discussion on pulling a letter for an initial, though I am not sure if anything has changed and the use case feels a little different so I figured worst case scenario I could just… ask.

Assuming general ChoiceScript wont work, what is the overarching language used for ChoiceScript? Is there any way through coding to achieve this effect?

I think it would just be very useful to allow for replays to have a feature where they could save the a single number for all the appearance information they selected in one playthrough, to be read and used in the subsequent one so they won’t be bogged down by all those options when playing again.

I haven’t tried it myself but for reference here’s a thread about pulling and printing individual letters from a variable. I’m not sure that you’d be able to use eg 12 for skin tone, you might need it to be 1 for skin tone, 2 for hair colour etc. But I am just guessing.

Thanks Hannah! I’ll give this a try and see if I can get it to work, I think limiting options a little more than I initially planned to offer this would be a worthwhile sacrifice. I’ll have to do some testing though!

You can do it that way with the use of string but I prefer using the numeric solutions as they are easier to modify check and correct, IMO.


Here is a sample code of how to extract values, from a single number, you can remove ${} if you want to use it in a check.

Code
*title test 17
*author xyz
*create variable2 1234567890

${variable2}

${variable2%100}

${((variable2%10000)-(variable2%100))/100}

${((variable2%1000000)-(variable2%10000))/10000}

${((variable2%100000000)-(variable2%1000000))/1000000}

${((variable2%10000000000)-(variable2%100000000))/100000000}

*finish

2 Likes

Yes it is possible. You’d have to write loops in ChoiceScript, which is not trivial. @HWKEye, I’m not sure I understand your solution and how you could use it to store different attributes individually.

@Phenrex, I suggest you use @Twiger_Fluff’s solution to polyfill arrays using strings.

I’ve been working on this for the past four or so hours. I didn’t need to do loops though, I just assigned the variables to names and used a metric crap load of *if statements for each of them for setting the text variables.

To work around the ‘one character’ limit for setting variables using the longer string, with the
*set wanted_variable string#2
I just checked at the beginning for if the first character is a 0 (for 0-9 you can just set them as the second value in the string)… For example

*temp string 30
*temp wanted_variable 0
*temp text_wanted_variable ""
*if ((string#1) = 0)
    *set wanted_variable string#2
*if ((string#1) = 1)
    *set wanted_variable 10
*if ((string#1) = 2)
    *set wanted_variable 11
*if ((string#1 )= 3)
    *set wanted_variable 12

... Later in file...

*if (wanted_variable = 1)
    *set text_wanted_variable "one"
*if (wanted_variable = 2)
    *set text_wanted_variable "two"
*if (wanted_variable = 3)
    *set text_wanted_variable "three"

... and so on...

Probably not the most elegant nor the most efficient way to write it but… I mean it seems to work?

I actually did it the same way. And yeah, I agree, it’s not the most elegant solution but it might help to make save system or character creator codes that will work as long as the player has the code.

That’s what loops are for.

Anyway, you don’t need to reinvent the wheel. Last I checked Twiger’s solution was pretty decent.

1 Like

Currently I have it set up so all the player needs to do is insert one of a few different types of codes (they select which to generate from their character) and it will automatically detect based on character length what type of code it is and set the variables accordingly.

I mean you sent it to me 15 minutes ago and I was working on a solution for 4 hours so-… I don’t know why you are acting like I ignored your response. I just haven’t had time to figure out how to do what was shown. I am not exactly fully versed on using loops in Choice. So I need a bit more time than three or so minutes since I’ve opened a tab to look at it to absorb the information since before I was just writing what I have done so far.

Ahh-- I understand! Sorry for the mixup. Either way I will take a look at it! Like I said it’s just… a little difficult to absorb. I’ve been looking at my own code long enough for my head to spin. The idea of redoing what I just spent so long doing makes me… sad. I will look into it for anything I need to add and try to weigh if it’s worth redoing it with loops instead. I appreciate you spending the time to send a response either way. :blush:

Whoa, whoa, whoa. No I’m not. That’s a clear case of the recepient misreading the message. I’m just reafirming you don’t need to waste your time with something that’s solved already. But if you want to use your own solution, go for it. :man_shrugging:

1 Like

I’m a huge advocate for not using code you don’t understand (there’s an argument for abstraction and APIs of course). But if you can code and understand your own solution, you’ll get more out of that than copying and pasting someone else’s. You’ll also be in a better place to debug it when things go wrong.

That said, and is if to counter my own point, is Twiger’s solution something we could add to cslib @cup_half_empty ?

5 Likes

Yeah, sorry about that too. I probably should have worded it better.

I get how you feel, I write code for a living and sometimes I just realize I have to start over and it’s really disheartening :joy:.

At some point, an *if ladder becomes unmanageable. And once you wrap your head around loops you realize it’s actually pretty simple.


I know you didn’t ask for it, but here’s a primer on loops. I hope it can be of help.

Loops

Imagine you own a restaurant and you hire an android to perform menial tasks, and you have a stack of plates to wash in the kitchen. You want the android to start with the first plate, wash it, dry it, and put it away. Then, move on to the next plate and do the same thing. It should keep doing this until it has washed and put away all the plates. But the android doesn’t understand English or any other human language. It only understand code!

In programming, you could write an *if statement for each plate.

*if (plate_1 = "dirty")
   *gosub wash_plate 1
*if (plate_2 = "dirty")
   *gosub wash_plate 2
.
.
.

But not only this makes your code verbose, it is not flexible. If the number of plates changes, you also have tweak your code.

Now imagine that instead of writing an *if statement for each plate you could write something like as long as there are plates, wash them. That’s a loop!

Use Case

For your use case, you want a way to extricate parts of a longer string, for example, you want the 32 in the string XPTO32FOO. That is a 2-digits long substring starting from index 5 (1. X, 2. P, 3. T, 4. O, 5. 3...).

So you need a subroutine that will copy character by character from the original string into a new string. This subroutine will take a string, an index and the length of the substring you want.

*label slice
*params original_string initial_index len
*return

Next, we need a control variable, let’s call i_ for “iteration”, and a temporary variable to hold the value of the substring.

*label slice
*params original_string initial_index len
*temp substring ""
*temp i_ 0
*return

Since ChoiceScript doesn’t have specific commands for loops, we’ll have to emulate it with control flow. That means we will use an *if statement to check if the loop is not over yet and redirect to the top of the loop again. We will use the control variable i_ for that.

*comment START OF LOOP
*comment --------------
*label slice_start_loop
*if (i_ < len)
   *set i_ +1
   *goto slice_start_loop
*comment --------------
*comment END OF LOOP

This loop will repeat until the control variable is the same as the desired length.

Next, we add the actual code to copy the characters. We find the current index by adding the control variable to the initial index .

*label slice_start_loop
*if (i_ < len)
   *temp current_index (initial_index + i_)
   *set substring &(original_string # current_index)
   *set i_ +1
   *goto slice_start_loop

We also need to check if the current index is not greater than the total length of the original string. For that we move the line where we create the current_index to before the *if statement.

*label slice_start_loop
*temp current_index (initial_index + i_)
*if ((i_ < len) and (current_index <= length(original_string)))
   ...

When the loop ends, we need to save the final result in a global variable, so you can use the value retrieved by the subroutine.

startup.txt

*create result ""

And in the subroutine:

*set result substring
*return

Now we put everything together.

*label slice
*params original_string initial_index len
*temp substring ""
*temp i_ 0
*comment START OF LOOP
*comment --------------
*label slice_start_loop
*temp current_index (initial_index + i_)
*if ((i_ < len) and (current_index <= length(original_string)))
   *set substring &(original_string # current_index)
   *set i_ +1
   *goto slice_start_loop
*comment --------------
*comment END OF LOOP
*set result substring
*return

The way you would use it is like this:

*gosub slice "XXXX42XXXX" 5 2

${result}

There are a couple ways you can blow this subroutine, for example, if you pass a negative value for the length.

I suggest you also take a look for future reference into CSLIB, which is a collection of useful subroutines including one to extract substrings and with necessary guards to avoid blowing the subroutine. :grin:


Now, if you want to spend a few brain cells trying to understand loops :joy:, you can try to come up with a solution of how to extract the values from the string and set the variables one by one.


@CJW, If I’m not mistaken, CSLIB covers most of the same use cases. But Twiger uses strings to emulate arrays, which you can definitely build on top of CSLIB, whereas CSLIB uses the pseudo-arrays of ChoiceScript. I just thought that Twiger’s solution fit @Phenrex’s need better for this specific use case.

It’s something to consider, but might be confusing to have two different way to deal with “arrays” in CSLIB.


Edit: for some reason, the forum messed with the formatting of the post :frowning_face:

8 Likes

Funny you should ask, I am trying to do a similar thing, but using strings since I am going to work in base 256 or perhaps higher.

ChoiceScript currently treats numbers and strings (of 0-9) the same, so there is a simple way to do this.

Here is a minimum working example. Simply copy and paste into startup.txt and you can test to see if this is what you want.

*title Pulling numbers from a string?
*author Joe S. Fung

*scene_list
  startup

*create stat1 0
*create stat2 0
*create stat3 0
*create stat4 0
*create stat5 0

*create inputString ""
*create statsLength 2

*create statNames_count 5
*create statNames_1 "stat1"
*create statNames_2 "stat2"
*create statNames_3 "stat3"
*create statNames_4 "stat4"
*create statNames_5 "stat5"

*comment If arrays are available: *create_array statNames 5 "stat1" "stat2" "stat3" "stat4" "stat5"

*comment ===========
*comment Start code
*comment ===========

*label inputString
Please input your character string:
*input_text inputString
*temp n (length(inputString))
*temp stringLength (statsLength * statNames_count)

*if n = stringLength
  *goto nextpart
*else
  Your string is invalid, please check that it is exactly ${stringLength} characters.
  
  *goto inputString


*label nextpart
*gosub extractString

stat1: ${stat1} 

stat2: ${stat2} 

stat3: ${stat3} 

stat4: ${stat4} 

stat5: ${stat5}

*finish

*comment =============
*comment subroutines
*comment =============

*label extractString
*temp i 1
*temp j 0
*temp count 1
*temp strNum ""

*label loop1
*set strNum & (inputString#{i})
*set i + 1
*set j + 1
*if j = statsLength
  *set {{"statNames_" & count}} (strNum + 0)
  *set strNum ""
  *set count + 1
  *set j 0

*if count <= statNames_count
  *goto loop1
*else
  *goto endloop1


*label endloop1
The loop is finished.
*return

You will need to adjust statsLength to be the number of characters per stat and stringLength, to be the number of characters in the string, and statNames_count to be the number of stats in the string.

I saw that @cup_half_empty came up with an excellent solution for slicing, which I just noticed is essentially the same as my answer. However, I don’t think my answer is redundant since it more specifically addresses the original problem. My answer goes down a inputString (of known stringLength 10) with a specified statLength (2 characters) and extracts each substring into the known variables stored in statNames_N and statNames_count where N is an integer.

My code isn’t packaged as a nice submodule/subroutine, though it could be done with some effort.

2 Likes

I hope you don’t mind me asking some questions… I haven’t touched loops in a few years so I feel really rusty.

So I looked up what the *params function is, and it explains it as a form of subroutine. Where I get a little lost is in what is going on with you using it then listing three variables?

Does the act of you listing them after *params mean these are all variables? I assume the “original_string” is just 012345523849234 (whatever your string was), and len is the length of your original string in characters. What is the “initial_index”? I am a little confused because I’m not sure what tells the function to pull two numbers from the string into a new function. Does *params predefine these variables or did you declare them somewhere earlier and just not include that in the given code? What value, if so, does *params assign these variables?

Following, you later define a “current_index” which is the “initial_index + i_”, so does that mean the “current_index” is tracking where in the line you are? (For example, current_index would be 21 if you are at plate_21)?

What is the “&” doing in “&(original_string#current_index)”?


I hope you don’t mind me asking for an expansion, but how could you apply this, if, for example, you had changing variable names… lets say two variables called count_dog and count_cat. Lets say you have 15 dogs and 47 cats. So your string is just 1547. How would you apply the general function for this use case? (I think it’s a little difficult to understand without following with any real values :flushed: )


I can see how this cleans up the earlier part of assigning the values. But I don’t know how this would solve assigning the strings their texts (which is the by far most insufficient part of the code I wrote since… For example, lets say I have a variable called breed_cat_value with the value 12 which means it is a persian. I wrote out eleven other “*if” functions for defining the breed_cat_typed before getting to “persian.”


Thanks again for spending the time to write out a reply to this because it genuinely is helpful! I’m sure others will be glad to have this forum discussion as a resource in the future!

I don’t mind. :grin:


Yes! :joy:

*params tells the subroutine that it’s expecting some parameters. Listing the parameters after the command is short way of declaring local temporary variables to hold the values you pass to the subroutine.

So when you call the subroutine like this:

*gosub slice "XXXX42XXXX" 5 2

It’s going to assign XXXX42XXXX to original_string, 5 to starting_index and 2 to len. What these parameters mean is that I want the subroutine to extract a substring from XXXX42XXXX, starting at index/position 5 and which has 2 characters length. So it will loop though the original string and concatenate character by character to a temporary string we declared inside the subroutine called substring until it either reaches the end of the original string or the substring has the required length (2).

Try doing this on your own:

*gosub say_hi "Phenrex"

*finish

*label say_hi
*params name
Hi, ${name}!
*return

That’s right. We would be at position 21 of the string, counting each character as a position.


In ChoiceScript & is the concatenation operator, that’s a fancy way of saying it’s “adding” strings together. So it takes the character at the position of the initial index, in your example it would be 2 and “adds” it so the variable substring. Next iteration it moves one position further and “adds” the next character to substring, which is 1 in your example. By the end the value of substring should be 21.

Try this on your own:

${ "Hello, " & "Phenrex" }

The next step would be something like what @Speedcubing_Gaming posted above. You are absolutely right that in doing things this way you can’t avoid a verbose code, since ChoiceScript has a limited set of data types.

@Speedcubing_Gaming’s suggestion of using array notation is, in my opinion, the most elegant way while still keeping it simple (you can also try to emulate arrays with strings like @Twiger_Fluff did, but it’s more complex and involves loops inside loops :sweat_smile:). To identify the type of cat for example, you’d need to create a numbered list of variables, one variable for each type of cat. This is called a pseudo-array.

*temp cat_type_1 "American Curl"
*temp cat_type_2 "Bengal"
*temp cat_type_3 "Oriental Longhair"
.
.
.
*temp cat_type_12 "Persian"

Then, you look up the value using array notation:

*set breed_cat_typed cat_type[type]

For type you pass the value you extricated previously from the long string containing all the values. The syntax cat_type[type] is the same as writing cat_type_12 if type is 12. But note we can look up variables dynamically this way. This is called “array notation”. You can already shed a few lines of code with this, but yeah, you still have to write that much.


Finally, following the same logic, to load all values, you’d need a pseudo-array with the names of the variables and a control variable with the expected size of the array. This is in effect what @Speedcubing_Gaming did above.

*temp my_var_len 42

*temp my_var_1 "name"
*temp my_var_2 "surname"
*temp my_var_3 "breed_cat_typed"
.
.
.
*temp my_var_42 "skin_color"

Then you’d write a loop to go over the array with the variable names and extricate the corresponding value from the big string containing all values. Supposing the values always have two digits, the the value for name would be the first two digits, the value for surname would be the next two digits, and so own. A simple way to calculate the initial index for each value would be ((current_position - 1) * 2) + 1. So:

  • The value for the first (1) variable starts at 1
  • For the second (2) starts at 3
  • etc

I’m only explaining this so you can understand the underlying logic, but I strongly suggest you take a look at either CSLIB or @Twiger_Fluff’s solution.

For example, I expect you want to allow users to input their own value for name for example, this already breaks the whole code, since name can have an arbitrary length.

1 Like

If anyone is curious what I have done so far for this solution: I focused on the latter half of the question first, on cleaning up the hundreds of “if” statements! I’m proud to say the current iteration doesn’t need any "ifs outside of potential Error checks! What I ended up doing for the latter half (setting texts from assigned variables) was creating a set of temporary text variables…

IN START UP:
*comment These are the temporary variables I used, I made them in start up so they can be used for various purposes. Whatever is needed in the future
*create temp_textvariable_1 "one"
*create temp_textvariable_2 "two"
*create temp_textvariable_3 "three"
...............
*create temp_textvariable_20 "twenty"

*comment These are the variables I actually want to set for this example.
*create t_catbreed "???"
*create n_catbreed 0
*create t_dogbreed "???"
*create n_dogbreed 0

IN TEXT FILE:
*comment Considering this focuses on the later half of figuring out setting variables using the string answers, I'm assigning the values as though they were done via one of the recommended methods.
*set n_catbreed 3
*set n_dogbreed 1

*temp temp_amount_of_options 0
*comment the temp_count just tracks how many times the subroutine was ran.
*temp temp_count 1
*temp temp_set_typed "???"
*temp temp_set_value 0
*temp temp_variable_name "variable"


*comment CATBREED
*set temp_variable_name  "catbreed"
*set temp_amount_of_options 20
*set temp_textvariable_1 "domestic shorthair"
*set temp_textvariable_2 "domestic longhair"
*set temp_textvariable_3 "persian"
...
*set temp_textvariable_20 "sphinx"
*gosub subroutine


*comment DOGBREED
*set temp_variable_name  "dogbreed"
*set temp_amount_of_options 8
*set temp_textvariable_1 "shitzu"
*set temp_textvariable_2 "chihuahua"
*set temp_textvariable_3 "pitbull"
...
*set temp_textvariable_8 "german shepherd"
*gosub subroutine

*goto check


*label subroutine
*set temp_set_value n[temp_variable_name]
*if ((temp_set_value > temp_amount_of_options) or (temp_set_value <= 0))
    ERROR: Please re-enter your generation code to ensure there are no-typos. If it still doesn't work, please ensure it is from the most recent iteration of the game! !
    *if (temp_set_value > temp_amount_of_options) 
        It appears the @{temp_count catbreed|dogbreed|...} was entered incorrectly!
    *if (temp_set_value <= 0)
        It appears a negative number or 0 was assigned to one of the variables!

*comment I recognize a redundancy between the first and third lines, I could cut them out, for some reason it wraps around easier for me to just do it this way though.
*set temp_set_text temp_text_variable[temp_set_value]
*set temp_count + 1
*set t[temp_variable_name] temp_set_text 
*return

*label check
Your cat is a ${t_catbreed}, while your dog is a ${t_dogbreed}!!
*page_break Is that so...?


I looked at the links you mentioned @cup_half_empty , I honestly can’t figure out what the hell is going on with that. I just can’t parse it. I need to understand code and just looking at what other people does doesn’t oftentimes make it easy for me to understand what to do myself. I just wrote out this example raw for fun to try to explain what I ended up doing, though I’m not even sure if this is better? Is it better? :sob:

2 Likes

Don’t worry, learning to code is a journey. You should be proud of your solution whether it’s a series of *ifs or something more complex. :grin:

5 Likes

Hmm… I think that you have a bit of a misunderstanding about how to most efficiently use arrays for your problem. I will break down how I think the program should flow:

  1. Initial Setup
  2. Create Variables and Dictionaries
  3. Choice to Input Save String
  4. Query and Validate User Input
    • Get user input
    • Validate length, if fail, go back to 3
  5. Extract Numbers from Input and Set Variables
    • Validate numbers, if fail, go back to 3
    • If valid, save to variables
  6. Skip Past Variable Sets.

Before I get into what goes on in each section, I think that it is really only necessary to store a numerical value for the catbreed and dogbreed if you are going to use arrays, but I can see the convenience of also storing the string if you are going to use it frequently in the text.

I also noticed that you prefixed the text variables with t_ and numbers with n_, so I will follow that convention and set the dictionaries as the same without prefixes.

Additionally, I think detailed error messages for parsing the string are unnecessary so long as you provide a way for the user to obtain a save string after they have made the choices (I will reply to this message with an example solution for this problem). Otherwise, I would include it in a help section, accessible from the stats page, maybe listing the acceptable ranges for the variables and the order they appear in the save string.


Initial Setup

  • Set title
  • Set author
  • Set scene_list
*title Story Title
*author Author Name

*scene_list
    startup

Create Variables and Dictionaries

  • Declare any variables that store values for the story

First, let’s assume that you are going to use the dog breeds and numbers elsewhere, say during normal section or at least allow them to correct an incorrect string containing the save data. In that case, you would want to be able to access the dog and cat breeds without setting all the values to an array every time. We should set the dog and cat breeds to their own arrays.

Think of an arrays in ChoiceScript like a dictionary which allows you to look up a value (such as a string containing a dog or cat breed) using integers (or strings if you construct them manually). In the case of your problem:

catbreed                      |    dogbreed
 count -> 20                  |     count -> 8
 1 -> "domestic shorthair"    |     1 -> "shitzu"
 2 -> "domestic longhair"     |     2 -> "chihauhua"
 3 -> "persian"               |     3 -> "pitbull"
 .                            |     .
 .                            |     .
 .                            |     .
20 -> "sphinx"                |     8 -> "german shepard"

This is accomplished with the following code:

*create_array catbreed 20 "domestic shorthair" "domestic longhair" "persian" ... "sphinx"
*create_array dogbreed 8 "shitzu" "chihauhua" "pitbull" ... "german shepard"

Now we can look up the type of dog or cat based on a number. A couple examples:

  • catbreed[1] gives "domestic shorthair"
  • dogbreed[8] gives "german shepard"

Note that the catbreed[count] and dogbreed[count] or catbreed_count and dogbreed_count are automatically specified by the *create_array command as seen here.

Since you intend to get a string from user input and extract these variable values from it, you will need a variable for the string, a variable telling the number of characters that each variable needs (note that the dogbreed requires two digits to store 10-20), a dictionary/array for the variables contained in the string, and variables for the breeds in text and numerical form:

*create inputString ""
*create variableLength 2
*create_array inputVariable 2 "catbreed" "dogbreed"
*create t_catbreed "???"
*create n_catbreed 0
*create t_dogbreed "???"
*create n_dogbreed 0

If you wanted different numbers of digits for each variable, things would get much more complicated (this is what I am doing for my project).


Choice to Input Save String

This is necessary to allow the user to have somewhere to go after entering a string incorrectly.

*label input_or_play
Do you want to input a save string or play the choices?
*choice
  #Input a save string.
    *goto input_string
  #Play the choices.
    *goto play_choices

Query and Validate User Input

We first take the user input and save it to inputString, then we check the length to make sure it is correct.

*label input_string
Please input your character string:
*input_text inputString

*temp n (length(inputString))

*if n = (variableLength * inputVariable_count)
  *gosub extract_numbers_from_string
  *goto skipped_choices
*else
  ERROR: Invalid save string length. Please re-enter your generation code to ensure there are no-typos. If it still doesn't work, please ensure it is from the most recent iteration of the game!
  
  *goto input_or_play

Extract Numbers from Input and Set Variables

Now, we can use our dictionary/array of variables inputVariable to extract and check to ensure that the values are possible (within the acceptable range). We can accomplish this with the following:

*label extract_numbers_from_string

*temp i 1
*temp j 0
*temp extractVar 1
*temp subString ""
*temp extractedNumber
*temp currentVarName

*label loop
*set subString & (inputString#{i})
*set i + 1
*set j + 1

*if j = variableLength
  *set extractedNumber (subString + 0)
  *set currentVarName (inputVariable[extractVar])
  *if ((extractedNumber < 0) or (extractedNumber > ({currentVarName & "_count"})))
    ERROR: Variable "${currentVarName}" out of acceptable range. Please re-enter your generation code to ensure there are no-typos. If it still doesn't work, please ensure it is from the most recent iteration of the game!

    *goto input_or_play
  *else
    *set n[currentVarName] extractedNumber
    *set t[currentVarName] {(currentVarName & "_") & extractedNumber}
    *set subString ""
    *set extractVar + 1
    *set j 0
    *goto check

*label check
*if extractVar <= inputVariable_count
  *goto loop
*else
  *return

Since the code might be difficult to understand, I will explain what the variables are:

  • i - keeps track of the current position in the input string
  • j - keeps track of the current position in the substring (always between 1 and variableLength)
  • extractVar - keeps track of which variable is being extracted (as a number)
  • subString - stores the portion of the input string making up the extractVar
  • extractedNumber - stores the complete substring for an extractVar converted to a numeric value
  • currentVarName - stores the string that describes the name of the current extractVar

Now, I will explain what this part does in words.

  1. First we initialize all of the variables above with default values. Then, we begin the loop.
  2. Start by adding the i-th character of inputString to subString.
  3. Next, increment i by one so we reference the next character.
  4. Next, increment j by one to show indicate the subString has gotten 1 character longer.
  5. If j, the length of the subString has gotten to be as long as we specified in variableLength then we have the entire value and can proceed to step 6. Otherwise proceed to step 13.
  6. We set extractedNumber equal to the numeric value of subString.
  7. If the extractedNumber is negative or greater than the number of options for that variable, throw an error and return to the page to choose whether to input a string. Otherwise, the extractedNumber is valid and we continue to step 8.
  8. Save the extractedNumber to the corresponding variable.
  9. Save the string corresponding to extractedNumber to the corresponding variable.
  10. Reset the subString to "".
  11. Increment the extractVar by one to allow us to extract the next variable.
  12. Reset j to 0.
  13. If we have extracted all of the variables specified by inputVariable_count end the loop and *return to where the subroutine was called from. If we have not, return to step 2.

Skip Past Variable Sets

Below is are the *fake_choice variable sets that you would skip by inputting the string.

*label play_choices

Choose a catbreed:
*fake_choice
  #${catbreed[1]}
    *set n_catbreed 1
    *set t_catbreed (catbreed[1])
  #${catbreed[2]}
    *set n_catbreed 2
    *set t_catbreed (catbreed[2])
  #${catbreed[3]}
    *set n_catbreed 3
    *set t_catbreed (catbreed[3])
  .
  .
  .
  #${catbreed[20]}
    *set n_catbreed 20
    *set t_catbreed (catbreed[20])

Choose a dogbreed:
*fake_choice
  #${dogbreed[1]}
    *set n_dogbreed 1
    *set t_dogbreed (dogbreed[1])
  #${dogbreed[2]}
    *set n_dogbreed 2
    *set t_dogbreed (dogbreed[2])
  #${dogbreed[3]}
    *set n_dogbreed 3
    *set t_dogbreed (dogbreed[3])
  .
  .
  .
  #${dogbreed[8]}
    *set n_dogbreed 8
    *set t_dogbreed (dogbreed[8])

*comment you can also include a "*goto skipped_choices" here if desired

And here is the *label that you jump to by inputting the string.

*label skipped_choices
This is the text after the choices.

Cat breed: ${t_catbreed}

Dog breed: ${t_dogbreed}
*finish

Putting it all together

To construct a minimum working example that will work when copied and pasted into startup, the missing dog and cat breeds were omitted leaving us with four of each. This code will work if the original 20 cat breeds and 8 dog breeds are specified, though.

*title Pulling numbers from a string? v2
*author Joe S. Fung

*scene_list
    startup

*create_array catbreed 4 "domestic shorthair" "domestic longhair" "persian" "sphinx"
*create_array dogbreed 4 "shitzu" "chihauhua" "pitbull" "german shepard"

*create inputString ""
*create variableLength 2
*create_array inputVariable 2 "catbreed" "dogbreed"
*create t_catbreed "???"
*create n_catbreed 0
*create t_dogbreed "???"
*create n_dogbreed 0

*label input_or_play
Do you want to input a save string or play the choices?
*choice
  #Input a save string.
    *goto input_string
  #Play the choices.
    *goto play_choices


*label play_choices

Choose a catbreed:
*fake_choice
  #${catbreed[1]}
    *set n_catbreed 1
    *set t_catbreed (catbreed[1])
  #${catbreed[2]}
    *set n_catbreed 2
    *set t_catbreed (catbreed[2])
  #${catbreed[3]}
    *set n_catbreed 3
    *set t_catbreed (catbreed[3])
  #${catbreed[4]}
    *set n_catbreed 4
    *set t_catbreed (catbreed[4])

Choose a dogbreed:
*fake_choice
  #${dogbreed[1]}
    *set n_dogbreed 1
    *set t_dogbreed (dogbreed[1])
  #${dogbreed[2]}
    *set n_dogbreed 2
    *set t_dogbreed (dogbreed[2])
  #${dogbreed[3]}
    *set n_dogbreed 3
    *set t_dogbreed (dogbreed[3])
  #${dogbreed[4]}
    *set n_dogbreed 4
    *set t_dogbreed (dogbreed[4])

*comment you can also include a "*goto skipped_choices" here if desired

*label skipped_choices
This is the text after the choices.

Cat breed ${n_catbreed}: ${t_catbreed}

Dog breed ${n_dogbreed}: ${t_dogbreed}
*finish

*comment =================================================
*comment Subroutines
*comment =================================================

*label input_string
Please input your character string:
*input_text inputString

*temp n (length(inputString))

*if n = (variableLength * inputVariable_count)
  *gosub extract_numbers_from_string
  *goto skipped_choices
*else
  ERROR: Invalid save string length. Please re-enter your generation code to ensure there are no-typos. If it still doesn't work, please ensure it is from the most recent iteration of the game!
  
  *goto input_or_play


*label extract_numbers_from_string

*temp i 1
*temp j 0
*temp extractVar 1
*temp subString ""
*temp extractedNumber
*temp currentVarName

*label loop
*set subString & (inputString#{i})
*set i + 1
*set j + 1

*if j = variableLength
  *set extractedNumber (subString + 0)
  *set currentVarName (inputVariable[extractVar])
  *if ((extractedNumber < 0) or (extractedNumber > ({currentVarName & "_count"})))
    ERROR: Variable "${currentVarName}" out of acceptable range. Please re-enter your generation code to ensure there are no-typos. If it still doesn't work, please ensure it is from the most recent iteration of the game!

    *goto input_or_play
  *else
    *set n[currentVarName] extractedNumber
    *set t[currentVarName] {(currentVarName & "_") & extractedNumber}
    *set subString ""
    *set extractVar + 1
    *set j 0
    *goto check

*label check
*if extractVar <= inputVariable_count
  *goto loop
*else
  *return

Sorry for the wall of text. Hopefully this answers all of your questions.

1 Like

As promised, here is how one might generate the save strings.

Simply modify the above minimum working example by replacing the code from *label skipped_choices to *finish with:

*label skipped_choices
This is the text after the choices.

Cat breed ${n_catbreed}: ${t_catbreed}

Dog breed ${n_dogbreed}: ${t_dogbreed}

*temp generatedSaveString ""
*gosub generate_save_string
The save string is: ${generatedSaveString}

*finish

Then, append this code at the bottom of the subroutines section.

*label generate_save_string
*temp k 1

*label loop2
*if n[inputVariable[k]] < 10
  *set generatedSaveString & "0"
*set generatedSaveString & (n[inputVariable[k]])
*set k + 1

*if k <= inputVariable_count
  *goto loop2
*else
  *return

This results in the following behavior:



OR




1 Like