A class which allows to create an interactive Connect-4 widget.
GuiC4 is an interactive Connect-4 graphical user interface (GUI) implemented using
Matplotlib, IPython widgets, and a backend agent from the BitBully engine. It
provides the following main features:
- Interactive Game Board: Presents a dynamic 6-row by 7-column
Connect-4 board with clickable board cells.
- Matplotlib Integration: Utilizes Matplotlib figures
to render high-quality game visuals directly within Jupyter notebook environments.
- User Interaction: Captures and processes mouse clicks and button events, enabling
intuitive gameplay via either direct board interaction or button controls.
- Undo/Redo Moves: Supports undo and redo functionalities, allowing users to
navigate through their move history during gameplay.
- Automated Agent Moves: Incorporates BitBully, a Connect-4 backend engine, enabling
computer-generated moves and board evaluations.
- Game State Handling: Detects game-over scenarios, including win/draw conditions,
and provides immediate user feedback through popup alerts.
Attributes:
| Name |
Type |
Description |
notify_output |
Output
|
Output widget for notifications and popups.
|
Examples:
Generally, you should this method to retreive and display the widget.
>>> %matplotlib ipympl
>>> c4gui = GuiC4()
>>> display(c4gui.get_widget())
Methods:
Source code in src/bitbully/gui_c4.py
| def __init__(self) -> None:
"""Init the GuiC4 widget."""
# Create a logger with the class name
self.m_logger = logging.getLogger(self.__class__.__name__)
self.m_logger.setLevel(logging.DEBUG) # Set the logging level
# Create a console handler (optional)
ch = logging.StreamHandler()
ch.setLevel(logging.INFO) # Set level for the handler
# Create a formatter and add it to the handler
formatter = logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s")
ch.setFormatter(formatter)
# Add the handler to the logger
self.m_logger.addHandler(ch)
# Avoid adding handlers multiple times
self.m_logger.propagate = False
assets_pth = Path(str(importlib.resources.files("bitbully").joinpath("assets")))
png_empty = plt.imread(assets_pth.joinpath("empty.png"), format=None)
png_empty_m = plt.imread(assets_pth.joinpath("empty_m.png"), format=None)
png_empty_r = plt.imread(assets_pth.joinpath("empty_r.png"), format=None)
png_red = plt.imread(assets_pth.joinpath("red.png"), format=None)
png_red_m = plt.imread(assets_pth.joinpath("red_m.png"), format=None)
png_yellow = plt.imread(assets_pth.joinpath("yellow.png"), format=None)
png_yellow_m = plt.imread(assets_pth.joinpath("yellow_m.png"), format=None)
self.m_png = {
0: {"plain": png_empty, "corner": png_empty_m, "underline": png_empty_r},
1: {"plain": png_yellow, "corner": png_yellow_m},
2: {"plain": png_red, "corner": png_red_m},
}
self.m_n_row, self.m_n_col = 6, 7
# TODO: probably not needed:
self.m_height = np.zeros(7, dtype=np.int32)
self.m_board_size = 3.5
# self.m_player = 1
self.is_busy = False
self.last_event_time = time.time()
# Create board first
self._create_board()
# Generate buttons for inserting the tokens:
self._create_buttons()
# Create control buttons
self._create_control_buttons()
# Capture clicks on the field
_ = self.m_fig.canvas.mpl_connect("button_press_event", self._on_field_click)
# Movelist
self.m_movelist: list[tuple[int, int, int]] = []
# Redo list
self.m_redolist: list[tuple[int, int, int]] = []
# Gameover flag:
self.m_gameover = False
# C4 agent
import bitbully_databases as bbd
# TODO: allow choosing opening book
db_path: str = bbd.BitBullyDatabases.get_database_path("12-ply-dist")
self.bitbully_agent = bitbully_core.BitBullyCore(Path(db_path))
|
bitbully_agent
instance-attribute
is_busy
instance-attribute
last_event_time
instance-attribute
m_board_size
instance-attribute
m_gameover
instance-attribute
m_height
instance-attribute
m_height = zeros(7, dtype=int32)
m_logger
instance-attribute
m_movelist
instance-attribute
m_png
instance-attribute
m_png = {0: {'plain': png_empty, 'corner': png_empty_m, 'underline': png_empty_r}, 1: {'plain': png_yellow, 'corner': png_yellow_m}, 2: {'plain': png_red, 'corner': png_red_m}}
m_redolist
instance-attribute
notify_output
class-attribute
instance-attribute
notify_output: Output = Output()
destroy
Destroy and release the acquired resources.
Source code in src/bitbully/gui_c4.py
| def destroy(self) -> None:
"""Destroy and release the acquired resources."""
plt.close(self.m_fig)
del self.bitbully_agent
del self.m_axs
del self.m_fig
del self.output
|
get_widget() -> AppLayout
Get the widget.
Examples:
Generally, you should this method to retreive and display the widget.
>>> %matplotlib ipympl
>>> c4gui = GuiC4()
>>> display(c4gui.get_widget())
Returns:
| Name | Type |
Description |
AppLayout |
AppLayout
|
|
Source code in src/bitbully/gui_c4.py
| def get_widget(self) -> AppLayout:
"""Get the widget.
Examples:
Generally, you should this method to retreive and display the widget.
```pycon
>>> %matplotlib ipympl
>>> c4gui = GuiC4()
>>> display(c4gui.get_widget())
```
Returns:
AppLayout: the widget.
"""
# Arrange buttons in a row
insert_button_row = HBox(
self.m_insert_buttons,
layout=Layout(
display="flex",
flex_flow="row wrap", # or "column" depending on your layout needs
justify_content="center", # Left alignment
align_items="center", # Top alignment
),
)
control_buttons_col = HBox(
[VBox(list(self.m_control_buttons.values()))],
layout=Layout(
display="flex",
flex_flow="row wrap", # or "column" depending on your layout needs
justify_content="flex-end", # Left alignment
align_items="center", # Top alignment
),
)
tb = self._create_column_labels()
return AppLayout(
header=None,
left_sidebar=control_buttons_col,
center=VBox(
[insert_button_row, self.output, tb],
layout=Layout(
display="flex",
flex_flow="column wrap",
justify_content="flex-start", # Left alignment
align_items="flex-start", # Top alignment
),
),
footer=None,
right_sidebar=None,
)
|