Brute Force generator

I just wrote a little Python 3 program to spit out every possible combination of a set of 99 characters.
It gets the job done, but I would be very interested in what you think of it.

I am just a few days into Python, so I would be grateful for even seemingly obvious advice.


import sys

# List of 99 characters and a blank string:

lib=["","0","1","2","3","4","5","6","7","8","9","A","B","C","D","E","F","G","H","I","J","K","L","M","N","O","P","Q","R","S","T","U","V","W","X","Y","Z","a","b","c","d","e","f","g","h","i","j","k","l","m","n","o","p","q","r","s","t","u","v","w","x","y","z","°","!","\"","§","$","%","&","/","(",")","=","ß","´","`","+","#","-",".",",",">","<","@","€","|","^","~","–","{","[","]","}","Ä","Ö","Ü","ä","ö","ü"]


# 10 counters for up to 10-digit-combinations:

counter0=-1
counter1=0
counter2=0
counter3=0
counter4=0
counter5=0
counter6=0
counter7=0
counter8=0
counter9=0

# Repetitive if-statements adding to the counters:

for i in range(sys.maxsize**99999):
    counter0+=1
    
    if counter0>99:
        counter0=counter0*0
        counter1+=1
        
    elif counter1>99:
        counter1=counter1*0
        counter2+=1
        
    elif counter2>99:
        counter2=counter2*0
        counter3+=1
        
    elif counter3>99:
        counter3=counter3*0
        counter4+=1
        
    elif counter4>99:
        counter4=counter4*0
        counter5+=1
        
    elif counter5>99:
        counter5=counter5*0
        counter6+=1
        
    elif counter6>99:
        counter6=counter6*0
        counter7+=1
        
    elif counter7>99:
        counter7=counter7*0
        counter8+=1
        
    elif counter8>99:
        counter8=counter8*0
        counter9+=1
        
    elif counter9>99:
        print("DONE.")
        
# Printing the translation from counters to character - and deleting the former output so it stays in one line:
        
    else:
        print(lib[counter0]+lib[counter1]+lib[counter2]+lib[counter3]+lib[counter4]+lib[counter5]+lib[counter6]+lib[counter7]+lib[counter8]+lib[counter9], end="\r")
        sys.stdout.write("\b"*10+" "*10+"\b"*10)

Answer

  • We can convert a plain string to a list rather than maintain a list for the characters.

    It is far easier to modify and read the following then a list.

    lib = [''] + list('0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz°!"§$%&/()=ß´`+#-.,><@€|^~–{[]}ÄÖÜäöü')
    
  • When we have counter0, counter1, …, countern it’s a hint that we should use a list.

    counters = [-1, 0, 0, 0, 0, 0, 0, 0, 0, 0]
    

    We can then use counters[0] as a drop in replacement for counter0.

  • Now that we have counters which is a list we can simplify your print from the following.

    print(lib[counters[0]] + lib[counters[1]] + lib[counters[2]] + lib[counters[3]] + > lib[counters[4]] + lib[counters[5]] + lib[counters[6]] + lib[counters[7]] + lib[counters[8]] + lib[counters[9]], end="\r")
    

    We can use a for loop to loop through counters, index lib and print the character. We’ll use end="" to get the same format that you have. Since we’ve changed from "\r" to "" we’ll need to print that afterwards.

    for counter in counters:
        print(lib[counter], end="")
    print(end="\r")
    
  • It would be better to use len(lib) rather than hard coding 99 in your ifs. If we change the content of lib it’s much easier to edit just lib rather than lib and 10 99’s.

  • Rather than counter0=counter0*0 it would make more sense to remove the multiplication and just set the value to 0.

    counter0 = 0
    
  • It’s convention in Python to put a space either side of operators. This means a+b should instead be a + b. This is as it makes it easier to see what is and is not an operator and either side of an operator.

  • It’s convention in Python to use _ as a ‘throw away’ variable. This means it’s normal to use _ rather than i in your for loop.

Together this gets:

import sys

lib = [''] + list('0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz°!"§$%&/()=ß´`+#-.,><@€|^~–{[]}ÄÖÜäöü')
counters = [-1, 0, 0, 0, 0, 0, 0, 0, 0, 0]

for _ in range(sys.maxsize**99999):
    counters[0] += 1
    if counters[0] >= len(lib):
        counters[0] = 0
        counters[1] += 1
    elif counters[1] >= len(lib):
        counters[1] = 0
        counters[2] += 1
    elif counters[2] >= len(lib):
        counters[2] = 0
        counters[3] += 1
    elif counters[3] >= len(lib):
        counters[3] = 0
        counters[4] += 1
    elif counters[4] >= len(lib):
        counters[4] = 0
        counters[5] += 1
    elif counters[5] >= len(lib):
        counters[5] = 0
        counters[6] += 1
    elif counters[6] >= len(lib):
        counters[6] = 0
        counters[7] += 1
    elif counters[7] >= len(lib):
        counters[7] = 0
        counters[8] += 1
    elif counters[8] >= len(lib):
        counters[8] = 0
        counters[9] += 1
    elif counters[9] >= len(lib):
        print("DONE.")
    else:
        for counter in counters:
            print(lib[counter], end="")
        print(end="\r")
        sys.stdout.write("\b"*10 + " "*10 + "\b"*10)

There are still some changes that we can make to your code to make it easier to work with.
These are a bit more advanced so don’t worry if you don’t get them straight away.

  • We can change your big if elif block into a single for loop.
    Lets see what we have so far:

    if counters[0] > len(lib):
        counters[0] = 0
        counters[1] += 1
    

    We know that this section is repeated each time for each index.
    So we can make this generic by changing 0 to index and 1 to index + 1.

    if counters[index] >= len(lib):
        counters[index] = 0
        counters[index + 1] += 1
    

    Now we just need to loop over range(len(counters) - 1) to duplicate the block 9 times.

    for index in range(len(counters) - 1):
        if counters[index] >= len(lib):
            counters[index] = 0
            counters[index + 1] += 1
    
  • We can use some Python sugar to make your print for loop ‘cleaner’.
    Firstly we can remove all the prints by building a list.

    combination = []
    for counter in counters:
        combination.append(lib[counter])
    

    From here we can join all the strings together with "".join and pass it to print like you did before. This will join the list by empty strings so it converts it’s like manually doing combination[0] + combination[1] + ....

    print("".join(combination), end="\r")
    

    We can then use a list comprehension to build combination in one line.
    This is just syntaxtic sugar and is the same as the for loop we used before.
    It’s just different, cleaner, syntax.

    combination = [lib[counter] for counter in counters]
    
  • We can use either a while True loop or itertools.count rather than range(sys.maxsize**99999) to loop infinitely.

    while True:
        counters[0] += 1
    
    import itertools
    
    for _ in range(itertools.count()):
        counters[0] += 1
    
  • We can probably just use print rather than sys.stdout.write.

    To make it so there is no newline we can pass end="".
    This however doesn’t flush the stream straight away, and so we need to pass flush=True.

lib = [''] + list('0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz°!"§$%&/()=ß´`+#-.,><@€|^~–{[]}ÄÖÜäöü')
counters = [-1, 0, 0, 0, 0, 0, 0, 0, 0, 0]

while True:
    counters[0] += 1
    for index in range(len(counters) - 1):
        if counters[index] >= len(lib):
            counters[index] = 0
            counters[index + 1] += 1
    
    if counters[9] >= len(lib):
        print("DONE.")
    else:
        print("".join([lib[counter] for counter in counters]), end="\r")
        print("\b"*10 + " "*10 + "\b"*10, end="", flush=True)

Attribution
Source : Link , Question Author : Sandra , Answer Author : Peilonrayz

Leave a Comment