-
Notifications
You must be signed in to change notification settings - Fork 1
/
rating.py
432 lines (373 loc) · 20.1 KB
/
rating.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
#!/usr/bin/env python3
"""
@author: Foo Zhi Yuan
ChessAI is being implemented using the concepts of bitboard and principal variation search
Requires Python 3 and Pillow(for GUI)
USAGE: python chessAI.py to play in GUI and python chessAI_console.py to play in console
"""
from moves import Moves
class Rating:
"""
Positional score credits to http://chessprogramming.wikispaces.com/Simplified+evaluation+function
"""
currKing=None;currQueen=None;currBishop=None;currKnight=None;currRook=None;currPawn=None
altKing=None;altQueen=None;altBishop=None;altKnight=None;altRook=None;altPawn=None
Moves=None
pawnBoard=[
[ 0, 0, 0, 0, 0, 0, 0, 0],
[50, 50, 50, 50, 50, 50, 50, 50],
[10, 10, 20, 30, 30, 20, 10, 10],
[ 5, 5, 10, 25, 25, 10, 5, 5],
[ 0, 0, 0, 20, 20, 0, 0, 0],
[ 5, -5,-10, 0, 0,-10, -5, 5],
[ 5, 10, 10,-20,-20, 10, 10, 5],
[ 0, 0, 0, 0, 0, 0, 0, 0]]
rookBoard=[
[ 0, 0, 0, 0, 0, 0, 0, 0],
[ 5, 10, 10, 10, 10, 10, 10, 5],
[-5, 0, 0, 0, 0, 0, 0, -5],
[-5, 0, 0, 0, 0, 0, 0, -5],
[-5, 0, 0, 0, 0, 0, 0, -5],
[-5, 0, 0, 0, 0, 0, 0, -5],
[-5, 0, 0, 0, 0, 0, 0, -5],
[ 0, 0, 0, 5, 5, 0, 0, 0]]
knightBoard=[
[-50,-40,-30,-30,-30,-30,-40,-50],
[-40,-20, 0, 0, 0, 0,-20,-40],
[-30, 0, 10, 15, 15, 10, 0,-30],
[-30, 5, 15, 20, 20, 15, 5,-30],
[-30, 0, 15, 20, 20, 15, 0,-30],
[-30, 5, 10, 15, 15, 10, 5,-30],
[-40,-20, 0, 5, 5, 0,-20,-40],
[-50,-40,-30,-30,-30,-30,-40,-50]]
bishopBoard=[
[-20,-10,-10,-10,-10,-10,-10,-20],
[-10, 0, 0, 0, 0, 0, 0,-10],
[-10, 0, 5, 10, 10, 5, 0,-10],
[-10, 5, 5, 10, 10, 5, 5,-10],
[-10, 0, 10, 10, 10, 10, 0,-10],
[-10, 10, 10, 10, 10, 10, 10,-10],
[-10, 5, 0, 0, 0, 0, 5,-10],
[-20,-10,-10,-10,-10,-10,-10,-20]]
queenBoard=[
[-20,-10,-10, -5, -5,-10,-10,-20],
[-10, 0, 0, 0, 0, 0, 0,-10],
[-10, 0, 5, 5, 5, 5, 0,-10],
[ -5, 0, 5, 5, 5, 5, 0, -5],
[ 0, 0, 5, 5, 5, 5, 0, -5],
[-10, 5, 5, 5, 5, 5, 0,-10],
[-10, 0, 5, 0, 0, 0, 0,-10],
[-20,-10,-10, -5, -5,-10,-10,-20]]
kingMidBoard=[
[-30,-40,-40,-50,-50,-40,-40,-30],
[-30,-40,-40,-50,-50,-40,-40,-30],
[-30,-40,-40,-50,-50,-40,-40,-30],
[-30,-40,-40,-50,-50,-40,-40,-30],
[-20,-30,-30,-40,-40,-30,-30,-20],
[-10,-20,-20,-20,-20,-20,-20,-10],
[ 20, 20, 0, 0, 0, 0, 20, 20],
[ 20, 30, 10, 0, 0, 10, 30, 20]]
kingEndBoard=[
[-50,-40,-30,-20,-20,-30,-40,-50],
[-30,-20,-10, 0, 0,-10,-20,-30],
[-30,-10, 20, 30, 30, 20,-10,-30],
[-30,-10, 30, 40, 40, 30,-10,-30],
[-30,-10, 30, 40, 40, 30,-10,-30],
[-30,-10, 20, 30, 30, 20,-10,-30],
[-30,-30, 0, 0, 0, 0,-30,-30],
[-50,-30,-30,-30,-30,-30,-30,-50]]
def __init__(self,Moves):
self.Moves=Moves
#type board: int
def reverse(self,board):
binary=format(board,"064b")
binary=binary[::-1]
return int(binary,2)
def flipBoard(self):
self.currKing=self.reverse(self.currKing)
self.currQueen=self.reverse(self.currQueen)
self.currBishop=self.reverse(self.currBishop)
self.currKnight=self.reverse(self.currKnight)
self.currRook=self.reverse(self.currRook)
self.currPawn=self.reverse(self.currPawn)
self.altKing=self.reverse(self.altKing)
self.altQueen=self.reverse(self.altQueen)
self.altBishop=self.reverse(self.altBishop)
self.altKnight=self.reverse(self.altKnight)
self.altRook=self.reverse(self.altRook)
self.altPawn=self.reverse(self.altPawn)
tempKing=self.currKing
tempQueen=self.currQueen
tempBishop=self.currBishop
tempKnight=self.currKnight
tempRook=self.currRook
tempPawn=self.currPawn
self.currKing=self.altKing
self.currQueen=self.altQueen
self.currBishop=self.altBishop
self.currKnight=self.altKnight
self.currRook=self.altRook
self.currPawn=self.altPawn
self.altKing=tempKing
self.altQueen=tempQueen
self.altBishop=tempBishop
self.altKnight=tempKnight
self.altRook=tempRook
self.altPawn=tempPawn
#type pieces: dictionary of bitboards
#type numOfPossibleMoves: int
#type depth: int
#type playerIsWhite: True if player is white, False otherwise
#note: Given a board, our score is only based on player's pieces.
def evaluate(self, history, whiteKing,whiteQueen,whiteBishop,whiteKnight,whiteRook,whitePawn,blackKing,blackQueen,blackBishop,blackKnight,blackRook,blackPawn,whiteQueenCastle,whiteKingCastle,blackQueenCastle,blackKingCastle,depth, playerIsWhite):
Moves=self.Moves
counter=0
self.currKing=whiteKing;self.currQueen=whiteQueen;self.currBishop=whiteBishop;self.currKnight=whiteKnight;self.currRook=whiteRook;self.currPawn=whitePawn
self.altKing=blackKing;self.altQueen=blackQueen;self.altBishop=blackBishop;self.altKnight=blackKnight;self.altRook=blackRook;self.altPawn=blackPawn
whitePossibleMoves=Moves.white_legalMoves(history,whiteKing,whiteQueen,whiteBishop,whiteKnight,whiteRook,whitePawn,blackKing,blackQueen,blackBishop,blackKnight,blackRook,blackPawn,whiteQueenCastle,whiteKingCastle,blackQueenCastle,blackKingCastle)
blackPossibleMoves=Moves.black_legalMoves(history,whiteKing,whiteQueen,whiteBishop,whiteKnight,whiteRook,whitePawn,blackKing,blackQueen,blackBishop,blackKnight,blackRook,blackPawn,whiteQueenCastle,whiteKingCastle,blackQueenCastle,blackKingCastle)
numOfWhitePossibleMoves=len(whitePossibleMoves.split())
numOfBlackPossibleMoves=len(blackPossibleMoves.split())
whiteKingMoves=Moves.whiteKing_moves(whiteKing,whiteQueen,whiteBishop,whiteKnight,whiteRook,whitePawn,blackKing,blackQueen,blackBishop,blackKnight,blackRook,blackPawn,whiteQueenCastle,whiteKingCastle,blackQueenCastle,blackKingCastle)
blackKingMoves=Moves.blackKing_moves(whiteKing,whiteQueen,whiteBishop,whiteKnight,whiteRook,whitePawn,blackKing,blackQueen,blackBishop,blackKnight,blackRook,blackPawn,whiteQueenCastle,whiteKingCastle,blackQueenCastle,blackKingCastle)
numOfWhiteKingMoves=len(whiteKingMoves.split())
numofBlackKingMoves=len(blackKingMoves.split())
boardFlipped=False
if(playerIsWhite):
pass
elif(not playerIsWhite):
self.flipBoard()
boardFlipped=True
"""
-> Our internal board is always represented with black pieces on top, white pieces at the bottom.
-> Our method usually will just evaluate white pieces, add it to our counter; flip the board, evaluate white pieces again,
and deduct them from our counter.
-> If player is white and if it's white turn to move, there's no problem at all with this approach,
since we will add white pieces' score to our counter, and deduct black pieces' score from our counter.
-> If player is white and if it's black turn to move, observe that there is still no problem with this approach.
In particular, we'll add white pieces' score to our counter, and deduct black pieces' score from our counter. This is okay, because
we want our board evaluation to be based on white player's perspective. We want to see if when black makes a move, how does it affect white player.
-> When player is black, we just need to flip board before computing. The rest of the procedure remains the same.
"""
#give each piece on the board a score, rate the board
#Player's perspective
material=self.rateMaterial(self.currKing,self.currQueen,self.currBishop,self.currKnight,self.currRook,self.currPawn)
counter=counter+self.rateAttack(self.currKing,self.currQueen,self.currBishop,self.currKnight,self.currRook,self.currPawn,self.altKing,self.altQueen,self.altBishop,self.altKnight,self.altRook,self.altPawn)
counter=counter+material
#score for white pieces (player is white)
if(not boardFlipped):
#rate Moveability
counter=counter+self.rateMoveability(self.currKing,self.currQueen,self.currBishop,self.currKnight,self.currRook,self.currPawn,self.altKing,self.altQueen,self.altBishop,self.altKnight,self.altRook,self.altPawn,depth,material,numOfWhitePossibleMoves)
#rate positional
counter=counter+self.ratePositional(self.currKing,self.currQueen,self.currBishop,self.currKnight,self.currRook,self.currPawn,material,numOfWhiteKingMoves)
#score for black pieces (player is black)
elif(boardFlipped):
#rate Moveability
counter=counter+self.rateMoveability(self.currKing,self.currQueen,self.currBishop,self.currKnight,self.currRook,self.currPawn,self.altKing,self.altQueen,self.altBishop,self.altKnight,self.altRook,self.altPawn,depth,material,numOfBlackPossibleMoves)
#rate positional
counter=counter+self.ratePositional(self.currKing,self.currQueen,self.currBishop,self.currKnight,self.currRook,self.currPawn,material,numofBlackKingMoves)
self.flipBoard()
#we need to update our board flipped variable according to if our board is flipped now
#if board is not flipped before, since we just do flipBoard(), our board is flipped now.
if(boardFlipped==False):
boardFlipped=True
#if board is flipped before, since we just do flipBoard(), board is now back to its original state. So board is not flipped now.
elif(boardFlipped==True):
boardFlipped=False
#Opponent's perspective
material=self.rateMaterial(self.currKing,self.currQueen,self.currBishop,self.currKnight,self.currRook,self.currPawn)
counter=counter-self.rateAttack(self.currKing,self.currQueen,self.currBishop,self.currKnight,self.currRook,self.currPawn,self.altKing,self.altQueen,self.altBishop,self.altKnight,self.altRook,self.altPawn)
counter=counter-material
#score for black pieces (player is white)
if(boardFlipped):
counter=counter-self.rateMoveability(self.currKing,self.currQueen,self.currBishop,self.currKnight,self.currRook,self.currPawn,self.altKing,self.altQueen,self.altBishop,self.altKnight,self.altRook,self.altPawn,depth,material,numOfBlackPossibleMoves)
counter=counter-self.ratePositional(self.currKing,self.currQueen,self.currBishop,self.currKnight,self.currRook,self.currPawn,material,numofBlackKingMoves)
#score for white pieces (player is black)
elif(not boardFlipped):
counter=counter-self.rateMoveability(self.currKing,self.currQueen,self.currBishop,self.currKnight,self.currRook,self.currPawn,self.altKing,self.altQueen,self.altBishop,self.altKnight,self.altRook,self.altPawn,depth,material,numOfWhitePossibleMoves)
counter=counter-self.ratePositional(self.currKing,self.currQueen,self.currBishop,self.currKnight,self.currRook,self.currPawn,material,numOfWhiteKingMoves)
self.flipBoard()
#we need to update our board flipped variable according to if our board is flipped now
#if board is not flipped before, since we just do flipBoard(), our board is flipped now.
if(boardFlipped==False):
boardFlipped=True
#if board is flipped before, since we just do flipBoard(), board is now back to its original state. So board is not flipped now.
elif(boardFlipped==True):
boardFlipped=False
#restore board to original state. black pieces on top, white pieces at bottom
if(boardFlipped):
self.flipBoard()
boardFlipped=False
return (counter+depth*500)
#Given a board, do a quick evaluation on the board. We only perform rateMaterial and rateMoveability.
def quickEvaluate(self, history, whiteKing,whiteQueen,whiteBishop,whiteKnight,whiteRook,whitePawn,blackKing,blackQueen,blackBishop,blackKnight,blackRook,blackPawn,whiteQueenCastle,whiteKingCastle,blackQueenCastle,blackKingCastle,depth, playerIsWhite):
Moves=self.Moves
counter=0
self.currKing=whiteKing;self.currQueen=whiteQueen;self.currBishop=whiteBishop;self.currKnight=whiteKnight;self.currRook=whiteRook;self.currPawn=whitePawn
self.altKing=blackKing;self.altQueen=blackQueen;self.altBishop=blackBishop;self.altKnight=blackKnight;self.altRook=blackRook;self.altPawn=blackPawn
whitePossibleMoves=Moves.white_legalMoves(history,whiteKing,whiteQueen,whiteBishop,whiteKnight,whiteRook,whitePawn,blackKing,blackQueen,blackBishop,blackKnight,blackRook,blackPawn,whiteQueenCastle,whiteKingCastle,blackQueenCastle,blackKingCastle)
blackPossibleMoves=Moves.black_legalMoves(history,whiteKing,whiteQueen,whiteBishop,whiteKnight,whiteRook,whitePawn,blackKing,blackQueen,blackBishop,blackKnight,blackRook,blackPawn,whiteQueenCastle,whiteKingCastle,blackQueenCastle,blackKingCastle)
numOfWhitePossibleMoves=len(whitePossibleMoves.split())
numOfBlackPossibleMoves=len(blackPossibleMoves.split())
boardFlipped=False
if(playerIsWhite):
pass
elif(not playerIsWhite):
self.flipBoard()
boardFlipped=True
"""
-> Our internal board is always represented with black pieces on top, white pieces at the bottom.
-> Our method usually will just evaluate white pieces, add it to our counter; flip the board, evaluate white pieces again,
and deduct them from our counter.
-> If player is white and if it's white turn to move, there's no problem at all with this approach,
since we will add white pieces' score to our counter, and deduct black pieces' score from our counter.
-> If player is white and if it's black turn to move, observe that there is still no problem with this approach.
In particular, we'll add white pieces' score to our counter, and deduct black pieces' score from our counter. This is okay, because
we want our board evaluation to be based on white player's perspective. We want to see if when black makes a move, how does it affect white player.
-> When player is black, we just need to flip board before computing. The rest of the procedure remains the same.
"""
#give each piece on the board a score, rate the board
#Player's perspective
material=self.rateMaterial(self.currKing,self.currQueen,self.currBishop,self.currKnight,self.currRook,self.currPawn)
counter=counter+material
#score for white pieces (player is white)
if(not boardFlipped):
#rate Moveability
counter=counter+self.rateMoveability(self.currKing,self.currQueen,self.currBishop,self.currKnight,self.currRook,self.currPawn,self.altKing,self.altQueen,self.altBishop,self.altKnight,self.altRook,self.altPawn,depth,material,numOfWhitePossibleMoves)
#score for black pieces (player is black)
elif(boardFlipped):
#rate Moveability
counter=counter+self.rateMoveability(self.currKing,self.currQueen,self.currBishop,self.currKnight,self.currRook,self.currPawn,self.altKing,self.altQueen,self.altBishop,self.altKnight,self.altRook,self.altPawn,depth,material,numOfBlackPossibleMoves)
self.flipBoard()
#we need to update our board flipped variable according to if our board is flipped now
#if board is not flipped before, since we just do flipBoard(), our board is flipped now.
if(boardFlipped==False):
boardFlipped=True
#if board is flipped before, since we just do flipBoard(), board is now back to its original state. So board is not flipped now.
elif(boardFlipped==True):
boardFlipped=False
#Opponent's perspective
material=self.rateMaterial(self.currKing,self.currQueen,self.currBishop,self.currKnight,self.currRook,self.currPawn)
counter=counter-material
#score for black pieces (player is white)
if(boardFlipped):
counter=counter-self.rateMoveability(self.currKing,self.currQueen,self.currBishop,self.currKnight,self.currRook,self.currPawn,self.altKing,self.altQueen,self.altBishop,self.altKnight,self.altRook,self.altPawn,depth,material,numOfBlackPossibleMoves)
#score for white pieces (player is black)
elif(not boardFlipped):
counter=counter-self.rateMoveability(self.currKing,self.currQueen,self.currBishop,self.currKnight,self.currRook,self.currPawn,self.altKing,self.altQueen,self.altBishop,self.altKnight,self.altRook,self.altPawn,depth,material,numOfWhitePossibleMoves)
self.flipBoard()
#we need to update our board flipped variable according to if our board is flipped now
#if board is not flipped before, since we just do flipBoard(), our board is flipped now.
if(boardFlipped==False):
boardFlipped=True
#if board is flipped before, since we just do flipBoard(), board is now back to its original state. So board is not flipped now.
elif(boardFlipped==True):
boardFlipped=False
#restore board to original state. black pieces on top, white pieces at bottom
if(boardFlipped):
self.flipBoard()
boardFlipped=False
return (counter+depth*500)
def getPieceCountInBitBoard(self,bitBoard):
binary=format(bitBoard,"064b")
return binary.count("1")
def rateMaterial(self,king,queen,bishop,knight,rook,pawn): #CORRECT
counter=0
#white queen
pieceCount=self.getPieceCountInBitBoard(queen)
counter=counter+(pieceCount*900)
#white bishop
pieceCount=self.getPieceCountInBitBoard(bishop)
if(pieceCount>=2):
counter=counter+(pieceCount*300)
else:
counter=counter+(pieceCount*250)
#white knight
pieceCount=self.getPieceCountInBitBoard(knight)
counter=counter+(pieceCount*300)
#white rook
pieceCount=self.getPieceCountInBitBoard(rook)
counter=counter+(pieceCount*500)
#white pawn
pieceCount=self.getPieceCountInBitBoard(pawn)
counter=counter+(pieceCount*100)
return counter
def ratePositional(self,king,queen,bishop,knight,rook,pawn,material,numofKingMoves):
counter=0
for i in range(64):
if((pawn>>i)&1==1):
counter=counter+self.pawnBoard[i//8][i%8]
elif((rook>>i)&1==1):
counter=counter+self.rookBoard[i//8][i%8]
elif((knight>>i)&1==1):
counter=counter+self.knightBoard[i//8][i%8]
elif((bishop>>i)&1==1):
counter=counter+self.bishopBoard[i//8][i%8]
elif((queen>>i)&1==1):
counter=counter+self.queenBoard[i//8][i%8]
elif((king>>i)&1==1):
#score the position of the king based on phase
#if the score of piece on the board >=1750, this is a middle phase
if(material>=1750):
counter=counter+self.kingMidBoard[i//8][i%8]
counter=counter+(numofKingMoves*10)
#end phase
else:
counter=counter+self.kingEndBoard[i//8][i%8]
counter=counter+(numofKingMoves*30)
return counter
#score our ability to move
#penalise heavily for checkmate and stalemate #CORRECT
def rateMoveability(self,playerKing,playerQueen,playerBishop,playerKnight,playerRook,playerPawn,opponentKing,opponentQueen,opponentBishop,opponentKnight,opponentRook,opponentPawn,depth,material,numOfPossibleMoves):
Moves=self.Moves
counter=0
#5 points for each valid move
counter=counter+(numOfPossibleMoves*5)
#either checkmate or stalemate
#stalemate means no square to move to, except for squares that will get king into checked
if(numOfPossibleMoves==0):
#if king currently in checked and no place to move to, checkmate
if(playerKing&Moves.whiteKing_illegalMoves(playerKing,playerQueen,playerBishop,playerKnight,playerRook,playerPawn,opponentKing,opponentQueen,opponentBishop,opponentKnight,opponentRook,opponentPawn)!=0):
counter=counter+(-200000*depth)
#if king currently is not in checked and no place to move to, stalemate
else:
counter=counter+(-150000*depth)
return counter
#check if each of my piece is under attack. if it is, return lower score #CORRECT
def rateAttack(self,playerKing,playerQueen,playerBishop,playerKnight,playerRook,playerPawn,opponentKing,opponentQueen,opponentBishop,opponentKnight,opponentRook,opponentPawn):
Moves=self.Moves
counter=0
kingPosition=None
underAttack=Moves.whiteKing_illegalMoves(playerKing,playerQueen,playerBishop,playerKnight,playerRook,playerPawn,opponentKing,opponentQueen,opponentBishop,opponentKnight,opponentRook,opponentPawn)
for i in range(64):
#we check if our piece at this position is under attack by placing our king at this position
#if our king is under attack, playerPawn at this position is also under attack
if((playerPawn>>i)&1==1):
kingPosition=(1<<i)
if(underAttack&kingPosition!=0):
counter=counter-64
#if our king is under attack, playerRook at this position is also under attack
elif((playerRook>>i)&1==1):
kingPosition=(1<<i)
if(underAttack&kingPosition!=0):
counter=counter-500
#if our king is under attack, playerKnight at this position is also under attack
elif((playerKnight>>i)&1==1):
kingPosition=(1<<i)
if(underAttack&kingPosition!=0):
counter=counter-300
#if our king is under attack, playerBishop at this position is also under attack
elif((playerBishop>>i)&1==1):
kingPosition=(1<<i)
if(underAttack&kingPosition!=0):
counter=counter-300
#if our king is under attack, playerQueen at this position is also under attack
elif((playerQueen>>i)&1==1):
kingPosition=(1<<i)
if(underAttack&kingPosition!=0):
counter=counter-900
kingPosition=playerKing
if(underAttack&kingPosition!=0):
counter=counter-2000
#our piece under attack is less important than our piece actually being captured by opponent, so we divide it by 2
return counter/2