-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathhomemade.py
More file actions
143 lines (112 loc) · 5.97 KB
/
Copy pathhomemade.py
File metadata and controls
143 lines (112 loc) · 5.97 KB
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
"""
Some example classes for people who want to create a homemade bot.
With these classes, bot makers will not have to implement the UCI or XBoard interfaces themselves.
"""
import chess
from chess.engine import PlayResult, Limit
import random
from lib.engine_wrapper import MinimalEngine
from lib.lichess_types import MOVE, HOMEMADE_ARGS_TYPE
import logging
# Use this logger variable to print messages to the console or log files.
# logger.info("message") will always print "message" to the console or log file.
# logger.debug("message") will only print "message" if verbose logging is enabled.
logger = logging.getLogger(__name__)
class ExampleEngine(MinimalEngine):
"""An example engine that all homemade engines inherit."""
# Bot names and ideas from tom7's excellent eloWorld video
class RandomMove(ExampleEngine):
"""Get a random move."""
def search(self, board: chess.Board, *args: HOMEMADE_ARGS_TYPE) -> PlayResult: # noqa: ARG002
"""Choose a random move."""
return PlayResult(random.choice(list(board.legal_moves)), None)
class Alphabetical(ExampleEngine):
"""Get the first move when sorted by san representation."""
def search(self, board: chess.Board, *args: HOMEMADE_ARGS_TYPE) -> PlayResult: # noqa: ARG002
"""Choose the first move alphabetically."""
moves = list(board.legal_moves)
moves.sort(key=board.san)
return PlayResult(moves[0], None)
class FirstMove(ExampleEngine):
"""Get the first move when sorted by uci representation."""
def search(self, board: chess.Board, *args: HOMEMADE_ARGS_TYPE) -> PlayResult: # noqa: ARG002
"""Choose the first move alphabetically in uci representation."""
moves = list(board.legal_moves)
moves.sort(key=str)
return PlayResult(moves[0], None)
class ComboEngine(ExampleEngine):
"""
Get a move using multiple different methods.
This engine demonstrates how one can use `time_limit`, `draw_offered`, and `root_moves`.
"""
def search(self,
board: chess.Board,
time_limit: Limit,
ponder: bool, # noqa: ARG002
draw_offered: bool,
root_moves: MOVE) -> PlayResult:
"""
Choose a move using multiple different methods.
:param board: The current position.
:param time_limit: Conditions for how long the engine can search (e.g. we have 10 seconds and search up to depth 10).
:param ponder: Whether the engine can ponder after playing a move.
:param draw_offered: Whether the bot was offered a draw.
:param root_moves: If it is a list, the engine should only play a move that is in `root_moves`.
:return: The move to play.
"""
if isinstance(time_limit.time, int):
my_time = time_limit.time
my_inc = 0
elif board.turn == chess.WHITE:
my_time = time_limit.white_clock if isinstance(time_limit.white_clock, int) else 0
my_inc = time_limit.white_inc if isinstance(time_limit.white_inc, int) else 0
else:
my_time = time_limit.black_clock if isinstance(time_limit.black_clock, int) else 0
my_inc = time_limit.black_inc if isinstance(time_limit.black_inc, int) else 0
possible_moves = root_moves if isinstance(root_moves, list) else list(board.legal_moves)
if my_time / 60 + my_inc > 10:
# Choose a random move.
move = random.choice(possible_moves)
else:
# Choose the first move alphabetically in uci representation.
possible_moves.sort(key=str)
move = possible_moves[0]
return PlayResult(move, None, draw_offered=draw_offered)
class MyBot(ExampleEngine):
"""Template code for hackathon participants to modify.
Quick start:
- Edit `search` to choose a move according to your strategy.
- You can keep it simple (pick from board.legal_moves) or implement a full search.
- Feel free to log with `logger.debug(...)` or `logger.info(...)` for debugging.
Ideas:
- Heuristics: prefer captures, checks, promotions, centralization.
- Search: implement minimax/negamax with alpha-beta pruning and a simple eval.
- Time: respect available time or increments if provided (see args).
- Constraints: obey `root_moves` if given (only choose among those).
- Draws: decide policies when a draw is offered (accept/reject based on evaluation).
"""
def search(self, board: chess.Board, *args: HOMEMADE_ARGS_TYPE) -> PlayResult:
"""
Called by the framework to get your move for the current position.
Args:
- board: The current chess position.
- args (optional): Typically (time_limit: Limit, ponder: bool, draw_offered: bool, root_moves: MOVE).
Note: These may or may not be provided depending on the adapter.
Template guidance:
- If you want to use time controls:
# time_limit.time (fixed) or per-color: white_clock/black_clock, white_inc/black_inc
# Decide how much time to spend this move, then search up to depth/time.
- If a draw is offered (third arg), decide whether to accept or play for win/draw.
- If `root_moves` (fourth arg) is provided as a list, only select from those moves.
- Use logging to understand what your bot is doing:
# logger.debug("Evaluated X moves, best was ...")
TODOs:
- Replace the random move with your selection logic.
- Add evaluation, search, or any heuristic you like.
- Respect `root_moves` and `time_limit` if you choose to support them.
- Optionally return a ponder move in the second PlayResult field.
Minimal starter: return any legal move quickly to avoid timeouts.
"""
# NOTE: This baseline keeps behavior simple and unchanged on purpose.
# Replace this with your own logic (heuristics or a search algorithm).
return PlayResult(random.choice(list(board.legal_moves)), None)