manim-chess 0.0.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- manim_chess-0.0.1.dist-info/METADATA +212 -0
- manim_chess-0.0.1.dist-info/RECORD +9 -0
- manim_chess-0.0.1.dist-info/WHEEL +5 -0
- manim_chess-0.0.1.dist-info/top_level.txt +1 -0
- src/__init__.py +11 -0
- src/board.py +532 -0
- src/evaluation_bar.py +84 -0
- src/game_player.py +711 -0
- src/pieces.py +246 -0
src/board.py
ADDED
@@ -0,0 +1,532 @@
|
|
1
|
+
from manim import *
|
2
|
+
import math
|
3
|
+
from typing import Tuple
|
4
|
+
from .pieces import Pawn, Knight, Bishop, Rook, Queen, King
|
5
|
+
|
6
|
+
class Board(Mobject):
|
7
|
+
"""
|
8
|
+
A class to represent a chess board using Manim for visualization.
|
9
|
+
|
10
|
+
Attributes:
|
11
|
+
----------
|
12
|
+
size_of_board : int
|
13
|
+
The number of squares along one side of the board (default is 8 for a standard chess board).
|
14
|
+
cell_size : float
|
15
|
+
The length of each square on the board.
|
16
|
+
squares : dict
|
17
|
+
A dictionary mapping coordinates (e.g., 'a1') to their corresponding Square objects.
|
18
|
+
pieces : dict
|
19
|
+
A dictionary mapping coordinates to their corresponding chess piece objects.
|
20
|
+
highlighted_squares : list
|
21
|
+
A list of coordinates of squares that are currently highlighted.
|
22
|
+
arrows : list
|
23
|
+
A list of arrow objects currently drawn on the board.
|
24
|
+
|
25
|
+
Methods:
|
26
|
+
-------
|
27
|
+
create_board():
|
28
|
+
Initializes the board with squares and labels.
|
29
|
+
add_number_label(square, number):
|
30
|
+
Adds a number label to a square.
|
31
|
+
add_letter_label(square, letter):
|
32
|
+
Adds a letter label to a square.
|
33
|
+
get_square(coordinate):
|
34
|
+
Returns the Square object at the given coordinate.
|
35
|
+
add_piece(piece_type, is_white, coordinate):
|
36
|
+
Adds a chess piece to the board at the specified coordinate.
|
37
|
+
get_piece_info_from_FEN(FEN):
|
38
|
+
Extracts piece placement information from a FEN string.
|
39
|
+
get_coordinate_from_index(index):
|
40
|
+
Converts a linear index to a board coordinate.
|
41
|
+
set_board_from_FEN(FEN):
|
42
|
+
Sets up the board pieces according to a FEN string.
|
43
|
+
is_light_square(coordinate):
|
44
|
+
Determines if a square is a light-colored square.
|
45
|
+
mark_square(coordinate):
|
46
|
+
Marks a square with a specific color.
|
47
|
+
unmark_square(coordinate):
|
48
|
+
Resets a square to its original color.
|
49
|
+
highlight_square(coordinate):
|
50
|
+
Highlights a square with a specific color.
|
51
|
+
get_arrow_buffer(end_position, tip_position):
|
52
|
+
Calculates buffer positions for drawing arrows.
|
53
|
+
draw_arrow(end_coordinate, tip_coordinate):
|
54
|
+
Draws an arrow between two squares.
|
55
|
+
remove_piece(coordinate):
|
56
|
+
Removes a piece from the board.
|
57
|
+
remove_arrows():
|
58
|
+
Removes all arrows from the board.
|
59
|
+
move_piece(starting_coordinate, ending_coordinate):
|
60
|
+
Moves a piece from one square to another.
|
61
|
+
promote_piece(coordinate, piece_type):
|
62
|
+
Promotes a piece to another piece type.
|
63
|
+
get_piece_at_square(coordinate):
|
64
|
+
Returns the piece at a given coordinate, if any.
|
65
|
+
"""
|
66
|
+
|
67
|
+
def __init__(self) -> None:
|
68
|
+
"""
|
69
|
+
Initializes the Board object.
|
70
|
+
"""
|
71
|
+
super().__init__()
|
72
|
+
self.size_of_board = 8
|
73
|
+
self.cell_size = 0.8 # Size of each square in the board
|
74
|
+
self.squares = {} # squares[coordinate] = square
|
75
|
+
self.create_board()
|
76
|
+
self.pieces = {} # pieces[coordinate] = piece
|
77
|
+
self.highlighted_squares = []
|
78
|
+
self.arrows = []
|
79
|
+
|
80
|
+
def create_board(self) -> None:
|
81
|
+
"""
|
82
|
+
Creates the chess board with squares and labels.
|
83
|
+
"""
|
84
|
+
total_size = self.size_of_board * self.cell_size # Total size of the board
|
85
|
+
offset = total_size / 2 # Offset to center of board
|
86
|
+
|
87
|
+
letters = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h']
|
88
|
+
|
89
|
+
for row in range(self.size_of_board):
|
90
|
+
for col in range(self.size_of_board):
|
91
|
+
CHESS_GREEN = ManimColor('#769656')
|
92
|
+
CHESS_WHITE = ManimColor('#eeeed2')
|
93
|
+
color = CHESS_WHITE if (row + col) % 2 else CHESS_GREEN
|
94
|
+
# Create a square for each cell in the board
|
95
|
+
square = Square(side_length=self.cell_size)
|
96
|
+
square.set_fill(color, opacity=1)
|
97
|
+
square.set_stroke(color, opacity=0)
|
98
|
+
square.move_to(np.array([col * self.cell_size - offset, row * self.cell_size - offset, 0]))
|
99
|
+
|
100
|
+
# Add number label if first col
|
101
|
+
if col == 0:
|
102
|
+
self.add_number_label(square, row + 1)
|
103
|
+
|
104
|
+
# Add letter label if first row
|
105
|
+
if row == 0:
|
106
|
+
self.add_letter_label(square, letters[col])
|
107
|
+
|
108
|
+
# Add square to dictionary so we can access it with key
|
109
|
+
self.squares[f'{letters[col]}{row + 1}'] = square
|
110
|
+
# Add square to self mobj
|
111
|
+
self.add(square)
|
112
|
+
|
113
|
+
def add_number_label(self, square: Square, number: str) -> None:
|
114
|
+
"""
|
115
|
+
Adds a number label to a square.
|
116
|
+
|
117
|
+
Parameters:
|
118
|
+
----------
|
119
|
+
square : Square
|
120
|
+
The square to which the number label is added.
|
121
|
+
number : str
|
122
|
+
The number to be displayed on the square.
|
123
|
+
"""
|
124
|
+
CHESS_GREEN = ManimColor('#769656')
|
125
|
+
CHESS_WHITE = ManimColor('#eeeed2')
|
126
|
+
|
127
|
+
offset = np.array([self.cell_size / 8, -self.cell_size / 6, 0])
|
128
|
+
|
129
|
+
number_color = CHESS_WHITE if square.fill_color == CHESS_GREEN else CHESS_GREEN
|
130
|
+
number = Text(f'{number}', color=number_color, font_size=14 * self.cell_size, font="Arial")
|
131
|
+
square_top_left = square.get_center() + np.array([-self.cell_size / 2, self.cell_size / 2, 0])
|
132
|
+
number.move_to(square_top_left + offset)
|
133
|
+
square.add(number)
|
134
|
+
|
135
|
+
def add_letter_label(self, square: Square, letter: str) -> None:
|
136
|
+
"""
|
137
|
+
Adds a letter label to a square.
|
138
|
+
|
139
|
+
Parameters:
|
140
|
+
----------
|
141
|
+
square : Square
|
142
|
+
The square to which the letter label is added.
|
143
|
+
letter : str
|
144
|
+
The letter to be displayed on the square.
|
145
|
+
"""
|
146
|
+
CHESS_GREEN = ManimColor('#769656')
|
147
|
+
CHESS_WHITE = ManimColor('#eeeed2')
|
148
|
+
|
149
|
+
offset = np.array([-self.cell_size / 8, self.cell_size / 6, 0])
|
150
|
+
|
151
|
+
letter_color = CHESS_WHITE if square.fill_color == CHESS_GREEN else CHESS_GREEN
|
152
|
+
letter = Text(f'{letter}', color=letter_color, font_size=14 * self.cell_size, font="Arial")
|
153
|
+
square_bot_right = square.get_center() + np.array([self.cell_size / 2, -self.cell_size / 2, 0])
|
154
|
+
letter.move_to(square_bot_right + offset)
|
155
|
+
square.add(letter)
|
156
|
+
|
157
|
+
def get_square(self, coordinate: str) -> Square:
|
158
|
+
"""
|
159
|
+
Returns the Square object at the given coordinate.
|
160
|
+
|
161
|
+
Parameters:
|
162
|
+
----------
|
163
|
+
coordinate : str
|
164
|
+
The coordinate of the square (e.g., 'a1').
|
165
|
+
|
166
|
+
Returns:
|
167
|
+
-------
|
168
|
+
Square
|
169
|
+
The Square object at the specified coordinate.
|
170
|
+
"""
|
171
|
+
return self.squares[coordinate]
|
172
|
+
|
173
|
+
def add_piece(self, piece_type: str, is_white: bool, coordinate: str) -> None:
|
174
|
+
"""
|
175
|
+
Adds a chess piece to the board at the specified coordinate.
|
176
|
+
|
177
|
+
Parameters:
|
178
|
+
----------
|
179
|
+
piece_type : str
|
180
|
+
The type of the piece (e.g., 'P' for Pawn).
|
181
|
+
is_white : bool
|
182
|
+
True if the piece is white, False if black.
|
183
|
+
coordinate : str
|
184
|
+
The coordinate where the piece is to be placed.
|
185
|
+
"""
|
186
|
+
piece_classes = {
|
187
|
+
'P': Pawn,
|
188
|
+
'N': Knight,
|
189
|
+
'B': Bishop,
|
190
|
+
'R': Rook,
|
191
|
+
'Q': Queen,
|
192
|
+
'K': King,
|
193
|
+
}
|
194
|
+
|
195
|
+
piece_class = piece_classes.get(piece_type)
|
196
|
+
if piece_class:
|
197
|
+
piece = piece_class(is_white=is_white).move_to(self.squares[coordinate].get_center())
|
198
|
+
self.pieces[coordinate] = piece
|
199
|
+
self.add(piece)
|
200
|
+
else:
|
201
|
+
raise ValueError(f"Unknown piece type: {piece_type}")
|
202
|
+
|
203
|
+
def get_piece_info_from_FEN(self, FEN: str) -> str:
|
204
|
+
"""
|
205
|
+
Extracts piece placement information from a FEN string.
|
206
|
+
|
207
|
+
Parameters:
|
208
|
+
----------
|
209
|
+
FEN : str
|
210
|
+
The FEN string representing the board state.
|
211
|
+
|
212
|
+
Returns:
|
213
|
+
-------
|
214
|
+
str
|
215
|
+
The piece placement part of the FEN string.
|
216
|
+
"""
|
217
|
+
return FEN.split()[0]
|
218
|
+
|
219
|
+
def get_coordinate_from_index(self, index: int) -> str:
|
220
|
+
"""
|
221
|
+
Converts a linear index to a board coordinate.
|
222
|
+
|
223
|
+
Parameters:
|
224
|
+
----------
|
225
|
+
index : int
|
226
|
+
The linear index of a square.
|
227
|
+
|
228
|
+
Returns:
|
229
|
+
-------
|
230
|
+
str
|
231
|
+
The board coordinate corresponding to the index.
|
232
|
+
"""
|
233
|
+
number_to_letter = {
|
234
|
+
0: 'a',
|
235
|
+
1: 'b',
|
236
|
+
2: 'c',
|
237
|
+
3: 'd',
|
238
|
+
4: 'e',
|
239
|
+
5: 'f',
|
240
|
+
6: 'g',
|
241
|
+
7: 'h'
|
242
|
+
}
|
243
|
+
coordinate = f'{number_to_letter[index % 8]}{8 - math.floor(index / 8)}'
|
244
|
+
return coordinate
|
245
|
+
|
246
|
+
def set_board_from_FEN(self, FEN: str="rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1") -> None:
|
247
|
+
"""
|
248
|
+
Sets up the board pieces according to a FEN string. Default FEN is the standard start of game.
|
249
|
+
|
250
|
+
Parameters:
|
251
|
+
----------
|
252
|
+
FEN : str
|
253
|
+
The FEN string representing the board state.
|
254
|
+
"""
|
255
|
+
piece_info = self.get_piece_info_from_FEN(FEN)
|
256
|
+
current_index = 0
|
257
|
+
for char in piece_info:
|
258
|
+
if char in {'1', '2', '3', '4', '5', '6', '7', '8'}:
|
259
|
+
current_index += int(char)
|
260
|
+
elif char == '/':
|
261
|
+
pass
|
262
|
+
else:
|
263
|
+
coordinate = self.get_coordinate_from_index(current_index)
|
264
|
+
self.add_piece(char.upper(), char.isupper(), coordinate)
|
265
|
+
current_index += 1
|
266
|
+
|
267
|
+
def is_light_square(self, coordinate: str) -> bool:
|
268
|
+
"""
|
269
|
+
Determines if a square is a light-colored square.
|
270
|
+
|
271
|
+
Parameters:
|
272
|
+
----------
|
273
|
+
coordinate : str
|
274
|
+
The coordinate of the square.
|
275
|
+
|
276
|
+
Returns:
|
277
|
+
-------
|
278
|
+
bool
|
279
|
+
True if the square is light-colored, False otherwise.
|
280
|
+
"""
|
281
|
+
if coordinate[0] in {'a', 'c', 'e', 'g'}:
|
282
|
+
if coordinate[1] in {'1', '3', '5', '7'}:
|
283
|
+
return False
|
284
|
+
else:
|
285
|
+
return True
|
286
|
+
else:
|
287
|
+
if coordinate[1] in {'1', '3', '5', '7'}:
|
288
|
+
return True
|
289
|
+
else:
|
290
|
+
return False
|
291
|
+
|
292
|
+
def mark_square(self, coordinate: str) -> None:
|
293
|
+
"""
|
294
|
+
Marks a square with a specific color.
|
295
|
+
|
296
|
+
Parameters:
|
297
|
+
----------
|
298
|
+
coordinate : str
|
299
|
+
The coordinate of the square to be marked.
|
300
|
+
"""
|
301
|
+
MARK_COLOR = ManimColor('#EC7D6A')
|
302
|
+
self.squares[coordinate].set_fill(MARK_COLOR)
|
303
|
+
|
304
|
+
# Add back text on square if needed
|
305
|
+
if coordinate[1] == '1':
|
306
|
+
self.add_letter_label(self.squares[coordinate], coordinate[0])
|
307
|
+
if coordinate[0] == 'a':
|
308
|
+
self.add_number_label(self.squares[coordinate], coordinate[1])
|
309
|
+
|
310
|
+
def unmark_square(self, coordinate: str) -> None:
|
311
|
+
"""
|
312
|
+
Resets a square to its original color.
|
313
|
+
|
314
|
+
Parameters:
|
315
|
+
----------
|
316
|
+
coordinate : str
|
317
|
+
The coordinate of the square to be unmarked.
|
318
|
+
"""
|
319
|
+
CHESS_GREEN = ManimColor('#769656')
|
320
|
+
CHESS_WHITE = ManimColor('#eeeed2')
|
321
|
+
|
322
|
+
if self.is_light_square(coordinate):
|
323
|
+
self.squares[coordinate].set_fill(CHESS_WHITE)
|
324
|
+
else:
|
325
|
+
self.squares[coordinate].set_fill(CHESS_GREEN)
|
326
|
+
|
327
|
+
# Add back text on square if needed
|
328
|
+
if coordinate[1] == '1':
|
329
|
+
self.add_letter_label(self.squares[coordinate], coordinate[0])
|
330
|
+
if coordinate[0] == 'a':
|
331
|
+
self.add_number_label(self.squares[coordinate], coordinate[1])
|
332
|
+
|
333
|
+
def highlight_square(self, coordinate: str) -> None:
|
334
|
+
"""
|
335
|
+
Highlights a square with a specific color.
|
336
|
+
|
337
|
+
Parameters:
|
338
|
+
----------
|
339
|
+
coordinate : str
|
340
|
+
The coordinate of the square to be highlighted.
|
341
|
+
"""
|
342
|
+
LIGHT_HIGHLIGHT_COLOR = ManimColor('#F7F769')
|
343
|
+
DARK_HIGHLIGHT_COLOR = ManimColor('#BBCB2B')
|
344
|
+
|
345
|
+
if self.is_light_square(coordinate):
|
346
|
+
self.squares[coordinate].set_fill(LIGHT_HIGHLIGHT_COLOR)
|
347
|
+
else:
|
348
|
+
self.squares[coordinate].set_fill(DARK_HIGHLIGHT_COLOR)
|
349
|
+
|
350
|
+
# Add back text on square if needed
|
351
|
+
if coordinate[1] == '1':
|
352
|
+
self.add_letter_label(self.squares[coordinate], coordinate[0])
|
353
|
+
if coordinate[0] == 'a':
|
354
|
+
self.add_number_label(self.squares[coordinate], coordinate[1])
|
355
|
+
|
356
|
+
def get_arrow_buffer(self, end_position: np.array, tip_position: np.array) -> Tuple[np.array]:
|
357
|
+
"""
|
358
|
+
Calculates buffer positions for drawing arrows.
|
359
|
+
|
360
|
+
Parameters:
|
361
|
+
----------
|
362
|
+
end_position : np.array
|
363
|
+
The end position of the arrow.
|
364
|
+
tip_position : np.array
|
365
|
+
The tip position of the arrow.
|
366
|
+
|
367
|
+
Returns:
|
368
|
+
-------
|
369
|
+
Tuple[np.array]
|
370
|
+
The buffered end and tip positions.
|
371
|
+
"""
|
372
|
+
# Calculate the direction vector
|
373
|
+
subtracted_position_vectors = end_position - tip_position
|
374
|
+
buffer = 0.25
|
375
|
+
|
376
|
+
# Calculate the normalized direction vector
|
377
|
+
direction_vector = subtracted_position_vectors / np.linalg.norm(subtracted_position_vectors)
|
378
|
+
|
379
|
+
end_position_buffer = direction_vector * buffer
|
380
|
+
tip_position_buffer = -direction_vector * buffer
|
381
|
+
|
382
|
+
return end_position_buffer, tip_position_buffer
|
383
|
+
|
384
|
+
def draw_arrow(self, end_coordinate: str, tip_coordinate: str) -> None:
|
385
|
+
"""
|
386
|
+
Draws an arrow between two squares.
|
387
|
+
|
388
|
+
Parameters:
|
389
|
+
----------
|
390
|
+
end_coordinate : str
|
391
|
+
The coordinate of the square where the arrow ends.
|
392
|
+
tip_coordinate : str
|
393
|
+
The coordinate of the square where the arrow starts.
|
394
|
+
"""
|
395
|
+
ARROW_COLOR = ManimColor('#E09651')
|
396
|
+
end_square = self.squares[end_coordinate]
|
397
|
+
tip_square = self.squares[tip_coordinate]
|
398
|
+
|
399
|
+
end_position = end_square.get_center()
|
400
|
+
tip_position = tip_square.get_center()
|
401
|
+
|
402
|
+
end_position_buffer, tip_position_buffer = self.get_arrow_buffer(end_position, tip_position)
|
403
|
+
|
404
|
+
# Check if horizontal or vertical line or perfect diagonal like bishop
|
405
|
+
subtracted_position_vectors = end_position - tip_position
|
406
|
+
dir_x = subtracted_position_vectors[0]
|
407
|
+
dir_y = subtracted_position_vectors[1]
|
408
|
+
if dir_x == 0 or dir_y == 0 or abs(dir_x) == abs(dir_y):
|
409
|
+
arrow = Line(stroke_width=15, stroke_opacity=.8, fill_color=ARROW_COLOR, stroke_color=ARROW_COLOR)
|
410
|
+
arrow.reset_endpoints_based_on_tip = lambda *args: None
|
411
|
+
arrow.set_points_as_corners([end_position - end_position_buffer, tip_position - tip_position_buffer])
|
412
|
+
tip = arrow.create_tip()
|
413
|
+
tip.move_to(tip_position - tip_position_buffer)
|
414
|
+
finished_arrow = VGroup(arrow, tip)
|
415
|
+
self.add(finished_arrow)
|
416
|
+
else:
|
417
|
+
arrow0 = Line(stroke_width=15, stroke_opacity=.8, fill_color=ARROW_COLOR, stroke_color=ARROW_COLOR)
|
418
|
+
arrow0.reset_endpoints_based_on_tip = lambda *args: None
|
419
|
+
arrow1 = Line(stroke_width=15, stroke_opacity=.8, fill_color=ARROW_COLOR, stroke_color=ARROW_COLOR)
|
420
|
+
arrow1.reset_endpoints_based_on_tip = lambda *args: None
|
421
|
+
|
422
|
+
if dir_y > 0:
|
423
|
+
buffer_y = np.array([0, 0.25, 0])
|
424
|
+
else:
|
425
|
+
buffer_y = np.array([0, -0.25, 0])
|
426
|
+
|
427
|
+
if dir_x > 0:
|
428
|
+
buffer_x = np.array([0.25, 0, 0])
|
429
|
+
else:
|
430
|
+
buffer_x = np.array([-0.25, 0, 0])
|
431
|
+
|
432
|
+
tip_buffer = -0.07 if dir_x > 0 else 0.07
|
433
|
+
|
434
|
+
if abs(dir_y) > abs(dir_x):
|
435
|
+
arrow0.set_points_as_corners([end_position - buffer_y, np.array([end_position[0], tip_position[1], 0])])
|
436
|
+
arrow1.set_points_as_corners([np.array([end_position[0]-tip_buffer, tip_position[1], 0]), tip_position + buffer_x])
|
437
|
+
tip = arrow1.create_tip()
|
438
|
+
tip.move_to(tip_position + buffer_x)
|
439
|
+
else:
|
440
|
+
arrow0.set_points_as_corners([end_position - buffer_x, np.array([tip_position[0], end_position[1], 0])])
|
441
|
+
arrow1.set_points_as_corners([np.array([tip_position[0]-tip_buffer, end_position[1], 0]), tip_position + buffer_y])
|
442
|
+
tip = arrow1.create_tip()
|
443
|
+
tip.move_to(tip_position + buffer_y)
|
444
|
+
|
445
|
+
finished_arrow = VGroup(arrow0, arrow1, tip)
|
446
|
+
self.add(finished_arrow)
|
447
|
+
self.arrows.append(finished_arrow)
|
448
|
+
|
449
|
+
def remove_piece(self, coordinate: str) -> None:
|
450
|
+
"""
|
451
|
+
Removes a piece from the board.
|
452
|
+
|
453
|
+
Parameters:
|
454
|
+
----------
|
455
|
+
coordinate : str
|
456
|
+
The coordinate of the piece to be removed.
|
457
|
+
"""
|
458
|
+
piece_to_remove = self.pieces[coordinate]
|
459
|
+
self.remove(piece_to_remove)
|
460
|
+
del piece_to_remove
|
461
|
+
del self.pieces[coordinate]
|
462
|
+
|
463
|
+
def remove_arrows(self) -> None:
|
464
|
+
"""
|
465
|
+
Removes all arrows from the board.
|
466
|
+
"""
|
467
|
+
for arrow in self.arrows:
|
468
|
+
self.remove(arrow)
|
469
|
+
|
470
|
+
def move_piece(self, starting_coordinate: str, ending_coordinate: str) -> None:
|
471
|
+
"""
|
472
|
+
Moves a piece from one square to another.
|
473
|
+
|
474
|
+
Parameters:
|
475
|
+
----------
|
476
|
+
starting_coordinate : str
|
477
|
+
The coordinate of the square where the piece is currently located.
|
478
|
+
ending_coordinate : str
|
479
|
+
The coordinate of the square where the piece is to be moved.
|
480
|
+
"""
|
481
|
+
if ending_coordinate in self.pieces.keys():
|
482
|
+
self.remove_piece(ending_coordinate)
|
483
|
+
try:
|
484
|
+
piece_to_move = self.pieces[starting_coordinate]
|
485
|
+
self.pieces[ending_coordinate] = piece_to_move
|
486
|
+
del self.pieces[starting_coordinate]
|
487
|
+
|
488
|
+
for coordinate in self.highlighted_squares:
|
489
|
+
self.unmark_square(coordinate)
|
490
|
+
self.highlighted_squares = []
|
491
|
+
|
492
|
+
piece_to_move.move_to(self.squares[ending_coordinate].get_center())
|
493
|
+
self.highlight_square(starting_coordinate)
|
494
|
+
self.highlight_square(ending_coordinate)
|
495
|
+
self.highlighted_squares.append(starting_coordinate)
|
496
|
+
self.highlighted_squares.append(ending_coordinate)
|
497
|
+
except Exception as e:
|
498
|
+
print(f'{e} has no piece associated')
|
499
|
+
|
500
|
+
def promote_piece(self, coordinate: str, piece_type: str) -> None:
|
501
|
+
"""
|
502
|
+
Promotes a piece to another piece type.
|
503
|
+
|
504
|
+
Parameters:
|
505
|
+
----------
|
506
|
+
coordinate : str
|
507
|
+
The coordinate of the piece to be promoted.
|
508
|
+
piece_type : str
|
509
|
+
The type of piece to which the piece is promoted (e.g., 'Q' for Queen).
|
510
|
+
"""
|
511
|
+
piece_color = self.pieces[coordinate].is_white
|
512
|
+
self.remove_piece(coordinate)
|
513
|
+
self.add_piece(piece_type.upper(), piece_color, coordinate)
|
514
|
+
|
515
|
+
def get_piece_at_square(self, coordinate: str):
|
516
|
+
"""
|
517
|
+
Returns the piece at a given coordinate, if any.
|
518
|
+
|
519
|
+
Parameters:
|
520
|
+
----------
|
521
|
+
coordinate : str
|
522
|
+
The coordinate of the square to check for a piece.
|
523
|
+
|
524
|
+
Returns:
|
525
|
+
-------
|
526
|
+
object or None
|
527
|
+
The piece object at the specified coordinate, or None if no piece is present.
|
528
|
+
"""
|
529
|
+
if coordinate in self.pieces:
|
530
|
+
return self.pieces[coordinate]
|
531
|
+
else:
|
532
|
+
return None # No piece at the given coordinate
|
src/evaluation_bar.py
ADDED
@@ -0,0 +1,84 @@
|
|
1
|
+
from manim import *
|
2
|
+
from typing import Tuple
|
3
|
+
|
4
|
+
class EvaluationBar(Mobject):
|
5
|
+
"""
|
6
|
+
A class to represent an evaluation bar using Manim for visualization.
|
7
|
+
|
8
|
+
Attributes:
|
9
|
+
----------
|
10
|
+
evaluation : float
|
11
|
+
The current evaluation value, default is 0.0.
|
12
|
+
BLACK : ManimColor
|
13
|
+
The color used for the black portion of the bar.
|
14
|
+
WHITE : ManimColor
|
15
|
+
The color used for the white portion of the bar.
|
16
|
+
black_rectangle : Rectangle
|
17
|
+
The rectangle representing the black portion of the evaluation bar.
|
18
|
+
white_rectangle : Rectangle
|
19
|
+
The rectangle representing the white portion of the evaluation bar.
|
20
|
+
bot_text : Text
|
21
|
+
The text object displaying the evaluation at the bottom of the bar.
|
22
|
+
top_text : Text
|
23
|
+
The text object displaying the evaluation at the top of the bar.
|
24
|
+
"""
|
25
|
+
|
26
|
+
def __init__(self, evaluation=0.0) -> None:
|
27
|
+
"""
|
28
|
+
Initializes the EvaluationBar object with default or specified evaluation.
|
29
|
+
|
30
|
+
Parameters:
|
31
|
+
----------
|
32
|
+
evaluation : float, optional
|
33
|
+
The initial evaluation value (default is 0.0).
|
34
|
+
"""
|
35
|
+
super().__init__()
|
36
|
+
self.evaluation = evaluation
|
37
|
+
self.BLACK = ManimColor("#403D39")
|
38
|
+
self.WHITE = ManimColor("#ffffff")
|
39
|
+
self.black_rectangle = Rectangle(width=0.25, height=6.4, stroke_color=self.BLACK, fill_opacity=1).set_fill(self.BLACK)
|
40
|
+
self.white_rectangle = Rectangle(width=0.25, height=3.2, stroke_color=self.WHITE, fill_opacity=1).set_fill(self.WHITE)
|
41
|
+
self.bot_text = Text('0.0', font="Arial").move_to(self.black_rectangle.get_bottom() + np.array([0, 0.2, 0])).set_fill(self.BLACK).scale(0.2)
|
42
|
+
self.top_text = Text('0.0', font="Arial").move_to(self.black_rectangle.get_top() + np.array([0, -0.2, 0])).set_fill(self.WHITE).scale(0.2)
|
43
|
+
self.__add_rectangles()
|
44
|
+
|
45
|
+
def set_evaluation(self, evaluation: float):
|
46
|
+
"""
|
47
|
+
Updates the evaluation value and adjusts the visual representation accordingly.
|
48
|
+
|
49
|
+
Parameters:
|
50
|
+
----------
|
51
|
+
evaluation : float
|
52
|
+
The new evaluation value to be set.
|
53
|
+
|
54
|
+
Returns:
|
55
|
+
-------
|
56
|
+
list
|
57
|
+
A list of Transform animations to update the evaluation bar.
|
58
|
+
"""
|
59
|
+
self.evaluation = evaluation
|
60
|
+
text_transformation = None
|
61
|
+
if self.evaluation > 0:
|
62
|
+
text_transformation = Transform(
|
63
|
+
self.bot_text,
|
64
|
+
Text(f'{self.evaluation:.1f}', font="Arial").move_to(self.black_rectangle.get_bottom() + np.array([0, 0.2, 0])).set_fill(self.BLACK).scale(0.2)
|
65
|
+
)
|
66
|
+
else:
|
67
|
+
text_transformation = Transform(
|
68
|
+
self.bot_text,
|
69
|
+
Text(f'{self.evaluation:.1f}', font="Arial").move_to(self.black_rectangle.get_top() + np.array([0, -0.2, 0])).set_fill(self.WHITE).scale(0.2)
|
70
|
+
)
|
71
|
+
|
72
|
+
height_from_evaluation = 0.737063 * self.evaluation + 3.2
|
73
|
+
rect_height = min(max(0.32, height_from_evaluation), 6.18)
|
74
|
+
pos = self.black_rectangle.get_bottom() + np.array([0, rect_height / 2, 0])
|
75
|
+
new_rect = Rectangle(width=0.25, height=rect_height, stroke_color=self.WHITE, fill_opacity=1).set_fill(self.WHITE).move_to(pos)
|
76
|
+
return [Transform(self.white_rectangle, new_rect), text_transformation]
|
77
|
+
|
78
|
+
def __add_rectangles(self) -> None:
|
79
|
+
"""
|
80
|
+
Adds the rectangles and text to the evaluation bar.
|
81
|
+
"""
|
82
|
+
self.add(self.black_rectangle)
|
83
|
+
self.add(self.white_rectangle.shift(DOWN * 1.6))
|
84
|
+
self.add(self.bot_text)
|