def main(): screen = p.display.set_mode((WIDTH, HEIGHT)) clock = p.time.Clock() screen.fill(p.Color(WHITE)) gs = GameState() # gs is the game state validMoves = gs.getValidMoves() moveMade = False loadImages() # we are only loading the images once in pygame running = True sqSelected = () # keeps track of the last user mouse clicks playerClicks = [] # keeps track of the last player clicks while running: for e in p.event.get(): if e.type == p.QUIT: running = False # mouse key handlers elif e.type == p.MOUSEBUTTONDOWN: location = p.mouse.get_pos() # location of x and y col = location[0] // SQ_SIZE row = location[1] // SQ_SIZE if sqSelected == (row, col): sqSelected = () playerClicks = [] # if the player clicks on a chess piece else: sqSelected = (row, col) playerClicks.append( sqSelected ) # this appends both the 1st and the 2nd clicks if len(playerClicks) == 2: # if the user made 2 mouse clicks move = Move(playerClicks[0], playerClicks[1], gs.board) print( str(move.getChessNotation()) + ' id: ' + str(move.moveID)) for i in range(len(validMoves)): if move == validMoves[i]: gs.makeMove(validMoves[i]) moveMade = True sqSelected = () playerClicks = [] # resets user clicks if not moveMade: playerClicks = [sqSelected] # keyboard handlers elif e.type == p.KEYDOWN: if e.key == p.K_z: # the undo key gs.undoMove() moveMade = True if moveMade: validMoves = gs.getValidMoves() moveMade = False drawGameState(screen, gs) clock.tick(MAX_FPS) p.display.flip()
def minimax(board: GameState, alpha: float, beta: float, maximizer: bool, curDepth: int, max_depth: int) -> Tuple[float, Move]: """ returns an integer score and move which is the best current player can get """ if board.is_game_over(): if board.staleMate: return 0, None if board.checkMate: return (-inf if maximizer else +inf), None if curDepth == max_depth: return evaluate(board, not (board.whiteToMove ^ maximizer)), None # sending inf so that the branch is ignored by parent if final_move is not None and time() - stime > timeout: return +inf if maximizer else -inf, None moves = list(board.getValidMoves()) # moves.sort(key=move_score, reverse=True) assert moves != [] best_move = None if maximizer: best_score = -inf def is_better_score(curr, currbest): return curr >= currbest def update_AB(score): nonlocal alpha alpha = max(alpha, score) else: best_score = +inf def is_better_score(curr, currbest): return curr <= currbest def update_AB(score): nonlocal beta beta = min(beta, score) for move in moves: board.makeMove(move, by_AI=True) global moves_cnt moves_cnt += 1 curr_score, _ = minimax(board, alpha, beta, not maximizer, curDepth + 1, max_depth) board.undoMove() if is_better_score(curr_score, best_score): best_score = curr_score best_move = move update_AB(best_score) if alpha >= beta: break return best_score, best_move
def next_move_restricted(board: GameState, max_depth: int) -> Tuple[float, Move]: """ returns best move calculated till depth given """ depth_stime = time() global eval_time, moves_cnt, evals_cnt eval_time = 0 moves_cnt = 0 evals_cnt = 0 moves = board.getValidMoves() length = len(moves) step_size = max(1, length//6) moves_sets: List[List[Move]] = [] procs_list: List[Process] = [] conn_list: List[Connection] = [] for start in range(0, length, step_size): end = start+step_size if length-end < step_size: end = length moves_sets.append(moves[start: end]) for moves_sb_set in moves_sets: par_conn, ch_conn = Pipe() p = Process(target=minimax_handler, args=( ch_conn, board, moves_sb_set, max_depth)) p.start() procs_list.append(p) conn_list.append(par_conn) score, move, line = -inf, None, [] for conn in conn_list: curr_score: int = conn.recv() curr_move: Move = conn.recv() curr_line: List[Move] = conn.recv() if curr_score >= score: score, move, line = curr_score, curr_move, curr_line for p in procs_list: p.join() line_str = [" " if not move else move.getChessNotation() for move in line] print( f"depth [{max_depth}] done in {time()-depth_stime} score: {score}" f"\ndepth [{max_depth}] {line_str}" # f"evals_time : {eval_time}, eval_cnt: {evals_cnt}, moves_cnt: {moves_cnt}" ) if not move: return -inf, None return score, move
def minimax(board: GameState, moves: List[Move], alpha: float, beta: float, maximizer: bool, curDepth: int, max_depth: int, moves_line: List[Move]) -> Tuple[float, Move, List[Move]]: """ returns an integer score and move which is the best current player can get """ if board.is_game_over(): moves_line.append(None) if board.staleMate: return 0, None, moves_line if board.checkMate: return (-inf if maximizer else +inf), None, moves_line if curDepth == max_depth: return evaluate(board, not(board.whiteToMove ^ maximizer)), None, moves_line # sending inf so that the branch is ignored by parent if final_move is not None and time() - stime > timeout: moves_line.append(None) return +inf if maximizer else -inf, None, moves_line # moves = list(board.getValidMoves()) assert moves != [] best_move = None best_line = [] best_score = -inf if maximizer else +inf for move in moves: board.makeMove(move, by_AI=True) moves_line.append(move) global moves_cnt moves_cnt += 1 curr_score, _, curr_line = minimax( board, board.getValidMoves(), alpha, beta, not maximizer, curDepth+1, max_depth, moves_line[:]) board.undoMove() moves_line.pop() if maximizer: if curr_score >= best_score: best_score, best_move, best_line = curr_score, move, curr_line alpha = max(alpha, best_score) else: if curr_score <= best_score: best_score, best_move, best_line = curr_score, move, curr_line beta = min(beta, best_score) if alpha >= beta: break return best_score, best_move, best_line