How Can I Make my engine generate the best move faster?

Question:
My engine takes 30 secs to generate a move. When i decrease the time, the engine starts making mistakes. How can I make this time faster while still letting the engine generate a good move?
Repl link:
https://replit.com/@jcsan/Tortoi#main.py

import chess
import time
import chess.polyglot

board = chess.Board()
book = chess.polyglot.open_reader("baron30.bin")
material = {
  chess.PAWN:10.0,
	chess.KNIGHT:32.0,
	chess.BISHOP:33.3,
	chess.ROOK:56.5,
	chess.QUEEN:95.5,
  chess.KING:200.0
	
}
scoring = {
 'Pawn': [
  0, 0, 0, 0, 0, 0, 0, 0, 5, 5, 5, 5, 5, 5, 5, 5, 1, 1, 2, 3, 3, 2, 1, 1, 0, 0,
  0, 2, 2, 0, 0, 0, 0, 0, 0, -2, -2, 0, 0, 0, 1, -1, -2, 0, 0, -2, -1, 1, 1, 2,
  2, -2, -2, 2, 2, 1, 0, 0, 0, 0, 0, 0, 0, 0
 ],
 'Knight': [
  -5, -4, -3, -3, -3, -3, -4, -5, -4, -2, 0, 0, 0, 0, -2, -4, -3, 0, 1, 1.5,
  1.5, 1, 0, -3, -3, 0.5, 1.5, 2, 2, 1.5, 0.5, -3, -3, 0.5, 1.5, 2, 2, 1.5,
  0.5, -3, -3, 0, 1, 1.5, 1.5, 1, 0, -3, -4, -2, 0, 0.5, 0.5, 0, -2, -4, -5,
  -4, -3, -3, -3, -3, -4, -5
 ],
 'Bishop': [
  -2,
  -1,
  -1,
  -1,
  -1,
  -1,
  -1,
  -2,
  -1,
  0.25,
  0,
  0,
  0,
  0,
  0.25,
  -1,
  -1,
  0,
  1,
  1,
  1,
  1,
  0,
  -1,
  -1,
  0,
  1,
  1.5,
  1.5,
  1,
  0,
  -1,
  -1,
  0,
  1,
  1.5,
  1.5,
  1,
  0,
  -1,
  -1,
  0,
  1,
  1,
  1,
  1,
  0,
  -1,
  -1,
  0.25,
  0,
  0,
  0,
  0,
  0.25,
  -1,
  -2,
  -1,
  -1,
  -1,
  -1,
  -1,
  -1,
  -2,
 ],
 'Rook': [
  0, 0, 0, 0, 0, 0, 0, 0, 0.5, 1, 1, 1, 1, 1, 1, 0.5, -0.5, 0, 0, 0, 0, 0, 0,
  -0.5, -0.5, 0, 0, 0, 0, 0, 0, -0.5, -0.5, 0, 0, 0, 0, 0, 0, -0.5, -0.5, 0, 0,
  0, 0, 0, 0, -0.5, -0.5, 1, 1, 1, 1, 1, 1, -0.5, 0, 0, 0, 0.5, 0.5, 0, 0, 0
 ],
 'Queen': [
  -2, -1, -1, -0.5, -0.5, -1, -1, -2, -1, 0, 0.5, 0, 0, 0.5, 0, -1, -1, 0.5,
  0.5, 0.5, 0.5, 0.5, 0.5, -1, -0.5, 0, 0.5, 0.5, 0.5, 0.5, 0, -0.5, 0, 0, 0.5,
  0.5, 0.5, 0, 0, 0, -0.5, 0, 0.5, 0.5, 0.5, 0.5, 0, -0.5, -1, 0.5, 0.5, 0.5,
  0.5, 0.5, 0.5, -1, -2, -1, -1, -0.5, -0.5, -1, -1, -2
 ],
 'King': [
  -3, -4, -4, -5, -5, -4, -4, -3, -3, -4, -4, -5, -5, -4, -4, -3, -3, -4, -4,
  -5, -5, -4, -4, -3, -3, -4, -4, -5, -5, -4, -4, -3, -2, -3, -3, -4, -4, -3,
  -3, -2, -1, -2, -2, -2, -2, -2, -2, -1, 2, 2, 0, 0, 0, 0, 2, 2, 2, 3, 1, 0,
  0, 1, 3, 2
 ],
}

pawn_table = scoring['Pawn']
knight_table = scoring['Knight']
bishop_table = scoring['Bishop']
rook_table = scoring['Rook']
queen_table = scoring['Queen']
king_table = scoring['King']


def evaluate(board):
	score = 0.00
	pst_score = 0.00
	pieces = board.piece_map()
	for square, piece in pieces.items():
		if piece.color:
			score += material[piece.piece_type]
			if piece.piece_type == chess.PAWN:
				pst_score += pawn_table[square]
			elif piece.piece_type == chess.KNIGHT:
				pst_score += knight_table[square]
			elif piece.piece_type == chess.BISHOP:
				pst_score += bishop_table[square]
			elif piece.piece_type == chess.ROOK:
				pst_score += rook_table[square]
			elif piece.piece_type == chess.QUEEN:
				pst_score += queen_table[square]
			elif piece.piece_type == chess.KING:
				pst_score += king_table[square]
		else:
			score -= material[piece.piece_type]
			if piece.piece_type == chess.PAWN:
				pst_score -= pawn_table[chess.square_mirror(square)]
			elif piece.piece_type == chess.KNIGHT:
				pst_score -= knight_table[chess.square_mirror(square)]
			elif piece.piece_type == chess.BISHOP:
				pst_score -= bishop_table[chess.square_mirror(square)]
			elif piece.piece_type == chess.ROOK:
				pst_score -= rook_table[chess.square_mirror(square)]
			elif piece.piece_type == chess.QUEEN:
				pst_score -= queen_table[chess.square_mirror(square)]
			elif piece.piece_type == chess.KING:
				pst_score -= king_table[chess.square_mirror(square)]
	if board.turn == chess.WHITE:
		score += 0.4
	else:
		score -= 0.4	
	return score + pst_score


def highest_value(BOARD, depth=20, max_time=30):
	start_time = time.time()
	best_move = None
	if BOARD.turn == chess.WHITE:
		best_value = -float("inf")
	else:
		best_value = float("inf")
	for d in range(1, depth + 1):
		if time.time() - start_time > max_time:
			break
		for move in BOARD.legal_moves:
			BOARD.push(move)
			value = alpha_beta_search(BOARD, d)
			BOARD.pop()
			if BOARD.turn == chess.WHITE:
				if value > best_value:
					best_value = value
					best_move = move
			else:
				if value < best_value:
					best_value = value
					best_move = move
	return best_move


def alpha_beta_search(board,
                      depth,
                      alpha=-float("inf"),
                      beta=float("inf"),
                      maximizing_player=True):
	if depth == 0 or board.is_game_over():
		return evaluate(board)
	if maximizing_player:
		value = -float("inf")
		for move in board.legal_moves:
			board.push(move)
			value = max(value, alpha_beta_search(board, depth - 1, alpha, beta, False))
			board.pop()
			alpha = max(alpha, value)
			if alpha >= beta:
				break
		return value
	else:
		value = float("inf")
		for move in board.legal_moves:
			board.push(move)
			value = min(value, alpha_beta_search(board, depth - 1, alpha, beta, True))
			board.pop()
			beta = min(beta, value)
			if alpha >= beta:
				break
		return value


while True:

  if (len(list(book.find_all(board))) != 0):
    board.push(book.weighted_choice(board).move)
    time.sleep(1)
    print(board)
    print("  ")
  else:
    best_move = highest_value(board)
    board.push(best_move)
    print(board)
    print("  ")
    if board.can_claim_draw() == True:
      print("draw")
      break
`

What are you decreasing the time for?

So the engine can make a move faster

you mean have it calculate the move faster? That’s something called doing a PhD. Optimizing algorithms with new innovations is what papers are written about

Yes but what variable are you changing?

1 Like

This is the variable he’s changing:

def highest_value(BOARD, depth=20, max_time=30):

Also, if you want to decrease the max_time you need to make several improvements in your engine (Quiescence Search, Better evaluation function, parallel search, etc)

3 Likes

yes optimizations in general to minmax (he’s using minmax right) will help, faster eval, and doing things in thread will make your program faster but I didn’t mean semantics I meant like actually decreasing the big o notation

2 Likes

How would i approach a Quiesence search?

1 Like

you would need to make the package yourself and implement minmax with some optimizations to “unstable” nodes

Is there any way i can improve the evaluate function?

1 Like

¯\_(ツ)_/¯ I mean I don’t see anything but that’s what @WindLother suggested; although, I can’t really find anything that would greatly improve speed

You need to consider more factors, ones that a high level player would do in a normal game, for example, you could take into account pawn structure, piece mobility, king safety, etc.

Taking king safety for example:

def king_safety(board):
    king = board.king(board.turn)
    if king is None:
        return 0

    safety_score = 0

    # make your engine aware of king exposure
    for square in chess.SquareSet(chess.BB_KING_ATTACKS[king]):
        if square in board.attackers(not board.turn, square):
            safety_score -= 1

    # Note: you can consider other factors that players use to protect their king, like pawn shield.

    return safety_score
2 Likes

This is a good article to start:

1 Like

What would be the code for a quiesence search

They explained better here: Quiescence Search - Chessprogramming wiki

import chess
import time
import chess.polyglot
import os
board = chess.Board()
book = chess.polyglot.open_reader("baron30.bin")
moves = ""
move_num = 0
material = {
  chess.PAWN:10.0,
	chess.KNIGHT:32.0,
	chess.BISHOP:33.3,
	chess.ROOK:56.5,
	chess.QUEEN:95.5,
  chess.KING:200.0
	
}
scoring = {
 'Pawn': [
  0, 0, 0, 0, 0, 0, 0, 0, 5, 5, 5, 5, 5, 5, 5, 5, 1, 1, 2, 3, 3, 2, 1, 1, 0, 0,
  0, 2, 2, 0, 0, 0, 0, 0, 0, -2, -2, 0, 0, 0, 1, -1, -2, 0, 0, -2, -1, 1, 1, 2,
  2, -2, -2, 2, 2, 1, 0, 0, 0, 0, 0, 0, 0, 0
 ],
 'Knight': [
  -5, -4, -3, -3, -3, -3, -4, -5, -4, -2, 0, 0, 0, 0, -2, -4, -3, 0, 1, 1.5,
  1.5, 1, 0, -3, -3, 0.5, 1.5, 2, 2, 1.5, 0.5, -3, -3, 0.5, 1.5, 2, 2, 1.5,
  0.5, -3, -3, 0, 1, 1.5, 1.5, 1, 0, -3, -4, -2, 0, 0.5, 0.5, 0, -2, -4, -5,
  -4, -3, -3, -3, -3, -4, -5
 ],
 'Bishop': [
  -2,
  -1,
  -1,
  -1,
  -1,
  -1,
  -1,
  -2,
  -1,
  0.25,
  0,
  0,
  0,
  0,
  0.25,
  -1,
  -1,
  0,
  1,
  1,
  1,
  1,
  0,
  -1,
  -1,
  0,
  1,
  1.5,
  1.5,
  1,
  0,
  -1,
  -1,
  0,
  1,
  1.5,
  1.5,
  1,
  0,
  -1,
  -1,
  0,
  1,
  1,
  1,
  1,
  0,
  -1,
  -1,
  0.25,
  0,
  0,
  0,
  0,
  0.25,
  -1,
  -2,
  -1,
  -1,
  -1,
  -1,
  -1,
  -1,
  -2,
 ],
 'Rook': [
  0, 0, 0, 0, 0, 0, 0, 0, 0.5, 1, 1, 1, 1, 1, 1, 0.5, -0.5, 0, 0, 0, 0, 0, 0,
  -0.5, -0.5, 0, 0, 0, 0, 0, 0, -0.5, -0.5, 0, 0, 0, 0, 0, 0, -0.5, -0.5, 0, 0,
  0, 0, 0, 0, -0.5, -0.5, 1, 1, 1, 1, 1, 1, -0.5, 0, 0, 0, 0.5, 0.5, 0, 0, 0
 ],
 'Queen': [
  -2, -1, -1, -0.5, -0.5, -1, -1, -2, -1, 0, 0.5, 0, 0, 0.5, 0, -1, -1, 0.5,
  0.5, 0.5, 0.5, 0.5, 0.5, -1, -0.5, 0, 0.5, 0.5, 0.5, 0.5, 0, -0.5, 0, 0, 0.5,
  0.5, 0.5, 0, 0, 0, -0.5, 0, 0.5, 0.5, 0.5, 0.5, 0, -0.5, -1, 0.5, 0.5, 0.5,
  0.5, 0.5, 0.5, -1, -2, -1, -1, -0.5, -0.5, -1, -1, -2
 ],
 'King': [
  -3, -4, -4, -5, -5, -4, -4, -3, -3, -4, -4, -5, -5, -4, -4, -3, -3, -4, -4,
  -5, -5, -4, -4, -3, -3, -4, -4, -5, -5, -4, -4, -3, -2, -3, -3, -4, -4, -3,
  -3, -2, -1, -2, -2, -2, -2, -2, -2, -1, 2, 2, 0, 0, 0, 0, 2, 2, 2, 3, 1, 0,
  0, 1, 3, 2
 ],
}

pawn_table = scoring['Pawn']
knight_table = scoring['Knight']
bishop_table = scoring['Bishop']
rook_table = scoring['Rook']
queen_table = scoring['Queen']
king_table = scoring['King']


def evaluate(board):
  if board.can_claim_draw() or board.is_stalemate() or board.is_insufficient_material():
    score = 0.00
    pst_score = 0.00
  elif board.is_checkmate():
    if board.turn == chess.BLACK:
      score = float("inf")
      pst_score = None
    else:
      score = -float("inf")
      pst_score = None
				
  else:
    score = 0.00
    pst_score = 0.00
    pieces = board.piece_map()
    for square, piece in pieces.items():
      if piece.color:
        score += material[piece.piece_type]
        if piece.piece_type == chess.PAWN:
          pst_score += pawn_table[square]
        elif piece.piece_type == chess.KNIGHT:
          pst_score += knight_table[square]
        elif piece.piece_type == chess.BISHOP:
          pst_score += bishop_table[square]
        elif piece.piece_type == chess.ROOK:
          pst_score += rook_table[square]
        elif piece.piece_type == chess.QUEEN:
          pst_score += queen_table[square]
        elif piece.piece_type == chess.KING:
          pst_score += king_table[square]
      else:
        score -= material[piece.piece_type]
        if piece.piece_type == chess.PAWN:
          pst_score -= pawn_table[chess.square_mirror(square)]
        elif piece.piece_type == chess.KNIGHT:
           pst_score -= knight_table[chess.square_mirror(square)]
        elif piece.piece_type == chess.BISHOP:
           pst_score -= bishop_table[chess.square_mirror(square)]
        elif piece.piece_type == chess.ROOK:
         pst_score -= rook_table[chess.square_mirror(square)]
        elif piece.piece_type == chess.QUEEN:
          pst_score -= queen_table[chess.square_mirror(square)]
        elif piece.piece_type == chess.KING:
          pst_score -= king_table[chess.square_mirror(square)]
  if board.turn == chess.WHITE:
    pst_score += 0.4
  else:
    pst_score -= 0.4
  return score + pst_score

threshhold_depth = 20
def highest_value(board):
  best_move = None
  if board.turn == chess.WHITE:
    best_value = -float("inf")
  else:
    best_value = float("inf")
  for d in range(1, threshhold_depth + 1):
    for move in board.legal_moves:
        board.push(move)
        value = alpha_beta_with_quiescence(board, threshhold_depth, float('-inf'), float('inf'), False)
        board.pop()
        if board.turn == chess.WHITE:
          if value > best_value:
            best_value = value
            best_move = move
        else:
          if value < best_value:
            best_value = value
            best_move = move
  return best_move

def alpha_beta_with_quiescence(board, depth, alpha, beta, maximizing_player):
    if depth == 0 or board.is_game_over():
        return evaluate(board)

    if maximizing_player:
        value = float('-inf')
        for move in board.legal_moves:
            board.push(move)
            if board.is_capture(move):
                value = max(value, alpha_beta_with_quiescence(board, depth - 1, alpha, beta, False))
            else:
                value = max(value, alpha_beta_with_quiescence(board, depth - 1, alpha, beta, False))
                alpha = max(alpha, value)
            board.pop()
            if alpha >= beta:
                break
        return value
    else:
        value = float('inf')
        for move in board.legal_moves:
            board.push(move)
            if board.is_capture(move):
                value = min(value, alpha_beta_with_quiescence(board, depth - 1, alpha, beta, True))
            else:
                value = min(value, alpha_beta_with_quiescence(board, depth - 1, alpha, beta, True))
                beta = min(beta, value)
            board.pop()
            if alpha >= beta:
                break
        return value

while True:

  if (len(list(book.find_all(board))) != 0):
    if board.turn == chess.WHITE:
      move_num += 1
      book_move = book.weighted_choice(board).move
      moves = moves + " "+str(move_num) + ". "+str(book_move)
    else:
      book_move = book.weighted_choice(board).move
      moves = moves +" "+ str(book_move) + " "		
    print(moves)
    board.push(book_move)
    time.sleep(1)
    os.system("clear")
    print(board)
  else:
    best_move = highest_value(board)
    if board.turn == chess.WHITE:
      move_num += 1
      moves = moves + " "+ str(move_num) + ". "+str(best_move)
    else:
      moves = moves + " "+str(best_move) + " "		
    board.push(best_move)
    os.system("clear")
    print(moves)
    print(board)
    if board.can_claim_draw() == True:
      print("draw")
      break

This is my new code. Would this be okay?

Click on the link to see other changes

ooh this is defenitely better!
You’ve added piece-square tables for each piece and you are dealing with special situations too.

Oh I also noticed somethings in your quiescence search and I have a few suggestions about your quiescence function (feel free to add them or not).

For example, you do not differ for capture and non-capture moves. The right thing to do is to only continue searching for capture moves when depth reaches 0. I suggest to create a separate function for the quiescence search

def quiescence_search(board, alpha, beta, maximizing_player):
    if board.is_game_over():
        return evaluate(board)

    if maximizing_player:
        value = float('-inf')
        for move in board.legal_moves:
            if board.is_capture(move):
                board.push(move)
                score = -quiescence_search(board, -beta, -alpha, False)
                board.pop()
                
                if score >= beta:
                    return beta
                alpha = max(alpha, score)
    else:
        value = float('inf')
        for move in board.legal_moves:
            if board.is_capture(move):
                board.push(move)
                score = -quiescence_search(board, -beta, -alpha, True)
                board.pop()

                if score <= alpha:
                    return alpha
                beta = min(beta, score)

    return alpha if maximizing_player else beta

So when depth reaches 0, the search will only continue for capturing moves.

After that you change your alpha_beta_with_quiescence to call the search from before.

def alpha_beta_with_quiescence(board, depth, alpha, beta, maximizing_player):
    if depth == 0 or board.is_game_over():
        return quiescence_search(board, alpha, beta, maximizing_player)

    if maximizing_player:
        value = float('-inf')
        for move in board.legal_moves:
            board.push(move)
            value = max(value, alpha_beta_with_quiescence(board, depth - 1, alpha, beta, False))
            board.pop()
            alpha = max(alpha, value)
            if alpha >= beta:
                break
    else:
        value = float('inf')
        for move in board.legal_moves:
            board.push(move)
            value = min(value, alpha_beta_with_quiescence(board, depth - 1, alpha, beta, True))
            board.pop()
            beta = min(beta, value)
            if beta <= alpha:
                break

    return alpha if maximizing_player else beta

So your full code to the quiescence function would be:

def quiescence_search(board, alpha, beta, maximizing_player):
    if board.is_game_over():
        return evaluate(board)

    if maximizing_player:
        value = float('-inf')
        for move in board.legal_moves:
            if board.is_capture(move):
                board.push(move)
                score = -quiescence_search(board, -beta, -alpha, False)
                board.pop()
                
                if score >= beta:
                    return beta
                alpha = max(alpha, score)
    else:
        value = float('inf')
        for move in board.legal_moves:
            if board.is_capture(move):
                board.push(move)
                score = -quiescence_search(board, -beta, -alpha, True)
                board.pop()

                if score <= alpha:
                    return alpha
                beta = min(beta, score)

    return alpha if maximizing_player else beta

def alpha_beta_with_quiescence(board, depth, alpha, beta, maximizing_player):
    if depth == 0 or board.is_game_over():
        return quiescence_search(board, alpha, beta, maximizing_player)

    if maximizing_player:
        value = float('-inf')
        for move in board.legal_moves:
            board.push(move)
            value = max(value, alpha_beta_with_quiescence(board, depth - 1, alpha, beta, False))
            board.pop()
            alpha = max(alpha, value)
            if alpha >= beta:
                break
    else:
        value = float('inf')
        for move in board.legal_moves:
            board.push(move)
            value = min(value, alpha_beta_with_quiescence(board, depth - 1, alpha, beta, True))
            board.pop()
            beta = min(beta, value)
            if beta <= alpha:
                break

    return alpha if maximizing_player else beta

This will make the AI have a better understanding about the consequences of captures and checks that are beyond the maximum search depth.

But again, is only a suggestion! Feel free to make changes that would benefit you more.

2 Likes
import chess
import time
import chess.polyglot
import os
import random
board = chess.Board()
book = chess.polyglot.open_reader("baron30.bin")
moves = ""
move_num = 0
material = {
  chess.PAWN:10.0,
	chess.KNIGHT:32.0,
	chess.BISHOP:33.3,
	chess.ROOK:56.5,
	chess.QUEEN:95.5,
  chess.KING:200.0
	
}
scoring = {
 'Pawn': [
  0, 0, 0, 0, 0, 0, 0, 0, 5, 5, 5, 5, 5, 5, 5, 5, 1, 1, 2, 3, 3, 2, 1, 1, 0, 0,
  0, 2, 2, 0, 0, 0, 0, 0, 0, -2, -2, 0, 0, 0, 1, -1, -2, 0, 0, -2, -1, 1, 1, 2,
  2, -2, -2, 2, 2, 1, 0, 0, 0, 0, 0, 0, 0, 0
 ],
 'Knight': [
  -5, -4, -3, -3, -3, -3, -4, -5, -4, -2, 0, 0, 0, 0, -2, -4, -3, 0, 1, 1.5,
  1.5, 1, 0, -3, -3, 0.5, 1.5, 2, 2, 1.5, 0.5, -3, -3, 0.5, 1.5, 2, 2, 1.5,
  0.5, -3, -3, 0, 1, 1.5, 1.5, 1, 0, -3, -4, -2, 0, 0.5, 0.5, 0, -2, -4, -5,
  -4, -3, -3, -3, -3, -4, -5
 ],
 'Bishop': [
  -2,
  -1,
  -1,
  -1,
  -1,
  -1,
  -1,
  -2,
  -1,
  0.25,
  0,
  0,
  0,
  0,
  0.25,
  -1,
  -1,
  0,
  1,
  1,
  1,
  1,
  0,
  -1,
  -1,
  0,
  1,
  1.5,
  1.5,
  1,
  0,
  -1,
  -1,
  0,
  1,
  1.5,
  1.5,
  1,
  0,
  -1,
  -1,
  0,
  1,
  1,
  1,
  1,
  0,
  -1,
  -1,
  0.25,
  0,
  0,
  0,
  0,
  0.25,
  -1,
  -2,
  -1,
  -1,
  -1,
  -1,
  -1,
  -1,
  -2,
 ],
 'Rook': [
  0, 0, 0, 0, 0, 0, 0, 0, 0.5, 1, 1, 1, 1, 1, 1, 0.5, -0.5, 0, 0, 0, 0, 0, 0,
  -0.5, -0.5, 0, 0, 0, 0, 0, 0, -0.5, -0.5, 0, 0, 0, 0, 0, 0, -0.5, -0.5, 0, 0,
  0, 0, 0, 0, -0.5, -0.5, 1, 1, 1, 1, 1, 1, -0.5, 0, 0, 0, 0.5, 0.5, 0, 0, 0
 ],
 'Queen': [
  -2, -1, -1, -0.5, -0.5, -1, -1, -2, -1, 0, 0.5, 0, 0, 0.5, 0, -1, -1, 0.5,
  0.5, 0.5, 0.5, 0.5, 0.5, -1, -0.5, 0, 0.5, 0.5, 0.5, 0.5, 0, -0.5, 0, 0, 0.5,
  0.5, 0.5, 0, 0, 0, -0.5, 0, 0.5, 0.5, 0.5, 0.5, 0, -0.5, -1, 0.5, 0.5, 0.5,
  0.5, 0.5, 0.5, -1, -2, -1, -1, -0.5, -0.5, -1, -1, -2
 ],
 'King': [
  -3, -4, -4, -5, -5, -4, -4, -3, -3, -4, -4, -5, -5, -4, -4, -3, -3, -4, -4,
  -5, -5, -4, -4, -3, -3, -4, -4, -5, -5, -4, -4, -3, -2, -3, -3, -4, -4, -3,
  -3, -2, -1, -2, -2, -2, -2, -2, -2, -1, 2, 2, 0, 0, 0, 0, 2, 2, 2, 3, 1, 0,
  0, 1, 3, 2
 ],
}
pawn_table = scoring['Pawn']
knight_table = scoring['Knight']
bishop_table = scoring['Bishop']
rook_table = scoring['Rook']
queen_table = scoring['Queen']
king_table = scoring['King']
def mobility(board):
    current = board.turn
    board.turn = chess.WHITE
    white_moves = len(list(board.legal_moves))
    board.turn = chess.BLACK
    black_moves = len(list(board.legal_moves))
    board.turn = current
    mobility_score = 0.4*(white_moves - black_moves)
    return mobility_score
def evaluate(board):
  if board.can_claim_draw() or board.is_stalemate() or board.is_insufficient_material():
    score = 0.00
    pst_score = 0.00
  elif board.is_checkmate():
    if board.turn == chess.BLACK:
      score = float("inf")
      pst_score = 0.00
    else:
      score = -float("inf")
      pst_score = 0.00			
  else:
    score = 0.00
    pst_score = 0.00
    pieces = board.piece_map()
    for square, piece in pieces.items():
      if piece.color:
        score += material[piece.piece_type]
        if piece.piece_type == chess.PAWN:
          pst_score += pawn_table[square]
        elif piece.piece_type == chess.KNIGHT:
          pst_score += knight_table[square]
        elif piece.piece_type == chess.BISHOP:
          pst_score += bishop_table[square]
        elif piece.piece_type == chess.ROOK:
          pst_score += rook_table[square]
        elif piece.piece_type == chess.QUEEN:
          pst_score += queen_table[square]
        elif piece.piece_type == chess.KING:
          pst_score += king_table[square]
      else:
        score -= material[piece.piece_type]
        if piece.piece_type == chess.PAWN:
          pst_score -= pawn_table[chess.square_mirror(square)]
        elif piece.piece_type == chess.KNIGHT:
           pst_score -= knight_table[chess.square_mirror(square)]
        elif piece.piece_type == chess.BISHOP:
           pst_score -= bishop_table[chess.square_mirror(square)]
        elif piece.piece_type == chess.ROOK:
         pst_score -= rook_table[chess.square_mirror(square)]
        elif piece.piece_type == chess.QUEEN:
          pst_score -= queen_table[chess.square_mirror(square)]
        elif piece.piece_type == chess.KING:
          pst_score -= king_table[chess.square_mirror(square)]
  if board.turn == chess.WHITE:
    pst_score += 0.4
  else:
     pst_score -=0.4
  mobility_score = mobility(board)
  pst_score += mobility_score
  return score + pst_score

threshhold_depth = 15
def highest_value(board):
  start_time = time.time()
  best_move = None
  if board.turn == chess.WHITE:
    best_value = -float("inf")
  else:
    best_value = float("inf")
  for d in range(1, threshhold_depth + 1):
    for move in board.legal_moves:
      board.push(move)
      if board.turn == chess.WHITE:
        value = alpha_beta_with_quiescence(board, threshhold_depth, float('-inf'), float('inf'), True)
      else:
        value = alpha_beta_with_quiescence(board, threshhold_depth, float('-inf'), float('inf'), True)
      board.pop()
      if board.turn == chess.WHITE:
        if value > best_value:
          best_value = value
          best_move = move
      else:
        if value < best_value:
          best_value = value
          best_move = move
      if time.time() - start_time >= 5:
        if best_move == None:
          legals = list(board.legal_moves)
          best_move = chess.Move.from_uci(random.choice(legals))
        return best_move
        break
  return best_move

def quiescence_search(board, alpha, beta, maximizing_player):
    if board.is_game_over():
        return evaluate(board)

    if maximizing_player:
        value = float('-inf')
        for move in board.legal_moves:
            if board.is_capture(move):
                board.push(move)
                score = -quiescence_search(board, -beta, -alpha, False)
                board.pop()
                
                if score >= beta:
                    return beta
                alpha = max(alpha, score)
    else:
        value = float('inf')
        for move in board.legal_moves:
            if board.is_capture(move):
                board.push(move)
                score = -quiescence_search(board, -beta, -alpha, True)
                board.pop()

                if score <= alpha:
                    return alpha
                beta = min(beta, score)

    return alpha if maximizing_player else beta

def alpha_beta_with_quiescence(board, depth, alpha, beta, maximizing_player):
    if depth == 0 or board.is_game_over():
        return quiescence_search(board, alpha, beta, maximizing_player)

    if maximizing_player:
        value = float('-inf')
        for move in board.legal_moves:
            board.push(move)
            value = max(value, alpha_beta_with_quiescence(board, depth - 1, alpha, beta, False))
            board.pop()
            alpha = max(alpha, value)
            if alpha >= beta:
                break
    else:
        value = float('inf')
        for move in board.legal_moves:
            board.push(move)
            value = min(value, alpha_beta_with_quiescence(board, depth - 1, alpha, beta, True))
            board.pop()
            beta = min(beta, value)
            if beta <= alpha:
                break

    return alpha if maximizing_player else beta
while True:

  if (len(list(book.find_all(board))) != 0):
    if board.turn == chess.WHITE:
      move_num += 1
      book_move = book.weighted_choice(board).move
      moves = moves + " "+str(move_num) + ". "+str(book_move)
    else:
      book_move = book.weighted_choice(board).move
      moves = moves +" "+ str(book_move) + " "		
    print(moves)
    board.push(book_move)
    time.sleep(1)
    os.system("clear")
    print(board)
  else:
    best_move = highest_value(board)
    if board.turn == chess.WHITE:
      move_num += 1
      moves = moves + " "+ str(move_num) + ". "+str(best_move)
    else:
      moves = moves + " "+str(best_move) + " "		
    board.push(best_move)
    os.system("clear")
    print(moves)
    print(board)
    if board.can_claim_draw() == True:
      print("draw")
      break

With this code it is still stuck after book moves and I dont know why

I would need more specific error messages or more details on how it’s getting stuck.
Can you add some debugging prints in your highest_value function?

(And make sure the opening book is not corrupted)

def highest_value(board):
  start_time = time.time()
  best_move = None
  if board.turn == chess.WHITE:
    best_value = -float("inf")
  else:
    best_value = float("inf")
  print(f"Initial best_value: {best_value}") # Insert debug print here
  for d in range(1, threshhold_depth + 1):
    print(f"Depth: {d}") # Here too
    for move in board.legal_moves:
      board.push(move)
      if board.turn == chess.WHITE:
        value = alpha_beta_with_quiescence(board, threshhold_depth, float('-inf'), float('inf'), True)
      else:
        value = alpha_beta_with_quiescence(board, threshhold_depth, float('-inf'), float('inf'), True)
      print(f"Move: {move}, Value: {value}") # Another one here
      board.pop()
      if board.turn == chess.WHITE:
        if value > best_value:
          best_value = value
          best_move = move
      else:
        if value < best_value:
          best_value = value
          best_move = move
      if time.time() - start_time >= 5:
        if best_move == None:
          legals = list(board.legal_moves)
          best_move = chess.Move.from_uci(random.choice(legals))
        print(f"Timeout, selected move: {best_move}") # Here too
        return best_move
        break
  print(f"Selected move: {best_move}") # Final debug, insert here too
  return best_move

1 Like