patternpythonMinor
Find diagonal positions for bishop movement
Viewed 0 times
positionsbishopdiagonalmovementforfind
Problem
In a chess board, I need to find the diagonals a bishop can move on, and more specifically, the coordinates of those squares. So, given a grid of any size and a position in that grid (expressed in coordinates within the grid), I have to compute the coordinates of the diagonals of that initial position.
I'm using zero-based indexing, and the
For example, on a 8x8 grid, with starting position of
On a 8x8 grid, with starting position of
This is my working program in Python 3:
Depending on the diagonal (4 cases), row and column are added or subtracted by one until the last square is reached, and everything is kept in a list, which in the end is returned.
It works, but it left me wondering if there's a simpler, different, better way of doing this, probably using linear algebra or something? And what about idiomatically, how can this be more pythonic?
I'm using zero-based indexing, and the
(row, column) notation for coordinates.For example, on a 8x8 grid, with starting position of
(0, 0), the returned list should be [(0, 0), (1, 1), (2, 2), (3, 3), (4, 4), (5, 5), (6, 6), (7, 7)].On a 8x8 grid, with starting position of
(3, 4), the returned list should be [(3, 4), (2, 3), (1, 2), (0, 1), (4, 5), (5, 6), (6, 7), (4, 3), (5, 2), (6, 1), (7, 0), (2, 5), (1, 6), (0, 7)]This is my working program in Python 3:
def diagonals(coord, size):
limit = size - 1
coords = [coord]
row = coord[0]
col = coord[1]
while row > 0 and col > 0:
row -= 1
col -= 1
coords.append((row, col))
row = coord[0]
col = coord[1]
while row 0:
row += 1
col -= 1
coords.append((row, col))
row = coord[0]
col = coord[1]
while row > 0 and col < limit:
row -= 1
col += 1
coords.append((row, col))
return coords
coord = (3, 4)
size = 8
print(diagonals(coord, size))Depending on the diagonal (4 cases), row and column are added or subtracted by one until the last square is reached, and everything is kept in a list, which in the end is returned.
It works, but it left me wondering if there's a simpler, different, better way of doing this, probably using linear algebra or something? And what about idiomatically, how can this be more pythonic?
Solution
The biggest problem you have is you have five lines that are nearly identical, four times.
There is two ways to come at this. (1) make a function that
The first way you need to enter a start, limit and step for both the x and y axis.
You then need to limit all inputs by
This is as we don't really know what direction we're going.
We then add the step to x each iteration.
This can make:
Where you'd use it like:
However this is quite alike to
Take
To then get both x and y you need to use
And so you can get:
This I would say is better than creating your own function as you don't really have to implement anything yourself.
Either way you'll want to remove the duplicate code to append to
To do this you can use itertools.chain.
This lets you chain iterators to become one iterator.
So since you want a list as output you'll have to 'wrap' the call in a
This can get you:
row = coord[0]
col = coord[1]
while row > 0 and col > 0:
row -= 1
col -= 1There is two ways to come at this. (1) make a function that
yield the rows and columns. (2) use zip and range.The first way you need to enter a start, limit and step for both the x and y axis.
You then need to limit all inputs by
0 < x < x_limit, and the same for the y axis too.This is as we don't really know what direction we're going.
We then add the step to x each iteration.
This can make:
def _diagonal(x, y, x_limit, y_limit, x_diff, y_diff):
while 0 < x < x_limit and 0 < y < y_limit:
x += x_diff
y += y_diff
yield (x, y)Where you'd use it like:
for x, y in _diagonal(row, col, limit, limit, 1, 1):
coords.append((x, y))However this is quite alike to
range, we enter a start, limit and step in both.Take
range(row + 1, limit, 1).To then get both x and y you need to use
zip which works perfectly as it stops iterating when any of the inputs stop.And so you can get:
for x, y in zip(range(row + 1, limit, 1), range(col + 1, limit, 1)):
coords.append((x, y))This I would say is better than creating your own function as you don't really have to implement anything yourself.
Either way you'll want to remove the duplicate code to append to
coords.To do this you can use itertools.chain.
This lets you chain iterators to become one iterator.
So since you want a list as output you'll have to 'wrap' the call in a
list call.This can get you:
from itertools import chain
def diagonals(coord, size):
x, y = coord
return list(chain(
[(x, y)],
zip(range(x - 1, -1, -1), range(y - 1, -1, -1)),
zip(range(x + 1, size, 1), range(y + 1, size, 1)),
zip(range(x + 1, size, 1), range(y - 1, -1, -1)),
zip(range(x - 1, -1, -1), range(y + 1, size, 1)),
))
print(diagonals((3, 4), 8))Code Snippets
row = coord[0]
col = coord[1]
while row > 0 and col > 0:
row -= 1
col -= 1def _diagonal(x, y, x_limit, y_limit, x_diff, y_diff):
while 0 < x < x_limit and 0 < y < y_limit:
x += x_diff
y += y_diff
yield (x, y)for x, y in _diagonal(row, col, limit, limit, 1, 1):
coords.append((x, y))for x, y in zip(range(row + 1, limit, 1), range(col + 1, limit, 1)):
coords.append((x, y))from itertools import chain
def diagonals(coord, size):
x, y = coord
return list(chain(
[(x, y)],
zip(range(x - 1, -1, -1), range(y - 1, -1, -1)),
zip(range(x + 1, size, 1), range(y + 1, size, 1)),
zip(range(x + 1, size, 1), range(y - 1, -1, -1)),
zip(range(x - 1, -1, -1), range(y + 1, size, 1)),
))
print(diagonals((3, 4), 8))Context
StackExchange Code Review Q#146935, answer score: 3
Revisions (0)
No revisions yet.