I'm currently in the process of building my own Chess game engine and could really use some suggestions on how to make this segment of code for calculating diagonal moves more efficient. (This is obviously only for diagonals going Up-Right.)
As of now I'm using "Try-Except" to iterate by 1, and then my return
statement filters out any off-board values. However this seems like a very bulky way of doing things.
import argparse, json
chessBoard = [[1, 1, 1, 1, 1, 1, 1, 1] for i in range(8)]
chess_map_from_alpha_to_index = {
"a" : 0,
"b" : 1,
"c" : 2,
"d" : 3,
"e" : 4,
"f" : 5,
"g" : 6,
"h" : 7
}
chess_map_from_index_to_alpha = {
0: "a",
1: "b",
2: "c",
3: "d",
4: "e",
5: "f",
6: "g",
7: "h"
}
def getBishopMoves(pos, chessBoard):
column, row = list(pos.strip().lower())
row = int(row) - 1
column = chess_map_from_alpha_to_index[column]
i,j = row, column
solutionMoves = []
#Up-Right Diagonal
try:
temp = chessBoard[i + 1][j + 1]
solutionMoves.append([i + 1, j + 1])
except:
pass
try:
temp = chessBoard[i + 2][j + 2]
solutionMoves.append([i + 2, j + 2])
except:
pass
try:
temp = chessBoard[i + 3][j + 3]
solutionMoves.append([i + 3, j + 3])
except:
pass
try:
temp = chessBoard[i + 4][j + 4]
solutionMoves.append([i + 4, j + 4])
except:
pass
try:
temp = chessBoard[i + 5][j + 5]
solutionMoves.append([i + 5, j + 5])
except:
pass
try:
temp = chessBoard[i + 6][j + 6]
solutionMoves.append([i + 6, j + 6])
except:
pass
try:
temp = chessBoard[i + 7][j + 7]
solutionMoves.append([i + 7, j + 7])
except:
pass
try:
temp = chessBoard[i + 7][j + 7]
solutionMoves.append([i + 7, j + 7])
except:
pass
temp = [i for i in solutionMoves if i[0] >=0 and i[1] >=0]
solutionMoves = ["".join([chess_map_from_index_to_alpha[i[1]], str(i[0] + 1)]) for i in temp]
solutionMoves.sort()
return solutionMoves
2 Answers 2
Go through the PEP-8 style guide. You have inconsistent naming convention. A mixed case of
camelCase
andsnake_case
throws off the developer. Followsnake_case
for variables andcamelCase
for class etc.
The first thing that comes to mind is a loop:
for pos in range(1, 8):
try:
temp = chessBoard[i + pos][j + pos]
solutionMoves.append([i + pos, j + pos])
except:
break
which about covers the whole of your try-except
blocks.
However, with chess; I'd suggest using coordinate system to move around the board.
Design a class Point
which takes \$ (x, y) \$ position and define the __add__
, __sub__
, __neg__
etc. as follows (rough, modify as per your needs):
class Point(tuple):
def __add__(self, other):
return Point(v + w for v, w in zip(self, other))
def __radd__(self, other):
return Point(w + v for v, w in zip(self, other))
def __sub__(self, other):
return Point(v - w for v, w in zip(self, other))
def __neg__(self):
return -1 * self
def __mul__(self, s):
return Vector(v * s for v in self)
def __rmul__(self, s):
return Vector(v * s for v in self)
Now, define your movement direction as a point vector:
DIR_TOP_RIGHT = Point(1, 1)
when you want to move inside the board, just add a multiple of direction to the current point:
current = Point(i, j)
new = current + (distance * DIR_TOP_RIGHT)
Next, define a method (inside a class ChessBoard
which checks whether a point lies inside the board or not. This should be quite easy. This ChessBoard
class should also be the one responsible for converting your Point
object from \$ (1, 1) \$ format to B1
etc.
-
\$\begingroup\$ I really appreciate the detailed response mate, though, I have to say a lot of that went over my head. Are there any articles you could suggest for me to get a better grasp on what you were saying? Mostly confused about what add, sub, neg, are supposed to represent. Also not really sure how the user would choose his "multiple of direction" through a GUI \$\endgroup\$Kevin Hebert– Kevin Hebert2018年06月24日 14:38:45 +00:00Commented Jun 24, 2018 at 14:38
-
2\$\begingroup\$ @Kevin Hebert
__add__()
,__sub__()
, and__neg__()
are examples of magic methods. Essentially they allow you to operate on two instances ofPoint
using common operators, like+
corresponding to__add__()
,-
corresponding to__sub__()
, etc. See also 'Data model: Special method names: basic customization' \$\endgroup\$Daniel– Daniel2018年06月24日 16:53:40 +00:00Commented Jun 24, 2018 at 16:53 -
\$\begingroup\$ Thx for the link; diving in now. Do you have any books you recommend? I'm feeling very overwhelmed already as I'm eventually trying to make this an online, multiplayer game... \$\endgroup\$Kevin Hebert– Kevin Hebert2018年06月24日 17:14:42 +00:00Commented Jun 24, 2018 at 17:14
-
1\$\begingroup\$ @KevinHebert I don't know of any books specifically about magic methods, and I'm hesitant to recommend any books because I don't know your skill level. If you have trouble understanding classes, the Python tutorial page on classes may be a useful resource. If you just want an awesome and complete introductory course to Python, check out Learn Python the Hard Way. Apart from that, a search for 'Python classes' will get you pretty far. \$\endgroup\$Daniel– Daniel2018年06月26日 08:43:55 +00:00Commented Jun 26, 2018 at 8:43
-
\$\begingroup\$ Completely get that. I'll check out the link as my knowledge on classes is definitely a little fuzzy. As for my skill level, I would say it's rather minimal. I've done quite a few tutorials on Python / Django + front end development, along with taking a university class on C++. However, I really struggle in trying to piece together all my knowledge. That's part of the reason why I'm trying to build this chess engine (and eventually online/multiplayer chess game). I understand it's quite an undertaking but I think doing a real project like this is the only way I'll actually learn \$\endgroup\$Kevin Hebert– Kevin Hebert2018年06月26日 21:01:23 +00:00Commented Jun 26, 2018 at 21:01
Computing moves seems like something that will happen a lot in a chess game :-).
That being said, why don't you write a function that will generate a list of all the possible moves for a piece from a given position and store them in a loadable format?
I just recently saw someone else ask a chess question mentioning something called bitboard
, so I know there's at least two standards for board positions. You can either use UCI notation ("a2") or bitboard notation (0x100).
However you choose to represent them, simply perform the computations to generate all possible moves for a given piece in a given position, possibly including some special moves as either a separate move lookup or as a separate piece lookup (for example, "king-n" for no castle yet and "king-y" for castled already).
Then you can just look up "raw" moves from a dictionary:
possible_moves = raw_moves['bishop'][position]
Subsequently, you would have to look for blocking pieces and filter them out. Depending on your storage implementation, this again seems like something you could precompute.
A lot of this suggestion is "trading space for time". That is, I'm suggesting you create a LOT of stored data that you could use in order to avoid doing computations at run-time. It would slow down the loading of your program, but should speed up the turn-by-turn execution.
If you choose to use UCI format for positions, you will want to look at Python's set
and frozenset
built-in types.
If you choose to go with bitboard, you can use bitwise operations on integer values, which Python supports directly.
getBishopMoves()
? \$\endgroup\$getBishopMoves()
? \$\endgroup\$