Recently I was given a programming assignment to mimic the 8 queens problem which I'm sure you are all aware of and was tasked with creating functions for different pieces on the board. For example, placing a queen on the board in such a way that she won't see other queens.
Below you'll find my first function, queensees, which basically just highlights the queen's position on the board and identifies whether or not she can see others.
def queenSees(pos,size):
""" Return a list of all squares"In view" of a queen in position pos on a board of size"""
inView=[]
#Row and column
for i in range(size):
#Column
setAppend(inView,(i,pos[1]))
#Row
setAppend(inView,(pos[0],i))
#Diagonals
for r in [-1,1]:
for c in [-1,1]:
appendIfInBounds(inView, pointShift(pos,r*i,c*i), size)
#Take out position of queen so she doesn't see herself...
inView.remove(pos)
Below I've also written a function for the rook piece because it was simply a matter of taking the diagonal search out of the equation.
def rooksees(pos,size):
""" Return a list of all squares"In view" of a rook in position pos on a board of size"""
inView=[]
#Row and column
for i in range(size):
#Column
setAppend(inView,(i,pos[1]))
#Row
setAppend(inView,(pos[0],i))
appendIfInBounds(inView, pointShift(pos,r*i,c*i), size)
#Take out position of queen so he doesn't see himself...
inView.remove(pos)
How would I modify this to accommodate the knight chess piece?
A knight moves one of two distances in a cardinal direction, and the other of those two distances in an orthogonal direction. 4 original directions * 2 orthogonal directions each * 2 original distances = 16 possible places. It looks like you already know how to filter out theoretical positions that are actually off the board, so all you need to do is generate those 16 positions in the first place. If you can't see a way to come up with those programaticaly, you could just hard code them like so:
positions = [[pos[0]+2,pos[1]+1],[pos[0]+2,pos[1]-1] ... ]
But this is very prone to minor typos leading to hard-to-track bugs. A better way might be to just hard code the "adjustments":
moves = [(2,1),(2,-1),(-2,1)(-2,-1) ... ]
And then use a loop to apply them to the current position:
positions = [[pos[0]+d[0],pos[1]+d[1]] for d in moves]
all you need to do for the rook the comment out the diagonal code.
I having trouble implementing a knight see even though I know the coordinates: can anyone tell me how to change the queensees function to display the knight - remember i have commented out the queensees function element i to try and display a knights movements which is not working.
def makeBoard(size):
board=[]
for i in range(size):
board.append([])
for j in range(size):
board[-1].append(False)
return board
def displayBoard(b):
divider=("+---"*len(b))+"+"
for row in b:
print divider
print "|"+"|".join({True:" X ",False:" "}[i] for i in row)+"|"
print divider
def setAppend(s,i):
""" Add i to s unless i is already in s """
if not i in s: s.append(i)
def inBoard(p,size):
""" if point is valid for a board of given size, return True. Else return False """
if 0<=p[0]
def pointShift(p,r,c):
""" Return position of cell r,c away from given point p """
return (p[0]+r,p[1]+c)
def appendIfInBounds(s,p,size):
""" If point p is within the bounds of a board of given size, append to s unless it's already there """
if inBoard(p,size):
setAppend(s,p)
def queenSees(pos,size):
""" Return a list of all squares "In view" of a queen in position pos on a board of size"""
inView=[]
moves = [(-2, -1), (-2, +1), (+2, -1), (+2, +1), (-1, -2), (-1, +2), (+1, -2), (+1, +2)]
#Row and column
for i in range(size):
setAppend(inview,(moves[i]))
#Column
#setAppend(inView,(i,pos[1]))
#Row
#setAppend(inView,(pos[0],i))
#Diagonals
#for r in [-1,1]:
#for c in [-1,1]:
# appendIfInBounds(inView, pointShift(pos,r*i,c*i), size)
#Take out position of queen so she doesn't see herself...
inView.remove(pos)
return inView
def hasQueen(board, points):
""" Returns True if any of the given points on the given board contain a queen """
for p in points:
if board[p[0]][p[1]]:
return True
return False
def cloneBoard(b,size):
""" Make a copy of a board. Boards are objects (lists are objects) so a=b just makes them refer to the same object..."""
c=makeBoard(size) #clone
for i in range(size):
for j in range(size):
c[i][j]=b[i][j]
return c
def fillBoardRecursion(board,row, size):
""" Given a board completed to given row, try all possible positions for next row and continue """
if row==size:
#Base case
return board
else:
for col in range(size):
#If we put a queen here, would it "see" another?
if not hasQueen(board,queenSees((row,col),size)):
b=cloneBoard(board,size)
b[row][col]=True
result= fillBoardRecursion(b,row+1,size)
if result!=False:
return result
return False #Failed at this point, so return False
b=makeBoard(8)
b=fillBoardRecursion(b,0,8)
displayBoard(b)
Related
This is a game of the Eight Puzzle (3x3). I have to move the empty space in the puzzle to the left (if possible). I can't figure out how to check or change values of keys in the directory which get_empty() returns.
DEFAULT_BOARD = [[1, 2, 3], [4, 5, 6], [7, 8, 0]]
'''
A default board if another board is not specified in the constructor
'''
def get_empty(board):
'''
Finds the position of the empty space in the board
:return: a dictionary eith the row and col of the empty space
'''
for i in range(3):
for j in range(3):
if board[i][j] == 0:
**return {"row": i, "col": j}**
def __init__(self, the_board = DEFAULT_BOARD):
'''
Makes a new node from an initial board
:param the_board: the initial board
'''
if EightGameNode.legal_board(the_board):
# if the board is legal go on
self.board = the_board
else:
# otherwise use the default board
self.board = EightGameNode.DEFAULT_BOARD
self.empty = EightGameNode.get_empty(self.board)
# set the empty space of the board
def move_left(self):
'''
Moving the space to left
:return: a new board position or None if not possible
'''
if self.empty ==
#If the "col" key value is 0 the empty space can't move to the left
return None
else:
#Increase the col value by +1
self.empty
Not sure I really understand what you are asking. You should not just update the self.empty dict, but instead move the actual values on the board and then use your function to re-calculate self.empty
def move_left(self):
r, c = self.empty["row"], self.empty["col"]
# assuming with "move left" you mean to move the "hole" left
if c > 0:
self.board[r][c] = self.board[r][c-1] # move piece to the right into the "hole"
self.board[r][c-1] = 0 # new position of the empty cell
self.empty = EightGameNode.get_empty(self.board)
This will ensure consistency between self.board and self.empty and make the code a bit shorter and less prone for copy-paste errors between the different move methods. Of course, you could also update self.empty directly, which would be faster than scanning the whole board again, but if this is actually a game played by a human (and not e.g. using millions of playouts to train an AI or similar) this will not matter at all.
If speed really is important, and this poses too much of a slow-down, you can update the empty position like this, within the if, and remove the call to get_empty below:
self.empty["col"] -= 1 # update self.empty to new "hole" position
This should work
def move_left(self):
'''
Moving the space to left
:return: a new board position or None if not possible
'''
if self.empty["col"] == 0:
return None
else:
self.empty["col"] = self.empty["col"] - 1
I'm trying to educate myself better about dynamic programming, and hoping to do so by attempting to solve the following problem (for reference here's a solution to it).
You have a keyboard layout as shown above in the XY plane, where each English uppercase letter is located at some coordinate, for example, the letter A is located at coordinate (0,0), the letter B is located at coordinate (0,1), the letter P is located at coordinate (2,3) and the letter Z is located at coordinate (4,1).
Given the string word, return the minimum total distance to type such string using only two fingers. The distance between coordinates (x1,y1) and (x2,y2) is |x1 - x2| + |y1 - y2|.
Note that the initial positions of your two fingers are considered free so don't count towards your total distance, also your two fingers do not have to start at the first letter or the first two letters.
For example for the input word "HAPPY" we would have:
Output: 6
Explanation:
Using two fingers, one optimal way to type "HAPPY" is:
Finger 1 on letter 'H' -> cost = 0
Finger 1 on letter 'A' -> cost = Distance from letter 'H' to letter 'A' = 2
Finger 2 on letter 'P' -> cost = 0
Finger 2 on letter 'P' -> cost = Distance from letter 'P' to letter 'P' = 0
Finger 1 on letter 'Y' -> cost = Distance from letter 'A' to letter 'Y' = 4
Total distance = 6
From what I read online there are 1D, 2D and 3D (in terms of space) dynamic programming solutions to the above problem. I have found 1D and 2D solutions online to this problem, but I find them too difficult to follow, so I'm hoping to start with the 3D one and gradually understand more efficient ones.
What's a 3D DP formulation to this problem? Does this problem have a name in particular?
I understand the recursive nature of the problem, but I'm struggling to formulate a simple bottom-up solution (e.g. in 3D).
A fairly simple 3-D dynamic programming approach is the following:
def dist(a,b): # gets distance between chars a and b
y1,x1 = a/6,a%6
y2,x2 = b/6,b%6
return abs(y1-y2)+abs(x1-x2)
def solve(s):
N = len(s)
dp = [[[float('inf') for x in range(26)] for x in range(26)] for x in range(N+1)]
dp[0] = [[0 for x in range(26)] for x in range(26)]
for i in range(N):
cur = ord(s[i])-ord('A')
for j in range(26):
for k in range(26):
dp[i+1][j][cur] = min(dp[i+1][j][cur], dp[i][j][k] + dist(k,cur)) # move right finger
dp[i+1][cur][k] = min(dp[i+1][cur][k], dp[i][j][k] + dist(j,cur)) # move left finger
res = float('inf')
for i in dp[N]:
res = min(res,min(i))
return res
In the solve function, we declare a dynamic programming table dp[N+1][26][26], and let's store at cell dp[i][j][k] the minimum distance required to type all characters in the string up to but not including the i'th character, with the left finger finishing on the j'th key and the right finger on the k'th key.
Our base case is at i = 0, we know it takes 0 total distance to have our fingers start anywhere, so the first row is initialized entirely with 0s. Then, the transition is from all possible configurations of the two fingers to either moving the left finger or the right finger to the new key. If you're currently at state dp[i][j][k], after pressing the i'th key (let's call it cur), if we press this new key with our left finger, the updated state is dp[i+1][cur][k], since the left finger moved from j to cur. Similarly, if we press it with our right finger, the updated state is dp[i+1][j][cur].
Our final answer is found somewhere in the N+1'th row, where N is the length of the string. We simply take the minimum of all combinations of left and right fingers in this row.
EDIT:
Here's the 2-D solution I described in the comments:
def solve(s):
N = len(s)
dp = [[float('inf') for x in range(26)]for x in range(N)]
dp[0] = [0 for x in range(26)]
for i in range(1,N):
cur = ord(s[i])-ord('A')
lst = ord(s[i-1])-ord('A')
for j in range(26): # one finger currently on s[i-1], other finger on j
dp[i][j] = min(dp[i][j], dp[i-1][j] + dist(lst,cur)) # move first finger, so second finger remains on j
dp[i][lst] = min(dp[i][lst], dp[i-1][j] + dist(j,cur)) # move second finger, so second finger becomes the new "first finger"
# and now the old "first finger" becomes the new "second finger"
res = min(dp[N-1])
return res
I know this question has been asked a dozen times before, but I've tried following several different guides and can't seem to figure out why my particular algorithm doesn't work.
Here is the code:
import networkx as nx
import matplotlib.pyplot as plt
import numpy as np
#The reason we went with this algorithm instad of filling a matrix with random int from 0,1 to begin with is because this is always a simple graph, while
#the other approach almost never makes one. So, rather than checking and fixing those graphs, we thought this would be faster and easier
def makeGraph(order):
p = .5 #represents the probability of an edge being created between any 2 verticies, if less than .4 its almost never connected for values 10+.
g = nx.erdos_renyi_graph(order, p) #generates the graph, verticies are 0-order, edges are ordered pairs of these numbers
return g
def Hamiltonian(matrix, order, path, currentVertex):
while(True):
if(currentVertex >= order):
print("There is no H cycle")
return
checkVertex(matrix, order, path, currentVertex)
if(path[currentVertex] == 0):
print("There is no H cycle")
return
if(currentVertex == (order-1) and matrix[path[currentVertex]][0] == 1):
path.append(0)
print("Hamiltonian found, path is:", path)
break
else:
Hamiltonian(matrix, order, path, currentVertex + 1)
return
def checkVertex(matrix, order, path, currentVertex):
i=1
while(True):
path[currentVertex] = ((path[currentVertex]+i)%order)
if(path[currentVertex] == 0):
return
if(matrix[path[currentVertex-1]][path[currentVertex]] == 1):
for j in range (0, currentVertex+1):
if(j != currentVertex and path[j] == path[currentVertex]):
path[currentVertex] = 0
i+=1
break
if(j == currentVertex):
return
else:
path[currentVertex] = 0
i+=1
#IN = input("Enter the number of desired vertices: ") #get input for number of verticies
#order = int(IN) #make input string an int
#TODO remove hard coded order
order = 10
graph = makeGraph(order) #make the graph
#TODO remove printing of edges/nodes
#print("Number of nodes: ", graph.nodes)
print("Edges: ", graph.edges)
#init matrix filled with 0's for conversion
matrix = np.random.randint(0,1,(order,order))
#makes a pretty picture of the graph, great for finding cycles visually
#nx.draw(graph, with_labels=True)
#plt.show()
#convert ordered pairs into adj matrix for easier calculation
for i in range (0, order):
for j in range (0, order):
e = (i,j)
if(graph.edges.__contains__(e)):
matrix[i][j] = 1 #if there is an edge between vertex i and vertex j, append the matrix to have a 1 in the ith column jth row
path = [0] * (order) #init list to have n 0's
currentVertex = 1 #start the current vertex at 1
#matrix = [[0,0,1,0,1],[0,0,0,1,1],[1,0,0,1,1],[0,1,1,0,0],[1,1,1,0,0]] #for testing, known cycle
Hamiltonian(matrix, order, path, currentVertex)
It appears that this algorithm works about half of the time. What I think my problem is is that I am not properly back tracking (or even at all, I genuinely don't know how to do so), meaning that as soon as the algorithm hits a spot where it can't continue it doesn't go back and try anything else.
How can I implement this? Can someone possibly point me towards a resource that can explain it to me?
Thanks!
For anyone that finds this post that got no love and needs some guidance, I figured out what the issue was.
It turns out, that by using a counter value in my checkVertex function (i) it restricted the way that things were being solved. I also did not need anything to be passed from one function to the other since technically the way I had defined things were such that matrix, path, etc were global in python. (I usually code in C++, but had heard this problem was easier to solve using python).
Anyway, here is the code with proper comments:
import networkx as nx
import matplotlib.pyplot as plt
import numpy as np
import sys
import random
#The reason we went with this algorithm instad of filling a matrix with random int from 0,1 to begin with is because this is always a simple graph, while
#the other approach almost never makes one. So, rather than checking and fixing those graphs, we thought this would be faster and easier
def makeGraph(order):
x = random.randint(2,10)
p = (x/10) #represents the probability of an edge being created between any 2 verticies, if less than .2 its almost never connected for values 10+.
g = nx.erdos_renyi_graph(order, p) #generates the graph, verticies are 0 to order, edges are ordered pairs of these numbers
return g
def Hamiltonian(currentVertex):
while(True): #do this until we find a cycle or show there isnt one
if(currentVertex == order): #once the value of cV is the order of G, its likely we found a cycle
if(matrix[path[currentVertex-1]][0] == 1): #but, we should only cont. if there is for sure an edge from the last vertex and 0
path.append(0) #add 0 to the end to illustrate that the path ends at 0
print("There is a cycle, a path is: ", path)
paintGraph()
else:
return
checkVertex(currentVertex) #dont have a cycle yet, so try again
#if after check vertex the current vertex is 0,
#we know that something went wrong so try a new value of cv, starting from 0
if(path[currentVertex] == 0):
return
Hamiltonian(currentVertex+1) #move on to the next vertex and try to find the next proper vertex
def checkVertex(cV):
while(True):
path[cV] = ((path[cV]+1)%order) #starting at 1, try and find a path from the last vertex to the cV
if(path[cV] == 0): #because of the mod, if checkVertex has tried every possible vertex then we know to return
return
if(matrix[path[cV-1]][path[cV]] == 1): #if there is an edge from the last vertex and the current vertex
#loop to ensure there are no duplicates
for j in range (0, cV+1):
if(path[j] == path[cV] and j!=cV):
break
if(j == cV):
if(cV<order): #if we arent at the end of the path yet, keep trying
return
#if we are on a potential end, then we should make sure that we can get back to 0,
#otherwise, we should try again.
if(cV == order and matrix[path[cV]][0] == 1):
return
else:
break
#this is for when we did find a cycle
def paintGraph():
nx.draw(graph, with_labels=True)
plt.show()
#the sys.exit is so that once we did find a cycle we stop looking, because the way
#I have things Hamiltonian would find all possible cycles
sys.exit("Finished")
IN = input("Enter the number of desired vertices: ") #get input for number of verticies
order = int(IN) #make input string an int
graph = makeGraph(order) #make the graph
#init matrix filled with 0's for conversion
matrix = np.random.randint(0,1,(order,order))
nx.draw(graph, with_labels=True)
plt.show()
#convert ordered pairs into adj matrix for easier calculation
for i in range (0, order):
for j in range (0, order):
e = (i,j)
if(graph.edges.__contains__(e)):
matrix[i][j] = 1 #if there is an edge between vertex i and vertex j, append the matrix to have a 1 in the ith column jth row
matrix[j][i] = 1
path = [0]*order #init list to have n 0's
currentVertex = 1 #start the current vertex at 1
Hamiltonian(currentVertex)
#If hamiltonian didn't find a cycle and force sys.exit, then we know no cycle exists
print("There is no Hamiltonian cycle")
#draw the graph again just so we can check.
nx.draw(graph, with_labels=True)
plt.show()
Hopefully this is helpful to someone. It was quite fun to solve, so definitely try doing it yourself before copy pasting!
Edit: In case you were wondering why I have a sys.exit call, its because I only cared about finding ONE H cycle. The way this code is written, we will find ALL H cycles of a given graph.
This can take a very long time. In fact, the run time of this algorithm is O(n!*log(n)) where n is the order of the graph. This code had to be tested with graphs of order 50, and even finding one cycle took my PC about an hour, and I only tested that once.
I have an assignment for a programming unit that asks this:
create the function enclosing(board, player, pos, direct), that represents the rules of Reversi according to the following specifications.
A board position is represented by a pair (r, c) where r and c are each integers from the range
range(8) representing a row index and a column index, respectively. For instance, the position \b3" is
represented by (2, 1). The row and column order of the \b3" style and the specication of position
is reversed. This is because it is natural to constuct lists of lists in such a way that the outer lists
correspond to rows, and the inner list positions corerspond to column positions, so we reference them
with row rst, then column. This row, column convention is quite common across maths and computer
science.
A direction is represented by a pair (dr, dc) where dr and dc are each integers from the set f-1, 0,
1g. For instance, \horizontal to the left" is described by (0, -1), \diagonal to the right and down" is
described by (1, 1), and so on).
The function enclosing(board, player, pos, dir) represents whether putting a player's stone on
a given position would enclose a straight line of opponent's stones in a given direction:
Input: A board conguration board, an integer player from the set f1, 2g, a board position pos and a direction dir.
Output: True if board contains a stone of player in direction dir from position pos and all positions
on the straight line between that stone and pos contain stones of the other player, and there is at
least one stone belonging to the other player on the straight line; False otherwise.
This is what I have:
def enclosing(board, player, pos, direct):
if player == 1:
a = direct[0]
b = direct[1]
i = 1
while i < 8:
newpos = (pos[0] + i*a , pos[1] + i*b)
if board[newpos[0]][newpos[1]] == 1:
return True
elif board[newpos[0]][newpos[1]] == 2:
i = i + 1
else:
return False
Also keep in mind this is a beginners course and I have about a months experience on python.
The code in your edit looks good, and is what I was getting at. Good luck with the rest of the project!
A couple edge cases:
board[newpos[0]][newpos[1]] will go out of bounds
Your function will return True in the case XOOOOX, which is not a capture by Othello rules (not sure if your assignment defines it differently
I strongly recommend writing a couple simple tests just to make sure your code works. They don't need to cover the full range of cases, and doesn't need to be the full board. It's usually not required, but makes it easier to evaluate: just hit "run" instead of trying to reason through your code.
Here's an example:
def test():
assert enclosing([[0, 0, 0], [0,0,0],[0,1,2]], 1, (0,2), (1,1)) == True
For a computer science project I am trying to make a game called snake using python, which the snake itself is represented by three XXX's that is printed onto an array. The snake also grows after 5 turns.
My question is concerning how to make a "For Loop" that iterates over the points in the snake and for each point in the snake, draws a new X on the board.
This is my code for that loop:
for i in snake:
board[i[0]] [i[1]] = "X"
return snake.pop()
print(snake)
No error shows up and the board is printed, but the snake (represented as XXX) is not printed. What is wrong with this for loop?
Additional information:
I have a function that takes a size and returns an array of that size
A function that takes a number of rows and a n number of columns and returns a board
A function that displays a board.
Here is the rest of the code:
def createArray(size):
return ["_"] * size
def createBoard(rows, cols):
matrix = createArray(rows)
for i in range(rows):
matrix[i] = createArray(cols)
return matrix
def displayBoard(m):
for i in range(len(m)):
for j in range(len(m[0])):
print(m[i][j], end=" ")
print()
def main():
snake = [[0,0],[0,1],[0,2]]
board = createBoard(20,30)
displayBoard(board)
for i in snake:
board[i[0]][i[1]] = "X"
return snake
print(snake)
main()