.. Comment: this file is automatically generated by `update_example_docs.py`. It should not be modified manually. Games ========================================== Some two-player games implemented using scene click events. .. code-block:: python :linenos: import time from typing import Literal import numpy as np import trimesh.creation from typing_extensions import assert_never import viser import viser.transforms as tf def main() -> None: server = viser.ViserServer() server.gui.configure_theme(dark_mode=True) play_connect_4(server) server.gui.add_button("Tic-Tac-Toe").on_click(lambda _: play_tic_tac_toe(server)) server.gui.add_button("Connect 4").on_click(lambda _: play_connect_4(server)) while True: time.sleep(10.0) def play_connect_4(server: viser.ViserServer) -> None: """Play a game of Connect 4.""" server.scene.reset() num_rows = 6 num_cols = 7 whose_turn: Literal["red", "yellow"] = "red" pieces_in_col = [0] * num_cols # Create the board frame. for col in range(num_cols): for row in range(num_rows): server.scene.add_mesh_trimesh( f"/structure/{row}_{col}", trimesh.creation.annulus(0.45, 0.55, 0.125), position=(0.0, col, row), wxyz=tf.SO3.from_y_radians(np.pi / 2.0).wxyz, ) # Create a sphere to click on for each column. def setup_column(col: int) -> None: sphere = server.scene.add_icosphere( f"/spheres/{col}", radius=0.25, position=(0, col, num_rows - 0.25), color=(255, 255, 255), ) # Drop piece into the column. @sphere.on_click def _(_) -> None: nonlocal whose_turn whose_turn = "red" if whose_turn != "red" else "yellow" row = pieces_in_col[col] if row == num_rows - 1: sphere.remove() pieces_in_col[col] += 1 cylinder = trimesh.creation.cylinder(radius=0.4, height=0.125) piece = server.scene.add_mesh_simple( f"/game_pieces/{row}_{col}", cylinder.vertices, cylinder.faces, wxyz=tf.SO3.from_y_radians(np.pi / 2.0).wxyz, color={"red": (255, 0, 0), "yellow": (255, 255, 0)}[whose_turn], ) for row_anim in np.linspace(num_rows - 1, row, num_rows - row + 1): piece.position = ( 0, col, row_anim, ) time.sleep(1.0 / 30.0) for col in range(num_cols): setup_column(col) def play_tic_tac_toe(server: viser.ViserServer) -> None: """Play a game of tic-tac-toe.""" server.scene.reset() whose_turn: Literal["x", "o"] = "x" for i in range(4): server.scene.add_spline_catmull_rom( f"/gridlines/{i}", ((-0.5, -1.5, 0), (-0.5, 1.5, 0)), color=(127, 127, 127), position=(1, 1, 0), wxyz=tf.SO3.from_z_radians(np.pi / 2 * i).wxyz, ) def draw_symbol(symbol: Literal["x", "o"], i: int, j: int) -> None: """Draw an X or O in the given cell.""" for scale in np.linspace(0.01, 1.0, 5): if symbol == "x": for k in range(2): server.scene.add_box( f"/symbols/{i}_{j}/{k}", dimensions=(0.7 * scale, 0.125 * scale, 0.125), position=(i, j, 0), color=(0, 0, 255), wxyz=tf.SO3.from_z_radians(np.pi / 2.0 * k + np.pi / 4.0).wxyz, ) elif symbol == "o": mesh = trimesh.creation.annulus(0.25 * scale, 0.35 * scale, 0.125) server.scene.add_mesh_simple( f"/symbols/{i}_{j}", mesh.vertices, mesh.faces, position=(i, j, 0), color=(255, 0, 0), ) else: assert_never(symbol) server.flush() time.sleep(1.0 / 30.0) def setup_cell(i: int, j: int) -> None: """Create a clickable sphere in a given cell.""" sphere = server.scene.add_icosphere( f"/spheres/{i}_{j}", radius=0.25, position=(i, j, 0), color=(255, 255, 255), ) @sphere.on_click def _(_) -> None: nonlocal whose_turn whose_turn = "x" if whose_turn != "x" else "o" sphere.remove() draw_symbol(whose_turn, i, j) for i in range(3): for j in range(3): setup_cell(i, j) if __name__ == "__main__": main()