HiveBrain v1.2.0
Get Started
← Back to all entries
patternpythonMinor

Efficiency of Recursive Checkers Legal Move Generator

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
generatorlegalmoverecursiveefficiencycheckers

Problem

I'm implementing a checkers engine for a scientific experiment. I found out through profiling that this is one of the functions that takes up a lot of time. I'm not looking for an in-depth analysis, because I don't expect you to dive super deep into my code.

Just: are there any obvious inefficiencies here? Like, is it slow to do those starting for loops (dx, dy) just for 2 values each?

```
def captures(self, (py, px), piece, board, captured=[], start=None):
""" Return a list of possible capture moves for given piece in a
checkers game.

:param (py, px): location of piece on the board
:param piece: piece type (BLACK/WHITE|MAN/KING)
:param board: the 2D board matrix
:param captured: list of already-captured pieces (can't jump twice)
:param start: from where this capture chain started.
"""
if start is None:
start = (py, px)
# Look for capture moves
for dx in [-1, 1]:
for dy in [-1, 1]:
dist = 1
while True:
jx, jy = px + dist dx, py + dist dy # Jumped square
# Check if piece at jx, jy:
if not (0 <= jx < 8 and 0 <= jy < 8):
break
if board[jy, jx] != EMPTY:
tx, ty = px + (dist + 1) dx, py + (dist + 1) dy # Target square
# Check if it can be captured:
if ((0 <= tx < 8 and 0 <= ty < 8) and
((ty, tx) == start or board[ty, tx] == EMPTY) and
(jy, jx) not in captured and
((piece & WHITE) and (board[jy, jx] & BLACK) or
(piece & BLACK) and (board[jy, jx] & WHITE))
):
# Normal pieces cannot continue capturing after reaching last row
if not piece & KING and (piece & WHITE and ty == 0 or piece & BLACK and ty == 7):
yield (N

Solution

A couple of little things that caught my eye:

You could simplify this

dist = 1 
while True:
    jx, jy = px + dist * dx, py + dist * dy 
    dist += 1


to this:

jx, jy = px, py
while True:
    jx += dx
    jy += dy


You don't need this range check

if not (0 <= jx < 8 and 0 <= jy < 8):
    break
if board[jy, jx] != EMPTY:


because, assuming board is a dict indexed by tuple, you can just catch KeyError when out of bounds:

try:
    if board[jy, jx] != EMPTY:
        ...
except KeyError:
    break


Instead of

((piece & WHITE) and (board[jy, jx] & BLACK) or
 (piece & BLACK) and (board[jy, jx] & WHITE))


you could use board[jy, jx] & opponent if you determine the opponent's color in the beginning of the function:

opponent = BLACK if piece & WHITE else WHITE

Code Snippets

dist = 1 
while True:
    jx, jy = px + dist * dx, py + dist * dy 
    dist += 1
jx, jy = px, py
while True:
    jx += dx
    jy += dy
if not (0 <= jx < 8 and 0 <= jy < 8):
    break
if board[jy, jx] != EMPTY:
try:
    if board[jy, jx] != EMPTY:
        ...
except KeyError:
    break
((piece & WHITE) and (board[jy, jx] & BLACK) or
 (piece & BLACK) and (board[jy, jx] & WHITE))

Context

StackExchange Code Review Q#23323, answer score: 2

Revisions (0)

No revisions yet.