Ren'Py Can't figure out how to make the game recognize duplicates

rb813

Well-Known Member
Aug 28, 2018
1,217
778
I'm working on a little deduction thing where some variables are arranged randomly in a list, and you're trying to figure out what order they're in. The clues come in the form of comparative statements, i.e. "First_Example is ranked higher than Second_Example." That part's easy, I just generate two random numbers and then have strings that point to that number's place in the list...

a = renpy.random.randrange(0,8)
c = renpy.random.randrange(0,8)
texta = ExampleList[a]
textc = ExampleList[c]
if a < c:
"[texta] is ranked higher than [textc]"

...And so forth. I don't know if that's necessarily the best way to do it, but that's not what I'm concerned about right now. The problem is, when I run a loop of a bunch of those statements (say 20), I see more than a couple duplicates. So I was trying to figure out how to have it check for duplicates before it said the clue, but apparently I'm missing something here (I'm much more familiar with C# than ren'py), because I can't figure out how to make that work. The last thing I tried was putting the two random numbers together as a tuple, then checking whether that tuple was in a different list, and adding the tuple to that list (and moving forward with the clue) if it wasn't. However, even when I set it up so that the random numbers would be the same every time (set the parameters to 0,1 instead of 0,8), it still didn't read them as being duplicates.

What am I missing here? I understand that I'm very novice when it comes to python, but this really doesn't seem like it should be that hard.
 

scrumbles

Well-Known Member
Jan 12, 2019
1,069
1,096
Both solutions run on Python 2.7 and return a list of tuples.
Edit: they don't generate any duplicate, I thought you wanted to avoid them. Apologies if I misunderstood your question.
Code:
#!/usr/bin/env python
import random

RANGE_SIZE = 8
NUMBER_OF_TUPLES_TO_EXTRACT = 20
extracted_tuples = []

# ########## #
# Solution 1 #
# ########## #

# generate a list of all possible tuples
unextracted_tuples = list((i,j) for i in range(RANGE_SIZE) for j in range(RANGE_SIZE))
# this returns: [(0,0), (0.1), (0,2), ..., (1,0), (1,1), ..., (2,0), (2,1), ..., (RANGE_SIZE-2, RANGE_SIZE-1), (RANGE_SIZE-1, RANGE_SIZE-1)]

# the previous statement is equivalent to:
# unextracted_tuples = []
# for i in range(RANGE_SIZE):
#   for j in range(RANGE_SIZE):
#       unextracted_tuples.append((i,j))
# but it's more pythonic ;)

# start the loop and extract some tuples
for i in range(NUMBER_OF_TUPLES_TO_EXTRACT):
    # extract a tuple and move it from one list to another
    extracted_tuples.append(unextracted_tuples.pop(random.randrange(len(unextracted_tuples))))
    # the previous statement is equivalent to:
    # 1. select a valid array index from the unextracted tuples list
    # random_id = random.randrange(len(unextracted_tuples))
    # 2. store the corresponding tuple
    # extracted_tuples.append(unextracted_tuples[random_id])
    # 3. remove the extracted tuple to avoid any duplicate
    # unextracted_tuples.pop(random_id)
    # Since pop() returns the removed item, the three statements can be merged

# ########## #
# Solution 2 #
# ########## #

# start the loop and extract some tuples
for i in range(NUMBER_OF_TUPLES_TO_EXTRACT):
    while True:
        # generate a tuple
        random_tuple = (random.randrange(RANGE_SIZE), random.randrange(RANGE_SIZE))
        if not (random_tuple in extracted_tuples):
            extracted_tuples.append(random_tuple)
            break
 
Last edited:

anne O'nymous

I'm not grumpy, I'm just coded that way.
Modder
Donor
Respected User
Jun 10, 2017
10,979
16,236
Simply use a for the storing, they take care of duplication to only have one occurrence of each entry. And like they aren't ordered, store the result of the randomization into a :

Code:
default baseList = set( [] )

label whatever:
    $ baseList.add( "something" )
    $ baseList.add( "another thing" )
    $ baseList.add( "another thing" )

    $ randList =renpy.random.shuffle( baseList )
Adapt to your own needs.
 

79flavors

Well-Known Member
Respected User
Jun 14, 2018
1,611
2,258
Since you're generating random numbers, sometimes those random numbers will be the same.

Is this more what you're looking for?

Python:
    $ a = renpy.random.randrange(0,8)

label get_another_random_c:

    $ c = renpy.random.randrange(0,8)
    if a == c:
        jump get_another_random_c

    $ texta = ExampleList[a]
    $ textc = ExampleList[c]
    if a < c:
        "[texta] is ranked higher than [textc]"
    else:
        "[texta] is NOT ranked higher than [textc]"

Part of me wants to suggest using if a <= c: instead of if a < c: - but I'm not sure that's actually needed in your example.

RenPy also has a quirky thing with random numbers. In some circumstances, it builds up the list of future random numbers ahead of time - It stops players save-scumming their way past random events. I don't think that would be relevant here either, but it's worth being aware of.

Something else to consider is a variation on what Anne suggested... $ renpy.random.shuffle(ExampleList). A quick google for python lists and 15 minutes... I come up with this semi random code...

Python:
default ExampleList = ["apple", "banana", "cherry", "elderberry", "grape", "kiwi", "lime", "mango", "peach"]
default myword = ""
default a = 0
default c = 0

label start:

    $ renpy.random.shuffle(ExampleList)

    scene black with fade

    "Start:.... "

    $ a = ExampleList.index("apple")
    $ c = ExampleList.index("mango")

    "Apple is at position [a] and Mango is at position [c].{p=0}First in the list is [ExampleList[0]], second is [ExampleList[1]] and last is [ExampleList[8]]"

    "I'm removing Elderberry right now, because I don't like Elderberries. Even though I don't know where it is stored in the list."
    $ ExampleList.remove("elderberry")

label get_more_words:

    if len(ExampleList) > 0:                 # len() is short for length. We're checking if it's not empty before continuing.
        $ myword = ExampleList.pop(0)        # remove first word in list, store it's value in "myword"
        "My new favorite word is [myword]"
        jump get_more_words

    "*** THE END ***"
    return

I suspect this isn't an actual answer to your question, but maybe one of the statements I've used to manipulate lists is. Or at least, points you in the right direction.
 
Last edited: