Need help getting this recursive function working - python

I have an object person which defines a person at location x, y
class Person:
def __init__(self, x, y):
self.x = x
self.y = y
def __repr__(self):
return "Person({}, {})".format(self.x, self.y)
# creates a duplicate of Person at a new x, y -> x_new, y_new
def next(self, x_movement, y_movement):
# this is not how the actual movement is calculated, but works for demonstration
return Person(self.x + x_movement, self.y + y_movement)
I desire to find all possible movements for this person, t_steps in the future.
The possible movements are bounded by an array (that may be different at any given time, so this is an example).
x_possible = [-1, 0, 1] Note: during another run of code it could be [3, 5, 2, 4] so the algorithm needs to use this array to know possible movements.
y_possible = [-1, 0, 1]
the method call is like so:
initial_person = Person(0, 0)
# all possible movements for person, 3 time steps into the future
all_possible_movements_for_person = get_possible_movements(initial_person , 3)
the method get_possible_movements must return an array of tuples where each tuple is structured like so:
(
x_new = FIRST movement of x from this branch of movements,
y_new = FIRST movement of y from this branch of movements,
next Person from the initial_person --> person_2 = initial_person.next(x_new, y_new),
next Person from the person_2 --> person_3 = person_2.next(x_possible[i] , y_possible[j],
.
.
will have a person count equal to t_step from the method call
)
example:
initial_person = Person(0, 0)
# all possible movements for person, 3 time steps into the future
all_possible_movements_for_person = get_possible_movements(initial_person , 3)
all_possible_movements_for_person contains a large array of tuples with first entry:
# I am showing the movements made on the person in the tuple for example
(-1, -1, person(-1,-1), person2(-1,-1), person3(-1,-1))
- first element is 1 because the algorithm should pick the first x_movement to be -1 based on the
possible movements array.
- second is -1 for the same reason with y movements.
- the first person in the array is from doing the operation initial_person.next(-1,-1)
- the second person in the array is from doing the operation person1.next(-1,-1)
- the third person in the array is from doing the operation person2.next(-1,-1)
following similar logic, the next tuple in the output array would be:
(-1, -1, person(-1,-1), person2(-1,-1), person4(-1,0))
the person 4 object is new and is the next entry in the y_movements array to get that person.
then
(-1, -1, person(-1,-1), person2(-1,-1), person5(-1,1))
(-1, -1, person(-1,-1), person2(-1,-1), person6(0,-1))
(-1, -1, person(-1,-1), person2(-1,-1), person7(0,0))
The output would look like example, but keep in mind I used strings to represent the objects in this output example.
my attempt is here.... it doesn't output near what I need it to and I don't think I am even close. I suck at recursion.
x_possible = [-1, 0, 1]
y_possible = [-1, 0, 1]
class Person:
def __init__(self, x, y):
self.x = x
self.y = y
def __repr__(self):
return "Person({}, {})".format(self.x, self.y)
# creates a duplicate of Person at a new x, y -> x_new, y_new
def next(self, x_movement, y_movement):
# this is not how the actual movement is calculated, but works for demonstration
return Person(self.x + x_movement, self.y + y_movement)
def get_possible_movements(c, n):
locs = []
get_people_recursion(c, n, n, 0, 0, locs, ())
return locs
def get_people_recursion(person, i, time_step, a_index, b_index, locs, tup):
if time_step < 0:
locs.append(tup)
return
if a_index >= len(x_possible) or b_index >= len(y_possible):
return
if time_step == i:
tup += (x_possible[a_index], y_possible[b_index])
c_next = person.next(x_possible[a_index], y_possible[b_index])
tup += (c_next,)
get_people_recursion(c_next, i, time_step-1, a_index, b_index, locs, copy.deepcopy(tup))
get_people_recursion(c_next, i, time_step, a_index + 1, b_index, locs, copy.deepcopy(tup))
all_people = get_possible_movements(Person(0, 0), 1)
print(len(all_people))
for i in all_people:
print(i)
output from this:
(-1, -1, Person(-1, -1), Person(-2, -2))
(-1, -1, Person(-1, -1), Person(-2, -2), Person(-2, -3))
(-1, -1, Person(-1, -1), Person(-2, -2), Person(-2, -3), Person(-1, -4))
(-1, -1, Person(-1, -1), 0, -1, Person(-1, -2), Person(-1, -3))
(-1, -1, Person(-1, -1), 0, -1, Person(-1, -2), Person(-1, -3), Person(0, -4))
(-1, -1, Person(-1, -1), 0, -1, Person(-1, -2), 1, -1, Person(0, -3), Person(1, -4))
Diagram that may or may not help... https://prnt.sc/sliwcx

Your code is close. The trick to matching your string output is to keep a single count variable to build the result strings or a static class variable to count ids.
Other than that, traverse recursively and push/pop a stack to store the path. Everything else is products.
Here's the code.
import itertools
class Person:
def __init__(self, n, a, b):
self.n = n
self.a = a
self.b = b
def __repr__(self):
return f"Person{self.n}"
def produce_c(a, b, n):
combos = list(itertools.product(a, b))
count = 0
def explore(pair, path=[]):
nonlocal count
count += 1
path.append(Person(count, *pair))
if len(path) == n:
yield tuple(path)
else:
for pair in combos:
yield from explore(pair, path)
path.pop()
for pair in combos:
for path in explore(pair):
yield (*pair, *path)
if __name__ == "__main__":
for x in produce_c([-1, 0, 1], [-1, 0, 1], 3):
print(x)

Related

How to model Bishop movement on a chessboard

I have a board, and I want to model a bishop's possible moves on it. I attempted this code:
for c1, c2 in [(1, -1), (1, 1), (-1, -1), (-1, 1)]:
for x, y in [range(x+c1, board_size), range(y+c2, board_size)]:
moves.append(x, y)
But it doesn't work to find all the moves. Yet, I don't understand why. Doesn't it check all four directions?
Your logic is sound, but your execution is not.
Half of your calculations must go from x or y to 0 (the other half go from x or y to board_size
Ranges don't work from larger to smaller values with the default step, so you'll need to introduce a step of -1 to count from x or y to 0
You should use zip() to create an iterable collection of tuples.
This will work:
right_up = zip(range(x + 1, board_size), range(y - 1, -1, -1))
right_down = zip(range(x + 1, board_size), range(y + 1, board_size))
left_up = zip(range(x - 1, -1, -1), range(y - 1, -1, -1))
left_down = zip(range(x - 1, -1, -1), range(y + 1, board_size))
for r in (right_up, right_down, left_up, left_down):
for new_x, new_y in r: # add coordinates to move list

Pythonic way to calculate pair wise dot product inside a list

I have a list that consists of all combinations of tuples that each elements can only be -1 or 1. The list can be generated as:
N=2
list0 = [p for p in itertools.product([-1, 1], repeat=N)]
For example, if the tuple has N=2 elements:
list0 = [(-1, -1), (-1, 1), (1, -1), (1, 1)]
Thus the total number of tuples is 2^2=4.
If the tuple has N=3 elements:
list0 = [(-1, -1, -1), (-1, -1, 1), (-1, 1, -1), (-1, 1, 1),
(1, -1, -1), (1, -1, 1), (1, 1, -1), (1, 1, 1)]
Here is my concern:
Now I would like to get all the results of dot products between any pair of tuples in the list(including ones a tuple with itself). So for N=2 there will be 6(pairs) + 4(itself) = 10 combinations; for N=3 there will be 28(pairs) + 8(itself) = 36 combinations.
For small N I can do something like:
for x in list0:
for y in list0:
print(np.dot(x,y))
However, assuming I already have list0, what is the optimal way to calculate all the possibilities of dot products, if N is large, like ~50?
You could use the np.dot itself:
import numpy as np
list0 = [(-1, -1, -1), (-1, -1, 1), (-1, 1, -1), (-1, 1, 1), (1, -1, -1), (1, -1, 1), (1, 1, -1), (1, 1, 1)]
# approach using np.dot
a = np.array(list0)
result = np.dot(a, a.T)
# brute force approach
brute = []
for x in list0:
brute.append([np.dot(x, y) for y in list0])
brute = np.array(brute)
print((brute == result).all())
Output
True
What you are asking is the matrix multiplication of a with itself, from the documentation:
if both a and b are 2-D arrays, it is matrix multiplication,
Note that the most pythonic solutio is to use the operator #:
import numpy as np
list0 = [(-1, -1, -1), (-1, -1, 1), (-1, 1, -1), (-1, 1, 1), (1, -1, -1), (1, -1, 1), (1, 1, -1), (1, 1, 1)]
# approach using np.dot
a = np.array(list0)
result = a # a.T
# brute force approach
brute = []
for x in list0:
brute.append([np.dot(x, y) for y in list0])
brute = np.array(brute)
print((brute == result).all())
Output
True
Note: The code was run in Python 3.5
You can stick with numpy
import numpy as np
import random
vals = []
num_vecs = 3
dimension = 4
for n in range(num_vecs):
val = []
for _ in range(dimension):
val.append(random.random())
vals.append(val)
# make into numpy array
vals = np.stack(vals)
print(vals.shape == (num_vecs, dimension))
# multiply every vector with every other using broadcastin
every_with_every_mult = vals[:, None] * vals[None, :]
print(every_with_every_mult.shape == (num_vecs, num_vecs, dimension))
# sum the final dimension
every_with_every_dot = np.sum(every_with_every_mult, axis=every_with_every_mult.ndim - 1)
print(every_with_every_dot.shape == (num_vecs, num_vecs))
# check it works
for i in range(num_vecs):
for j in range(num_vecs):
assert every_with_every_dot[i,j] == np.sum(vals[i]*vals[j])

Sorting according to clockwise point coordinates

Given a list in Python containing 8 x, y coordinate values (all positive) of 4 points as [x1, x2, x3, x4, y1, y2, y3, y4] ((xi, yi) are x and y coordinates of ith point ),
How can I sort it such that new list [a1, a2, a3, a4, b1, b2, b3, b4] is such that coordinates (ai, bi) of 1 2 3 4 are clockwise in order with 1 closest to origin of xy plane, i.e. something like
2--------3
| |
| |
| |
1--------4
Points will roughly form a parallelogram.
Currently, I am thinking of finding point with least value of (x+y) as 1, then 2 by the point with least x in remaining coordinates, 3 by largest value of (x + y) and 4 as the remaining point
You should use a list of 2-item tuples as your data structure to represent a variable number of coordinates in a meaningful way.
from functools import reduce
import operator
import math
coords = [(0, 1), (1, 0), (1, 1), (0, 0)]
center = tuple(map(operator.truediv, reduce(lambda x, y: map(operator.add, x, y), coords), [len(coords)] * 2))
print(sorted(coords, key=lambda coord: (-135 - math.degrees(math.atan2(*tuple(map(operator.sub, coord, center))[::-1]))) % 360))
This outputs:
[(0, 0), (0, 1), (1, 1), (1, 0)]
import math
def centeroidpython(data):
x, y = zip(*data)
l = len(x)
return sum(x) / l, sum(y) / l
xy = [405952.0, 408139.0, 407978.0, 405978.0, 6754659.0, 6752257.0, 6754740.0, 6752378.0]
xy_pairs = list(zip(xy[:int(len(xy)/2)], xy[int(len(xy)/2):]))
centroid_x, centroid_y = centeroidpython(xy_pairs)
xy_sorted = sorted(xy_pairs, key = lambda x: math.atan2((x[1]-centroid_y),(x[0]-centroid_x)))
xy_sorted_x_first_then_y = [coord for pair in list(zip(*xy_sorted)) for coord in pair]
# P4=8,10 P1=3,5 P2=8,5 P3=3,10
points=[8,3,8,3,10,5,5,10]
k=0
#we know these numbers are extreme and data won't be bigger than these
xmin=1000
xmax=-1000
ymin=1000
ymax=-1000
#finding min and max values of x and y
for i in points:
if k<4:
if (xmin>i): xmin=i
if (xmax<i): xmax=i
else:
if (ymin>i): ymin=i
if (ymax<i): ymax=i
k +=1
sortedlist=[xmin,xmin,xmax,xmax,ymin,ymax,ymax,ymin]
print(sortedlist)
output:[3, 3, 8, 8, 5, 10, 10, 5]
for other regions you need to change sortedlist line. if center is inside the box then it will require more condition controlling
What we want to sort by is the angle from the start coordinate. I've used numpy here to interpret each vector from the starting coordinate as a complex number, for which there is an easy way of computing the angle (counterclockwise along the unit sphere)
def angle_with_start(coord, start):
vec = coord - start
return np.angle(np.complex(vec[0], vec[1]))
Full code:
import itertools
import numpy as np
def angle_with_start(coord, start):
vec = coord - start
return np.angle(np.complex(vec[0], vec[1]))
def sort_clockwise(points):
# convert into a coordinate system
# (1, 1, 1, 2) -> (1, 1), (1, 2)
coords = [np.array([points[i], points[i+4]]) for i in range(len(points) // 2)]
# find the point closest to the origin,
# this becomes our starting point
coords = sorted(coords, key=lambda coord: np.linalg.norm(coord))
start = coords[0]
rest = coords[1:]
# sort the remaining coordinates by angle
# with reverse=True because we want to sort by clockwise angle
rest = sorted(rest, key=lambda coord: angle_with_start(coord, start), reverse=True)
# our first coordinate should be our starting point
rest.insert(0, start)
# convert into the proper coordinate format
# (1, 1), (1, 2) -> (1, 1, 1, 2)
return list(itertools.chain.from_iterable(zip(*rest)))
Behavior on some sample inputs:
In [1]: a
Out[1]: [1, 1, 2, 2, 1, 2, 1, 2]
In [2]: sort_clockwise(a)
Out[2]: [1, 1, 2, 2, 1, 2, 2, 1]
In [3]: b
Out[3]: [1, 2, 0, 2, 1, 2, 3, 1]
In [4]: sort_clockwise(b)
Out[4]: [1, 0, 2, 2, 1, 3, 2, 1]
Based on BERA's answer but as a class:
code
import math
def class Sorter:
#staticmethod
def centerXY(xylist):
x, y = zip(*xylist)
l = len(x)
return sum(x) / l, sum(y) / l
#staticmethod
def sortPoints(xylist):
cx, cy = Sorter.centerXY(xylist)
xy_sorted = sorted(xylist, key = lambda x: math.atan2((x[1]-cy),(x[0]-cx)))
return xy_sorted
test
def test_SortPoints():
points=[(0,0),(0,1),(1,1),(1,0)]
center=Sorter.centerXY(points)
assert center==(0.5,0.5)
sortedPoints=Sorter.sortPoints(points)
assert sortedPoints==[(0, 0), (1, 0), (1, 1), (0, 1)]
As suggested by IgnacioVazquez-Abrams, we can also do sorting according to atan2 angles:
Code:
import math
import copy
import matplotlib.pyplot as plt
a = [2, 4, 5, 1, 0.5, 4, 0, 4]
print(a)
def clock(a):
angles = []
(x0, y0) = ((a[0]+a[1]+a[2]+a[3])/4, (a[4]+ a[5] + a[6] + a[7])/4) # centroid
for j in range(4):
(dx, dy) = (a[j] - x0, a[j+4] - y0)
angles.append(math.degrees(math.atan2(float(dy), float(dx))))
for k in range(4):
angles.append(angles[k] + 800)
# print(angles)
z = [copy.copy(x) for (y,x) in sorted(zip(angles,a), key=lambda pair: pair[0])]
print("z is: ", z)
plt.scatter(a[:4], a[4:8])
plt.show()
clock(a)
Output is :
[2, 4, 5, 1, 0.5, 4, 0, 4]
[-121.60750224624891, 61.92751306414704, -46.73570458892839, 136.8476102659946, 678.3924977537511, 861.9275130641471, 753.2642954110717, 936.8476102659946]
z is: [2, 5, 4, 1, 0.5, 0, 4, 4]
Try this line of code
def sort_clockwise(pts):
rect = np.zeros((4, 2), dtype="float32")
s = pts.sum(axis=1)
rect[0] = pts[np.argmin(s)]
rect[2] = pts[np.argmax(s)]
diff = np.diff(pts, axis=1)
rect[1] = pts[np.argmin(diff)]
rect[3] = pts[np.argmax(diff)]
return rect

How can I continue a loop inside exception handling?

I am creating a simple program that operates using Moore's Neighborhood. So given a grid, row, and a column it should return the amount of cells in the vicinity of the position that contain a 1. It works, except when given a position on the edge of the grid. Since it is checking all grids surrounding it, it throws an IndexError when it tries to check a position outside of the grid. What I want it to do is just ignore it without stopping, throwing an error, or manipulating my results, and move onto the next one. But I'm not sure how, I tried doing an exception on the IndexError but it quits out of the loop once it encounters one.
def count_neighbours(grid, row, col):
count = 0
pos = grid[row][col]
try:
for cell in [grid[row+1][col], #(0,-1) All relative to pos
grid[row-1][col], #(0,1)
grid[row+1][col+1], #(1,-1)
grid[row+1][col-1], #(-1,-1)
grid[row][col-1], #(-1,0)
grid[row][col+1], #(1,0)
grid[row-1][col+1], #(1,-1)
grid[row-1][col-1]]: #(-1,1)
if cell == 1:
count += 1
except IndexError:
pass
return count
assert count_neighbours(((1, 1, 1),
(1, 1, 1),
(1, 1, 1),), 0, 2) == 3
The loop is stopping because you are wrapping the entire loop in a try except you want something like this
def count_neighbours(grid, row, col): count = 0
pos = grid[row][col]
for cell in [[row+1,col], #(0,-1) All relative to pos
[row-1,col], #(0,1)
[row+1,col+1], #(1,-1)
[row+1,col-1], #(-1,-1)
[row,col-1], #(-1,0)
[row,col+1], #(1,0)
[row-1,col+1], #(1,-1)
[row-1,col-1]]: #(-1,1)
try:
temp_cell = grid[cell[0]][cell[1]]
if temp_cell == 1:
count += 1
except IndexError:
pass
return count
assert count_neighbours(((1, 1, 1),
(1, 1, 1),
(1, 1, 1),), 0, 2) == 3
Try a different approach, first compute the valid coords for a given point and then check for ones.
For instance you could use this function:
def compute_coords_around(x, y, boundary):
xcoords = [x-1, x, x+1]
ycoords = [y-1, y, y+1]
valid_coords = []
for xc in xcoords:
for yc in ycoords:
if xc <= boundary and yc <= boundary:
valid_coords.append((xc,yc))
return valid_coords
and lets say you want check for adjacent cells of (2, 2) in a matrix of 3x3. You know the maximum value of a column or a row is 2. So you can:
compute_coords_around(2, 2, 2)
That will give you the list:
[(1, 1), (1, 2), (2, 1), (2, 2)]
while:
compute_coords_around(1, 1, 2)
give you:
[(0, 0), (0, 1), (0, 2), (1, 0), (1, 1), (1, 2), (2, 0), (2, 1), (2, 2)]
Then your code could be modified to:
def count_neighbours(grid, row, col):
count = 0
pos = grid[row][col]
for (x, y) in compute_coords_around(row, col, len(grid) - 1)
if grid[x][y] == 1:
count += 1
return count
You need finer-grained exception handling (and your algorithm needs to explicitly check for otherwise legal -- in Python -- indices less than zero). Here's one way to achieve both:
OFFSETS = ((-1, -1), (-1, 0), (-1, 1),
( 0, -1), ( 0, 1),
( 1, -1), ( 1, 0), ( 1, 1))
def count_neighbours(grid, row, col):
count = 0
for dr, dc in OFFSETS:
try:
x, y = row+dr, col+dc
if x < 0 or y < 0: # Disallow negative indices.
raise IndexError
if grid[x][y] == 1: # Could also cause an IndexError.
count += 1
except IndexError:
pass
return count
assert count_neighbours(((1, 1, 1),
(1, 1, 1),
(1, 1, 1),), 0, 2) == 3
However having to add an explicit check for negative indices in the inner-most loop is kind of ugly. As I mentioned in a comment, adding an extra row and column to the grid would certainly simplify the processing, as illustrated below:
OFFSETS = ((-1, -1), (-1, 0), (-1, 1),
( 0, -1), ( 0, 1),
( 1, -1), ( 1, 0), ( 1, 1))
def count_neighbours(grid, row, col):
count = 0
for dr, dc in OFFSETS:
try:
if grid[row+dr][col+dc] == 1:
count += 1
except IndexError:
pass
return count
# Note the changed position coordinate arguments.
assert count_neighbours(((0, 0, 0, 0),
(0, 1, 1, 1),
(0, 1, 1, 1),
(0, 1, 1, 1),), 1, 3) == 3

How do I generate all of a knight's moves?

I am writing a Chess program in Python that needs to generate all the moves of a knight. For those not familiar with chess, a knight moves in an L shape.
So, given a position of (2, 4) a knight could move to (0, 3), (0, 5), (1, 2), (3, 2), etc. for a total of (at most) eight different moves.
I want to write a function called knight_moves that generates these tuples in a list. What is the easiest way to do this in Python?
def knight_moves(position):
''' Returns a list of new positions given a knight's current position. '''
pass
Ok, so thanks to Niall Byrne, I came up with this:
from itertools import product
def knight_moves(position):
x, y = position
moves = list(product([x-1, x+1],[y-2, y+2])) + list(product([x-2,x+2],[y-1,y+1]))
moves = [(x,y) for x,y in moves if x >= 0 and y >= 0 and x < 8 and y < 8]
return moves
Why not store the relative pairs it can move in ? So take your starting point, and add a set of possible moves away from it, you then would just need a sanity check to make sure they are still in bounds, or not on another piece.
ie given your (2, 4) starting point, the options are (-2,-1), (-2,+1), (-1,+2), (+2,+1)
The relative positions would thus always be the same.
Not familiar with chess...
deltas = [(-2, -1), (-2, +1), (+2, -1), (+2, +1), (-1, -2), (-1, +2), (+1, -2), (+1, +2)]
def knight_moves(position):
valid_position = lambda (x, y): x >= 0 and y >= 0 and ???
return filter(valid_position, map(lambda (x, y): (position[0] + x, position[1] + y), deltas))
Instead of using an array, I would suggest you use bitboards. Not only are they very easy to manipulate, they will also reduce the need for boundary checking. With as few as 12 bitboards, you could probably encode the information you need for the whole game.
https://www.chessprogramming.org/Bitboards
The basic idea of bitboards is to use a 64 bit integer and set 1 if a piece is present on the bit. For example, if you had a 64 bit integer to represent white knights, you would set the 2nd and 6th bits at the starting of the game as they are the positions where the white knights are located. Using this notation, it becomes easy to calculate the knight's moves. It will be easy to calculate other pieces' moves too.
With this representation, you could take a look at this link to the chess engine for a ready made algorithm to implement knight's moves.
http://www.mayothi.com/nagaskakichess6.html
This might sound as an overkill if you're not familiar with analytical geometry (or complex numbers geometry) but I came up with a very elegant mathematical solution when
I was implementing a validation for the movement of pieces.
The knight's moves are lying on a circle which can be defined as
(x-x_0)^2+(y-y_0)^2=5 where x_0 and y_0 are the Knight's current coordinates. If you switch to polar coordinates, you can get all possible coordinates with this simple code:
import math
def knight_moves(x,y):
new_positions=[]
r=math.sqrt(5) #radius of the circle
for phi in [math.atan(2),math.atan(1/2)]: #angles in radians
for quadrant in range(4):
angle=phi+quadrant*math.pi/2 # add 0, 90, 180, 270 degrees in radians
new_x=round(x+r*math.cos(angle))
new_y=round(y+r*math.sin(angle))
if max(new_x,new_y,7-new_x,7-new_y)<=7: #validation whether the move is in grid
new_positions.append([new_x,new_y])
return(new_positions)
def validate_knight_move(x,y,x_0,y_0):
return((x-x_0)**2+(y-y_0)**2==5)
x_0=2
y_0=4
moves=knight_moves(x_0,y_0)
print(moves)
validation=[validate_knight_move(move[0],move[1],x_0,y_0) for move in moves]
print(validation)
[[3, 6], [0, 5], [1, 2], [4, 3], [4, 5], [1, 6], [0, 3], [3, 2]]
[True, True, True, True, True, True, True, True]
It's good to point here, that it is much simpler to validate the position than to construct it directly. Therefore, it might be a good idea to just try whether all possible moves lie on the circle or not:
def knight_moves2(x,y):
new_positions=[]
for dx in [-2,-1,1,2]:
for dy in [-2,-1,1,2]:
if(validate_knight_move(x+dx,y+dy,x,y)): #is knight move?
if max(x+dx,y+dy,7-(x+dx),7-(y+dy))<=7: #validation whether the move is in grid
new_positions.append([x+dx,y+dy])
return(new_positions)
new_positions=knight_moves2(x_0,y_0)
print(new_positions)
[[0, 3], [0, 5], [1, 2], [1, 6], [3, 2], [3, 6], [4, 3], [4, 5]]
Here's an easy implementation:
def knights_moves():
a = []
b = (1, 2)
while 1:
a.append(b)
b = (-b[0], b[1])
a.append(b)
b = (b[1], b[0])
if b in a:
return a
[(1, 2), (-1, 2), (2, -1), (-2, -1), (-1, -2), (1, -2), (-2, 1), (2, 1)]
From there you can just simply add the current position to every member of this list, and then double check for validity.
Completing xiaowl's answer,
possible_places = [(-2, -1), (-2, +1), (+2, -1), (+2, +1), (-1, -2), (-1, +2), (+1, -2), (+1, +2)]
def knight_moves(cur_pos):
onboard = lambda (x, y): x >= 0 and y >= 0 and x<8 and y<8
eval_move = lambda(x,y): (cur_pos[0] + x, cur_pos[1] + y)
return filter(onboard, map(eval_move, possible_places))
For the knights moves:
def getAllValidMoves(x0, y0):
deltas = [(-2, -1), (-2, +1), (+2, -1), (+2, +1), (-1, -2), (-1, +2), (+1, -2), (+1, +2)]
validPositions = []
for (x, y) in deltas:
xCandidate = x0 + x
yCandidate = y0 + y
if 0 < xCandidate < 8 and 0 < yCandidate < 8:
validPositions.append([xCandidate, yCandidate])
return validPositions
print getAllValidMoves(3,3)
I just stored all the possible deltas, applied each one of them to the "initial position" and saved the ones that were inside the chessboard
from itertools import product
def moves():
""" The available (relative) moves"""
a = list(product( (1, -1), (2,-2)))
return a + [tuple(reversed(m)) for m in a]
def neighbors(a,b):
# true if x,y belongs in a chess table
in_table = lambda (x, y): all((x < 8, y < 8, x >= 0, y >= 0))
# returns the possible moving positions
return filter(in_table, [(a+x, b+y) for x, y in moves()])
"neighbors" are the available positions that a knight can go from a,b
The below method is implemented in python. It accepts the board (which can be of any m*n & has values 0(available) and 1(occupied) and current position of knight)
def get_knight_moves(board, position):
KNIGHT_STEPS = ((1, 2), (-1, 2), (1, -2), (-1, -2), (2, 1), (-2, 1), (2, -1), (-2, -1))
knight_moves = []
for (i, j) in KNIGHT_STEPS:
try:
x, y = position[0] + i, position[1] + j
if board[x][y] == 0:
knight_moves.append((x, y))
except IndexError:
pass
print(knight_moves)

Categories

Resources