Non Repeating Random without Infinite Loop Error

Discuss how to use the Ren'Py engine to create visual novels and story-based games. New releases are announced in this section.
Forum rules
This is the right place for Ren'Py help. Please ask one question per thread, use a descriptive subject like 'NotFound error in option.rpy' , and include all the relevant information - especially any relevant code and traceback messages. Use the code tag to format scripts.
Post Reply
Message
Author
User avatar
Sword of Akasha
Regular
Posts: 61
Joined: Sun Dec 14, 2014 6:51 am
Projects: Shadows of Shattered Dreams
Organization: Akashic Creative Studios
Deviantart: Sword-of-Akasha
Contact:

Non Repeating Random without Infinite Loop Error

#1 Post by Sword of Akasha »

Hi, I've been tearing my hair out trying to figure out a solution to this issue I've been having. In my game there's randomize lorecards in the intro that show up. I want them to be random and non repeating. Eventually I reach a point I get an infinite loop error since it get caught up with old integers and eventually the chance to get a repeat loop is higher than not.

I'd really appreciate any help. Thanks in advance for anyone looking. Is there a more elegant way of doing this that I've missed?

Code: Select all

    label morelore:  
    if lorecardplayed == 25:    
        $ lorecardplayed += 1
        $ renpy.block_rollback()
        scene black with dissolve
        show freya curious
        freya aww "\"Thy curiosity, tis commendable.\""
        show freya coy
        $ renpy.block_rollback()
        scene black with dissolve
    if lorecardplayed == 50:    
        $ lorecardplayed += 1
        $ renpy.block_rollback()
        scene black with dissolve
        show freya curious
        freya curious "\"Thou art the curious sort.\""
        show freya coy
        pause (1)
        $ renpy.block_rollback()
        scene black with dissolve
        jump morelorepart2
        
    $ lorecard =  renpy.random.randint(1,87)
    if lorecard == 1:
        if lorecard1  == True:
            scene black
            jump morelore
        $ lorecard1 = True
        $ lorecardplayed += 1
        show card1
    elif lorecard == 2:
        if lorecard2  == True:
            scene black
            jump morelore
        $ lorecard2 = True
        $ lorecardplayed += 1
        show card2
    elif lorecard == 3:
        if lorecard3  == True:
            scene black
            jump morelore
        $ lorecard3 = True
        $ lorecardplayed += 1
        show card3
    elif lorecard == 4:
        if lorecard4  == True:
            scene black
            jump morelore
        $ lorecard4 = True
        $ lorecardplayed += 1
        show card4

GOES ON FOR 87 CARDS UNTIL THE END.
     
    else:
        jump morelore
        
        
Image

rames44
Veteran
Posts: 233
Joined: Sun May 29, 2016 4:38 pm
Contact:

Re: Non Repeating Random without Infinite Loop Error

#2 Post by rames44 »

As a suggestion, why not create an array of the cards you haven’t issued. (Or just their indices) This is your “yet to be used deck.”

Pick a random number between 0 and “current length of the array.” Remove the card (or card index) at that index in the array, shrinking the array. (And thus reducing the random range you use next time)

This way, as long as you have entries in the array, you can pick an unused card without having to discard preciously-used random numbers, since each time the random number will index an unused card.

Away from my computer, so I can’t write the actual code for you, but hopefully you get the idea.

User avatar
Sword of Akasha
Regular
Posts: 61
Joined: Sun Dec 14, 2014 6:51 am
Projects: Shadows of Shattered Dreams
Organization: Akashic Creative Studios
Deviantart: Sword-of-Akasha
Contact:

Re: Non Repeating Random without Infinite Loop Error

#3 Post by Sword of Akasha »

I'm grateful for the swift response. That's an interesting proposal. I'm not sure how to accomplish that. I have my code in the splashscreen portion.

Whenever you have a moment would you be able to help me out with that? Although I've done an insane amount of art and writing, coding is still difficult for me. I've already searched the renpy forums and haven't found anything like what you said.
Image

kivik
Miko-Class Veteran
Posts: 786
Joined: Fri Jun 24, 2016 5:58 pm
Contact:

Re: Non Repeating Random without Infinite Loop Error

#4 Post by kivik »

Can I ask when you say non-repeating, what's supposed to happen when all the cards have been shown? And am I right in understanding that you're trying to do this across play sessions? i.e. show a different card each time I launch your game?

If you want this across play sessions, then you'll need to use a persistent variable to make this work, because otherwise the game will forget everytime at relaunch. You can learn more about persistent variables here: https://www.renpy.org/doc/html/persistent.html

Regarding arrays that rames44 suggested, you can basically use a python list to story your list of cards, and use the renpy.random.choice() function to pick one of the items from your list at random and do stuff with it. The benefits is that you won't have to write out your 87 condition statements, store True or False values of whether you've seen a card already, worry about infinite loops, and wasted processing power going to unused random chances.

python list works something like this:

Code: Select all

default cards = ["card1", "card2", "card3", etc...]
The square brackets denotes the list, the quoted values (e.g. "card1") are string (text) values, separated by commas, separating each string as a different item on the list

Python comes with really cool shorthands that allows you to prepopulate lists like this easily:

Code: Select all

$ cards = ["card%s" % x for x in range(1,87)]
It basically creates the list above by cycling through a loop from 1 to 87 and creating a value "card(n)" where (n) is the value 1 to 87.


Now armed with this list called cards, you can use renpy.random.choice() on it like so:

Code: Select all

$ card = renpy.random.choice(cards)
Now card will contain 1 of the 87 cards in your list.

You can then get rid of the card from your cards by using the remove() function:

Code: Select all

$ cards.remove(card)
So to combine everything above, this is a quick demo of showing the 87 cards at random in succession:

Code: Select all

label start:
    $ cards = ["card%s"% x for x in range(1,87)]
    while len(cards):
        $ card = renpy.random.choice(cards)
        $ cards.remove(card)
        show expression card
Note: I used show expression to show the card based on the card variable: https://www.renpy.org/doc/html/displayi ... -statement (scroll down to show expression)

If you can explain whether this is meant to be done across play sessions, I can add more code to make it work that way for you. I just wanted to break down the little individual coding mechanics first to ease you into understanding what the code's doing.

User avatar
Remix
Eileen-Class Veteran
Posts: 1628
Joined: Tue May 30, 2017 6:10 am
Completed: None... yet (as I'm still looking for an artist)
Projects: An un-named anime based trainer game
Contact:

Re: Non Repeating Random without Infinite Loop Error

#5 Post by Remix »

You might also note there are methods of the python random module that might make things a lot easier, the primary one being sample() which returns x number of distinct choices from a list.
Once they are chosen, you can then rebuild your initial list by leaving out the chosen items.

Note: we have to address the method through renpy.python.rng as it is not normally exposed afaik

Code: Select all

label start:

    $ opts = range(100)
    $ s = renpy.python.rng.sample( opts, 6 )
    $ opts = [ k for k in opts if k not in s ]
    $ nl = len(opts)
    "Chose: [s!q]\nOptions Left: [nl]"

    $ s = renpy.python.rng.sample( opts, 8 )
    $ opts = [ k for k in opts if k not in s ]
    $ nl = len(opts)
    "Chose: [s!q]\nOptions Left: [nl]"
If you just wanted one item, renpy.random.choice would do, then i.e. opts.remove( chosen_item )... as kivik posted while I wrote this... lol
Frameworks & Scriptlets:

User avatar
Sword of Akasha
Regular
Posts: 61
Joined: Sun Dec 14, 2014 6:51 am
Projects: Shadows of Shattered Dreams
Organization: Akashic Creative Studios
Deviantart: Sword-of-Akasha
Contact:

Re: Non Repeating Random without Infinite Loop Error

#6 Post by Sword of Akasha »

kivik wrote: Sun Jun 10, 2018 5:33 pm Can I ask when you say non-repeating, what's supposed to happen when all the cards have been shown? And am I right in understanding that you're trying to do this across play sessions? i.e. show a different card each time I launch your game?
Thanks for the reply, sorry if I forgot to elucidate better on what exactly I needed.

I was originally going to have the cards shown persistently where players could go through them in sessions but I I decided against it. I've got a menu that supposed to cycle through the cards. Players can otherwise select no and the game will be brought to the main menu.

I wanted to reward players by having characters comment if players were inquisitive and went through a certain number of cards.

I could upload a set of cards if that will help. They're just full screen images.

I've got a screen button where they can press to go through more cards while looking at the current cards..

Code: Select all


    call screen morelore
    
    $ result = _return
    if result == "yes":
        jump morelore
    elif result == "no":
        jump nomorelore
        
Image

philat
Eileen-Class Veteran
Posts: 1910
Joined: Wed Dec 04, 2013 12:33 pm
Contact:

Re: Non Repeating Random without Infinite Loop Error

#7 Post by philat »

My preference for non-repeating random is to shuffle a list and iterate through it. In this particular instance, the number of cards seen is basically the iterator.

Code: Select all

default card_indices = range(87)
default lore_seen = 0

label start:
    $ renpy.random.shuffle(card_indices)

label lore:
    if lore_seen <= len(card_indices) - 1:
        show expression "card" + str(card_indices[lore_seen])
        "[card_indices] / [lore_seen] # for debugging
        if lore_seen > 30:
            "You're the curious sort, aren't you?" # also easy to pop in something like this
        $ lore_seen += 1
        jump lore
    else:
        "No more lore cards available." 

User avatar
Sword of Akasha
Regular
Posts: 61
Joined: Sun Dec 14, 2014 6:51 am
Projects: Shadows of Shattered Dreams
Organization: Akashic Creative Studios
Deviantart: Sword-of-Akasha
Contact:

Re: Non Repeating Random without Infinite Loop Error

#8 Post by Sword of Akasha »

Thank you so very much, everyone, for your help.
philat wrote: Sun Jun 10, 2018 9:11 pm My preference for non-repeating random is to shuffle a list and iterate through it. In this particular instance, the number of cards seen is basically the iterator.
I think you found the most elegant solution. Bravo. You've saved me from going bald ahahhahha.

Suggestions for folks who find this thread and want to implement it in their splash screens. Quick menu appears whenever you want to have dialogue say a sassy hostess comment on the player's inquisitiveness :lol: . My workaround was to make the quick menu toggle. Solution for that seen below. Be sure to have the quick menu toggle back on for the main game.
viewtopic.php?t=39604

For me I changed the defaults to defines and put them atop my script. I also popped in my call screen inside the code. Here's my final code. I put in a little reward for players that got to the end. :wink:

Code: Select all

label splashscreen:
    $ show_quick_menu = False
    $ renpy.random.shuffle(card_indices)
    label morelore:
    if lore_seen <= len(card_indices) - 1:
        show expression "card" + str(card_indices[lore_seen])
        #debug testing "[card_indices] / [lore_seen]"
        if lore_seen == 25:    
            $ renpy.block_rollback()
            scene black with dissolve
            show freya curious
            freya aww "\"Thy curiosity, tis commendable.\""
            show freya coy
            $ renpy.block_rollback()
            scene black with dissolve
        if lore_seen == 50:    
            $ renpy.block_rollback()
            scene black with dissolve
            show freya curious
            freya curious "\"Thou art the curious sort.\""
            show freya coy
            pause (1)
            $ renpy.block_rollback()
            scene black with dissolve
        if lore_seen == 75:    
            $ renpy.block_rollback()
            scene black with dissolve
            show freya coy
            freya coy "\"Careful that thou do not learn too much. Remember thou that increaseth knowledge increaseth sorrow.\""
            scene black with dissolve     
        $ lore_seen += 1
        call screen morelore
        $ result = _return
        if result == "yes":
            jump morelore
        elif result == "no":
            jump nomorelore
    else:
        $ renpy.block_rollback()
        scene black with dissolve
        show freya blush
        freya blush "\"Thy appetite is insatiable.\""
        show freya coy
        freya coy "\"For thy dilligence, I might impart thee with a boon. What wouldst thou desire of me?\""
        menu:
            "A kiss!":
                show freya sarcasm
                freya sarcasm "\"Thou art too bold. Thy affections shall not be requited.\""
                jump nomorelore
            "A hug!":
                show freya sad
                freya sad "\"Alas I cannot... know that is no fault of yours.\""
                freya gentle "\"Be not afraid, thou art naught alone for I shall be in your constant company.\""
                jump nomorelore
            "Advice.":
                show freya worried
                freya worried "\"Be mindful of the shadows.\""
                freya nervous "\"Beware of those without.\""
                freya worried "\"Thy journey shall be one fraught with much misery.\""
                show freya gentle
                freya gentle "\"However, I believe in thee. Prithee, go safely.\""
                jump nomorelore

Image

Post Reply

Who is online

Users browsing this forum: Google [Bot]