So, I'm generating an array of spaces, which have the property that they can be either red or black. However, I want to prevent red from being enclosed by black. I have some examples to show exactly what I mean:
0 0 0 0 0 0 0 1
0 1 0 0 0 0 1 0
1 0 1 0 0 0 0 1
0 1 0 0 1 1 1 0
0 0 0 0 1 0 1 0
1 1 1 0 1 1 1 0
0 0 1 0 0 0 0 0
0 0 1 0 0 0 0 0
If red is 0 and black is 1, then this example contains four enclosures, all of which I want to avoid when I generate the array. The inputs I have are the size of the array and the number of 1s I can generate.
How would I go about doing this?
Does this code fits well for you?
Basically I fill a matrix from left to right, from top to bottom.
When I have to assign 0 or 1 to a cell, I check (north and west) if adding a 1 could enclose a 0; in this case I put a 0, else a random 0 or 1.
import sys, random
n = int(sys.argv[1])
m = int(sys.argv[2])
# fill matrix with zeroes
matrix = [[0 for _ in xrange(m)] for _ in xrange(n)]
# functions to get north, south, west and east
# cell wrt this cell.
# If we are going out of bounds, we suppose the matrix
# is sorrounded by 1s.
def get_n(r, c):
if r <= 0: return 1
return matrix[r - 1][c]
def get_s(r, c):
if r >= n - 1: return 1
return matrix[r + 1][c]
def get_w(r, c):
if c <= 0: return 1
return matrix[r][c - 1]
def get_e(r, c):
if c >= m - 1: return 1
return matrix[r][c + 1]
# Checks if the cell is already enclosed by 3 1s.
def enclosed(r, c):
enclosing = get_n(r, c) + get_s(r, c) + get_w(r, c) + get_e(r, c)
if (enclosing > 3): raise Exception('Got a 0 enclosed by 1s')
return enclosing == 3
for r in xrange(n):
for c in xrange(m):
# check west and north
if enclosed(r, c - 1) or enclosed(r - 1, c):
matrix[r][c] = 0
else:
matrix[r][c] = random.randint(0, 1)
print str(matrix[r][c]) + ' ',
print ''
Sample run: python spaces.py 10 10
So you can do the following:
Fill array with zeroes
Randomly select a point
If the condition holds, flip color
Repeat from step 2 or exit
The condition holds for all-zeros array. It is hold on any iteration. So, by induction, it is also true for the final array.
In the step 4 you can decide whether to stop or continue by doing, say N=a*b*1000 iterations or whether the ratio red/black is close to 1. In both cases, the result would be slightly biased since you start from all zeros.
Now, what is the condition. You have to ensure that all black points connected and all red points connected as well. In other words, there's maximum 2 connected clusters. Flipping a color could create more connected clusters, so you flip only when the its number is one or two. You can do the check quite efficiently using Union-Find algorithm, described here.
Edit: if however you want to permit black points to be surrounded by red ones but not vice-versa, you may change the condition to have any number of black clusters but only 0 or 1 of red clusters.
This would be a possible way to check the condition:
def: findStart(myArr):
for i in range(len(myArr)):
for j in range(len(myArr[0])):
if(myArr[i][j] == 0):
return (i,j)
def: checkCon(myArr, number_Ones):
width = len(myArr[0])
height = len(myArr)
pen = [] #A list of all points that are waiting to get a visit
vis = [] #A list of all points that are already visited
x = findStart(myArr)
while(len(pen) != 0): #Visit points as long as there are points left
p = pen.pop() #Pick a point to visit
if p in vis:
#do nothing since this point already was visited
else:
vis.append(p)
x,y = p
#A vertical check
if(x == 0 and myArr[x+1][y] == 0):
pen.append((x+1,y))
elif(x == (height-1) and myArr[x-1][y] == 0):
pen.append((x-1,y))
else:
if(myArr[x-1][y] == 0 and x-1 >= 0):
pen.append((x-1,y))
if(myArr[x+1][y] == 0):
pen.append((x+1,y))
#A horizontal check
if(y == 0 and myArr[x][y+1] == 0):
pen.append((x,y+1))
elif(y == (width-1) and myArr[x][y-1] == 0):
pen.append((x,y-1))
else:
if(myArr[x][y+1] == 0):
pen.append((x,y+1))
if(myArr[x][y-1] == 0 and y-1 >= 0):
pen.append((x,y-1))
print((height*width-number_Ones) == len(vis)) #if true, alle Zeros are connected and not enclosed
To clarify this is just a concept to check the condition. The idea is to visit all connected zeros and see if there are any left (that are not connected). If that is the case, there are some enclosed.
This method also doesn't work when the 1's form a frame around the matrix like this:
1 1 1 1
1 0 0 1
1 0 0 1
1 1 1 1
Again, just a concept :)
The problem has two parts actually. Generating the board state, and then checking if it is correct. I realised that checking the correctness was actually worse than just being sure correct states were always generated. This is what I did:
Note that I have defined self.WallSpaces to be an array equal in length to the height of my array, comprised of integers with the number of bits equal to the width of my array. self.Width and self.Height provide the end indices for the array. Basically, Intersects works by checking all the spaces surrounding a point for 1s, except the direction the space was "built from" (see below) and returning True if any of these are the edge of the array or a 1.
def Intersects(self, point, direction):
if (point[0] > 0):
if (direction != [1, 0] and self.WallSpaces[point[0] - 1] & (1 << point[1]) != 0):
return True
if (point[1] == 0 or self.WallSpaces[point[0] - 1] & (1 << (point[1] - 1)) != 0):
return True
if (point[1] == self.Width or self.WallSpaces[point[0] - 1] & (1 << (point[1] + 1)) != 0):
return True
else:
return True
if (point[0] < self.Height):
if (direction != [-1, 0] and self.WallSpaces[point[0] + 1] & (1 << point[1]) != 0):
return True
if (point[1] == 0 or self.WallSpaces[point[0] + 1] & (1 << (point[1] - 1)) != 0):
return True
if (point[1] == self.Width or self.WallSpaces[point[0] + 1] & (1 << (point[1] + 1)) != 0):
return True
else:
return True
if (point[1] == 0 or (direction != [0, 1] and self.WallSpaces[ point[0] ] & (1 << (point[1] - 1)) != 0)):
return True
if (point[1] == self.Width or (direction != [0, -1] and self.WallSpaces[ point[0] ] & (1 << (point[1] + 1)) != 0)):
return True
return False
The directions GPacW.Left, GPacW.Right, GPackW.Up, and GPacW.Down represent the cardinal directions for movement. This function works by constructing "walls" in the array from random points, which can turn in random directions, ending when they have intersected twice.
def BuildWalls(self):
numWalls = 0
directions = [ [GPacW.Left, GPacW.Right], [GPacW.Up, GPacW.Down] ]
start = [ random.randint(0, self.Height), random.randint(0, self.Width) ]
length = 0
horizontalOrVertical = random.randint(0, 1)
direction = random.randint(0, 1)
d = directions[horizontalOrVertical][direction]
intersected = False
while (numWalls < self.Walls):
while (start == [0, 0] or start == [self.Height, self.Width] or self.Intersects(start, d)):
start = [ random.randint(0, self.Height), random.randint(0, self.Width) ]
if (length == 0):
horizontalOrVertical = not horizontalOrVertical
direction = random.randint(0, 1)
length = random.randint(3, min(self.Height, self.Width))
d = directions[horizontalOrVertical][direction]
if (self.WallSpaces[ start[0] ] & (1 << start[1] ) == 0):
self.WallSpaces[ start[0] ] |= 1 << start[1]
numWalls += 1
length -= 1
if (0 <= (start[0] + d[0]) <= self.Height and 0 <= (start[1] + d[1]) <= self.Width):
start[0] += d[0]
start[1] += d[1]
else:
start = [0,0]
if (self.Intersects(start, d)):
if (intersected):
intersected = False
start = [0,0]
length = 0
else:
intersected = True
return
Related
I've been struggling to find the right logic for my sudoku solver. So far I've created a function to take (x,y) coordinates and the number and check if it is a legal move.
Though I don't understand how I can iterate through the grid replacing every 0 with a legal number, then go back and fix numbers that make the solve incorrect.
For example sometimes I will be left with 0's on the grid because some numbers can no longer be played. I took a deeper look at it and realized that numbers played previously in the same row were legal but ruined the puzzle. Instead, if those numbers were a bit higher the puzzle would be solved.
I understand backtracking would be my friend here but, I have no clue on how to implement it. So far here is what I have.
solve.py
import numpy as np
import time
class sodoku:
def __init__(self,grid,boxRange):
self.grid = grid
self.boxRange = boxRange
def show(self):
for row in self.grid:
print(row)
def solve(self):
def possible(num,x,y):
def box(x,y):
board = np.array(self.grid)
result = {}
size = 3
for i in range(len(board) // size):
for j in range(len(board) // size):
values = board[j * size:(j + 1) * size, i * size:(i + 1) * size]
result[i * size + j + 1] = values.flatten()
if y <= 2 and x <= 2:
squareBox = result[1]
if (y <= 5 and y > 2) and x <= 2:
squareBox = result[2]
if (y <= 8 and y > 5) and x <= 2:
squareBox = result[3]
if (y <= 2 ) and (x <= 5 and x > 2):
squareBox = result[4]
if (y <= 5 and y > 2)and (x <= 5 and x > 2):
squareBox = result[5]
if (y <= 8 and y > 5)and (x <= 5 and x > 2):
squareBox = result[6]
if (y <= 2) and (x <= 8 and x > 5):
squareBox = result[7]
if (y <= 5 and y > 2)and (x <= 8 and x > 5):
squareBox = result[8]
if (y <= 8 and y > 5)and (x <= 8 and x > 5):
squareBox = result[9]
return squareBox
row = self.grid[y]
column= [r[x] for r in self.grid]
square = box(x,y)
if (num not in row) and (num not in column) and (num not in square):
return True
else:
return False
y = 0
for row in self.grid:
x = 0
for number in row:
if number == 0:
for i in range(1,10):
if possible(i,x,y):
row[x] = i
elif i == 9 and possible(i,x,y) == False: pass
#what would I do here now
x += 1
y += 1
boxRange = "3x3"
bxd = []
with open('board.txt', 'r') as f:
for line in f:
line = line.strip()
line = line.split(' ')
bLine = [int(x) for x in line]
bxd.append(bLine)
# brd = [[3,0,0,2],[0,4,1,0],[0,3,2,0],[4,0,0,1]]
brd = sodoku(bxd,boxRange)
brd.show()
brd.solve()
print('-----Solved------')
brd.show()
board.txt
5 3 0 0 7 0 1 0 0
6 0 0 1 9 5 0 0 0
0 9 8 0 0 0 0 6 0
8 0 0 0 6 0 0 0 3
4 0 0 8 0 3 0 0 1
7 0 0 0 2 0 0 0 6
0 6 0 0 0 0 2 8 0
0 0 0 4 1 9 0 0 5
0 0 0 0 8 0 0 7 9
Recursive pseudocode backtracing sudoku solver:
#solve will return a solved board, or None if it fails
def solve(board):
#case 1: board is solved
if board.is_solved: #simple check for leftover empty spaces
return board #board is solved. unzip the call stack
pos = board.next_empty_space()
valid = [i for i in range(1,10) if board.is_valid(pos, i)]
#case 2: not solved and no more valid moves
if not valid:
return None #no valid moves left
new_board = copy(board) #don't step on the original data in case this isn't the solution
for value in valid:
new_board[pos] = value
result = solve(new_board)
#case 3: one of the valid moves led to a valid solution
if result is not None: #we found a fully solved board
return result #here's where we unzip the call stack
#case 4: none of the valid moves led to a valid solution
return None #none of the valid moves panned out
Basically you consider each empty space on the board as a branch in a tree, and sub-branches from each branch you insert a new number which is currently valid at that point in the tree. If you get to the end of a branch, and there are no more valid moves left (sub-branches) you have either successfully filled in all the blank spaces or one of the numbers is wrong. When None gets returned, and execution goes back to the caller (up a frame in the call stack), the local position in the for loop going over valid moves is what "remembers" where you're at, and what the next possible valid move should be. It's basically a depth-first tree search algorithm for a correct board state.
I am trying to create a function that finds if a move is winning on an NxN board where the winning condition is M pieces in a row in Python 3.
I am pretty new to programming and in my specific case I am creating a Gomoku game (15x15 board with 5 pieces in a row to win). To get it working I created 6 for loops to check vertical, horizontal and 4 diagonals. See the code below for examples on the 2 options for left to right digonals. This takes way too long though when I need to loop through it many times (8) for computer to find if I can win or if it has a winning move.
end_row = 15
for j in range(11):
end_row -= 1
counter = 0
for i in range(end_row):
if board[i+j][i] == board[i+1+j][i+1] and board[i+j][i] != ' ':
counter += 1
if counter == 4:
winning_line = [(i+j-3, i-3), (i+j-2, i-2), (i+j-1, i-1), (i+j, i), (i+1+j, i+1)]
winner = True
break
else:
counter = 0
# Top left to bottom right, lower side
end_row = 15
for j in range(11):
end_row -= 1
counter = 0
for i in range(end_row):
if board[i][i+j] == board[i+1][i+1+j] and board[i][i+j] != ' ':
counter += 1
if counter == 4:
winning_line = [(i-3, i+j-3), (i-2, i+j-2), (i-1, i+j-1), (i, i+j), (i+1, i+1+j)]
winner = True
break
else:
counter = 0
# What I want to do instead, where x and y are coordinates of last move:
# Horizontal
counter = 0
for i = x - (n - 1) to x + (n - 1):
if board[i][y] == board[x][y] :
counter++
else :
counter = 0
if counter == n:
return true
The problem with the lower part of the code is that if I place a piece on e.g. position (0, 0) the program will complain when trying to reach board[-4][0] in the first looping. I will have to place lots of if statements when I get close to the edge, which is not an elegant solution.
I thought of making a 3*15 x 3*15 board instead, where the actual board is the inner 15x15 part and the rest just contains placeholders:
15x15 || 15x15 || 15x15
15x15 || board || 15x15
15x15 || 15x15 || 15x15
This to avoid getting outside of my list of lists when looping through. Not an elegant solution either, but takes less space in the code.
Any suggestions on how to solve this problem? Thank you in advance from a beginner programmer!
As #MePsyDuck mentioned in comments, you can use min and max functions to limit the range to only reference valid squares in the board matrix.
Furthermore, you could make a generic function that does the count-job on any given list of values. Then you can call that generic function four times: once for every direction (horizontal, vertical, diagonal \ and diagonal /)
Here is how that could work:
def is_win(board, n, x, y):
end_row = len(board)
color = board[x][y]
def check(values):
counter = 0
for value in values:
if value == color:
counter += 1
else:
counter = 0
if counter == n:
return True
return False
return (check([board[i][y] for i in range(max(0, x - n + 1), min(end_row, x + n))])
or check([board[x][i] for i in range(max(0, y - n + 1), min(end_row, y + n))])
or check([board[x+i][y+i] for i in range(max(-x, -y, 1 - n), min(end_row - x, end_row - y, n))])
or check([board[x+i][y-i] for i in range(max(-x, y - end_row + 1, 1 - n), min(end_row - x, y + 1, n))]))
Instead of looping from 0 to 14, just loop from 0 to (board_size - winning_length).
Here's an example for a 1-dimensional board:
BOARD_SIZE = 15
WINNING_LENGTH = 5
for x in range(BOARD_SIZE - WINNING_LENGTH):
players_here = set()
for pos in range(x, x + WINNING_LENGTH):
players_here.add(board[pos])
if len(players_here) == 1:
# Exactly 1 player occupies every position in this line, so they win
In my code I did the same thing twice, but it works only the first time.
for y, row in enumerate(matrix):
for x, color in enumerate(row):
if matrix[y][x] == 1:
som = (matrix[y-1][x-1] + matrix[y-1][x] + matrix[y-1][x+1] + matrix[y][x-1] + matrix[y][x+1] + matrix[y+1][x-1] + matrix[y+1][x] + matrix[y+1][x+1])
if som == (2 or 3):
matrix[y][x] = 1
else:
matrix[y][x] = 0
pygame.display.update()
time.sleep(1)
else:
#here somewhere it goes wrong
som = (matrix[y-1][x-1] + matrix[y-1][x] + matrix[y-1][x+1] + matrix[y][x-1] + matrix[y][x+1] + matrix[y+1][x-1] + matrix[y+1][x] + matrix[y+1][x+1])
if som == 3:
matrix[y][x] = 1
else:
matrix[y][x] = 0
When I tried this code without the second else, it worked perfect. Now it gives an error: IndexError: list index out of range.
Also I want that the loop is only repeated when 1 second has passed. When I printed som I could see that it only repeated after a second, but on the display from the game, nothing changed until suddenly ten 1s turned into 0s.
How can I change this, so that after every second the display gets updated?
If you loop over a sequence using enumerate then by definition the valid indices are [0] to [x]. So when you index [x+1] you will index out of bounds.
Similarly, when x == 0 your index [x-1] will be [-1] which will index the back of your sequence, which I doubt is what you're expecting.
Your issue is that if you were in the first row of the matrix, you were trying to access the row above it (y-1) which doesn't exist. The same goes for when you are in the last row and accessing y+1, and the same for the x axis.
When you access index y-1 and y is 0 it won't throw the exception, but it would actually give you the value from the end of the list. The exception is thrown when the index does not exist in the list.
I've made a lot of changes to your code to reduce repetition. It should be a lot easier to understand what is going on and I've also implemented the checks to stop the IndexError and your conditional as mentioned by #ForceBru in the comments.
I have gone with the assumption that if the index doesn't exist, to default the value to 0.
for y,row in enumerate(matrix):
for x,color in enumerate(row):
center = matrix[y][x]
top = matrix[y-1][x] if y > 0 else 0
top_right = matrix[y-1][x+1] if y > 0 and x < len(row)-1 else 0
right = matrix[y][x+1] if x < len(row)-1 else 0
bottom_right = matrix[y+1][x+1] if y < len(matrix)-1 and x < len(row)-1 else 0
bottom = matrix[y+1][x] if y < len(matrix)-1 else 0
bottom_left = matrix[y+1][x-1] if y < len(matrix)-1 and x > 0 else 0
left = matrix[y][x-1] if x > 0 else 0
top_left = matrix[y-1][x-1] if y > 0 and x > 0 else 0
surround_sum = (top_left + top + top_right + left + right + bottom_left + bottom + bottom_right)
if center == 1:
if surround_sum == 2 or surround_sum == 3:
matrix[y][x] = 1
else:
center = 0
pygame.display.update()
time.sleep(1)
else:
#here somewhere it goes wrong
if surround_sum == 3:
matrix[y][x] = 1
else:
matrix[y][x] = 0
TLDR: I want to navigate all directions in a 2D array starting at the center
I am trying to do a particular navigation in a 2D Python array. Say I have this 2D array (A1):
I want to navigate through this array and apply the summation of the index for A1 * index of A2 where A2 is another 2D array (A2):
When applying the summation, I want the current index, to start with the center of the second 2D array. So at A1[0][0] (1) I want to use the center of A2 (A21, or 0). I then want to move through each item in the second array and continue this process:
1(0) + 2(1) + 5(2) + 6(1)
This will take care of all the items from A2[center x][center y]. I then want to double back, and add all the values that are below the center. Here is a quick image illustrating what I want to do, one showing a case where we are out of bounds, and on, in bounds:
What I am trying to do is write a function that will navigate, from the center of a 2D array, out in each direction of that 2D array. Here is the code I have for it:
for row in range(A1.length):
for column in range(A1[0].length):
cRow = 0
pSum = 0
kRow = midRow - 1
while kRow < A2.length and row + kRow < A1.length:
kColumn = midColumn - 1
cColumn = 0
while kColumn < A2[0].length and column + kColumn < A1[0].length:
pSum += float(A1[row + cRow][column + cColumn] * A2[kRow][kColumn])
kColumn += 1
cColumn += 1
kRow += 1
cRow += 1
nKRow = midRow - 1
cRow = 0
#Get rows past
while nKRow >= 0 and row - nKRow >= 0:
nKColumn = midColumn - 1
cColumn = 0
while nKColumn >= 0 and column - nKColumn >= 0:
# Account for row 0
if row == 1 and cColumn == 0:
pSum += float(A1[0][column - cColumn] * A2[nKRow][nKColumn])
else:
pSum += float(A1[row - cRow][column - cColumn] * A2[nKRow][nKColumn])
nKColumn -= 1
cColumn += 1
nKRow -= 1
cRow += 1
value = float(pSum / average)
A3[row][column] = value
This works in most cases, but does not work correctly in edge cases. The problem is that I am having issues setting values when row = 0 or column = 0.
Is there a better approach to navigate every direction in a 2d array starting at the center?
It looks like you are trying to do kernel convolution, yes?
Where are midRow, midColumn, and average defined in your example?
Why do you have to start in the center of A2? Summation and multiplication are both commutative operations, so the order shouldn't matter. It would be much simpler to start in the top left corner of A2. Something like (I haven't tested this):
kernelSize = 3
kernelOffset = int(kernelSize / 2) # Assumes only odd-sized kernels
for x in xrange(len(A1)):
for y in xrange(len(A1[0])):
total = 0
for kx in xrange(kernelSize):
for ky in xrange(kernelSize):
offsetX = -kernelOffset + kx # Should give something in the range [-1, 1]
offsetY = -kernelOffset + ky
currentX = x + offsetX
currentY = y + offsetY
if currentX < 0 or currentX >= len(A1) or currentY < 0 or currentY >= len(A1[0]): # do nothing if "out of bounds"
continue
total += A1[currentX][currentY] * A2[kx][ky]
A3[x][y] = total
I need to write a function def amountofNeighbours(row, column) that prints the amount of neighbours there are to a certain element in the matrix. For example, given the matrix [[2, 3, 4], [5, 6, 7], [8, 9, 10]], there are three neighbours to the element 2 at position [0][0], while there are eight neighbours to the element 6 at position [1][1]. I'm not sure what is the best way to handle a problem like this. I went through all the possibilities, and this gave me the following:
def amountofNeighbours(row, column):
neighbours = 0
for i in range(row):
for j in range(column):
if i == 0 and j == 0 or i == 0 and j == column - 1:
neighbours = 3
elif i == row - 1 and j == 0 or i == row-1 and j == column - 1:
neighbours = 3
elif i > 0 and j == 0 or i == 0 and j > 0:
neighbours = 5
elif i == row - 1 and j > 0:
neighbours = 5
elif j == column - 1 and i > 0:
neighbours = 5
else:
neighbours = 8
return neighbours
When I called this with amountofNeighbours(1, 1) it gave me the correct answer, namely 3, but if I called it with amountofNeighbours(2,2) the answer should be 8 while it gave me 3. Anyone has an idea for improvement?
A straight forward way to do it is to say, "If the cell is at corner, it has three neighbors, otherwise if it is on an edge, it has five, otherwise it has 8."
def numberOfNeighbors(rows, columns, row, column):
topBottom = row in (0, rows-1)
leftRight = column in (0, columns-1)
if topBottom and leftRight:
return 3
if topBottom or leftRight:
return 5
return 8
Your function as it is designed now does not do what you specified. It takes in the number of rows and columns. Then it loops through all elements of your matrix and calculates the number of neighbours. Then it returns the last value calculated, so the bottom right element of your matrix, which has 3 neighbours indeed.
You should get rid of the loops to get it to do what you want. To clarify:
def amountofNeighbours(row, column, n_rows, n_cols):
neighbours = 0
if row == 0 and column == 0 or row == 0 and column == n_cols - 1:
neighbours = 3
elif row == n_rows - 1 and column == 0 or row == n_rows-1 and column == n_cols - 1:
neighbours = 3
elif row > 0 and column == 0 or row == 0 and column > 0:
neighbours = 5
elif row == n_rows - 1 and column > 0:
neighbours = 5
elif column == n_cols - 1 and row > 0:
neighbours = 5
else:
neighbours = 8
return neighbours
One solution to avoid many IFs is to start from the element and compute an "enlarged" box around it (a 3x3 square) possibly placed partially outside the matrix.
Then you clamp the result and return the number of elements minus one:
def neighbors(row, col, rows, cols):
ra, rb = row-1, row+2
ca, cb = col-1, col+2
dx = min(cb, cols) - max(0, ca)
dy = min(rb, rows) - max(0, ra)
return dx*dy - 1
The image shows the selected element and the enlarged box around it. The min/max operation will cut off the extra squares leaving a 2x3 box resulting in 2*3-1=5 neighbors count.