Double ```for``` loop - python

I'm trying to make a nesting for loop to calculate points for my cribbage game. Right now I'm trying to figure out a method for counting all the '15's in the players hand. The way I currently have the system set up makes it so if 'dT' is in the hand, the card art for the ten of diamonds will print.
What I currently have (that is related to this question) is
hand = [] # this is filled by the rest of the program
handCombos = []
def count15s():
for _ in range(6):
possiblePairs = {'T5','J5','Q5','K5','78','96','5T','5J','5Q','5K','87','69'}
first = hand[0][1]
second = hand[2][1]
combo = ''.join(first+second)
if(combo in possiblePairs):
print('ham') # placeholder so the program can run without an error
print(combo)
This works for what it is supposed to--if the second part of the first term + the second part of the third term is in the list of possible pairs, then it prints 'ham' to the console. The problem with this, however, is that 1) It prints 'ham' 6 times, and 2) It actually does what it's supposed to.
I'm looking for a way that I can nest a for loop inside another one so it can scan every combination of '15's in the hand, then that specific combination to the handCombos array so that it can't be used again. I know that I need a for loop somewhere but the way I'm thinking of doing it would end up with a triple loop and I don't think that this is the best way to do it.

Related

Loop through array of shuffled WebElements without them getting stale

My dilemma is that if I use
a=[]
a=driver.find_elements_by_class_name("card")
random.shuffle(a)
for card in a:
nextup=(str(card.text) + '\n' + "_" * 15)
do a bunch of stuff that takes about 10 min
The first round works but then I get a StaleElementException because it clicks links and goes to diff pages. So then I switched to this:
a=[]
a=driver.find_elements_by_class_name("card")
i=0
cardnum=len(a)
while i != cardnum:
i += 1 #needed because first element thats found doesnt work
a=driver.find_elements_by_class_name("card") #also needed to refresh the list
random.shuffle(a)
nextup=(str(card.text) + '\n' + "_" * 15)
do a bunch of stuff that takes about 10 min
The problem with this one is the i variable because the same card could be clicked due to the shuffle with each loop. Then I added a catch to check if card had already been clicked and continue if it has. Sounds like it'd work but sadly the i variable counts these and then eventually counts past the index. I thought about periodically setting i back to 1 but I do not know if it will work. Edit: would make an infinite loop since once all are clicked i will be zero and it will never exit.
I know the code works it's been tested extensively, however, bots get banned for not being humanlike and random. The basics of this script is goes thru a list of categories and then goes thru all the cards in a category. Tried randomizing the categories but similar dilemma because to refresh the list you have to remake array in each loop like the above block then comes the problem with categories already being completed would be clicked again... Any advice would be appreciated.
What is happening here is that, as you interact with the page, the DOM gets refreshed, eventually causing the elements you've been storing to go stale.
Rather than keeping a list of the elements, keep a reference to their individual element paths, and reacquire the elements as needed:
# The base css path for all the cards
base_card_css_path = ".card"
# Get all the target elements. You are only doing this to
# get a count of the number of elements on the page
card_elems = driver.find_elements_by_css_selector(base_card_css_path)
# Convert this to a list of indexes, starting with 1
card_indexes = list(range(1, len(card_elems)+1))
# Shuffle it
random.shuffle(card_indexes)
# Use `:nth-child(X)` syntax to get these elements on an as needed basis
for index in card_indexes:
card_css = base_card_css_path + ":nth-child({0})".format(index)
card = driver.find_element_by_css_selector(card_css)
nextup=(str(card.text) + '\n' + "_" * 15)
# do a bunch of stuff that takes about 10 min
(The above is untested for obvious reasons)

Add threads to a Sudoku checker

I have just created a simple function in Python which checks whether or not a Sudoku board (input is given as a list) is valid or not. The way I did it is pretty straight forward:
check if the sudoku board is 9x9
check that each number appears only once per row
check that each number appears only once per column
check that each number appears exactly once per 3x3 grid
Now, once I started this, I wanted to take advantage and learn a bit about Python threads. I read the docs, and also some awesome general multithreading related posts here on SO, but I just couldn't think of a way of implementing them in my checker.
Now, the way I'd like the threads works (as I thought) is one thread to check that each column contains 1-9 digits, another one to check the lines for the same thing, and another nine threads to check each 3x3 sub-grid. Could you guys please tell me (eventually with some explanations) how I could achieve this ? Thanks
So to give some general pointers on how to achieve this, without taking away any challenge. Lets start by import Threading:
import threading
Which will let us use thread objects! Also, in order to know if the Sudoku grid will be valid after the fact, we need a variable to store the True/False condition in. You can either go for a single variable, and use Thread Locks to ensure no other threads access it at the same time, or go with three separate. For simplicity, I'll use three separate variables in this example
LinesValid = False
ColumnsValid = False
GridsValid = False
Then, since threads require a function or another callable to run as their target, and you desire a thread for columns, rows and for each 3x3 grid, we need three functions for each thread. However, since there are 9 columns, 9 rows and also 9 grids I believe it would be a lot better to just do a single thread for the grids as well, but for the purpose of the exercise I suppose it is fine to do one for each.
def CheckLines():
# Line Checking Code
def CheckColumns():
# ColumnCheckingCode
def CheckGrid(UpperLeft, BottomRight):
# GridCheckingCode
Anyway, here we define our three functions, with their appropriate line checking code. Like the lines would check the X axis, and the Columns the Y axis, but the idea is to split it up. For the CheckGrid you would need to specify corners if you want it to have a thread for each tile, or if you decide on a single thread you would just define it as:
def CheckGrid():
# GridCheckingCode
After that we need to make our Threads:
LineThread = threading.Thread(target=CheckLines)
ColumnThread = threading.Thread(target=CheckLines)
GridThread = threading.Thread(target=CheckLines, args=([0, 0], [2, 2]))
You can ignore the arguments from the GridThread if you do not need the grids. Otherwise you need to specify corners, and find a way to loop through the specified tiles.
After that it is a matter of starting the threads, and joining them with the main thread, before showing the data to the user:
LineThread.start()
ColumnThread.start()
GridThread.start()
LineThread.join()
ColumnThread.join()
GridThread.join()
if sum(LinesValid, ColumnsValid, GridsValid) == 3:
print("The grid is valid!")
else:
print("The grid is invalid!")
Here we check if all our Bools are True: ( 1 + 1 + 1 ) == 3 if they are, and print data to the user based on this. These bools would be set to True/False within their respective Check** functions!
If you desire some more direct solutions, rather than a general direction with explanations, please let me know and I'll throw something together! The final piece of code looks something like this:
import threading
def CheckLines(self):
# Line Checking Code
def CheckColumns(self):
# ColumnCheckingCode
def CheckGrid(self, UpperLeft, BottomRight):
# GridCheckingCode
LinesValid = False
ColumnsValid = False
GridsValid = False
LineThread = threading.Thread(target=CheckLines)
ColumnThread = threading.Thread(target=CheckLines)
GridThread = threading.Thread(target=CheckLines, args=([0, 0], [2, 2]))
LineThread.start()
ColumnThread.start()
GridThread.start()
LineThread.join()
ColumnThread.join()
GridThread.join()
if sum(LinesValid, ColumnsValid, GridsValid) == 3:
print("The grid is valid!")
else:
print("The grid is invalid!")

How can a for-loop that touches no important code change program output?

I'm working on a relatively large python file, and am working on adding in the next important feature. However, I've noticed that inserting ANY for loop is changing the program's output.
To test this, I put in the following loop:
for fff in range(2):
print 'test'
The variable fff appears nowhere else in the code. Output is iterated through a list called trueRepeats, which clearly is not touched by this code (nor is any variable, for that matter). And yet, no matter where in the code I put this loop -- even before trueRepeats is declared; even if it's the first code that is executed after the imports, the output changes.
EDIT: Here is the full source: BitBucket. The original location where I tried to add the loop was under comment block #4, but as mentioned, it seemed to cause an issue in a variety of other places. I'm running on the file "example.py" also in the repository. Without the loop, the program returns two repeats, at lines {4, 7} and {1, 10}. With the loop, it returns instances at {2, 3} and {1, 10}.
I know this is not much code to go off of, but before I can diagnose what the issue is, I don't even have a clue how this is possible, which is my question: What in python would make it so that an independent for-loop can be affecting output that only involves variables that the loop doesn't touch?
EDIT 2: When I talk about the output changing, I am not talking about the two "test" lines printed out by the loop. These could be replaced by pass but I felt print would have fewer side effects.
EDIT 3: Upon further investigation, this problem runs much deeper than a for loop, and thus this question is probably not well-suited for StackOverflow, since the real question seems to be much less focused. Commenting out the line:
if flag == 5:
print "TEST: " + str(key)
Also changed program output, as did commenting out just a section of a line, which just appended the literal + "Parent: " to the end of a string that was being printed. Side effects like adding a literal to a string do not seem normal, so I'm going to need to investigate further to figure out why python is behaving so strangely.
No matter what the rest of your code is, that loop will print out "test" twice.
Why? Because for loops don't need a variable, "fff" is just a placeholder to iterate through.
I could have this code:
def add(a, b):
print a + b
add(5, 5)
for fff in range(2):
print "test"
And the output would be:
10testtest
I think this is the bug in your code:
alphaset = set() ## 1
...
for i in range(len(partition)):
for j in range(len(partition[i])):
alphaset.add(type(partition[i][j])) ## 2
nodecount += 1
...
count = 0
mapping = dict()
for elem in alphaset: ## 3
mapping[elem] = count ## 4
count+=1
alphasize = count
...
for i in range(len(partition)):
j = len(partition) - i - 1
...
for v in range(len(partition[j])):
node = partition[j][v]
SV = list()
SV.append(mapping[type(node)]) ## 5
Here's what happening. At 1, you have a set that is filled at 2 with some types that are taken from a list with a specific "order." Then, at 3 and 4, you iterate over the set and map each element in the set to some number. Then, at 5, that number is added to a list. If I understood your code correctly, the rest of the code depends on the "order" of the elements in that list, which depends on the mapping, which in itself depends on the order of the elements in the set at 1.
Now, there are no guarantees of the order of the elements in a set nor of the iteration order over it. So, at 3 and 4, and for different runs, you will get different mappings.
The solution is to either explicitly sort the set into a list, or use an OrderedSet which preserves insertion order (assuming the code before point 1 always inserts the nodes in the same order).

Most straightforward alternative to a while loop in python

The general point of the program is to search for words and make sure that they occur in the right order.
I am currently using the code
while not corpus[index_F2-1] == corpus[index_B]:
index_F2 = corpus.index(corpus[index_F2], index_F2+1)
in order to search for a word, check to make sure that a given word appears before it, and find a different instance of the same word in the corpus if the word does not appear in the correct environment. The only problem with this code is that, if the conditions are never met, it returns an error. Rather than having it return an error, it would be better if it could exit the loop, and then assign a new value to "F2" in order to try again. The obvious way to make the program exit the loop would be to use a while loop, but I am unsure how to get a while loop to not find a new value for F2 if the current one works. In short, it would work best if the script ran in this order.
Check to see if the current F2 will ever work
If it will not work, assign a new value to F2.
Check again recurse and check to see if the new F2 will work
and so on
An if statement would probably be in order, but exactly where and how it would work, I am unsure. Also, the code for reassigning F2 already exists. It is
index_E2= corpus.index(corpus[index_E2], index_E2+1)
index_F = index_E2+1
index_F2 = corpus.index(corpus[index_F], index_F+1)
A future problem that could present itself is that the corpus may run out of F2 values ( as a result of running out of E2 values) and display a similar error, but that is another issue.
I think I might understand what you're looking for. It sounds like you want to have multiple nested loops, with an exit point within the innermost loop. The most obvious way to achieve this, in my mind, is to enclose the loops within a function. Since all this corpus stuff is confusing, I'll use a simple example:
words_to_find = ['tree', 'cow', 'lots_of_other_words']
words_to_search = ['bathtub', 'more_words']
def find_words(words_to_search, words_to_find):
for i, fword in enumerate(words_to_find):
for j, sword in enumerate(words_to_search):
if fword == sword:
return (fword, i, j)
You can nest as many loops as you like here -- for, while, whatever -- and still break out of all of them easily once you've found what you want.
Let me know if that doesn't answer your question.
EDIT: I suppose if you wanted to have it all in one while loop you could do this:
word_list = ['blah', 'blah2', 'etc']
word_list2 = ['other', 'words']
i = 0
j = 0
while True:
if i == len(word_list):
i = 0
j += 1
elif thing_I_want(word_list[i], word_list2[j]):
do_something()
break
else:
i += 1

Random list with rules

I'm trying to create a list of tasks that I've read from some text files and put them into lists. I want to create a master list of what I'm going to do through the day however I've got a few rules for this.
One list has separate daily tasks that don't depend on the order they are completed. I call this list 'daily'. I've got another list of tasks for my projects, but these do depend on the order completed. This list is called 'projects'. I have a third list of things that must be done at the end of the day. I call it 'endofday'.
So here are the basic rules.
A list of randomized tasks where daily tasks can be performed in any order, where project tasks may be randomly inserted into the main list at any position but must stay in their original order relative to each other, and end of day tasks appended to the main list.
I understand how to get a random number from random.randint(), appending to lists, reading files and all that......but the logic is giving me a case of 'hurty brain'. Anyone want to take a crack at this?
EDIT:
Ok I solved it on my own, but at least asking the question got me to picture it in my head. Here's what I did.
random.shuffle(daily)
while projects:
daily.insert(random.randint(0,len(daily)), projects.pop(0))
random.shuffle(endofday)
daily.extend(endofday)
for x in daily: print x
Thanks for the answers, I'll give ya guys some kudos anyways!
EDIT AGAIN:
Crap I just realized that's not the right answer lol
LAST EDIT I SWEAR:
position = []
random.shuffle(daily)
for x in range(len(projects)):
position.append(random.randint(0,len(daily)+x))
position.sort()
while projects:
daily.insert(position.pop(0), projects.pop(0))
random.shuffle(endofday)
daily.extend(endofday)
for x in daily: print x
I LIED:
I just thought about what happens when position has duplicate values and lo and behold my first test returned 1,3,2,4 for my projects. I'm going to suck it up and use the answerer's solution lol
OR NOT:
position = []
random.shuffle(daily)
for x in range(len(projects)):
while 1:
pos = random.randint(0,len(daily)+x)
if pos not in position: break
position.append(pos)
position.sort()
while projects:
daily.insert(position.pop(0), projects.pop(0))
random.shuffle(endofday)
daily.extend(endofday)
for x in daily: print x
First, copy and shuffle daily to initialize master:
master = list(daily)
random.shuffle(master)
then (the interesting part!-) the alteration of master (to insert projects randomly but without order changes), and finally random.shuffle(endofday); master.extend(endofday).
As I said the alteration part is the interesting one -- what about:
def random_mix(seq_a, seq_b):
iters = [iter(seq_a), iter(seq_b)]
while True:
it = random.choice(iters)
try: yield it.next()
except StopIteration:
iters.remove(it)
it = iters[0]
for x in it: yield x
Now, the mixing step becomes just master = list(random_mix(master, projects))
Performance is not ideal (lots of random numbers generated here, we could do with fewer, for example), but fine if we're talking about a few dozens or hundreds of items for example.
This insertion randomness is not ideal -- for that, the choice between the two sequences should not be equiprobable, but rather with probability proportional to their lengths. If that's important to you, let me know with a comment and I'll edit to fix the issue, but I wanted first to offer a simpler and more understandable version!-)
Edit: thanks for the accept, let me complete the answer anyway with a different way of "random mixing preserving order" which does use the right probabilities -- it's only slightly more complicated because it cannot just call random.choice;-).
def random_mix_rp(seq_a, seq_b):
iters = [iter(seq_a), iter(seq_b)]
lens = [len(seq_a), len(seq_b)]
while True:
r = random.randrange(sum(lens))
itindex = r < lens[0]
it = iters[itindex]
lens[itindex] -= 1
try: yield it.next()
except StopIteration:
iters.remove(it)
it = iters[0]
for x in it: yield x
Of course other optimization opportunities arise here -- since we're tracking the lengths anyway, we could rely on a length having gone down to zero rather than on try/except to detect that one sequence is finished and we should just exhaust the other one, etc etc. But, I wanted to show the version closest to my original one. Here's one exploiting this idea to optimize and simplify:
def random_mix_rp1(seq_a, seq_b):
iters = [iter(seq_a), iter(seq_b)]
lens = [len(seq_a), len(seq_b)]
while all(lens):
r = random.randrange(sum(lens))
itindex = r < lens[0]
it = iters[itindex]
lens[itindex] -= 1
yield it.next()
for it in iters:
for x in it: yield x
Use random.shuffle to shuffle a list
random.shuffle(["x", "y", "z"])
How to fetch a random element in a list using python:
>>> import random
>>> li = ["a", "b", "c"]
>>> len = (len(li))-1
>>> ran = random.randint(0, len)
>>> ran = li[ran]
>>> ran
'b'
But it seems you're more curious about how to design this. If so, the python tag should probably not be there. If not, the question is probably to broad to get you any good answers code-wise.
Combine all 3 lists into a DAG
Perform all possible topological sorts, store each sort in a list.
Choose one from the list at random
In order for the elements of the "project" list to stay in order, you could do the following:
Say you have 4 project tasks: "a,b,c,d". Then you know there are five spots where other, randomly chosen elements can be inserted (before and after each element, including the beginning and the end), while the ordering naturally stays the same.
Next, you can add five times a special element (e.g. "-:-") to the daily list. When you now shuffle the daily list, these special items, corresponding to "a,b,c,d" from above, are randomly placed. Now you simply have to insert the elements of the "projects" list sequentially for each special element "-:-". And you keep the ordering, yet have a completely random list regarding the tasks from the daily list.

Categories