16 self.
m_logger = logging.getLogger(self.__class__.__name__)
17 self.
m_logger.setLevel(logging.DEBUG)
20 ch = logging.StreamHandler()
21 ch.setLevel(logging.INFO)
24 formatter = logging.Formatter(
25 "%(asctime)s - %(name)s - %(levelname)s - %(message)s"
27 ch.setFormatter(formatter)
34 assets_pth = importlib.resources.files(
"bitbully").joinpath(
"assets")
35 with assets_pth.joinpath(
"empty.png").open(
"rb")
as file:
36 png_empty = plt.imread(file, format=
None)
37 with assets_pth.joinpath(
"empty_m.png").open(
"rb")
as file:
38 png_empty_m = plt.imread(file, format=
None)
39 with assets_pth.joinpath(
"empty_r.png").open(
"rb")
as file:
40 png_empty_r = plt.imread(file, format=
None)
41 with assets_pth.joinpath(
"red.png").open(
"rb")
as file:
42 png_red = plt.imread(file, format=
None)
43 with assets_pth.joinpath(
"red_m.png").open(
"rb")
as file:
44 png_red_m = plt.imread(file, format=
None)
45 with assets_pth.joinpath(
"yellow.png").open(
"rb")
as file:
46 png_yellow = plt.imread(file, format=
None)
47 with assets_pth.joinpath(
"yellow_m.png").open(
"rb")
as file:
48 png_yellow_m = plt.imread(file, format=
None)
50 0: {
"plain": png_empty,
"corner": png_empty_m,
"underline": png_empty_r},
51 1: {
"plain": png_yellow,
"corner": png_yellow_m},
52 2: {
"plain": png_red,
"corner": png_red_m},
58 self.
m_height = np.zeros(7, dtype=np.int32)
88 db_path = importlib.resources.files(
"bitbully").joinpath(
89 "assets/book_12ply_distances.dat"
96 self.
m_height = np.zeros(7, dtype=np.int32)
100 im.set_data(self.
m_png[0][
"plain"])
102 self.
m_fig.canvas.draw_idle()
103 self.
m_fig.canvas.flush_events()
106 def get_fig_size_px(self):
108 size_in_inches = self.
m_fig.get_size_inches()
109 self.
m_logger.debug(f
"Figure size in inches: {size_in_inches}")
113 self.
m_logger.debug(f
"Figure DPI: {dpi}")
116 fig_size_in_pixels = size_in_inches * dpi
124 return fig_size_in_pixels
126 def create_control_buttons(self):
133 wh = f
"{-3 + (fig_size_px[1] / self.m_n_row)}px"
134 btn_layout = Layout(height=wh, width=wh)
136 button = Button(description=
"đ", tooltip=
"Reset Game", layout=btn_layout)
137 button.on_click(
lambda b: self.
reset())
140 button = Button(description=
"âŠī¸", tooltip=
"Undo Move", layout=btn_layout)
141 button.disabled =
True
142 button.on_click(
lambda b: self.
undo_move())
145 button = Button(description=
"âĒī¸", tooltip=
"Redo Move", layout=btn_layout)
146 button.disabled =
True
147 button.on_click(
lambda b: self.
redo_move())
150 button = Button(description=
"đšī¸", tooltip=
"Computer Move", layout=btn_layout)
154 button = Button(description=
"đ", tooltip=
"Evaluate Board", layout=btn_layout)
157 def computer_move(self):
160 b = bitbully_core.Board()
161 assert b.setBoard([mv[1]
for mv
in self.
m_movelist])
166 def create_board(self):
170 fig, axs = plt.subplots(
181 self.
ims.append(ax.imshow(self.
m_png[0][
"plain"], animated=
True))
183 ax.set_xticklabels([])
184 ax.set_yticklabels([])
188 wspace=0.05, hspace=0.05, left=0.0, right=1.0, top=1.0, bottom=0.0
191 fig.canvas.toolbar_visible =
False
192 fig.canvas.resizable =
False
193 fig.set_facecolor(
"darkgray")
194 fig.canvas.toolbar_visible =
False
195 fig.canvas.header_visible =
False
196 fig.canvas.footer_visible =
False
197 fig.canvas.capture_scroll =
True
198 plt.show(block=
False)
208 notify_output = widgets.Output()
209 display(notify_output)
211 @notify_output.capture()
212 def popup(self, text):
214 display(Javascript(
"alert('{}')".format(text)))
216 def is_legal_move(self, col):
221 def insert_token(self, col, reset_redo_list=True):
227 button.disabled =
True
229 board = bitbully_core.Board()
230 board.setBoard([mv[1]
for mv
in self.
m_movelist])
231 if self.
m_gameover or not board.playMove(col):
250 except Exception
as e:
282 self.
ims[img_idx].set_data(self.
m_png[0][
"plain"])
283 self.
m_axs[img_idx].draw_artist(self.
ims[img_idx])
287 self.
m_fig.canvas.blit(self.
ims[img_idx].get_clip_box())
288 self.
m_fig.canvas.flush_events()
292 except Exception
as e:
302 def update_insert_buttons(self):
321 Get the index of the image to paint.
323 This corresponds to the last token in the column
325 self.
m_logger.debug(f
"Got column: {col}")
328 self.
m_logger.debug(f
"{col}, {img_idx}")
331 def paint_token(self):
337 self.
m_logger.debug(f
"Paint token: {img_idx}")
342 self.
ims[img_idx].set_data(self.
m_png[p][
"corner"])
347 self.
m_axs[img_idx].draw_artist(self.
ims[img_idx])
348 blit_boxes.append(self.
ims[img_idx].get_clip_box())
356 self.
ims[img_idx].set_data(self.
m_png[p][
"plain"])
357 self.
m_axs[img_idx].draw_artist(self.
ims[img_idx])
358 blit_boxes.append(self.
ims[img_idx].get_clip_box())
360 self.
m_fig.canvas.blit(blit_boxes[0])
365 self.
m_fig.canvas.flush_events()
367 def create_buttons(self):
374 for col
in range(self.
m_n_col):
378 width=f
"{-3 + (fig_size_px[0] / self.m_n_col)}px", height=
"50px"
381 button.on_click(
lambda b, col=col: self.
insert_token(col))
384 def create_column_labels(self):
388 width = f
"{-3 + (fig_size_px[0] / self.m_n_col)}px"
392 value=chr(ord(
"a") + i),
394 justify_content=
"center", align_items=
"center", width=width
403 flex_flow=
"row wrap",
404 justify_content=
"center",
405 align_items=
"center",
410 def on_field_click(self, event):
411 ix, iy = event.xdata, event.ydata
412 self.
m_logger.debug(f
"click (x,y): {ix, iy}")
413 idx = np.where(self.
m_axs == event.inaxes)[0][0] % self.
m_n_col
417 def get_widget(self):
419 insert_button_row = HBox(
423 flex_flow=
"row wrap",
424 justify_content=
"center",
425 align_items=
"center",
428 control_buttons_col = HBox(
432 flex_flow=
"row wrap",
433 justify_content=
"flex-end",
434 align_items=
"center",
442 left_sidebar=control_buttons_col,
444 [insert_button_row, self.
output, tb],
447 flex_flow=
"column wrap",
448 justify_content=
"flex-start",
449 align_items=
"flex-start",
458 Check for Win or draw.
461 winner =
"Yellow" if board.movesLeft() % 2
else "Red"
462 self.
popup(f
"Game over! {winner} wins!")
464 if board.movesLeft() == 0:
465 self.
popup(
"Game over! Draw!")
469 plt.close(self.
m_fig)