Thank you very much for taking the time to read this. I am a tadpole when it comes to write python and right now I just written the framework to create a brute force algo to sort up to 3 digits worth of numbers. Am I in the right direction?
I am assuming the web app actually reveal the number of digits it is send to your email. I first learn about this from TryHackMe.
Now it can randomly create up to 4 digit numbers as seen in BookFace and the above can crack the code as seen using their example. My question is am I doing it in the right way? Because I seen other people's sample of bruteforce and they use a lot of function. Am I being too long winded?
import random
digits = 4 #reveal the number of digits
i=0
z=0
x="" #init password to nothing
#generate random password up to 4 digits
while i < digits:
z = random.randint(0,9)
print(z)
x = str(x) + str(z)
i+=1
y = 1
print("Code Random Generated: " + x)
if digits == 1:
while y!=int(x):
print(y)
y+=1
elif digits == 2:
while y!=int(x):
if len(str(y)) == 1:
print("0" + format(y))
elif len(str(y)) == 2:
print(y)
y+=1
elif digits == 3:
while y!=int(x):
if len(str(y)) ==1:
print("00" + format(y))
elif len(str(y)) == 2:
print("0" + format(y))
elif len(str(y)) == 3:
print(y)
y+=1
elif digits == 4:
while y!=int(x):
if len(str(y)) ==1:
print("000" + format(y))
elif len(str(y)) == 2:
print("00" + format(y))
elif len(str(y)) == 3:
print("0" + format(y))
elif len(str(y)) == 4:
print(y)
y+=1
if y !=0:
print("Reset Code Revealed: " + format(y))
You are sort of asking for a code review, not for a problem's solution. This is what Code Review Stack Exchange is for.
Anyway, let's look at my own solution to the problem :
"""
An humble try at cracking simple passwords.
"""
import itertools
import random
from typing import Callable, Iterable, Optional
def generate_random_numerical_password(ndigits: int) -> str:
return "".join(str(random.randint(0, 9)) for _ in range(ndigits))
def guesses_generator(ndigits: int) -> Iterable[str]:
yield from ("".join(digits) for digits in itertools.product("0123456789", repeat=ndigits))
PasswordOracle = Callable[[str], bool]
def cracker(ndigits: int, oracle: PasswordOracle) -> Optional[str]:
for guess in guesses_generator(ndigits):
if oracle(guess):
return guess
else:
return None
if __name__ == "__main__":
NDIGITS = 4 # difficulty
print(f"the difficulty (number of digits) is set to {NDIGITS}")
password = generate_random_numerical_password(NDIGITS)
print(f"the password to crack is {password!r}")
password_oracle = lambda guess: guess == password # do not use `is`, see Python's string interning
if match := cracker(NDIGITS, password_oracle):
print("Cracked!")
else:
print("Error, not found")
Some differences :
The different parts of the program are clearly delimited using functions (password creation and cracking).
The guess generation is generalized : if the number of digits become 5, you will have to write another (tedious and error-prone) elif.
The "flow" of the program is made very clear in the "main" part : generate a password and the corresponding oracle, call the cracker and check if a result was found. There is very few lines to read, and using descriptive names helped too.
The names come from the domain ("cracking") : "guess", "difficulty", "generator" instead of abstract ones like "x" and "y".
Some language knowledge is used : standard types operations (str-joining instead of concatenation), generators (yield from), libraries (itertools.product), syntax (walrus oparetor if match := cracker(...), ...
There is more documentation : comments, docstring at the top of the module, type annotations, ... all of these helping to understand how the program works.
My question is am I doing it in the right way? Because I seen other people's sample of bruteforce and they use a lot of function. Am I being too long winded?
I did not do use functions on purpose to do like the others people, but because I see things in a different way than you, which I will try to explain.
In the end, both your solution and mine solve the initial problem "crack a four-digit password". In this way, they are not much different. But there could be other ways to consider :
Will the problem change ? Could the password be 5 digits, or contain alphabetic characters, or special characters ? In such cases, how long will it take to adapt the script ? This is the malleability of the code.
How clear is the code ? If I have a bug to fix, how long will it take me to find where it comes from ? What if it is someone new to Python ? Or someone that knows Python well but never saw the code ? This is the maintainability of the code.
How easy is it to test the code ? What can be tested ? This is teastability.
How fast is the code ? How much memory does it uses ? This is performance ?
Can the program crash ? What if the program is given bad inputs ? Could it cause an infinite loop ? This is program-safety.
...
Depending on how your objectives about malleability, maintainability, testability, performance, safety, ... (other qualities of a program), but also depending on the context. who is writing the code ? Who will read it later ? How much experienced they are ? How much time do they have to finish writing it ? Will it be run only once then thrown away or will it be deployed on millions of devices ?
All of that affects how you write it. If I was in your shoes (beginner to Python, writing a run-once-then-forget script) I would have done the same as you. The difference is the context and the objectives. I wrote my code as an example to show you the difference. But neither is good nor bad.
You are not bad at running just because you are slower than an Olympic athlete. You can only be bad relative to a context and objectives. In Physical Education class, you are graded on your running speed according to the average for your age and the progress you made.
Seeing things in their perspective is a very useful skill, not just for programming.
Whan you compare your code to other's, yours seem less "clever", less "clean", less "elegant". But you are comparing apples to oranges. You are not the others. If your solution was accepted (correct and fast enough) that's a good start at your level.
My years of professionnal experience working with several other people on tens-of-thousand-lines codebases that have to be maintained for 20 years are a different set of contexts and objectives than you learning the language in fun ways (TryHackMe). Neither is objectively bad, both are subjectively good.
TL;DR : your code is fine for a beginner, you still have lots to learn if you want to, and keep having fun !
Related
The Scenario:
I am doing a question on Leetcode called nth Ugly Number. The algorithm is to find the nth number whose prime factors include only 1, 2, 3, and 5.
I created a solution which was accepted and passed all the tests. Then, I wanted to memoize it for practice with memoization with python - however, something has gone wrong with the memoization. It works for my own personal tests, but Leetcode does not accept the answer.
The memoized code is detailed below:
class Solution:
uglyNumbers = [1, 2, 3, 4, 5]
latest2index = 2
latest3index = 1
latest5index = 1
def nthUglyNumber(self, n: int) -> int:
while len(self.uglyNumbers) <= n:
guess2 = self.uglyNumbers[self.latest2index] * 2
guess3 = self.uglyNumbers[self.latest3index] * 3
guess5 = self.uglyNumbers[self.latest5index] * 5
nextUgly = min(guess2, guess3, guess5)
if(nextUgly == guess2):
self.latest2index += 1
if(nextUgly == guess3):
self.latest3index += 1
if(nextUgly == guess5):
self.latest5index += 1
self.uglyNumbers.append(nextUgly)
return self.uglyNumbers[n-1]
The only change I made when memoizing was to make uglyNumbers, latest2index, etc. to be class members instead of local variables.
The Problem:
When I submit to LeetCode, it claims that the solution no longer works. Here is where it breaks:
Input 12 /// Output 6 /// Expected 16
However, when I test the code myself and provide it with input 12, it gives the expected output 16. It does this even if I call nthUglyNumber with a bunch of different inputs before and after 12, so I have no idea why the test case breaks upon being submitted to LeetCode
Here's the testing I performed to confirm that the algorithm appears to work as expected:
# This code goes inside Class Solution
def nthUglyNumber(self, n: int) -> int:
print("10th: " + str(self.nthUgliNumber(10)))
print("11th: " + str(self.nthUgliNumber(11)))
print("12th: " + str(self.nthUgliNumber(12)))
print("9th: " + str(self.nthUgliNumber(9)))
print("14th: " + str(self.nthUgliNumber(14)))
print("10th: " + str(self.nthUgliNumber(10)))
print("11th: " + str(self.nthUgliNumber(11)))
print("12th: " + str(self.nthUgliNumber(12)))
return self.nthUgliNumber(n)
def nthUgliNumber(self, n: int) -> int:
# The regular definition of nthUglyNumber goes here
What I want to know
Is there some edge case in Python memoization that I am not seeing that's causing the code to trip up? Or is it fully Leetcode's fault? I know my algorithm works without memoization, but I want to understand what's going wrong so I gain a better understanding of Python and so that I can avoid similar mistakes in the future.
I appreciate the help!
I believe leetcode is probably running all tests in parallel on multiple threads using separate instances of the Solution class. Since you are storing nthUgliNumber as a class variable, instances may be updating it (and the 3 indexes) in a conflicting manner.
From leetcode's perspective, each test is not expected to have side effects that would impact other tests. So, parallel execution in distinct instances is legitimate. Caching beyond the scope of the test case is likely undesirable as it would make performance measurements inconsistent and dependent on the order and content of the test cases.
I am quite new to the PseudoCode concept... I would like to get an idea of how functions and operands like modulus, floor division and the likes would be written in PseudoCode. Writing the PseudoCode for this code might actually help me understand better...
user_response=input("Input a number: ")
our_input=float(user_response)
def string (our_input):
if (our_input % 15) == 0 :
return ("fizzbuzz")
elif (our_input % 3) == 0 :
return ("fizz")
elif (our_input % 5) == 0 :
return ("buzz")
else :
return ("null")
print(string(our_input))
Your code really isn't that hard to understand assuming the knowledge of % as the modulus operation. Floor division can really just be written as a regular division. Floor the result, if really necessary.
If you don't know how to express them, then explicitly write modulus(x, y) or floor(z).
Pseudocode is meant to be readable, not complicated.
Pseudocode could even be just words, and not "code". The pseudo part of it comes from the logical expression of operations
The basic idea of PseudoCode is either to
a) Make complicated code understandable, or
b) Express an idea that you are going to code/haven't yet figured out how to code.
For example, if I am going to make a tool that needs to read information in from a database, parse it into fields, get just the info that the user requests, then format the information and print it to the screen, my first draft of the code would be simple PseudoCode as so:
# Header information
# Get user input
# Connect to Database
# Read in values from database
# Gather useful information
# Format information
# Print information
This gives me a basic structure for my program, that way I don't get lost as I'm making it. Also, if someone else is working with me, we can divvy up the work (He works on the code to connect to the database, and I work on the code to get the user input.)
As the program progresses, I would be replacing PseudoCode with real, working code.
# Header information
user_input_row = int(input("Which row (1-10)? "))
user_input_column = input("Which column (A, B, C)? "))
dbase = dbconn("My_Database")
row_of_interest = dbase.getrow(user_input_row)
# Gather useful information
# Format information
# Print information
At any point I might realize there are other things to do in the code, and if I don't want to stop what I'm working on, I add them in to remind myself to come back and code them later.
# Header information #Don't forget to import the database dbconn class
user_input_row = int(input("Which row (1-10)? "))
#Protect against non-integer inputs so that the program doesn't fail
user_input_column = input("Which column (A, B, C)? "))
#Make sure the user gives a valid column before connecting to the database
dbase = dbconn("My_Database")
#Verify that we have a connection to the database and that the database is populated
row_of_interest = dbase.getrow(user_input_row)
# Separate the row by columns -- use .split()
# >> User only wants user_input_column
# Gather useful information
# Format information
# >> Make the table look like this:
# C C1 C2 < User's choice
# _________|________|_______
# Title | Field | Group
# Print information
After you're done coding, the old PseudoCode can even serve to be good comments to your program so that another person will know right away what the different parts of your program are doing.
PseudoCode also works really well when asking a question when you don't know how to code something but you know what you want, for example if you had a question about how to make a certain kind of loop in your program:
my_list = [0,1,2,3,4,5]
for i in range(len(my_list)) but just when i is even:
print (my_list[i]) #How do I get it to print out when i is even?
The PseudoCode helps the reader know what you're trying to do and they can help you easier.
In your case, useful PseudoCode for things like explaining your way through code might look like:
user_response=input("Input a number: ") # Get a number from user as a string
our_input=float(user_response) # Change that string into a float
def string (our_input):
if (our_input % 15) == 0 : # If our input is divisible by 15
return ("fizzbuzz")
elif (our_input % 3) == 0 : # If our input is divisible by 3 but not 15
return ("fizz")
elif (our_input % 5) == 0 : # If our input is divisible by 5 but not 15
return ("buzz")
else : # If our input is not divisible by 3, 5 or 15
return ("null")
print(string(our_input)) # Print out response
THANKS GUYS FOR ALL OF YOUR INPUT, FROM ALL I READ, I CAME UP WITH THIS, IF THERE ARE ANY AREAS U THINK NEEDS TO BE ADJUSTED PLEASE LET ME KNOW.
THANKS AGAIN
THE FUNCTION USING PYTHON
user_response=input("Input a number: ")
our_input=float(user_response)
def string (our_input):
if (our_input % 15) == 0 :
return ("fizzbuzz")
elif (our_input % 3) == 0 :
return ("fizz")
elif (our_input % 5) == 0 :
return ("buzz")
print(string(our_input))
<<------------------------------->>
IT'S PSEUDOCODE
Request an input from the user
Ensure that the input is a number
If the input is divisible by 15
send "fizzbuzz" to the main
program
But if the input is divisible by 3 and not 15
send "fizz" to the main program
But if the input is divisible by 5 and not 15 or 3
send "buzz" to the main program
Display the result.
I am trying to learn python and trying to create a simple application where I have it set to read lines from the text file. The first line is the question and second line is answer. Now, I am able to read the question and answer. But the part where I compare user answer with the actual answer, it doesn't perform the actions in the loop even when the answer entered is correct.
My code :
def populate():
print("**************************************************************************************************************")
f=open("d:\\q.txt")
questionList=[]
b = 1
score=0
start=0
for line in f.read().split("\n"):
questionList.append(line)
while b<len(questionList):
a = questionList[start]
print(a)
userinput=input("input user")
answer=questionList[b]
b = b + 2
print(answer)
if userinput==answer:
score =score+1
print(score)
else:
break
start += 2
I would really appreciate any guidance on this.
My q.txt file:
1. Set of instructions that you write to tell a computer what to do.
Program
2. A language's set of rules.
Syntax
3. Describes the programs that operate the computer.
System Software
4.To achieve a working program that accomplishes its intended tasks by removing all syntax and logical errors from the program
Debugging
5.a program that creates and names computer memory locations and can hold values, and write a series of steps or operations to manipulate those values
Procedural Program
6. The named computer memory locations.
Variables
7. The style of typing the first initial of an identifier in lowercase and making the initial of the second word uppercase. -- example -- "payRate"
Camel Casing
8. Individual operations within a computer program that are often grouped together into logical units.
Methods
9. This is an extension of procedural programming in terms of using variables and methods, but it focuses more on objects.
Object Oriented Programming
10. A concrete entity that has behaviors and attributes.
Objects
Your code was:
always asking the same question: questionList[start]
throwing away the value of every
replacing every space in answers with nothing, so "System Software" becomes "SystemSoftware"
failing to factor in case: need to use .lower() on userinput and answer.
Here's a more pythonic implementation:
#!/usr/bin/env python3
from itertools import islice
# Read in every line and then put alternating lines together in
# a list like this [ (q, a), (q, a), ... ]
def get_questions(filename):
with open(filename) as f:
lines = [line.strip() for line in f]
number_of_lines = len(lines)
if number_of_lines == 0:
raise ValueError('No questions in {}'.format(filename))
if number_of_lines % 2 != 0:
raise ValueError('Missing answer for last question in {}'.filename)
return list(zip(islice(lines, 0, None, 2), islice(lines, 1, None, 2)))
def quiz(questions):
score = 0
for question, answer in questions:
user_answer = input('\n{}\n> '.format(question))
if user_answer.lower() == answer.lower():
print('Correct')
score += 1
else:
print('Incorrect: {}'.format(answer))
return score
questions = get_questions('questions.txt')
score = quiz(questions)
num_questions = len(questions)
print('You scored {}/{}'.format(score, num_questions))
I'm toying around with writing creating a serial code generator/validator, but I can't seem to get how to do a proper check.
Here's my generator code:
# Serial generator
# Create sequences from which random.choice can choose
Sequence_A = 'ABCDEF'
Sequence_B = 'UVWQYZ'
Sequence_C = 'NOPQRS'
Sequence_D = 'MARTIN'
import random
# Generate a series of random numbers and Letters to later concatenate into a pass code
First = str(random.randint(1,5))
Second = str(random.choice(Sequence_A))
Third = str(random.randint(6,9))
Fourth = str(random.choice(Sequence_B))
Fifth = str(random.randint(0,2))
Sixth = str(random.choice(Sequence_C))
Seventh = str(random.randint(7,8))
Eighth = str(random.choice(Sequence_D))
Ninth = str(random.randint(3,5))
serial = First+Second+Third+Fourth+Fifth+Sixth+Seventh+Eighth+Ninth
print serial
I'd like to make a universal check so that my validation code will accept any key generated by this.
My intuition was to create checks like this:
serial_check = raw_input("Please enter your serial code: ")
# create a control object for while loop
control = True
# Break up user input into list that can be analyzed individually
serial_list = list(serial_check)
while control:
if serial_list[0] == range(1,5):
pass
elif serial_list[0] != range(1,5):
control = False
if serial_list[1] == random.choice('ABCDEF'):
pass
elif serial_list[1] != random.choice('ABCDEF'):
control = False
# and so on until the final, where, if valid, I would print that the key is valid.
if control == False:
print "Invalid Serial Code"
I'm well aware that the second type of check won't work at all, but it's a place holder because I've got no idea how to check that.
But I thought the method for checking numbers would work, but it doesn't either.
The expression `range(1, 5)' creates a list of numbers from 1 to 4. So in your first test, you're asking whether the first character in your serial number is equal to that list:
"1" == [1, 2, 3, 4]
Probably not...
What you probably want to know is whether a digit is in the range (i.e. from 1 to 5, I assume, not 1 to 4).
Your other hurdle is that the first character of the serial is a string, not an integer, so you would want to take the int() of the first character. But that will raise an exception if it's not a digit. So you must first test to make sure it's a digit:
if serial_list[0].isdigit() and int(serial_list[0]) in range(1, 6):
Don't worry, if it's not a digit, Python won't even try to evaluate the part after and. This is called short-circuiting.
However, I would not recommend doing it this way. Instead, simply check to make sure it is at least "1" and no more than "5", like this:
if "1" <= serial_list <= "5":
You can do the same thing with each of your tests, varying only what you're checking.
Also, you don't need to convert the serial number to a list. serial_check is a string and accessing strings by index is perfectly acceptable.
And finally, there's this pattern going on in your code:
if thing == other:
pass
elif thing != other:
(do something)
First, because the conditions you are testing are logical opposites, you don't need elif thing != other -- you can just say else, which means "whatever wasn't matched by any if condition."
if thing == other:
pass
else:
(do something)
But if you're just going to pass when the condition is met, why not just test the opposite condition to begin with? You clearly know how to write it 'cause you were putting it in the elif. Put it right in the if instead!
if thing != other:
(do something)
Yes, each of your if statements can easily be cut in half. In the example I gave you for checking the character range, probably the easiest way to do it is using not:
if not ("1" <= serial_list <= "5"):
Regarding your python, I'm guessing that when your wrote this:
if serial_list[0] == range(1,5):
You probably meant this:
if 1 <= serial_list[0] <= 5:
And when you wrote this:
if serial_list[1] == random.choice('ABCDEF'):
You probably meant this:
if serial_list[1] in 'ABCDEF':
There are various other problems with your code, but I'm sure you'll improve it as you learn python.
At a higher level, you seem to be trying to build something like a software activation code generator/validator. You should know that just generating a string of pseudo-random characters and later checking that each is in range is an extremely weak form of validation. If you want to prevent forgeries, I would suggest learning about HMAC (if you're validating on a secure server) or public key cryptography (if you're validating on a user's computer) and incorporating that into your design. There are libraries available for python that can handle either approach.
Yesterday I had to parse a very simple binary data file - the rule is, look for two bytes in a row that are both 0xAA, then the next byte will be a length byte, then skip 9 bytes and output the given amount of data from there. Repeat to the end of the file.
My solution did work, and was very quick to put together (even though I am a C programmer at heart, I still think it was quicker for me to write this in Python than it would have been in C) - BUT, it is clearly not at all Pythonic and it reads like a C program (and not a very good one at that!)
What would be a better / more Pythonic approach to this? Is a simple FSM like this even still the right choice in Python?
My solution:
#! /usr/bin/python
import sys
f = open(sys.argv[1], "rb")
state = 0
if f:
for byte in f.read():
a = ord(byte)
if state == 0:
if a == 0xAA:
state = 1
elif state == 1:
if a == 0xAA:
state = 2
else:
state = 0
elif state == 2:
count = a;
skip = 9
state = 3
elif state == 3:
skip = skip -1
if skip == 0:
state = 4
elif state == 4:
print "%02x" %a
count = count -1
if count == 0:
state = 0
print "\r\n"
The coolest way I've seen to implement FSMs in Python has to be via generators and coroutines. See this Charming Python post for an example. Eli Bendersky also has an excellent treatment of the subject.
If coroutines aren't familiar territory, David Beazley's A Curious Course on Coroutines and Concurrency is a stellar introduction.
You could give your states constant names instead of using 0, 1, 2, etc. for improved readability.
You could use a dictionary to map (current_state, input) -> (next_state), but that doesn't really let you do any additional processing during the transitions. Unless you include some "transition function" too to do extra processing.
Or you could do a non-FSM approach. I think this will work as long as 0xAA 0xAA only appears when it indicates a "start" (doesn't appear in data).
with open(sys.argv[1], 'rb') as f:
contents = f.read()
for chunk in contents.split('\xaa\xaa')[1:]:
length = ord(chunk[0])
data = chunk[10:10+length]
print data
If it does appear in data, you can instead use string.find('\xaa\xaa', start) to scan through the string, setting the start argument to begin looking where the last data block ended. Repeat until it returns -1.
I am a little apprehensive about telling anyone what's Pythonic, but here goes. First, keep in mind that in python functions are just objects. Transitions can be defined with a dictionary that has the (input, current_state) as the key and the tuple (next_state, action) as the value. Action is just a function that does whatever is necessary to transition from the current state to the next state.
There's a nice looking example of doing this at http://code.activestate.com/recipes/146262-finite-state-machine-fsm. I haven't used it, but from a quick read it seems like it covers everything.
A similar question was asked/answered here a couple of months ago: Python state-machine design. You might find looking at those responses useful as well.
I think your solution looks fine, except you should replace count = count - 1 with count -= 1.
This is one of those times where fancy code-show-offs will come up ways of have dicts mapping states to callables, with a small driver function, but it isn't better, just fancier, and using more obscure language features.
I suggest checking out chapter 4 of Text Processing in Python by David Mertz. He implements a state machine class in Python that is very elegant.
I think the most pythonic way would by like what FogleBird suggested, but mapping from (current state, input) to a function which would handle the processing and transition.
You can use regexps. Something like this code will find the first block of data. Then it's just a case of starting the next search from after the previous match.
find_header = re.compile('\xaa\xaa(.).{9}', re.DOTALL)
m = find_header.search(input_text)
if m:
length = chr(find_header.group(1))
data = input_text[m.end():m.end() + length]