forked from lichess-bot-devs/lichess-bot
-
Notifications
You must be signed in to change notification settings - Fork 1
/
strategies.py
104 lines (77 loc) · 2.84 KB
/
strategies.py
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
"""
Some example strategies for people who want to create a custom, homemade bot.
And some handy classes to extend
"""
import chess
import random
from engine_wrapper import EngineWrapper
class FillerEngine:
"""
Not meant to be an actual engine.
This is only used to provide the property "self.engine"
in "MinimalEngine" which extends "EngineWrapper"
"""
def __init__(self, main_engine, name=None):
self.id = {
"name": name
}
self.name = name
self.main_engine = main_engine
def __getattr__(self, method_name):
main_engine = self.main_engine
def method(*args, **kwargs):
nonlocal main_engine
nonlocal method_name
return main_engine.notify(method_name, *args, **kwargs)
return method
class MinimalEngine(EngineWrapper):
"""
Subclass this to prevent a few random errors
Even though MinimalEngine extends EngineWrapper,
you don't have to actually wrap an engine.
At minimum, just implement `search`,
however you can also change other methods like
`notify`, `first_search`, `get_time_control`, etc.
"""
def __init__(self, *args, name=None):
super().__init__(*args)
self.name = self.__class__.__name__ if name is None else name
self.last_move_info = []
self.engine = FillerEngine(self, name=self.name)
def search_with_ponder(self, board, wtime, btime, winc, binc, ponder):
timeleft = 0
if board.turn:
timeleft = wtime
else:
timeleft = btime
return self.search(board, timeleft, ponder)
def search(self, board, timeleft, ponder):
raise NotImplementedError("The search method is not implemented")
def notify(self, method_name, *args, **kwargs):
"""
The EngineWrapper class sometimes calls methods on "self.engine".
"self.engine" is a filler property that notifies <self>
whenever an attribute is called.
Nothing happens unless the main engine does something.
Simply put, the following code is equivalent
self.engine.<method_name>(<*args>, <**kwargs>)
self.notify(<method_name>, <*args>, <**kwargs>)
"""
pass
class ExampleEngine(MinimalEngine):
pass
# Strategy names and ideas from tom7's excellent eloWorld video
class RandomMove(ExampleEngine):
def search(self, board, *args):
return random.choice(list(board.legal_moves))
class Alphabetical(ExampleEngine):
def search(self, board, *args):
moves = list(board.legal_moves)
moves.sort(key=board.san)
return moves[0]
class FirstMove(ExampleEngine):
"""Gets the first move when sorted by uci representation"""
def search(self, board, *args):
moves = list(board.legal_moves)
moves.sort(key=str)
return moves[0]