Adaptive Stockfish
Tom 7’s 30 Weird Chess Algorithms: Elo World inspired me to implment what I’m calling Adaptive Stockfish. It’s a chess algorithm that tries to match its current advantage with a given target. With a target of 0, it tries to play as well as its opponent. No better, no worse. Tuned up to +100 and it tries to get a little leg up on you, but doesn’t get carried away.
Code
#!/usr/bin/env python3
import argparse
import chess
import chess.engine
import chess.pgn
STOCKFISH_ENGINE = "/usr/games/stockfish"
MULTIPV_MAX = 500 # Capped by Stockfish's ucioption.cpp
SCORE_THRESHOLD = 5000 # To convert mates to centi-pawns
def get_adaptive_stockfish_move(board, engine, time_limit, target):
return min(
engine.analyse(board, limit=time_limit, multipv=MULTIPV_MAX),
key=lambda move: abs(move["score"].relative.score(mate_score=SCORE_THRESHOLD) - target),
)
if __name__ == "__main__":
parser = argparse.ArgumentParser()
parser.add_argument("white", type=int, help="In centi-pawns")
parser.add_argument("black", type=int, help="In centi-pawns")
parser.add_argument("--time-limit", type=float, default=3)
args = parser.parse_args()
try:
board = chess.Board()
engine = chess.engine.SimpleEngine.popen_uci(STOCKFISH_ENGINE)
time_limit = chess.engine.Limit(time=args.time_limit)
while board.legal_moves:
target = args.white if board.turn == chess.WHITE else args.black
move = get_adaptive_stockfish_move(board, engine, time_limit, target)
board.push(move["pv"][0])
print(f"{move['pv'][0]} {move['score']}")
finally:
engine.quit()
game = chess.pgn.Game.from_board(board)
game.headers["White"] = f"Adaptive Stockfish ({args.white:+d})"
game.headers["Black"] = f"Adaptive Stockfish ({args.black:+d})"
print(game)
Edit: Updated to allow it to be capable of winning. The previous code considered a mating move to be so good that it would avoid it at all costs.
Example games
- White (+3000) vs Black (-3000): White tries to win and Black tries to lose
- White (+0) vs Black (-3000): Black tries to lose and White aims for balance
- White (+0) vs Black (+3000): Black tries to win and White aims for balance
- White (+100) vs Black (+0): White tries to win a little and Black aims for balance
Notes
- If you make a really bad move, AdaptiveStockfish(0) will immediately respond with an equally bad move to balance the game out. It might be better served to slowly make slightly bad moves over a series of turns to not make the opponent feel that it’s throwing the game.
- It might be worth an evening’s coding to get this accessible via https://lichess.org/api.