Chess-Recognition-2D / chess_positions.py
mohd-saifuddin's picture
App and model files
6e9f22f
raw
history blame contribute delete
No virus
13.3 kB
import re
import numpy as np
class Board(object):
"""
This class is defines the chessboard.
"""
def __init__(self, fen_label):
self.fen_label = re.sub(pattern=r'\d',
repl=lambda x: self.get_ones(char=x.group()),
string=fen_label)
self.fen_matrix = self.get_fen_matrix()
def get_ones(self, char):
"""
This method returns repetitive 1s based on input digit character.
"""
if char.isdigit():
return '1' * int(char)
def get_fen_matrix(self):
"""
This method constructs a FEN matrix.
"""
fen_matrix = np.array([list(row) for row in self.fen_label.split('/')])
return fen_matrix
def get_piece_positions(self, notation):
"""
This method returns the 2D index of the piece from FEN matrix.
"""
(i, j) = np.where(self.fen_matrix == notation)
try:
if i is not None and j is not None:
return i, j
except:
return None
class Check(Board):
"""
This class finds if there are any checks in the chessboard.
"""
def __init__(self, fen_label):
super().__init__(fen_label=fen_label)
def get_sub_matrix(self, ai, aj, di, dj):
"""
This method chops the chessboard to a sub-matrix.
"""
corners = np.array([(ai, aj), (di, aj), (ai, dj), (di, dj)])
min_i, max_i = min(corners[:, 0]), max(corners[:, 0])
min_j, max_j = min(corners[:, 1]), max(corners[:, 1])
sub_matrix = self.fen_matrix[min_i:max_i+1, min_j:max_j+1]
return sub_matrix, sub_matrix.shape
def get_straight_checks(self, ai, aj, di, dj, a, d):
"""
This method returns the checks along the straight path.
"""
checks = list()
for (i, j) in zip(ai, aj):
if di == i:
attack_path = self.fen_matrix[di]
elif dj == j:
attack_path = self.fen_matrix[:, dj]
else:
continue
a_ind = np.where(attack_path == a)[0]
d_ind = np.where(attack_path == d)[0][0]
for a_i_ in a_ind:
attack_path_ = attack_path[min(a_i_, d_ind): max(a_i_, d_ind)+1]
checks.append(np.where(attack_path_ != '1')[0])
checks = list(filter(lambda x: len(x) == 2, checks))
return checks
def get_diagonal_checks(self, ai, aj, di, dj, a):
"""
This method returns the checks along the diagonal path.
"""
checks = list()
for (i, j) in zip(ai, aj):
sub_mat, sub_shape = self.get_sub_matrix(ai=i, aj=j, di=di, dj=dj)
if sub_shape[0] == sub_shape[1]:
if a not in sub_mat.diagonal():
sub_mat = np.flipud(m=sub_mat)
checks.append(np.where(sub_mat.diagonal() != '1')[0])
else:
continue
checks = list(filter(lambda x: len(x) == 2, checks))
return checks
def get_knight_checks(self, ai, aj, di, dj):
"""
This method returns the checks along the L-shaped paths for knights.
"""
checks = list()
for (i, j) in zip(ai, aj):
attack_positions = [(i-2, j-1), (i-2, j+1),
(i-1, j-2), (i-1, j+2),
(i+1, j-2), (i+1, j+2),
(i+2, j-1), (i+2, j+1)]
if (di, dj) in attack_positions:
checks.append((i, j))
return checks
def get_pawn_checks(self, ai, aj, di, dj):
"""
This method returns the checks for pawns.
"""
checks = list()
for (i, j) in zip(ai, aj):
_, sub_shape = self.get_sub_matrix(ai=i, aj=j, di=di, dj=dj)
if sub_shape[0] == 2 and sub_shape[1] == 2:
checks.append((i, j))
else:
continue
return checks
def king_checks_king(self, attacker, defendant):
"""
This method checks if the king is being attacked by the other king.
This is unlikely, but I am just adding a validation rule.
"""
flag = False
di, dj = self.get_piece_positions(notation=defendant)
if len(di) == 1 and len(dj) == 1:
di, dj = di[0], dj[0]
else:
return flag
ai, aj = self.get_piece_positions(notation=attacker)
ai, aj = ai[0], aj[0]
attack_positions = [(di, dj-1), (di, dj+1),
(di-1, dj), (di+1, dj),
(di-1, dj+1), (di-1, dj-1),
(di+1, dj-1), (di+1, dj+1)]
if (ai, aj) in attack_positions:
flag = True
return flag
def rook_checks_king(self, attacker, defendant):
"""
This method checks if the king is being attacked by the rook.
"""
flag = False
di, dj = self.get_piece_positions(notation=defendant)
if len(di) == 1 and len(dj) == 1:
di, dj = di[0], dj[0]
else:
return flag
ai, aj = self.get_piece_positions(notation=attacker)
checks = self.get_straight_checks(
ai=ai, aj=aj, di=di, dj=dj, a=attacker, d=defendant)
if checks:
flag = True
return flag
def bishop_checks_king(self, attacker, defendant):
"""
This method checks if the king is being attacked by the bishop.
"""
flag = False
di, dj = self.get_piece_positions(notation=defendant)
if len(di) == 1 and len(dj) == 1:
di, dj = di[0], dj[0]
else:
return flag
ai, aj = self.get_piece_positions(notation=attacker)
checks = self.get_diagonal_checks(
ai=ai, aj=aj, di=di, dj=dj, a=attacker)
if checks:
flag = True
return flag
def knight_checks_king(self, attacker, defendant):
"""
This method checks if the king is being attacked by the knight.
"""
flag = False
di, dj = self.get_piece_positions(notation=defendant)
if len(di) == 1 and len(dj) == 1:
di, dj = di[0], dj[0]
else:
return flag
ai, aj = self.get_piece_positions(notation=attacker)
checks = self.get_knight_checks(ai=ai, aj=aj, di=di, dj=dj)
if checks:
flag = True
return flag
def queen_checks_king(self, attacker, defendant):
"""
This method checks if the king is being attacked by the queen.
"""
flag = False
di, dj = self.get_piece_positions(notation=defendant)
if len(di) == 1 and len(dj) == 1:
di, dj = di[0], dj[0]
else:
return flag
ai, aj = self.get_piece_positions(notation=attacker)
straight_checks = self.get_straight_checks(
ai=ai, aj=aj, di=di, dj=dj, a=attacker, d=defendant)
diagonal_checks = self.get_diagonal_checks(
ai=ai, aj=aj, di=di, dj=dj, a=attacker)
if straight_checks or diagonal_checks:
flag = True
return flag
def pawn_checks_king(self, attacker, defendant):
"""
This methos checks if the king is being attacked by the pawn.
Note: It is hard to determine from an image, which side of
the chessboard is black or is white.
Hence, this method assumes the pawn is attacking the king
if both are diagnolly aligned by 1 step.
"""
flag = False
di, dj = self.get_piece_positions(notation=defendant)
if len(di) == 1 and len(dj) == 1:
di, dj = di[0], dj[0]
else:
return flag
ai, aj = self.get_piece_positions(notation=attacker)
checks = self.get_pawn_checks(ai=ai, aj=aj, di=di, dj=dj)
if checks:
flag = True
return flag
class IllegalPosition(Check):
"""
This class finds if the pieces are illegally positioned in the chessboard.
"""
def __init__(self, fen_label):
super().__init__(fen_label=fen_label)
def are_kings_less(self):
"""
Rule on kings.
"""
k_c = self.fen_label.count('k')
K_c = self.fen_label.count('K')
return (k_c < 1 and K_c < 1) or (k_c < 1) or (K_c < 1)
def are_kings_more(self):
"""
Rule on kings.
"""
k_c = self.fen_label.count('k')
K_c = self.fen_label.count('K')
return (k_c > 1 and K_c > 1) or (k_c > 1) or (K_c > 1)
def are_queens_more(self):
"""
Rule on queens.
"""
q_c = self.fen_label.count('q')
Q_c = self.fen_label.count('Q')
return (q_c > 9 and Q_c > 9) or (q_c > 9) or (Q_c > 9)
def are_bishops_more(self):
"""
Rule on bishops.
"""
b_c = self.fen_label.count('b')
B_c = self.fen_label.count('B')
return (b_c > 10 and B_c > 10) or (b_c > 10) or (B_c > 10)
def are_knights_more(self):
"""
Rule on knights.
"""
n_c = self.fen_label.count('n')
N_c = self.fen_label.count('N')
return (n_c > 10 and N_c > 10) or (n_c > 10) or (N_c > 10)
def are_rooks_more(self):
"""
Rule on rooks.
"""
r_c = self.fen_label.count('r')
R_c = self.fen_label.count('R')
return (r_c > 10 and R_c > 10) or (r_c > 10) or (R_c > 10)
def are_pawns_more(self):
"""
Rule on pawns.
"""
p_c = self.fen_label.count('p')
P_c = self.fen_label.count('P')
return (p_c > 8 and P_c > 8) or (p_c > 8) or (P_c > 8)
def rule_1(self):
"""
This method checks the count of the kings and the pieces in the board.
1. The count of white king and black king should always be 1.
2. The count of white queen and/or black queen should not cross 9.
3. The count of white bishop and/or black bishop should not cross 10.
4. The count of white knight and/or black knight should not cross 10.
5. The count of white rook and/or black rook should not cross 10.
6. The count of while pawn and/or black pawn should not cross 8.
7. The chessboard should never be empty.
"""
flag = False
if self.are_kings_less():
flag = True
elif self.are_kings_more():
flag = True
elif self.are_queens_more():
flag = True
elif self.are_bishops_more():
flag = True
elif self.are_knights_more():
flag = True
elif self.are_rooks_more():
flag = True
elif self.are_pawns_more():
flag = True
return flag
def rule_2(self):
"""
This method checks if the pawns are in the first and last row of the board.
1. No pawn should be on the first row and/or on the last row.
The pawn that reaches the last row always gets promoted.
Hence no pawns on the last row.
"""
flag = False
fen_label_list = self.fen_label.split('/')
f_row, l_row = fen_label_list[0], fen_label_list[-1]
p_f_row = 'p' in f_row
p_l_row = 'p' in l_row
P_f_row = 'P' in f_row
P_l_row = 'P' in l_row
if (p_f_row and p_l_row) or p_f_row or p_l_row:
flag = True
elif (P_f_row and P_l_row) or P_f_row or P_l_row:
flag = True
return flag
def rule_3(self):
"""
This method checks if the king is attacking the other king.
1. The king never checks the other king.
2. The king can attack other enemy pieces except the enemy king.
"""
return self.king_checks_king(attacker='k', defendant='K')
def rule_4(self):
"""
This method checks if the kings are under check simultaneously.
1. The two kings are never under check at the same time.
"""
r_checks_K = self.rook_checks_king(attacker='r', defendant='K')
n_checks_K = self.knight_checks_king(attacker='n', defendant='K')
b_checks_K = self.bishop_checks_king(attacker='b', defendant='K')
q_checks_K = self.queen_checks_king(attacker='q', defendant='K')
p_checks_K = self.pawn_checks_king(attacker='p', defendant='K')
R_checks_k = self.rook_checks_king(attacker='R', defendant='k')
N_checks_k = self.knight_checks_king(attacker='N', defendant='k')
B_checks_k = self.bishop_checks_king(attacker='B', defendant='k')
Q_checks_k = self.queen_checks_king(attacker='Q', defendant='k')
P_checks_k = self.pawn_checks_king(attacker='P', defendant='k')
is_K_checked = r_checks_K or n_checks_K or b_checks_K or q_checks_K or p_checks_K
is_k_checked = R_checks_k or N_checks_k or B_checks_k or Q_checks_k or P_checks_k
return is_K_checked and is_k_checked
def is_illegal(self):
"""
This method is a consolidation of all the above basic rules of chess.
"""
return self.rule_1() or self.rule_2() or self.rule_3() or self.rule_4()