Using Statics in Wisej.NET: Tic-Tac-Toe

In Wisej.NET, it’s simple to keep multiple browser sessions synchronized if you use static events. Let’s look at a sample that does this. You can find the project on GitHub here: https://github.com/LRufenacht/wisej-tic-tac-toe-statics

The sample shows a Tic-Tac-Toe game. You can click the boxes to place X and O, and there is a button to reset the game. To test the game in multiple browser sessions, simply open a second browser tab and navigate to the game.

There are three key features of the sample that we’ll dive into in this post:

  • Single server-side board state updates all clients in real time

  • Turn tracking and win detection fire static events across tabs

  • Reset restarts the match for everyone with one click

Single server-side board state updates all clients in real time

Let’s look at TicTacToeGame.cs. Notice the public static variables:

public static char[,] Board { get; private set; } = new char[3, 3];
public static char CurrentPlayer { get; private set; } = 'X';
public static bool GameOver { get; private set; }
public static string Message { get; private set; } = "Player X's turn";

In Wisej.NET, static variables are shared across all sessions. This means that if you start a new session—perhaps by opening a new browser tab—you will have access to all of the static variables, and they will have consistent values.

Where are these values used?

Let’s look at Page1.cs.

Board

The first static variable in the list is Board, which stores the X and O characters in the game board. It is used in RenderBoard() in Page1.cs.

CurrentPlayer

The next static variable is CurrentPlayer, which stores an X or O depending on whose turn it is.

CurrentPlayer is used in the MakeMove() and Reset() functions in TicTacToeGame.cs.

GameOver

The next static variable is GameOver, a boolean that is true if the game is over and false otherwise.

GameOver is used in Page1.cs in TicTacToeGame_MoveMade. If the game is over, the board is disabled so no more moves can be made.

GameOver is also used in the MakeMove() and Reset() functions in TicTacToeGame.cs, ensuring it is set correctly when the game ends (either in a draw or a win).

Message

Message is the text displayed on the screen. It either indicates whose turn it is, or shows that the game has been won or ended in a draw.

In TicTacToeGame.cs, Message is set in the MakeMove() and Reset() functions.

Message is used in Page1.cs to update the UI text in:

  • RenderBoard
  • TicTacToeGame_MoveMade
  • TicTacToeGame_GameReset

Turn tracking and win detection fire static events across tabs

We can see several static events in TicTacToeGame.cs:

public static event EventHandler<MoveEventArgs> MoveMade;
public static event EventHandler GameReset;
public static event EventHandler<string> GameEnded;

These events have event handlers attached in Page1.cs:

private void Page1_Load(object sender, EventArgs e)
{
    CreateBoard();

    TicTacToeGame.MoveMade += TicTacToeGame_MoveMade;
    TicTacToeGame.GameReset += TicTacToeGame_GameReset;
    TicTacToeGame.GameEnded += TicTacToeGame_GameEnded;

    RenderBoard();
}

These events are fired in the MakeMove() and Reset() functions in TicTacToeGame.cs. For example:

MoveMade?.Invoke(null, new MoveEventArgs(row, col, CurrentPlayer));

Reset the match for everyone with one click

As shown earlier, GameReset is a static event created in TicTacToeGame.cs. In Page1.cs, an event handler is attached to it.

In Page1.cs, this code runs when the reset button is clicked:

private void buttonReset_Click(object sender, EventArgs e)
{
    TicTacToeGame.Reset();
}

In TicTacToeGame.cs, this is the Reset() function. Note that GameReset is invoked.

public static void Reset()
{
    Board = new char[3, 3];
    CurrentPlayer = 'X';
    GameOver = false;
    Message = "Player X's turn";
    GameReset?.Invoke(null, EventArgs.Empty);
}

What happens when a static event is fired?

Let’s take the example of the static event GameReset and walk through what happens when it fires.

A player clicks the reset button. This causes the event GameReset to be fired:

GameReset?.Invoke(null, EventArgs.Empty);

Because it is a static event, every session receives the event.

TicTacToeGame_GameReset() is attached to the GameReset event, so that handler runs in every session. However, there is a problem: the event is out of context—it is still connected to the sender’s session.

We fix this in TicTacToeGame_GameReset() by using Application.Update:

Application.Update(this, () =>
{
    // code to reset the UI of the game
});

Here, this represents the context of the current browser session. By passing this, we tell the application to run the update in the context of the current session.

Next, the code inside Application.Update runs, which updates the UI. Like all UI updates in Wisej.NET, this sends a WebSocket message to the browser with the relevant UI changes.