*Rand values (traits) repeating twice help

Hmm alright, I’ll break the code down and try to explain how it works bit by bit. I’ll also only leave the essentials. It’s gonna be long but I’ll try my best to break it down. If you still don’t feel comfortable in using it, I don’t mind. I had fun making it.

Here is the simplified code, I removed the search and made only one type of display available. This one is 159 lines.

image

*title Trait randomizer
*author GS

*create implicit_control_flow true

*create trait_1 "Compassionate"
*create trait_2 "Tall"
*create trait_3 "Strong"
*create trait_4 "Pretty"
*create trait_5 "Patient"
*create trait_6 "Smart"
*create trait_7 "Diligent"
*create trait_8 "Just"
*create trait_9 "Honest"
*create trait_10 "Shy"
*create trait_11 "Young"
*create trait_12 "Greedy"
*create trait_13 "Brave"
*create trait_14 "Groomed"
*create trait_15 "Stubborn"
*create trait_16 "Fussy"
*create trait_17 "Curious"
*create trait_18 "Playful"
*create trait_19 "Quick"
*create trait_20 "Humble"

*create trait_quantity 20
*create char_quantity 10
*create allowed_traits 3

*create char_1_1 "Michelle"
*create char_1_2 ""
*create char_1_3 ""
*create char_1_4 ""

*create char_2_1 "John"
*create char_2_2 ""
*create char_2_3 ""
*create char_2_4 ""

*create char_3_1 "Kevin"
*create char_3_2 ""
*create char_3_3 ""
*create char_3_4 ""

*create char_4_1 "Erica"
*create char_4_2 ""
*create char_4_3 ""
*create char_4_4 ""

*create char_5_1 "Jackson"
*create char_5_2 ""
*create char_5_3 ""
*create char_5_4 ""

*create char_6_1 "Samuels"
*create char_6_2 ""
*create char_6_3 ""
*create char_6_4 ""

*create char_7_1 "Merlin"
*create char_7_2 ""
*create char_7_3 ""
*create char_7_4 ""

*create char_8_1 "Stanley"
*create char_8_2 ""
*create char_8_3 ""
*create char_8_4 ""

*create char_9_1 "Waits"
*create char_9_2 ""
*create char_9_3 ""
*create char_9_4 ""

*create char_10_1 "Ripley"
*create char_10_2 ""
*create char_10_3 ""
*create char_10_4 ""

*choice
	#Randomize traits.
		*gosub randomizer

*gosub show_chars_simpler

*finish

*comment =============SHOW CHARACTERS SIMPLER===============
*label show_chars_simpler
*temp counter 0
*temp trait_counter 2
*label show_chars_simpler_loop
*set counter + 1

*if (counter > char_quantity)
	*return

==================
*line_break
${char[counter][1]}'s traits (Character id: ${counter})
*line_break
==================

*label show_chars_simpler_traits_loop
*if (trait_counter > "${allowed_traits+1}")
	*goto show_chars_simpler_loop
-${char[counter][trait_counter]}
*line_break
*if (trait_counter > allowed_traits)
	*set trait_counter 2
	*goto show_chars_simpler_loop
*else
	*set trait_counter + 1
	*goto show_chars_simpler_traits_loop


*comment =============RANDOMIZER===============
*label randomizer
*temp new_trait 0
*temp char_counter 1
*temp trait_counter 2
*label randomizer_loop
*temp proceed false
*rand new_trait 1 trait_quantity

*if (char_counter > char_quantity)
	*return
*else
	*gosub check_existent char_counter new_trait
	*if (trait_counter > "${allowed_traits+1}")
		*set char_counter + 1
		*set trait_counter 2
		*goto randomizer_loop
	*else
		*if (proceed)
			*set char[char_counter][trait_counter] trait[new_trait]
			*set trait_counter + 1
			*goto randomizer_loop
		*else
			*goto randomizer_loop

*comment =============CHECK EXISTENT TRAIT===============
*label check_existent
*params char_to_check trait_being_added
*temp check_counter 1
*label check_existent_loop
*set check_counter + 1
*if (check_counter = "${allowed_traits+1}")
	*set proceed true
	*return
*else
	*if ("$!!{char[char_to_check][check_counter]}" = "$!!{trait[trait_being_added]}")
		*set proceed false
		*return
	*else
		*goto check_existent_loop

*return


To start off, let’s go over the variable creation:

*create trait_1 "Compassionate"
*create trait_2 "Tall"
*create trait_3 "Strong"
*create trait_4 "Pretty"
*create trait_5 "Patient"
*create trait_6 "Smart"
*create trait_7 "Diligent"
*create trait_8 "Just"
*create trait_9 "Honest"
*create trait_10 "Shy"
*create trait_11 "Young"
*create trait_12 "Greedy"
*create trait_13 "Brave"
*create trait_14 "Groomed"
*create trait_15 "Stubborn"
*create trait_16 "Fussy"
*create trait_17 "Curious"
*create trait_18 "Playful"
*create trait_19 "Quick"
*create trait_20 "Humble"

This part is creating an array of 20 elements. An array is like a list that holds objects which you can get by index. To use this, you use the name of the variable and square brackets right after it. Like this: variable[index].

(For a short explanation about arrays, check this video.) (In ChoiceScript arrays start at 1 unlike what he is using there though.)

For example, trait[3] would contain “Strong”. trait[14] is “Groomed”.

The good thing about this is that you can use variables for the index as well. Suppose I have a variable called var1:

*create var1 17

And that var1 equals 17. I could use this variable inside those square brackets too. If I used trait[var1] it would be the same as trait[17] which contains the “Curious” trait. This allows you to access things in your list more dynamically which will be very useful because it allows the code to accomodate however many traits you want and “loop” through the list when checks need to be made.

Now let’s see the character declaration:

*create char_1_1 "Michelle"
*create char_1_2 ""
*create char_1_3 ""
*create char_1_4 ""

*create char_2_1 "John"
*create char_2_2 ""
*create char_2_3 ""
*create char_2_4 ""

*create char_3_1 "Kevin"
*create char_3_2 ""
*create char_3_3 ""
*create char_3_4 ""

This one might be a little more complicated to explain but I’ll try. We are going to use multidimensional arrays (in this case two-dimensional).

What we are doing here is making a character that will contain 4 things. A name and 3 trait slots. For this it’s easier to imagine a table of sorts.

image

The “ID” is always the number inside the first brackets. char[THIS][].

The rest of the attributes (name, trait slots) would always be the things inside the second brackets. char[][THIS].

And when our “table” would be filled with traits it would be like:

image

To get things here we would need to use two square brackets. I think to better explain this you can imagine as each block of 4 things as one character. The first square brackets will be the character’s “id” and the second set of square brackets what you want to select from it (the name or the traits).

So say I want to select John’s variable that stores his name. I would need to use char[2][1]. The first square brackets will go to the character in question, which in this case all attributes of John’s is of “id” number 2. If I wanted to get Kevin’s name I would use char[3][1] and so on.

For now the traits are empty, but say I want to see what is Michelle’s second trait, I would use char[1][3] since I’m going for character of “id” 1 which is Michelle and then getting the second trait slot (which is index 3 in that example). It would be empty now but it will be randomized later.


So the actual randomization is done in the *gosub randomizer:

*comment =============RANDOMIZER===============
*label randomizer
*temp new_trait 0
*temp char_counter 1
*temp trait_counter 2
*label randomizer_loop
*temp proceed false
*rand new_trait 1 trait_quantity

*if (char_counter > char_quantity)
	*return
*else
	*gosub check_existent char_counter new_trait
	*if (trait_counter > "${allowed_traits+1}")
		*set char_counter + 1
		*set trait_counter 2
		*goto randomizer_loop
	*else
		*if (proceed)
			*set char[char_counter][trait_counter] trait[new_trait]
			*set trait_counter + 1
			*goto randomizer_loop
		*else
			*goto randomizer_loop

The code will use the arrays from both the trait list and the characters to randomize all traits from all characters in one swoop. It will be first a loop through all the traits of one character, then it will proceed to the next character and assign their traits and so on, as in looping through all available characters.

First what it’s doing is creating three variables:

  • new_trait = This is the variable that will hold the randomized trait
  • char_counter = Since we will loop through the characters, we will need a counter for the characters. This will start at 1 since the first “id” we have for our characters starts at 1.
  • trait_counter = Since we will also loop through trait slots of a character, we need a counter for them as well. This will start at 2 because the trait slots for everyone start at index 2 (since index 1 of everybody holds their name).

Alright, after making those variables it will also create a proceed variable, but let’s ignore that one as it’s not important right now.

Now we apply a *rand to our new_trait variable to randomize it to a specific number between 1 and the amount of traits possible (in the example I’ve made that would be 20). So after new_trait is randomized, it will have a value between 1 to 20.

Right after that we have a big *if chain. The first condition is to treat the character counter, but since we didn’t fiddle with it for now, we can ignore it. Since this is the first character this won’t be passed now anyways.

The code will get inside the *else and it will hit the *gosub check_existent char_counter new_trait. What this is doing is calling the *gosub called check_existent and passing our char_counter variable and our new_trait variable as parameters. From what we saw when we created them above, their current values are 1 and something between 1-20 respectively.

That *gosub is where the trait is checked to make sure it’s not a trait the character already has.

*comment =============CHECK EXISTENT TRAIT===============
*label check_existent
*params char_to_check trait_being_added
*temp check_counter 1
*label check_existent_loop
*set check_counter + 1
*if (check_counter = "${allowed_traits+1}")
	*set proceed true
	*return
*else
	*if ("$!!{char[char_to_check][check_counter]}" = "$!!{trait[trait_being_added]}")
		*set proceed false
		*return
	*else
		*goto check_existent_loop

*return

It starts by assigning both our parameters as char_to_check and trait_being_added in the same order we called them. So this *gosub will make these two variables in its scope:

  • char_to_check = This is the char_counter we passed before, which equals to 1.
  • trait_being_added = This is the new_trait variable we randomized before, which is somewhere between 1 and 20. For easier to explain purposes, let’s assume the random result was 6, which equals “Smart” in our list.

Now what we need to do is to check all the trait slots of this character to see if the character already has this trait that was just randomized. (Since this is the first trait that we are going to assign to Michelle, we could just have assigned it instead of checking but I forgot to account for that, oops! But the code will work and check it either way.)

Since we are going to be looping through the character trait slots, we need a counter for that, so we create a check_counter. It starts at 1 because it will be added right after +1 after our loop label, so that it starts at 2 for the first check, with 2 being the index of first slot traits for everyone.

Now we get to the first condition, *if (check_counter = "${allowed_traits+1}"), this will not be triggered now because this is a condition to see if we reached the number of available slots for our characters with our counter. Since we did not yet reach that (as our counter is 2 and the allowed_traits+1 is 4, we can still run this a few times).

Since that condition will fail, we get to the *else, and this is where the important part is at. The following condition:

*else
	*if ("$!!{char[char_to_check][check_counter]}" = "$!!{trait[trait_being_added]}")
		*set proceed false
		*return
	*else
		*goto check_existent_loop

Is where we check if the trait exists in a trait slot. Let’s break it down to parts:

  1. "$!!{char[char_to_check][check_counter]}" = What we are getting here is the content of the variable that would be of the character, in this case we are still checking Michelle, so char_to_check is 1. So what we have now would be like char[1][check_counter]. check_counter is what we made in this *gosub, that starts at 1 and then receives +1.
*temp check_counter 1
*label check_existent_loop
*set check_counter + 1
  1. (cont) So this means our variable would be char[1][2] right now. This means that what’s on the left of the condition on the first run would be char[1][2]. So the first thing being checked in our *if would be this: "$!!{char[1][2]}". This is the content of Michelle’s first trat slot. Which now is empty but there will be something there in the next runs.

(OBS: You may notice there is a $!!. This is to make sure the trait being compared is in ALL CAPS. This is better for comparisons as if a trait is typed as “Smart” or “smART” they will all be made into caps to make sure the upper or lower case doesn’t matter, but this isn’t really necessary for this code, just a little bonus.)

Now we need to decipher the second part of this *if condition, the "$!!{trait[trait_being_added]}".

  1. "$!!{trait[trait_being_added]}" = Now we are messing with the trait list, which we have 20 elements. The trait_being_added is a variable that we got from passing the new_trait as a parameter, which is our randomized one between 1 to 20. Like I said before we will consider the result of the randomization as 6. So this would be "$!!{trait[6]}". If we go into our trait list and we check what trait[6] is, it would be “Smart”.

So, in all, our condition during runtime would be:

*if ("" = "Smart")

The “” is because the first trait slot of Michelle (char[1][2]) is currently empty. So this means our condition would fail and go to the *else.

Now let’s see what happens after this *if has its result.

*if ("" = "Smart")
	*set proceed false
	*return
*else
	*goto check_existent_loop

If the result is:

  • true = This means that the character already has the trait we randomized. The code will enter what is below the *if. It will set a variable called proceed as false and then *return (break out of the *gosub it’s in) to go back to the previous *gosub.
  • false = This means that this trait slot we checked does NOT have the current trait we randomized. Beware that this is the trait SLOT, not the entire character. Since we only checked the first trait slot, we need to go back to the loop, which adds +1 to the check_counter so that the next slot is checked this time (as in char[1][3] is checked). This will make the code check the same condition again but with all traits.

If at any time in the loop this true condition is never reached (eventually the check_counter will be higher than the allowed_traits so we need to stop it as we checked all trait slots), this means that the character we checked does not have the trait we randomized. This means that we are now free to assign this trait. If the true is reached, the character already has this trait and it needs to be re-randomized again.

*if (check_counter = "${allowed_traits+1}")
	*set proceed true
	*return

The proceed variable is the key here, it will be set to true when we verify that this trait is not located in any of the slots.

Now we will have *returned to the randomizer *gosub carrying the value of our proceed variable.

	*gosub check_existent char_counter new_trait
	WE ARE NOW HERE <--------------------------------
	*if (trait_counter > "${allowed_traits+1}")
		*set char_counter + 1
		*set trait_counter 2
		*goto randomizer_loop
	*else
		*if (proceed)
			*set char[char_counter][trait_counter] trait[new_trait]
			*set trait_counter + 1
			*goto randomizer_loop
		*else
			*goto randomizer_loop

The check there is to see if we already went over the amount of traits for a character by checking this *gosub's trait_counter, but we have not since this is still slot 2 (which there are 4). This means this condition will not yet be triggered so we enter the *else

	*else
		*if (proceed)
			*set char[char_counter][trait_counter] trait[new_trait]
			*set trait_counter + 1
			*goto randomizer_loop
		*else
			*goto randomizer_loop

So this is when we check our proceed variable. If proceed is:

  • true = It means that this randomized trait was not found in the other trait slots of this character, therefore we can now safely assign this trait to the slot we are currently working with and then continue the randomizer loop to do all this over again to the next trait slot of this character. Therefore we assign the trait[new_trait] (with new_trait being the randomized variable we got, in this case 6) to the current slot *set char[char_counter][trait_counter] with char_counter still being 1 for Michelle and the trait_counter being 2 since it’s our first slot. So we have *set char[1][2] trait[6] which would be *set char[1][2] "Smart". This menans now that “Smart” is Michelle’s first trait.
  • false = It means that the character already has the trait we randomized. So we need to go back to the loop without adding anything as it will re-do all the work for this same trait slot. It will re-randomize it again and compare if the char already has it until it manages to randomize something that the char doesn’t have.

So after all the trait slots for this character are done, then it will go to the next character and start the process again in the:

*if (trait_counter > "${allowed_traits+1}")
	*set char_counter + 1
	*set trait_counter 2
	*goto randomizer_loop

By adding 1 to char_counter and resetting the trait_slot to randomize to 2 (which is everyone’s first trait slot). So then it would go with (char[2][2]) which are John’s slots and after he finishes move on to the next character.

Since this code uses loops and stuff it can fit whatever changes you want by altering those control variables (trait_quantity, char_quantity and allowed_traits).

This is pretty much it, hopefully I could explain it well but I do understand it’s quite complex since it’s a lot of loops with other more complicated stuff. There is a section to list all characters but it’s pretty much a loop like the previous ones; I can still explain it if anyone wishes. I can also explain the search and add it back to the code if you want (it functions very similarly with the check to see if a character already has a trait that was randomized).

9 Likes