I have created a password checker which generates a random character and compares it until all the characters are the same. But as it rolls through it repeats the character multiple times. Is there a way of making it so the character doesn't repeat, possibly putting the characters into a list? I could then use the
list.del()
function to stop it repeating. Here is my code:
import string
import random
import time
print('Welcome to the password checker')
characters = string.ascii_lowercase + string.digits + string.ascii_uppercase + ' .,!?;:`"+/*()##$&_-='
password = input("Enter your password: ")
tryFirst = ''.join(random.choice(characters) for i in range(len(password)))
trySecond = ''
Finished = False
attempt = 0
while Finished == False:
print("{}) {}".format(attempt,tryFirst))
trySecond = ''
Finished = True
for num in range(len(password)):
if tryFirst[num] != password[num]:
Finished = False
trySecond += random.choice(characters)
else:
trySecond += password[num]
attempt += 1
tryFirst = trySecond
time.sleep(0.05)
print("Password found! That took {} attempts".format(attempt))
if attempt < 100:
print("Your password isn't safe")
elif attempt < 250:
print("Your password is quite safe, could do with improvement")
elif attempt < 400:
print("Your password is adequately safe")
elif attempt < 600:
print("Your pasword is safe")
Quick Answer
First, as others already have mentioned, your method will not give you the answer you seek. For example, inputting example as a first password and Qm2cl?X as a second. Your program would return a similar result although they are obviously not equally strong passwords. The first can be found in a dictionary while the second is much harder to guess (it possesses more randomness).
To answer your question, you could use the same random character for all positions in your word each iteration. Then you can safely pop each checked character off the characters list. Here is a possible implementation:
import string
import random
import time
print('Welcome to the password checker')
characters = string.ascii_lowercase + string.digits + string.ascii_uppercase + \
' .,!?;:`"+/*()##$&_-='
characters = list(characters)
random.shuffle(characters)
password = input("Enter your password: ")
tryFirst = characters.pop() * len(password)
Finished = False
attempt = 0
while not Finished:
print("{}) {}".format(attempt, tryFirst))
trySecond = ''
Finished = True
try:
char = characters.pop()
except IndexError:
print 'No more characters to check'
for num in range(len(password)):
if tryFirst[num] != password[num]:
Finished = False
trySecond += char
else:
trySecond += password[num]
attempt += 1
tryFirst = trySecond
time.sleep(0.05)
print("Password found! That took {} attempts".format(attempt))
if attempt < 100:
print("Your password isn't safe")
elif attempt < 250:
print("Your password is quite safe, could do with improvement")
elif attempt < 400:
print("Your password is adequately safe")
elif attempt < 600:
print("Your password is safe")
Fundamental Problem
A problem however is that you check each character individually, which would be impossible to do for a real hacker. By doing this, you reduce the problem difficulty of finding the correct password significantly. For example, in your case you have 83 different characters in your characters list. By checking each character individually, you will never need more than 83 attempts to get the correct password (if you remove your duplicates problem). If you would only be able to check a complete password instead of characters individually, as would be the case in a live situation, the number of attempts to find the correct password is much higher, being 83^password_length/2. For example, you would already need 3444 attempts on average to guess a password of two characters and 285893 for a password of three characters.
So, if you would really like to know the number of attempts a primitive brute-force algorithm would need to guess a given password composed of the 83 characters in your characters list, just use this formula: 83^password_length/2.
But be warned that this does not take into account that humans tend to pick passwords with a certain structure and thus with a certain predictability. Password cracking programs exploit this weakness by using for example dictionary attacks and rainbow tables. Thus the actual number of attempts a cracking program would need may be much lower than this formula would suggest.
Related
I am just getting into programming recently and still many things are not clear to me. However in this code I just don't understand the logic. I premise that I don't want to use either import or def(), but I want to understand how I have to reason to make this piece of code work.
A User enters the pwd, and I want there to be a check for numbers and letters that must be present.
The first while is to avoid empty pwd; at this point I dismember the pwd and check for letters and numbers.
Enter the second while and here if I enter only numbers or only letters it works but if I put numbers and letters it keeps staying in the loop.
I tried putting a break (is commented) but it doesn't work.
If anyone can make me understand what I am doing wrong I will be grateful.
Regards
pwd = input("Enter password.\n")
# This loop is used to check if the User has not typed in the password.
while len(pwd) == 0: # Enter the loop when it finds the pwd box empty
print("You left the password box empty.\n")
pwd = input("Enter the password.\n") # Query the password
n = ''.join(filter(str.isdigit, pwd))
char = pwd.isalpha()
num = n.isdigit()
# print(char, num)
# Control loop so that there are letters and numbers
while (char== False and num == True) or (char== True and num == False):
n = ''.join(filter(str.isdigit, pwd))
char= pwd.isalpha()
num = n.isdigit()
print("Attention the password must contain numbers and letters.")
pwd = input("Enter password again.\n") # Prompt for password
# break
if (char== True and num == True):
break
print("Password contains numbers and letters. Accepted!")
PS: Instead of While I tried using if, but if the User keeps putting either only numbers or only letters I have no way to go back as I can with While.
First, Issue you are getting is because of the line
char = pwd.isalpha()
At this point char will always be False, unless pwd is only alpha. So, when you enter a password that is mix of alpha and number example 123asd, char will always be False as you are directly using isalpha() method on pwd. What you want to do here is use same approach you used to check numbers. That mean adding the line below,
c = ''.join(filter(str.isalpha, pwd))
before
char = c.isalpha()
This way you are filtering all the alpha out.
Second, inside your 2nd while loop. You need to move the lines
print("Attention the password must contain numbers and letters.")
pwd = input("Enter password again.\n") # Prompt for password
on top, before doing all those alpha and digit filters. So, that you will always have the latest value stored in the pwd.
[OPTIONAL]
Furthermore, using your same approach, I would suggest to refactor your code as following.
pwd = input("Enter password.\n")
num = ''.join(filter(str.isdigit, pwd))
char = ''.join(filter(str.isalpha, pwd))
while not pwd or not num or not char:
if not pwd:
print("You left the password box empty.")
else:
print("Attention the password must contain numbers and letters.")
pwd = input("Enter password again.\n")
num = ''.join(filter(str.isdigit, pwd))
char = ''.join(filter(str.isalpha, pwd))
print("Password contains numbers and letters. Accepted!")
I was working on this assignment for the EdX MIT Python course and decided I wanted the output to display differently. Based on my code, I thought that the task program would end when guesses = 0. However, I'm either getting "IndexError: list assignment index out of range" or the program ends at guess 2. This appears to depend on the length of the secretWord. Can anyone point me in the right direction?
def hangman(secretWord):
'''
secretWord: string, the secret word to guess.
Starts up an interactive game of Hangman.
* At the start of the game, let the user know how many
letters the secretWord contains.
* Ask the user to supply one guess (i.e. letter) per round.
* The user should receive feedback immediately after each guess
about whether their guess appears in the computers word.
* After each round, you should also display to the user the
partially guessed word so far, as well as letters that the
user has not yet guessed.
Follows the other limitations detailed in the problem write-up.
'''
trackedguess = []
letcount = ()
letterlist = []
guess = ''
for i in range(0, (len(secretWord)-1)):
trackedguess.append('_')
letcount = len(secretWord)
guessesleft = 8
for i in range(0, 7):
if ''.join(trackedguess) == secretWord:
print('You win!')
break
if guessesleft < 1:
print('You have 0 guesses remaining.')
break
print(trackedguess)
print("You have ", guessesleft, " guesses remaining.")
guess = input('Please guess a letter and press return: ')
if guess in letterlist:
print("You've already guessed that. Try again.")
else:
guessesleft -= 1
letterlist.append(guess)
for i in range(0, len(secretWord)):
if secretWord[i] in letterlist:
coordinate = i
trackedguess[coordinate] = secretWord[i]
hangman(chooseWord(wordlist))
A few things stick out to me:
range uses an exclusive end. That means range(0, (len(secretWord)-1) will iterate one time less than the length of secretWord. You want the lengths to match. More straightforward, less error-prone approaches would be just: trackedGuess = ['_'] * len(secretWord) or trackedGuess = list('_' * len(secretWord)).
You should verify your assumptions. For example, the above case could have been caught easily if you did assert(len(trackedGuess) == len(secretWord)).
for i in range(0, 7) suffers from the same problem as your earlier usage of range(). If you want to iterate 8 (guessesleft) times, then you should use range(0, 8). However, you're also decrementing guessesleft inside the loop and exiting when it reaches 0. Do one or the other, but not both. As your code currently is, if someone enters a guess that they've already made, it will count one iteration against them (which I'm not sure is what you want).
It's a very good attempt, but there are some things that are wrong. I'm going to try to walk through them one by one.
Instead of a for loop, I'd suggest using a while guessesleft>0. In the current implementation the for loop will run for 8 times regardless of whether or not there are any guesses left (for example, try providing the same letter as a guess every time). With the while, however, you gain much more control over the loop.
The trackedguess generation is flawed. It will always miss the last letter of the secretword (this is also the reason you were getting the IndexError) Try for i in range(len(secretWord)) instead. You'll also find it much more concise and readable.
I also took the liberty of moving the win or lose condition down in the loop. Previously, if you won on the last guess, you'd still lose (because the win check condition happened before the input and after that the loop ended); also, the guess wasn't printed if you won (because the loop would break before the print statement).
Revised code below:
def hangman(secretWord):
'''
secretWord: string, the secret word to guess.
Starts up an interactive game of Hangman.
* At the start of the game, let the user know how many
letters the secretWord contains.
* Ask the user to supply one guess (i.e. letter) per round.
* The user should receive feedback immediately after each guess
about whether their guess appears in the computers word.
* After each round, you should also display to the user the
partially guessed word so far, as well as letters that the
user has not yet guessed.
Follows the other limitations detailed in the problem write-up.
'''
trackedguess = []
letterlist = []
for i in range(len(secretWord)):
trackedguess.append('_')
guessesleft = 8
while guessesleft > 0:
print(trackedguess)
print("You have ", guessesleft, " guesses remaining.")
guess = input('Please guess a letter and press return: ')
if guess in letterlist:
print("You've already guessed that. Try again.")
else:
guessesleft -= 1
letterlist.append(guess)
for i in range(0, len(secretWord)):
if secretWord[i] in letterlist:
coordinate = i
trackedguess[coordinate] = secretWord[i]
if ''.join(trackedguess) == secretWord:
print(trackedguess)
print('You win!')
break
if guessesleft < 1:
print('You have 0 guesses remaining.')
break
hangman('test')
Hope that helps.
This question already has answers here:
How to detect lowercase letters in Python?
(6 answers)
Closed 4 years ago.
I need to make a simple password checker, it needs to check if the string has at least one lowercase letter, one uppercase letter and one number.
here's what i have
passwd = raw_input("enter your password: ")
upper = 0
lower = 0
number = 0
while len(passwd) < 7 and upper <= 0 and lower <= 0 and number <= 0:
print 'start'
for x in passwd:
if x.isupper()==True:
print 'upper'
upper+=1
elif x.islower()==True:
print 'lower'
lower+=1
elif x.isalpha()==False:
print 'num'
number+=1
print ('password not complex enough')
upper = 0
lower = 0
number = 0
passwd = raw_input('please enter password again ')
those random looking prints are for debugging so i can see where it's reaching.
and what looks like is happening is that it only checks the first condition, the length condition and then leaves the loop.
here's a sample of what is happening
enter your password: pA1
start
lower
upper
num
password not complex enough
please enter password again password
why is it only checking the length condition? what did i do wrong?
You can simplify the whole logic by utilising unicodedata.category to collect what category of a character is and then check to see if all expected are present, eg:
import unicodedata
is_valid = (
len(password) >= 7 and
{'Ll', 'Lu', 'Nd'}.issubset(unicodedata.category(ch) for ch in password)
)
You have some problems with your checking logic. First of all you re-set your control variables before the while condition could check them. Furthermore you used and to test if all conditions are true, while or is what you should be using. If you want to keep the basic structure of your code, the best way to go is to use only True in your while statement and then break the loop once all conditions are fulfilled. Of course, as shown in the other answers, there are much more compact ways to check that all conditions are fulfilled.
passwd = raw_input("enter your password: ")
upper = 0
lower = 0
number = 0
while True:
for x in passwd:
if x.isupper()==True:
print 'upper'
upper+=1
elif x.islower()==True:
print 'lower'
lower+=1
elif x.isalpha()==False:
print 'num'
number+=1
if len(passwd) < 7 or upper <= 0 or lower <= 0 or number <= 0:
print ('password not complex enough')
passwd = raw_input('please enter password again ')
upper = 0
lower = 0
number = 0
else:
break
print 'final password:', passwd
Output:
enter your password: pA
lower
upper
password not complex enough
please enter password again pA1ghlfx78
lower
upper
num
lower
lower
lower
lower
lower
num
num
final password: pA1ghlfx78
If a password must consist of ASCII letters and numbers only, then the most efficient approach would be to use sets:
from string import ascii_lowercase, ascii_uppercase, digits
def is_valid(password):
if len(password) < 7:
return False
password_set = set(password)
return not any(password_set.isdisjoint(x)
for x in (ascii_lowercase, ascii_uppercase, digits))
If unicode characters are allowed, you could use any():
def is_valid(password):
return (len(password) >= 7 and
any(c.islower() for c in password) and
any(c.isupper() for c in password) and
any(c.isdigit() for c in password))
May this is helpful for you.I use sum(1 for c in the string if c.islower()) to find the total number of lowercase. and same way to find the total number of uppercase and digit.len(string) to find length of string.
string=input()
if ((sum(1 for c in string if c.islower())>=1) and (sum(1 for c in string if
c.isupper())>=1) and (sum(1 for c in string if c.isdigit())>=1) and (len(string)>=7)):
print("Password is complex")
else:
print ("Password not complex enough")
I am making a hang man game. I am trying to cycle through a word and have all the repeats in the letter be appended to my list. For example the word "hello": if the user types in "l" I want all the l's to be added to my list. Right now it is only finding one "l" and if the user types an "l" again it finds the second "l".
I also want the user not to be able to type in another letter if they previously already typed it in.
I have two lists one for right guesses and wrong guesses that store every guess. For example if a user types in "h" in "hello"
"h" is a right guess so it appends to [h] but if they type in "h" again it adds it to the list so it says ["h","h"]. The wrong box works the same way but for words that are wrong. If they type in "z" for the word "hello" it says ["z"] in the wrong box.
Here is my code:
import random
user_input = ""
turns = 5
print("Welcome to Advanced Hang Man!")
print("Use your brain to unscramble the word without seeing its order!")
words = ["hello","goolge","czar","gnat","relationship","victor","patric","gir","foo","cheese"]
# Picks a random word from the list and prints the length of it
random_word = (random.choice(words))
random_word_legnth = (len(random_word))
print("Hint! The length of the word is",random_word_legnth)
hold_random_word = [i for i in random_word]
while turns != 0 and set(right_guess) != set(hold_random_word):
user_input = input("Please type your guess one letter at a time:")
right_guess = []
wrong_guess = []
#Calculating every input
if len(user_input) == 1 and user_input.isalpha():
for i in user_input:
if i in hold_random_word:
right_guess.append(i)
else:
wrong_guess.append(i)
print("Correct guess", ''.join(right_guess))
print("Wrong guess", ''.join(wrong_guess))
I'm not sure what your direct question is, but thinking about a hangman game you want to take the users guess and parse the entire word or phrase they are guessing to see if their guess matches anywhere in the word. I made a hang man game below that will function as expected (minus any error handling) Let me know if any parts confuse you, and i can explain
import random
wordcomp = []
wordguess = []
#this is a word bank for all puzzles, they are randomly chosen
word_bank = ['guess this phrase', 'Lagrange', 'foo', 'another phrase to guess']
# This loop stores each letter in a list, and generates a blank list with correct spaces and blanks for user
rand_word = random.randrange(4)
for i in word_bank[rand_word]:
wordcomp.append(i)
if i == ' ':
wordguess.append(' ')
else:
wordguess.append('__')
print('I am thinking of a word,' , wordguess , ' it has ', len(wordguess), ' characters total, GOOD LUCK \n')
wordlist = wordcomp
count = 0
placeletter = 0
wrongguess = []
guesscount = 0
while wordlist != wordguess:
guess = input('please input a lower case letter within the english alphabet!') ##Check that input is one character, and lower case
guesscount = guesscount + 1
# This for loop scans through to see if the letter that was guessed is in the actual puzzle, and places in the correct spot!!
for t in wordcomp:
if t == guess:
wordguess[placeletter] = guess
placeletter = placeletter + 1
# This check tells them they already guessed that letter... then makes fun of them
if guess in wordguess:
pass
else:
wrongguess.append(guess)
while wrongguess.count(guess) > 1:
wrongguess.remove(guess)
print('you guessed the letter ' , guess , ' already, are you person that suffers short term memory loss...')
print('The word I am thinking of: ' , wordguess)
print('The letters you have already guess are: ', wrongguess)
placeletter = 0
# This tells them they finished the puzzle and the number of guesses it took, if its over 26, it calls them stupid for obvious reasons...
if guesscount >= 26:
print('you are an idiot if it took you more than 26 guesses..... now take a minute, sit silently, and think about why you are idiot if it took over 26 guesses... for hangman... where you guess the letters of the alphabet... YOU GET IT, stupid')
elif guesscount < 26:
print('Congrats you solved the puzzle, w00t!!')
if len(user_input) == 1 and user_input.isalpha():
for i in user_input:
if i in hold_random_word and i not in right_guess:
right_guess.append(i)
elif i not in hold_random_word or i not in wrong_guess:
wrong_guess.append(i)
elif i in hold_random_word:
# here user types something that is already typed and is a right_guess
pass
else:
# Types something wrong, that was already typed
pass
print("Correct guess", ''.join(right_guess))
print("Wrong guess", ''.join(wrong_guess))
It is not clear how you are taking inputs, but I think this code can be further optimized. Give it a shot.
Edit 1:
import random
user_input = ""
turns = 5
print("Welcome to Advanced Hang Man!")
print("Use your brain to unscramble the word without seeing its order!")
words = ["hello","goolge","czar","gnat","relationship","victor","patric","gir","foo","cheese"]
random_word = (random.choice(words))
random_word_legnth = (len(random_word))
print("Hint! The length of the word is",random_word_legnth)
hold_random_word = [i for i in random_word]
# This condition can lead to issues in situations like this - abc and aabbcc [sorry couldn't quickly come up with a good actual example :)]
while turns != 0 and set(right_guess) != set(hold_random_word):
user_input = input("Please type your guess one letter at a time:").strip()
right_guess = []
wrong_guess = []
#Calculating every input
if len(user_input) == 1 and user_input.isalpha():
# user_input is 1 letter so for i in user_input will execute only once
# Use the if structure as defined above
if user_input in hold_random_word:
right_guess.append(i)
else:
# this is missing
turns -= 1
wrong_guess.append(i)
print("Correct guess", ''.join(right_guess))
print("Wrong guess", ''.join(wrong_guess))
elif len(user_input) > 1:
print("Please type only one letter at a time")
elif not user_input.isalpha():
print("Please enter only valid English letters")
else:
# handle this however you want :)
pass
def main():
#word = input("Word to guess for player 2:")
word = ['h','e','l','l','o']
word2 = "hello"
#make a list of _ the same length as the word
display =[]
for i in range (0,len(word)):
display.append("_")
chances = int(input("Number of chances to guess word:"))
if len(word)== 11:
print ("Your word is too long. It has to be 10 charecters or less")
else:
word = word
if chances < len(word):
answer = input("Your word is {0} letters long , are you sure you don't want more chances? Yes or no?". format (len(word)))
if answer == "no":
chances= int(input("Number of chances:"))
else:
chances = chances
("Ok then lets continue with the game")
print ("Player 2, you have {0} chances to guess the word.". format (chances))
won = False
underscore = False
while chances > 0 and won == False and underscore == False:
guess = input("Enter your guess: ")
gC=False
for i in range (0,len(word)):
if guess == word[i]:
gC=True
display[i]=guess
if not gC:
chances = chances - 1
display2 = ""
for i in display:
display2 = display2 + i + " "
For some reason the code doesn't work when I state my while loop as the game continues to go on until the user runs out of guess'. Does anybody have any suggestions as to how I can fix this?
You never set won to True when the user wins the game by guessing all the letters.
This is not an answer to your original question, instead more of a code-review, but maybe you'll find it useful.
word = list('hello') # replaces manual splitting of string into letters
display = [ '_' ] * len(word) # replaces build-up using for-loop
chances = input("Number ... ") # already returns int if the user enters an int
# but will evaluate any valid python expression the
# user enters; this is a security risk as what
# ever is done this way will be done using your
# permissions
chances = int(raw_input("Number ...")) # probably what you wanted
...
else:
word = word # does nothing. remove this line and the "else:" above
chances -= 1 # replaces 'chances = chances - 1' and does the same
display2 = ' '.join(display) # replaces the for-loop to build display2
Furthermore I suggest to use better names. Variables like display2 or gC aren't very helpful in this context. In professional programming you always have to keep in mind that you are writing your code (also or even mainly) for the next developer who has to maintain it. So make it readable and understandable. Choose names like displayString or guessedCorrectly instead.