BlueMoonBot commited on
Commit
dbb8d8e
·
verified ·
1 Parent(s): 696dbbb

Create strategies.py

Browse files
Files changed (1) hide show
  1. strategies.py +233 -0
strategies.py ADDED
@@ -0,0 +1,233 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import chess
2
+ import time
3
+ import math
4
+ import random
5
+
6
+ class ComplexMaiaEngine:
7
+ def __init__(self, skill_level=3):
8
+ # Default skill_level = 3 (~1300 Elo). Max = 20
9
+ self.skill_level = max(1, min(20, skill_level))
10
+ self.nodes_visited = 0
11
+
12
+ # Piece values for Evaluation
13
+ self.PIECE_VALUES = {
14
+ chess.PAWN: 100,
15
+ chess.KNIGHT: 320,
16
+ chess.BISHOP: 330,
17
+ chess.ROOK: 500,
18
+ chess.QUEEN: 900,
19
+ chess.KING: 20000
20
+ }
21
+
22
+ # Piece-Square Tables (Heuristics for positional strength)
23
+ self.PAWN_TABLE = [
24
+ 0, 0, 0, 0, 0, 0, 0, 0,
25
+ 50, 50, 50, 50, 50, 50, 50, 50,
26
+ 10, 10, 20, 30, 30, 20, 10, 10,
27
+ 5, 5, 10, 25, 25, 10, 5, 5,
28
+ 0, 0, 0, 20, 20, 0, 0, 0,
29
+ 5, -5,-10, 0, 0,-10, -5, 5,
30
+ 5, 10, 10,-20,-20, 10, 10, 5,
31
+ 0, 0, 0, 0, 0, 0, 0, 0
32
+ ]
33
+
34
+ self.KNIGHT_TABLE = [
35
+ -50,-40,-30,-30,-30,-30,-40,-50,
36
+ -40,-20, 0, 0, 0, 0,-20,-40,
37
+ -30, 0, 10, 15, 15, 10, 0,-30,
38
+ -30, 5, 15, 20, 20, 15, 5,-30,
39
+ -30, 0, 15, 20, 20, 15, 0,-30,
40
+ -30, 5, 10, 15, 15, 10, 5,-30,
41
+ -40,-20, 0, 5, 5, 0,-20,-40,
42
+ -50,-40,-30,-30,-30,-30,-40,-50
43
+ ]
44
+
45
+ self.BISHOP_TABLE = [
46
+ -20,-10,-10,-10,-10,-10,-10,-20,
47
+ -10, 0, 0, 0, 0, 0, 0,-10,
48
+ -10, 0, 5, 10, 10, 5, 0,-10,
49
+ -10, 5, 5, 10, 10, 5, 5,-10,
50
+ -10, 0, 10, 10, 10, 10, 0,-10,
51
+ -10, 10, 10, 10, 10, 10, 10,-10,
52
+ -10, 5, 0, 0, 0, 0, 5,-10,
53
+ -20,-10,-10,-10,-10,-10,-10,-20
54
+ ]
55
+
56
+ self.ROOK_TABLE = [
57
+ 0, 0, 0, 0, 0, 0, 0, 0,
58
+ 5, 10, 10, 10, 10, 10, 10, 5,
59
+ -5, 0, 0, 0, 0, 0, 0, -5,
60
+ -5, 0, 0, 0, 0, 0, 0, -5,
61
+ -5, 0, 0, 0, 0, 0, 0, -5,
62
+ -5, 0, 0, 0, 0, 0, 0, -5,
63
+ -5, 0, 0, 0, 0, 0, 0, -5,
64
+ 0, 0, 0, 5, 5, 0, 0, 0
65
+ ]
66
+
67
+ self.QUEEN_TABLE = [
68
+ -20,-10,-10, -5, -5,-10,-10,-20,
69
+ -10, 0, 0, 0, 0, 0, 0,-10,
70
+ -10, 0, 5, 5, 5, 5, 0,-10,
71
+ -5, 0, 5, 5, 5, 5, 0, -5,
72
+ 0, 0, 5, 5, 5, 5, 0, -5,
73
+ -10, 5, 5, 5, 5, 5, 0,-10,
74
+ -10, 0, 5, 0, 0, 0, 0,-10,
75
+ -20,-10,-10, -5, -5,-10,-10,-20
76
+ ]
77
+
78
+ self.KING_TABLE = [
79
+ -30,-40,-40,-50,-50,-40,-40,-30,
80
+ -30,-40,-40,-50,-50,-40,-40,-30,
81
+ -30,-40,-40,-50,-50,-40,-40,-30,
82
+ -30,-40,-40,-50,-50,-40,-40,-30,
83
+ -20,-30,-30,-40,-40,-30,-30,-20,
84
+ -10,-20,-20,-20,-20,-20,-20,-10,
85
+ 20, 20, 0, 0, 0, 0, 20, 20,
86
+ 20, 30, 10, 0, 0, 10, 30, 20
87
+ ]
88
+
89
+ def set_skill_level(self, level):
90
+ self.skill_level = max(1, min(20, level))
91
+
92
+ def evaluate_board(self, board):
93
+ """ Statically evaluates the board layout. Positive = White advantage """
94
+ if board.is_checkmate():
95
+ if board.turn == chess.WHITE:
96
+ return -99999 # Black wins
97
+ else:
98
+ return 99999 # White wins
99
+ if board.is_stalemate() or board.is_insufficient_material():
100
+ return 0
101
+
102
+ score = 0
103
+
104
+ for square in chess.SQUARES:
105
+ piece = board.piece_at(square)
106
+ if piece:
107
+ piece_type = piece.piece_type
108
+ value = self.PIECE_VALUES[piece_type]
109
+
110
+ if self.skill_level < 5 and piece_type in [chess.KNIGHT, chess.BISHOP]:
111
+ value -= 15
112
+
113
+ table_score = 0
114
+ square_idx = square if piece.color == chess.WHITE else chess.square_mirror(square)
115
+
116
+ if piece_type == chess.PAWN: table_score = self.PAWN_TABLE[square_idx]
117
+ elif piece_type == chess.KNIGHT: table_score = self.KNIGHT_TABLE[square_idx]
118
+ elif piece_type == chess.BISHOP: table_score = self.BISHOP_TABLE[square_idx]
119
+ elif piece_type == chess.ROOK: table_score = self.ROOK_TABLE[square_idx]
120
+ elif piece_type == chess.QUEEN: table_score = self.QUEEN_TABLE[square_idx]
121
+ elif piece_type == chess.KING: table_score = self.KING_TABLE[square_idx]
122
+
123
+ # Fixed low-ELO blunder logic: 15% random chance to evaluate squares backward
124
+ if self.skill_level <= 3 and random.random() < 0.15:
125
+ table_score = -table_score
126
+
127
+ if piece.color == chess.WHITE:
128
+ score += (value + table_score)
129
+ else:
130
+ score -= (value + table_score)
131
+
132
+ return score
133
+
134
+ def order_moves(self, board, moves):
135
+ scored_moves = []
136
+ for move in moves:
137
+ score = 0
138
+ if board.is_capture(move):
139
+ captured = board.piece_at(move.to_square)
140
+ attacker = board.piece_at(move.from_square)
141
+ if captured and attacker:
142
+ score += 10 * self.PIECE_VALUES[captured.piece_type] - self.PIECE_VALUES[attacker.piece_type]
143
+
144
+ if move.promotion:
145
+ score += 900
146
+
147
+ if board.is_attacked_by(not board.turn, move.to_square):
148
+ score -= self.PIECE_VALUES[board.piece_at(move.from_square).piece_type] if board.piece_at(move.from_square) else 50
149
+
150
+ scored_moves.append((score, move))
151
+
152
+ if self.skill_level > 8:
153
+ scored_moves.sort(key=lambda x: x[0], reverse=True)
154
+
155
+ return [move for score, move in scored_moves]
156
+
157
+ def alpha_beta(self, board, depth, alpha, beta, maximizing_player):
158
+ self.nodes_visited += 1
159
+
160
+ if depth == 0 or board.is_game_over():
161
+ return self.evaluate_board(board)
162
+
163
+ legal_moves = self.order_moves(board, board.legal_moves)
164
+
165
+ if maximizing_player:
166
+ max_eval = -math.inf
167
+ for move in legal_moves:
168
+ board.push(move)
169
+ evaluation = self.alpha_beta(board, depth - 1, alpha, beta, False)
170
+ board.pop()
171
+ max_eval = max(max_eval, evaluation)
172
+ alpha = max(alpha, evaluation)
173
+ if beta <= alpha:
174
+ break
175
+ return max_eval
176
+ else:
177
+ min_eval = math.inf
178
+ for move in legal_moves:
179
+ board.push(move)
180
+ evaluation = self.alpha_beta(board, depth - 1, alpha, beta, True)
181
+ board.pop()
182
+ min_eval = min(min_eval, evaluation)
183
+ beta = min(beta, evaluation)
184
+ if beta <= alpha:
185
+ break
186
+ return min_eval
187
+
188
+ def select_best_move(self, board):
189
+ self.nodes_visited = 0
190
+ best_move = None
191
+
192
+ if self.skill_level <= 3:
193
+ target_depth = 3
194
+ elif self.skill_level <= 8:
195
+ target_depth = 4
196
+ else:
197
+ target_depth = 5
198
+
199
+ legal_moves = self.order_moves(board, board.legal_moves)
200
+ if not legal_moves:
201
+ return None
202
+
203
+ best_move = legal_moves[0]
204
+
205
+ for current_depth in range(1, target_depth + 1):
206
+ if board.turn == chess.WHITE:
207
+ best_value = -math.inf
208
+ for move in legal_moves:
209
+ board.push(move)
210
+ board_value = self.alpha_beta(board, current_depth - 1, -math.inf, math.inf, False)
211
+ board.pop()
212
+ if board_value > best_value:
213
+ best_value = board_value
214
+ best_move = move
215
+ else:
216
+ best_value = math.inf
217
+ for move in legal_moves:
218
+ board.push(move)
219
+ board_value = self.alpha_beta(board, current_depth - 1, -math.inf, math.inf, True)
220
+ board.pop()
221
+ if board_value < best_value:
222
+ best_value = board_value
223
+ best_move = move
224
+
225
+ print(f"Skill Level: {self.skill_level} | Evaluated {self.nodes_visited} Positions | Best Move: {best_move}")
226
+ return best_move
227
+
228
+ # ==============================================================================
229
+ # MANDATORY LICHESS-BOT WRAPPER FUNCTION
230
+ # ==============================================================================
231
+ def play(self, board, game=None):
232
+ """Standardized interface entry point for lichess-bot framework"""
233
+ return self.select_best_move(board)