Python while loop terminating early - python

I am trying to make a Python program that churns out a solved sudoku puzzle. It randomly generates coordinates for a tile, and if that tile already has a number in it, it tries again. It then generates a number between 1 and 9 to put there, and if that number isn't already in that row, column, or section, it'll assign the value and add those coordinates to the list of occupied tiles. Once all the tiles are filled, it's supposed to exit the loop and return the completed grid.
The trouble is, it's always stopping for no reason after about 70 loops, causing the program to freeze.
Here is the code for the function I'm talking about:
def populate(grid):
usedCoords = []
populated = False
while not populated:
x = random.randrange(len(grid))
y = random.randrange(len(grid))
while [x,y] in usedCoords:
x = random.randrange(len(grid))
y = random.randrange(len(grid))
value = random.randrange(1, len(grid) + 1)
if not rowCheck(grid, x, y, value) and not columnCheck(grid, x, y, value) and not squareCheck(grid, x, y, value):
grid[x][y] = value
usedCoords.append([x,y])
print(len(usedCoords))
if len(usedCoords) == len(grid) ** 2:
populated = True
return grid
And here is the code for the functions it references:
def rowCheck(grid, x, y, value):
for i in range(len(grid)):
if not i == x:
if grid[i][y] == value:
return True
return False
def columnCheck(grid, x, y, value):
for i in range(len(grid)):
if not i==y:
if grid[x][i] == value:
return True
return False
def squareCheck(grid, x, y, value):
grid2 = [0] * (sectionSide) #new grid for the specific section
for i in range(len(grid2)):
grid2[i] = [0] * sectionSide
for i in range(x - (sectionSide - 1), x + sectionSide): #scanning only nearby coordinates
for j in range(y - (sectionSide - 1), y + sectionSide):
try:
if i // sectionSide == x // sectionSide and j // sectionSide == y // sectionSide:
grid2[i][j] = grid[x][y]
except IndexError:
pass
for i in range(len(grid2)):
for j in range(len(grid2[i])):
if grid2[i][j] == value and not (i == x and j == y):
return True
return False

There may be other issues, but a big problem with your code is that it has no way to backtrack if it finds it's created a board state that cannot be solved. Consider what would happen if your code put the following values on the first two rows of the board:
1 2 3 4 5 6 7 8 9
4 5 6 7 8 1 2 3
The numbers that have been placed so far are all legal, but there is no number that you can put in the last space of the second row. I'd guess that your code is eventually getting stuck when it creates a bunch of board positions like this, which cannot take any value. If there are no legal moves left it can make, it will keep on looping forever.
You need a more sophisticated algorithm to avoid this issue.

Related

Python: Loop through two lists, adding to them if condition is met, but looping through every element of the list everytime

If I run this once, it will work and return success. Then, when the loop runs again, it will only check the last element of the lists. How do I do this such that the loop runs and checks every element of the list in the condition statement?
def draw():
mesasX, mesasY = [], []
x, y = random.randint(6,94), random.randint(6,94)
mesasX.append(x)
mesasY.append(y)
for mesax, mesay in zip(mesasX, mesasY):
x, y = random.randint(6,94), random.randint(6,94)
if (x - 8 < mesax < x + 8 and y + 8 > mesay > y - 8):
print("Failed")
else:
mesasX.append(x)
mesasY.append(y)
print("Success!")
break
You want to compare every new item to every old item. The way to do this is to move the loops around. And use all:
def draw():
mesasX, mesasY = [], []
x, y = random.randint(6,94), random.randint(6,94)
mesasX.append(x)
mesasY.append(y)
while True:
x, y = random.randint(6,94), random.randint(6,94)
if all(x - 8 < mesax < x + 8 and y + 8 > mesay > y - 8
for mesax, mesay in zip(mesasX, mesasY)):
print("Failed")
else:
mesasX.append(x)
mesasY.append(y)
print("Success!")
break
This reads like this generate an item test if it fits if it doesn't try again if it does add it to the list and stop.
This can be made much faster if the list is replaced with a spacial index of 16 by 16 unit boxes centered at a point. This would permit one to turn the near by test into a point in box test which could be near constant time.
Then if one could uniformly sample from the union of these boxes that would remove the testing as every generated item would pass the test.

Using rolling window to accurately detect sequence in dataframe with repeating values (same head and same tail)

I have a multiple pandas dataframe each with a column containing values and another with a corresponding matching time.
i.e., : [z,x,y,n,z,z,x, etc.] [1.234, 2.4467, 2.999, 6.432, 9.6764, etc.]
I want to detect a specific pattern (i.e., z,x,y,n,z) and create a new column with info about whether the value is part of the sequence (called 'seq_bool', with either True or False for each value). Which then looks like this:
0 1 seq_bool
z 1.234 True
x 2.4467 True
y 2.999 True
n 6.432 True
z 9.6764 True
x 10.111 False
y 11.344 False
z 12.33 True
x 14.33 True
y 15.66 True
n 19.198 True
z 20.222 True
[...]
And then I use this information to compute some stats on the corresponding timepoints, essentially only taking values part of the sequence.
I have this already through the following code, from a solution already found on stackoverflow
def rolling_window(a, window):
shape = a.shape[:-1] + (a.shape[-1] - window + 1, window)
strides = a.strides + (a.strides[-1],)
c = np.lib.stride_tricks.as_strided(a, shape=shape, strides=strides)
return c
arr = new_df[0].values
b = np.all(rolling_window(arr, N) == sequence_pattern, axis=1)
c = np.mgrid[0:len(b)][b]
d = [i for x in c for i in range(x, x + N)]
new_df['seq_bool'] = np.in1d(np.arange(len(arr)), d)
My problem is that this doesn't accurately recognize the sequence, because the sequence starts and end with the same character (i.e., 'z' )
Specifically, if I have the following values in my data [z, x, y, n, z, x, y, n, z], the function recognizes all these values are being part of the sequence (and being all 'True') when in fact they are not. There is only one correct sequence(i.e., [z, x, y, n, z]).
I am somewhat new to python, and I don't know how to go about solving this. Is there a way to specify that, when a sequence has been found, output the necessary variable and then discard it and move forward to the next value in the column?
As to not mistakenly take the tail of the previous correct sequence (i.e., z) as the beginning of a new sequence.
Thank you
Building on what you already have, prior to using it, you could remove all values in c for which the distance to the previous value is less than 5, making sure to remove the relevant values before moving on. That is, if c = np.array([0, 7, 11, 15]), we'd remove 11 but keep 15.
Now, you can vectorize parts of this as necessary, but otherwise what you are looking for just boils down to
i = 0
while i < len(c)-1:
if c[i+1] - c[i] < 5:
c = np.delete(c, i+1)
else:
i += 1
My approach would be seeing this as a find-substring-problem. Have a look at this if you like:
word = ''.join(df['0'].values)
seq_bool = np.zeros(len(word)).astype(bool)
start = 0
while True:
idx = word.find('zxynz', start)
if idx < 0:
break
else:
start = idx + 5
seq_bool[idx:idx+5] = True
df['seq_bool'] = seq_bool
EDIT:
assumed there's at least one character which is known to never show up in df['0'], there's an even shorter way:
Let's say T would be ok for the indicator job:
word = ''.join(df['0'].values)
new_word = word.replace('zxynz', 'TTTTT')
df['seq_bool'] = np.array(list(new_word))=='T')

backtracking not trying all possibilities

so I've got a list of questions as a dictionary, e.g
{"Question1": 3, "Question2": 5 ... }
That means the "Question1" has 3 points, the second one has 5, etc.
I'm trying to create all subset of question that have between a certain number of questions and points.
I've tried something like
questions = {"Q1":1, "Q2":2, "Q3": 1, "Q4" : 3, "Q5" : 1, "Q6" : 2}
u = 3 #
v = 5 # between u and v questions
x = 5 #
y = 10 #between x and y points
solution = []
n = 0
def main(n_):
global n
n = n_
global solution
solution = []
finalSolution = []
for x in questions.keys():
solution.append("_")
finalSolution.extend(Backtracking(0))
return finalSolution
def Backtracking(k):
finalSolution = []
for c in questions.keys():
solution[k] = c
print ("candidate: ", solution)
if not reject(k):
print ("not rejected: ", solution)
if accept(k):
finalSolution.append(list(solution))
else:
finalSolution.extend(Backtracking(k+1))
return finalSolution
def reject(k):
if solution[k] in solution: #if the question already exists
return True
if k > v: #too many questions
return True
points = 0
for x in solution:
if x in questions.keys():
points = points + questions[x]
if points > y: #too many points
return True
return False
def accept(k):
points = 0
for x in solution:
if x in questions.keys():
points = points + questions[x]
if points in range (x, y+1) and k in range (u, v+1):
return True
return False
print(main(len(questions.keys())))
but it's not trying all possibilities, only putting all the questions on the first index..
I have no idea what I'm doing wrong.
There are three problems with your code.
The first issue is that the first check in your reject function is always True. You can fix that in a variety of ways (you commented that you're now using solution.count(solution[k]) != 1).
The second issue is that your accept function uses the variable name x for what it intends to be two different things (a question from solution in the for loop and the global x that is the minimum number of points). That doesn't work, and you'll get a TypeError when trying to pass it to range. A simple fix is to rename the loop variable (I suggest q since it's a key into questions). Checking if a value is in a range is also a bit awkward. It's usually much nicer to use chained comparisons: if x <= points <= y and u <= k <= v
The third issue is that you're not backtracking at all. The backtracking step needs to reset the global solution list to the same state it had before Backtracking was called. You can do this at the end of the function, just before you return, using solution[k] = "_" (you commented that you've added this line, but I think you put it in the wrong place).
Anyway, here's a fixed version of your functions:
def Backtracking(k):
finalSolution = []
for c in questions.keys():
solution[k] = c
print ("candidate: ", solution)
if not reject(k):
print ("not rejected: ", solution)
if accept(k):
finalSolution.append(list(solution))
else:
finalSolution.extend(Backtracking(k+1))
solution[k] = "_" # backtracking step here!
return finalSolution
def reject(k):
if solution.count(solution[k]) != 1: # fix this condition
return True
if k > v:
return True
points = 0
for q in solution:
if q in questions:
points = points + questions[q]
if points > y: #too many points
return True
return False
def accept(k):
points = 0
for q in solution: # change this loop variable (also done above, for symmetry)
if q in questions:
points = points + questions[q]
if x <= points <= y and u <= k <= v: # chained comparisons are much nicer than range
return True
return False
There are still things that could probably be improved in there. I think having solution be a fixed-size global list with dummy values is especially unpythonic (a dynamically growing list that you pass as an argument would be much more natural). I'd also suggest using sum to add up the points rather than using an explicit loop of your own.

Connect 4 Game logic

am developing http://en.wikipedia.org/wiki/Connect_Four game in python using tkinter.
I have come up with the board and the two player pieces. I am now trying to check if the game is over. I have implemented the following logic, but this doesnt seem to work.
def checkWin():
for row in range(canvas.data.rows):
for col in range(canvas.data.cols):
checkWinFromCell(row, col)
def checkWinFromCell(row, col):
if canvas.data.board[row][col] == 0:
return False
dirs = [[0,1], [1,0], [1,1], [1,-1], [0,-1], [-1,0], [-1,-1], [-1,1]]
for direction in dirs:
checkWinFromCellInDir(row, col, direction)
return False
def checkWinFromCellInDir(row, col, direction):
drow, dcol = direction[0], direction[1]
for i in range(1,4):
if row+i*drow<0 or row+i*drow>=canvas.data.rows or col+i*dcol<0 or col+i*dcol>=canvas.data.cols:
return False
if canvas.data.board[row][col] != canvas.data.board[row+i*drow][col+i*dcol]:
return False
return canvas.data.board[row][col]
I need to know the logic to check if my game has been completed ie the four dots have been connected.
I'm not very familiar with Tkinter, so this is a halfhearted answer at best. However since it's been nearly an hour and no answer is forthcoming, I did work one up for you.
class Map(list):
def __init__(self, tiles, width, height):
"""This object functions exactly as a tile map for your connect four
game. It is a subclass of list, so you can iterate through its rows.
"y" increases from top to bottom and "x" increases from left to right"""
for y in range(height):
self.append([random.choice(tiles) for x in range(width)])
# for ease of use, we're generating a random tile map here
def __str__(self):
return '\n'.join([' '.join([ch for ch in row]) for row in self])
# this will make print(map_object) show something pretty
Vector = collections.namedtuple("Vector", ['x','y'])
# build a namedtuple to contain our directions. It's a bit easier on the eyes
# to use object access, IMO. YMMV.
def checkwin(map_):
directions = [Vector(x, y) for (x, y) in [(1, 0), (-1, 1), (0, 1), (1, 1)]]
# directions = E, SW, S, SE
for y, row in enumerate(map_):
for x, ch in enumerate(row):
value = ch
if value == ' ': continue # blank squares can't win
for vector in directions:
result = checkfour(map_, x, y, vector)
if result:
return result
return False
def checkfour(map_, x, y, vector):
"""Checks map_ for four squares from the given x and y in vector direction"""
value = map_[y][x]
try:
lst = [map_[y + k*vector.y][x + k*vector.x]==value for k in range(1,4)]
# 2 2 2 1 would return [True, True, False]
return all(lst) and (x,y)
except IndexError:
return False
# we know we'll run off the edge of the map. It's cheaper (in programmer
# time) to simply return False here rather than check each square to make
# sure there ARE 3 more squares in vector-direction.
map_ = Map("12 ", 8, 8)
print(checkwin(map_))
# if your randomly generated map would win in connect four, it should print
# the first (x,y) coordinate that begins a win going E, SW, S, or SE
print(map_)

python list of lists index out of range [closed]

This question is unlikely to help any future visitors; it is only relevant to a small geographic area, a specific moment in time, or an extraordinarily narrow situation that is not generally applicable to the worldwide audience of the internet. For help making this question more broadly applicable, visit the help center.
Closed 9 years ago.
I have read in a matrix of numbers and I am trying to work with each cell and perform tests on each one. If the number is != 0 I want to use it so if it is 0 I am currently incrementing x and y to find a non zero number.
In the end I will just be first looking across the top row and then if they are all 0 start looking down the first column and this works fine as long as I am referring to just one (row or column).
Why am I receiving this error? Am I thinking incorrectly about how the cells are set up or is my code wrong?
The matrix looks like this:
0,2,4,1,6,0,0
2,0,0,0,5,0,0
4,0,0,0,0,5,0
1,0,0,0,1,1,0
6,5,0,1,0,5,5
0,0,5,1,5,0,0
0,0,0,0,5,0,0
When I start trying to do this:
y = y + 5
x = x + 5
node = Alist[x][y]
I receieve an error saying:
node = Alist[x][y]
IndexError: list index out of range
If I was to just write:
y = y + 5
node = Alist[x][y]
print node
It would work completely fine but when I introduce both x and y I start to get list index out of range problems. In my mind it should now read:
node = Alist[5][5]
Which is 0 if you follow the matrix
def create_matrix(file):
with open('network.txt') as f:
Alist = []
for line in f:
part = []
for x in line.split(','):
part.append(int(x))
Alist.append(part)
return Alist
#used to set the start node, used once
def start_node(Alist):
x=0
y=0
#point node to pos [0][0] of Alist
node = Alist[x][y]
#test if node == 0
while node == 0:
y = y + 5
x = x + 5
node = Alist[x][y]
#create a list to hold co-ordinates
if node != 0:
#return node for processing by check_neighbours
return node, x, y
#def current_node(Alist, x, y)
#test neighbours to see if they can be used
def check_neighbours(node, Alist, i, j):
#print out values of x and y
print "x = %d" %i
print "y = %d" % j
print "node in check_neighbours is " + str(node)
#running of code begins here
def main():
file = ("F:/media/KINGSTON/Networking/network.txt")
Alist = create_matrix(file)
node, x, y = start_node(Alist)
check_neighbours(node, Alist, x, y)
main()
It's the second iteration round the loop that's failing, because it sets x and y to 10, where you only have 7 items in each dimension.
Your loop while node == 0: moves down the diagonal of the matrix five steps at a time, falling off the end. It would fall off the end even if it went one at a time, because that diagonal is all zeros.
Perhaps you meant if node == 0?
I don't know what the code is actually trying to do, but you need to rethink the algorithm. :-)
If I follow your code in start_node you are
a) Not performing any kind of bounds checking
and
b) Jumping down the diagonal elements of your matrix, which are all zero, until you go out of bounds, as your error tells you. Presumably when you are incrementing only one of your indices you are lucky in that you hit a non-zero element before going out of bounds
In this code, start_node returns the first non-zero "node".
I used mat instead of Alist as I feel it is more Pythonic.
Comments note the changes and tips.
def create_matrix(fname): # don't use built-ins as variable names!!
with open(fname) as f: # you take filename in arguments, but don't use it..
# why not use in-built functions and list comprehensions ?
mat = [map(int,line.split(',')) for line in f]
return mat
#used to set the start node, used once
def start_node(mat):
# for each row in matrix (index)..
for i1 in range(len(mat)):
# for each cell in row (index)..
for i2 in range(len(mat[i1])):
# mat[i1][i2] is the node/cell..
if mat[i1][i2] != 0:
# return node, x, y :)
return mat[i1][i2], i2, i1
# test neighbours to see if they can be used
# same as yours
def check_neighbours(node, Alist, i, j):
#print out values of x and y
print "x = %d" % (i,)
print "y = %d" % (j,)
print "node (in check_neighbours) is " + str(node)
#running of code begins here
def main():
fname = "F:/media/KINGSTON/Networking/network.txt"
mat = create_matrix(fname)
node, x, y = start_node(mat)
check_neighbours(node, mat, x, y)
main()
Explanation:
mat = [map(int,line.split(',')) for line in f]
This is a list comprehension. map is an in-built method.
The for loops in start_node...
for i1 in range(len(mat)): # 1
for i2 in range(len(mat[i1])): # 2
len(mat) is the number of rows in your matrix.
range(len(mat)) gives us the indexes of each row.
len(mat[i1]) gives us the length of the current row.
range(len(mat[i1])) gives us the indexes of each column in that row.
In your code, you were incrementing both x and y (here i2 and i1 respectively) so were moving diagonally.

Categories

Resources