# Chess game in Python - follow-up

Three weeks ago I wrote the first version of my chess game in Python and shared on Code Review. Thanks to your suggestions, I improved my code. I would like to know if I am going in good direction with all this stuff.

__author__ = 'Goldsmitd'
class Condition:

def range(x,y):
return x > 8 and y > 8

def if_figure(board,x,y):
return board[x][y].sl == '.'

def same_team(x1,y1,x2,y2,board):
if board[x1][y1].team == board[x2][y2].team:
return True
else:
return False

def s_choice(board):
while True:
try:
print('give x and y')
x = int(input())
y = int(input())
except:
print('Coordinates can only be integers')
if Condition.range(x,y):
print('Coordinates are out of range')
elif Condition.if_figure(board,x,y):
print('Square chosen by you is empty')
else:
return x,y
break

def d_choice(board):
while True:
try:
print('give x and y')
x=int(input())
y=int(input())
except:
print('Coordinates can only be integers')
if Condition.range(x,y):
print('Coordinates are out of range')
else:
return x,y
break

def kill(x1,y1,x2,y2,board):
if board[x1][y1].team == 'white' and board[x2][y2].team == 'black':
return True
elif board[x1][y1].team == 'black' and board[x2][y2].team == 'white':
return True
else:
return False

def Pawnkill(x1,y1,x2,y2,board):
if board[x1][y1].team == 'white' and board[x2][y2].team == 'black' and board[x1][y1].name == 'Pawn':
return True
elif board[x1][y1].team == 'black' and board[x2][y2].team == 'white'and board[x1][y1].name == 'Pawn':
return True
else:
return False

def solid(x1,y1,x2,y2,board):
if board[x1][y1].name=='Rook':
if x2>x1:
for i in range(x1+1,x2):
if board[i][y1].sl != '.':
return False
break
elif x2<x1:
for i in range(x2+1,x1):
if board[i][y1].sl != '.':
return False
break
elif y2>y1:
for j in range(y1+1,y2):
if board[x1][j].sl != '.':
return False
break
elif y2<y1:
for j in range(y2+1,y1):
if board[x1][j].sl != '.':
return False
break
else:
return True

elif board[x1][y1].name=='Bishop':
if x2>x1 and y2>y1:
for i in range(x1+1,x2):
for j in range(y1+1,y2):
if board[i][j].sl != '.':
return False
break
elif x2<x1 and y2<y1:
for i in range(x2+1,x1):
for j in range(y2+1,y1):
if board[i][j].sl != '.':
return False
break
elif x2<x1 and y2>y1:
for i in range(x2+1,x1):
for j in range(y1+1,y2):
if board[i][j].sl != '.':
return False
break
elif x2>x1 and y2<y1:
for i in range(x1+1,x2):
for j in range(y2+1,y1):
if board[i][j].sl != '.':
return False
break
else:
return True

elif board[x1][y1].name=='Queen':
if x2>x1 and y2>y1:
for i in range(x1+1,x2):
for j in range(y1+1,y2):
if board[i][j].sl != '.':
return False
break
elif x2<x1 and y2<y1:
for i in range(x2+1,x1):
for j in range(y2+1,y1):
if board[i][j].sl != '.':
return False
break
elif x2<x1 and y2>y1:
for i in range(x2+1,x1):
for j in range(y1+1,y2):
if board[i][j].sl != '.':
return False
break
elif x2>x1 and y2<y1:
for i in range(x1+1,x2):
for j in range(y2+1,y1):
if board[i][j].sl != '.':
return False
break
elif x2>x1:
for i in range(x1+1,x2):
if board[i][y1].sl != '.':
return False
break
elif x2<x1:
for i in range(x2+1,x1):
if board[i][y1].sl != '.':
return False
break
elif y2>y1:
for j in range(y1+1,y2):
if board[x1][j].sl != '.':
return False
break
elif y2<y1:
for j in range(y2+1,y1):
if board[x1][j].sl != '.':
return False
break

else:
return True

else:
return True

class Number:
def __init__(self,sl):
self.sl=sl

class Empty:
def __init__(self,x,y,sl,team):
self.name = 'Empty'
self.x = x
self.y = y
self.sl = sl
self.team = team

class Rook:
def __init__(self,x,y,sl,team):
self.name = 'Rook'
self.x = x
self.y = y
self.sl = sl
self.team = team

def req(self,sx,sy,dx,dy,board):
if  ( dx==sx or dy==sy ) :
return True
else:
return False

class Knight:
def __init__(self,x,y,sl,team):
self.name = 'Knight'
self.x = x
self.y = y
self.sl = sl
self.team = team

def req(self,sx,sy,dx,dy,board):
if  (abs(dx - sx)**2+abs(dy - sy)**2 == 5) :
return True
else:
return False

class Bishop:
def __init__(self,x,y,sl,team):
self.name = 'Bishop'
self.x = x
self.y = y
self.sl = sl
self.team = team

def req(self,sx,sy,dx,dy,board):
if  (abs(dx - sx)==abs(dy - sy)) :
return True
else:
return False

class Queen:
def __init__(self,x,y,sl,team):
self.name = 'Queen'
self.x = x
self.y = y
self.sl = sl
self.team = team

def req(self,sx,sy,dx,dy,board):
if  (dx == sx or dy == sy or (abs(dx - sx) == abs(dy - sy))) :
return True
else:
return False

class King:
def __init__(self,x,y,sl,team):
self.name = 'King'
self.x = x
self.y = y
self.sl = sl
self.team = team

def req(self,sx,sy,dx,dy,board):
if  abs(dx-sx) < 2 and abs(dy-sy) <  2 :
return True
else:
return False

class Pawn:
def __init__(self,x,y,sl,team):
self.name = 'Pawn'
self.x = x
self.y = y
self.sl = sl
self.team = team
def req(self,sx,sy,dx,dy,board):
if board[sx][sy].team == "white" and dx-sx == -1:
return True
elif board[sx][sy].team == 'black' and dx-sx == 1:
return True
else:
return False

class Chess_Board:
def __init__(self):
self.board = [[Empty(x='',y='',sl='.',team='')]*9 for _ in range(9)]
self.board[0][0] = Rook(x=0,y=0,sl='r',team='black')
self.board[0][1] = Knight(x=0,y=1,sl='n',team='black')
self.board[0][2] = Bishop(x=0,y=2,sl='b',team='black')
self.board[0][3] = Queen(x=0,y=3,sl='q',team='black')
self.board[0][4] = King(x=0,y=4,sl='k',team='black')
self.board[0][5] = Bishop(x=0,y=5,sl='b',team='black')
self.board[0][6] = Knight(x=0,y=6,sl='n',team='black')
self.board[0][7] = Rook(x=0,y=7,sl='r',team='black')
self.board[1][0] = Pawn(x=1,y=0,sl='p',team='black')
self.board[1][1] = Pawn(x=1,y=1,sl='p',team='black')
self.board[1][2] = Pawn(x=1,y=2,sl='p',team='black')
self.board[1][3] = Pawn(x=1,y=3,sl='p',team='black')
self.board[1][4] = Pawn(x=1,y=4,sl='p',team='black')
self.board[1][5] = Pawn(x=1,y=5,sl='p',team='black')
self.board[1][6] = Pawn(x=1,y=6,sl='p',team='black')
self.board[1][7] = Pawn(x=1,y=7,sl='p',team='black')
self.board[7][0] = Rook(x=7,y=0,sl='R',team='white')
self.board[7][1] = Knight(x=7,y=1,sl='N',team='white')
self.board[7][2] = Bishop(x=7,y=2,sl='B',team='white')
self.board[7][3] = Queen(x=7,y=3,sl='Q',team='white')
self.board[7][4] = King(x=7,y=4,sl='K',team='white')
self.board[7][5] = Bishop(x=7,y=5,sl='B',team='white')
self.board[7][6] = Knight(x=7,y=6,sl='N',team='white')
self.board[7][7] = Rook(x=7,y=7,sl='R',team='white')
self.board[2][0] = Pawn(x=3,y=0,sl='P',team='white')
self.board[6][1] = Pawn(x=6,y=1,sl='P',team='white')
self.board[6][2] = Pawn(x=6,y=2,sl='P',team='white')
self.board[6][3] = Pawn(x=6,y=3,sl='P',team='white')
self.board[6][4] = Pawn(x=6,y=4,sl='P',team='white')
self.board[6][5] = Pawn(x=6,y=5,sl='P',team='white')
self.board[6][6] = Pawn(x=6,y=6,sl='P',team='white')
self.board[6][7] = Pawn(x=6,y=7,sl='P',team='white')
for i in range(9):
self.board[i][8 ]= Number(sl=i)
for j in range(9):
self.board[8][j] = Number(sl=j)

def display(self):
for i in range(9):
for j in range(9):
print (self.board[i][j].sl, end=' ')
print()

def move(self):
while True:
print('Give a position of figure')
sx,sy=Condition.s_choice(self.board)
print(self.board[sx][sy].name)
print('Now choose a destnation')
dx,dy=Condition.d_choice(self.board)
mark_same=Condition.same_team(sx,sy,dx,dy,self.board)
mark_kill=Condition.kill(sx,sy,dx,dy,self.board)
mark_Pawnkill=Condition.Pawnkill(sx,sy,dx,dy,self.board)
mark_solid=Condition.solid(sx,sy,dx,dy,self.board)
mark_move=self.board[sx][sy].req(sx,sy,dx,dy,self.board)
if mark_solid==False:
print('Figures are not ghosts')

elif (mark_Pawnkill == True and abs(dx-sx) == abs(dy-sy) and mark_same == False):
self.board[dx][dy] = self.board[sx][sy]
self.board[dx][dy].x = dx
self.board[dx][dy].y = dy
self.board[sx][sy] = Empty(x='',y='',sl='.',team='')
return self.board
break

elif (mark_move == True and mark_Pawnkill == False and (mark_kill == True or mark_same == False)):
self.board[dx][dy] = self.board[sx][sy]
self.board[dx][dy].x = dx
self.board[dx][dy].y = dy
self.board[sx][sy] = Empty(x='',y='',sl='.',team='')
return self.board
break
else:
print('Figure can not move here, try again')
continue

a=Chess_Board()
a.display()
#Early version of game engine
while True:
a.move()
a.display()


Very nice game. Good effort! A few suggestions to make it better.

The instruction to give x and y is a little confusing i tried entering 3, 6 then 3 and 6 ... it took me a while to figure out that I needed to enter one and then the other. making this small change could make it easier:

try:
print('give x and y')
x = int(input("x:"))
y = int(input("y:"))


Not really a "code problem" per se, but rather a chess problem. The numbering on the board should start from the bottom left corner and move out. And it should count from 1, not from 0.

r n b q k b n r 0
p p p p p p p p 1
P . . . . . . . 2
. . . . . . . . 3
. . . . . . . . 4
. . . . . . . . 5
. P P P P P P P 6
R N B Q K B N R 7
0 1 2 3 4 5 6 7 8


So that the white Rook on The Queen side is at coordinates (1,1) and the other white rook at coordinates (1,8)

The system accepts negative values for x and y, as well as the number 8... all of these generate an error (since the grid currently starts counting at 0)

At the start of the game, the left-most white pawn is sitting all the way across the board

 r n b q k b n r 0
p p p p p p p p 1
>P . . . . . . . 2
. . . . . . . . 3
. . . . . . . . 4
. . . . . . . . 5
. P P P P P P P 6
R N B Q K B N R 7
0 1 2 3 4 5 6 7 8


You could use unicode chess pieces, this would allow you to free the alphabet and use the traditional Letter-Number chess coordinates. This is the Wikipedia link. You can get them into python using chr()

for i in range(12):
chr(9812+i)


output:

'♔'
'♕'
'♖'
'♗'
'♘'
'♙'
'♚'
'♛'
'♜'
'♝'
'♞'
'♟'

• Condition is not a class, but a set of static functions, so you should define those functions outside a class, probably in a module (a file).

• Inconsistent function signatures: if_figure(board,x,y) and same_team(x1,y1,x2,y2,board). Board should be always first or last.

• Your classes Rook, Knight, etc. are nearly identical, so you could save a lot of code lines by using sub-classing.

• You duplicate the position information: self.board[0][0] = Rook(x=0,y=0,sl='r',team='black'). The position is both in the board and the Rook. You want to avoid information duplication since it adds a lot of complication because you have to make sure you update values everywhere at the same time. I think part of the problem here is that you did not use "separation of concerns": you should different (and separated) code for the game logic and for the GUI. You should not have the edge numbers "1", "2", on the same board as the board used for logic.

• Similarly, the possible motions are coded in both req and solid. Should be only coded in one place. BTW, solid should be renamed to something more informative.

• For the team instead of "white"/"black", I would just use a boolean.

• Chess_board.__init__ could use for-loops for the pawns.

• Chess_Board should be ChessBoard.