Closed OfekShochat closed 4 years ago
@OfekShochat thanks for submitting this! It seems like you may have accidentally missed some lines when copy and pasting the example above. In order to reproduce it, I will need a runnable example (ideally including all imports). Thanks!
thanks.this is the full code(sorry if this is long...):
from math import inf
import chess
from time import time
import sys
import os
import chess.polyglot as bookloader
import json
from tqdm import tqdm
import pyximport; pyximport.install()
from numba import njit
from multiprocessing import Process
from threading import Thread
from time import sleep
################################### piece squre table ###################################
# piece square tables (for analysis) (change these to change the engine behavior)
#import numpy as np
bPawnTable = ( 0, 0, 0, 0, 0, 0, 0, 0,
78, 83, 86, 73, 102, 82, 85, 90,
7, 29, 21, 44, 40, 31, 44, 7,
-17, 16, -2, 15, 14, 0, 15, -13,
-26, 3, 10, 9, 6, 1, 0, -23,
-22, 9, 5, -11, -10, -2, 3, -19,
-31, 8, -7, -37, -36, -14, 3, -31,
0, 0, 0, 0, 0, 0, 0, 0)
wPawnTable = (0,0,0,0,0,0,0,0,
10,8,9,5,3,6,2,5,
11, 6,5,6,8,3,10,
-6,5,3,60,60,4,6,1,
10, 11, 12, 11, 10, 9, 5,3,
12, 13, 14, 15,14, 13, 12, 12,
70, 70, 80, 70, 70, 80 , 80, 80,
90,90,90,90,90,90,90,90)
bKnightTable = ( -66, -53, -75, -75, -10, -55, -58, -70,
-3, -6, 100, -36, 4, 62, -4, -14,
10, 67, 1, 74, 73, 27, 62, -2,
24, 24, 45, 37, 33, 41, 25, 17,
-1, 5, 31, 21, 22, 35, 2, 0,
-18, 10, 13, 22, 18, 15, 11, -14,
-23, -15, 2, 0, 2, 0, -23, -20,
-74, -23, -26, -24, -19, -35, -22, -69)
wKnightTable = bKnightTable[::-1]
bBishopTable = ( -59, -78, -82, -76, -23,-107, -37, -50,
-11, 20, 35, -42, -39, 31, 2, -22,
-9, 39, -32, 41, 52, -10, 28, -14,
25, 17, 20, 34, 26, 25, 15, 10,
13, 10, 17, 23, 17, 16, 0, 7,
14, 25, 24, 15, 8, 25, 20, 15,
19, 20, 11, 6, 7, 6, 20, 16,
-7, 2, -15, -12, -14, -15, -10, -10)
wBishopTable = bBishopTable[::-1]
bRookTable = ( 35, 29, 33, 4, 37, 33, 56, 50,
55, 29, 56, 67, 55, 62, 34, 60,
19, 35, 28, 33, 45, 27, 25, 15,
0, 5, 16, 13, 18, -4, -9, -6,
-28, -35, -16, -21, -13, -29, -46, -30,
-42, -28, -42, -25, -25, -35, -26, -46,
-53, -38, -31, -26, -29, -43, -44, -53,
-30, -24, -18, 5, -2, -18, -31, -32)
wRookTable = bRookTable[::-1]
bQueenTable = ( 6, 1, -8,-104, 69, 24, 88, 26,
14, 32, 60, -10, 20, 76, 57, 24,
-2, 43, 32, 60, 72, 63, 43, 2,
1, -16, 22, 17, 25, 20, -13, -6,
-14, -15, -2, -5, -1, -10, -20, -22,
-30, -6, -13, -11, -16, -11, -16, -27,
-36, -18, 0, -19, -15, -15, -21, -38,
-39, -30, -31, -13, -31, -36, -34, -42)
wQueenTable = bQueenTable[::-1]
bKingTable = ( 4, 54, 47, -99, -99, 60, 83, -62,
-32, 10, 55, 56, 56, 55, 10, 3,
-62, 12, -57, 44, -67, 28, 37, -31,
-55, 50, 11, -4, -19, 13, 0, -49,
-55, -43, -52, -28, -51, -47, -8, -50,
-47, -42, -43, -79, -64, -32, -29, -32,
-4, 3, -14, -50, -57, -18, 13, 4,
17, 30, -3, -14, 6, -1, 40, 18)
wKingTable = bKingTable[::-1]
king_safty = (
0, 0, 1, 2, 3, 5, 7, 9, 12, 15,
18, 22, 26, 30, 35, 39, 44, 50, 56, 62,
68, 75, 82, 85, 89, 97, 105, 113, 122, 131,
140, 150, 169, 180, 191, 202, 213, 225, 237, 248,
260, 272, 283, 295, 307, 319, 330, 342, 354, 366,
377, 389, 401, 412, 424, 436, 448, 459, 471, 483,
494, 500, 500, 500, 500, 500, 500, 500, 500, 500,
500, 500, 500, 500, 500, 500, 500, 500, 500, 500,
500, 500, 500, 500, 500, 500, 500, 500, 500, 500,
500, 500, 500, 500, 500, 500, 500, 500, 500, 500
)
tables = {"doubled":-1, "isolated": -3,"-100": bPawnTable, "100":wPawnTable, "-320":bKnightTable, "320":wKnightTable, "-330":bBishopTable,"330":wBishopTable,"500":wRookTable,"-500":bRookTable,"900":wQueenTable, "-900":bQueenTable,"-20000":bKingTable, "20000":wKingTable}
weights = {"mobility": 0.2, "piece_table":0.1, "king_table":0.4} # np.random.random_sample()
################################### /* piece squre table /* ###################################
def opening_move(board, book_path):
return bookloader.open_reader(book_path).find(board).move
################################### evaluation ###################################
def get_piece_val(piece:str):
temp = 0
if piece == "k":
temp = -200
if piece == "q":
temp = -9
if piece == "r":
temp = -5
if piece == "b":
temp = -3.4
if piece == "n":
temp = -3.2
if piece == "p":
temp = -1
if piece == "K":
temp = 200
if piece == "Q":
temp = 9
if piece == "R":
temp = 5
if piece == "B":
temp = 3.3
if piece == "N":
temp = 3.20
if piece == "P":
temp = 1
return temp
def passed_pawn(pm, is_end_game):
whiteYmax = [ -1 ] * 8
blackYmin = [ 8 ] * 8
for key, p in pm.items():
if p.piece_type != chess.PAWN:
continue
x = key & 7
y = key >> 3
if p.color == chess.WHITE:
whiteYmax[x] = max(whiteYmax[x], y)
else:
blackYmin[x] = min(blackYmin[x], y)
scores = [ [ 0, 5, 20, 30, 40, 50, 80, 0 ], [ 0, 5, 20, 40, 70, 120, 200, 0 ] ]
score = 0
for key, p in pm.items():
if p.piece_type != chess.PAWN:
continue
x = key & 7
y = key >> 3
if p.color == chess.WHITE:
left = (x > 0 and (blackYmin[x - 1] <= y or blackYmin[x - 1] == 8)) or x == 0
front = blackYmin[x] < y or blackYmin[x] == 8
right = (x < 7 and (blackYmin[x + 1] <= y or blackYmin[x + 1] == 8)) or x == 7
if left and front and right:
score += scores[is_end_game][y]
else:
left = (x > 0 and (whiteYmax[x - 1] >= y or whiteYmax[x - 1] == -1)) or x == 0
front = whiteYmax[x] > y or whiteYmax[x] == -1
right = (x < 7 and (whiteYmax[x + 1] >= y or whiteYmax[x + 1] == -1)) or x == 7
if left and front and right:
score -= scores[is_end_game][7 - y]
return score
def evaluate(board_fen, debugger=False):
thisboard = chess.Board(board_fen)
if list(chess.LegalMoveGenerator(thisboard)) == []:
if thisboard.is_check():
return 10000
return 0
if thisboard.is_insufficient_material():
return 0
evaluation = 0
index = 0
for i in str(thisboard):
evaluation += get_piece_val(i)
try:
piece_val = get_piece_val(i)
if piece_val != 20000:
evaluation += tables[str(piece_val)][index] * weights["piece_table"]
else:
evaluation += tables[str(piece_val)][index] * weights["king_table"]
except KeyError:
index -= 1
index += 1
for i in chess.LegalMoveGenerator(thisboard): evaluation += 1 * weights["mobility"]
if list(chess.LegalMoveGenerator(thisboard)) == []:
if thisboard.is_check():
return 10000
return 0
evaluation += passed_pawn(thisboard.piece_map(), False)
move = list(thisboard.legal_moves)[0]
move = chess.Move.from_uci(str(move))
thisboard.push(move)
for i in chess.LegalMoveGenerator(thisboard): evaluation -= 1 * weights["mobility"]
return evaluation
def update_nodes(pbar, st):
while tomove:
sleep(1)
pbar.set_description("calculating %s at %s nodes/s" % (str(move), nodesss/(time() - st)))
def negamax_root(board, depth, alpha, beta, color):
global tomove
global move
tomove = True
st = time()
value = -inf
possible = tuple(board.legal_moves)
with tqdm(total=len(possible), unit="move") as pbar:
nodesssss = Thread(target=update_nodes, args=[pbar, st])
nodesssss.daemon = True
nodesssss.start()
for move in possible:
tomove = True
#assert board.is_legal(move)
board.push(move)
firstguess = -negamax(board, depth, -10000, 10000, color)
board.pop()
if str(move) == "c8c1":
print( " " + str(move), firstguess, "fff")
if firstguess > value:
best_move = move
value = firstguess
#print( " " + str(best_move), value)
if firstguess >= 10000:
break
pbar.update(1)
print("""{
"depth": %s,
"score": %s,
"move": "%s",
"time_took": %s,
"nodes searched": %s,
"nodes per second": %s}""" % (depth, value, best_move, time() - st, nodesss, nodesss/(time() - st)))
tomove = False
return best_move
nodesss = 0
def nodes(num_to_increase = 0):
global nodesss
nodesss += num_to_increase
return nodesss, nodesss / (time() - st)
nodesss = 0
def order(board):
for move in board.legal_moves:
pass
def negamax(board, depth, alpha, beta, color):
global nodesss
"""
color can be either 1 or -1. 1 for white and -1 for black
"""
possible = tuple(board.legal_moves)
if depth == 0 or len(possible) == 0:
#quiesced = quiesce(-10000, 10000, board, 1) * -color
return evaluate(board.fen()) * -color #quiesced
value = -inf
for move in possible:
nodesss += 1
#nodes(1)
#assert board.is_legal(move)
board.push(move)
nega = -negamax(board, depth-1, alpha, beta, -color)
board.pop()
value = max(value, nega) # can do class node that stores a board and the possible moves and then I can say if it is a terminal node (a node that determains loss, win and draws) with a parameter purpose.
alpha = max(alpha, value)
if alpha >= beta:
break
return value
def quiesce(alpha,beta, board, depth):
captures = tuple(board.generate_pseudo_legal_captures())
stand_pat = evaluate(board.fen())
if( stand_pat >= beta ): return beta
alpha = max(stand_pat, alpha)
if depth == 0: return stand_pat
for capture in captures:
#nodes(1)
board.push(capture)
score = -quiesce( -beta, -alpha, board, depth - 1 )
board.pop()
if( score >= beta ): return beta
alpha = max(alpha, score)
return alpha
def main2():
opt = sys.argv
board = chess.Board(opt[1])
depth = int(opt[2])
color = int(opt[3])
negaa = njit(negamax_root, nogil=True)
return negamax_root(board, depth, -inf, 10000, color)
if __name__=="__main__":
main2()
@OfekShochat thanks for following up. I think part of the problem here is that Numba doesn't know how to handle tqdm
and potentially also some of the other constructs you are using. Additionally, Numba is more of a function compiler, rather than a full-program compiler and a common use case is to identify bottleneck functions and to compile those. My suggestion would be to start with the 5 min guide to Numba, which you can find here:
https://numba.readthedocs.io/en/stable/user/5minguide.html
and then look at the supported Python constructs here:
https://numba.readthedocs.io/en/stable/reference/pysupported.html
thanks!
@OfekShochat another thought: since you seem to be developing something educational/fun it might also be worth opening a thread on the Numba discourse here: https://numba.discourse.group/ -- perhaps other members of our community will find this interesting and join in on a discussion about this subject?
sure!
I am closing this issue now, since I believe it has been resolved. As a side-note: Numba is currently unable to handle with ... as ...
statements and this leads to the error message.
Probably we can catch the error message and give you a better message along the lines of: "Numba is currently unable to compile your code."
I'm making a chess engine in python (I would do it in c but this is for fun) and it is pretty good but really slow. so I wanted to make it faster with numba njit. but it gets an error. full error code:
and the jited function: