Next Cycle

Previous Cycle
Back to Exercise Overview

Cycle 4: Manage user's turn

One of the server's tasks is to supervise the correct order of the player's moves. In almost all card games only one of the players can make the next draw and the other players have to wait. It is very important that all user actions of the waiting players (except quitting the game) are disabled until the new game state is completely established. To handle this situation it is appropriate to introduce a state flag isMyTurn that indicates whether the player has the next turn or not. Every player gets the information from the server if he/she can make the next draw or if he/she has to wait. To exchange this information we add two more commands to the command list in Command.java:

int MY_TURN = 7;
int OTHER_TURN = 8;

In the class CardTable we define a boolean instance variable

private boolean isMyTurn = false;

and initialize it to false. Whenever the player makes his/her move, the callback leftDoubleClick() is invoked. To inhibit any further actions we immediately set the flag to false:

public void leftDoubleClicked(Card card)
{
  if (!isMyTurn)
    return;
  isMyTurn = false;
  ...
}

In order to set the game table to the desired state we define two methods. We want also to improve the user interface by displaying state information in the status bar.

protected void setMyTurn()
{
  setStatusText("It's your turn." +
                " Double-click on one of the cards to get it.");
  isMyTurn = true;
}

protected void setOtherTurn()
{
  setStatusText("Wait for you turn.");
}

It is the server's responsability to determine which player has the next move by sending a MY_TURN command to one of the players and a OTHER_TURN to the rest. Before doing so, all players have to be in a stable state. They inform the server about this by sending a READY_TO_PLAY command. The server counts these messages and waits for further actions until the count reaches the number of players. To do so, we modify the READY_TO_PLAY case in the pipeRequest() callback by using a integer instance variable nbReady:

case Command.READY_TO_PLAY:
  System.out.println("Got READY_TO_PLAY from " + source);
  nbReady++;
  if (nbReady == nbPlayers)
  {
    nbReady = 0;
    giveTurn();
  }
  break;

The method giveTurn() will be called when all players are ready.

private void giveTurn()
{
  currentPlayerId += 1;
  currentPlayerId %= nbPlayers;

  for (int i = 0; i < nbPlayers; i++)
  {
    if (i != currentPlayerId)
    {
      sendCommand("", playerList.get(i),
        Command.OTHER_TURN);
      System.out.println("Sent OTHER_TURN to " + playerList.get(i));
    }
  }
  sendCommand("", playerList.get(currentPlayerId), Command.MY_TURN);
  System.out.println("Sent MY_TURN to " + playerList.get(currentPlayerId));
}

The integer instance variable currentPlayerId indicates which player has the next move. It is run through circularly. At the program start currentPlayerId is initialized to nbPlayers - 1, so the next player will have playerId = 0 (the first player that entered the game room):

private int currentPlayerId = nbPlayers - 1;

Now to your job. Find out, where exactly each player needs to send the READY_TO_PLAY. Two hints that may help you:

  1. The current player is ready after all card pairs arrived in the stock
  2. The waiting players will transfer the card pairs card by card and they cannot know a priori when the transfer is finished. Thus the current player must send this additional information to the waiting players. There are different solutions to this problem. In order to avoid a new command, we decide that the current player sends a card transfer command with a cardId = -1 to indicate the end of the pair transfer. The waiting player must check the cardId and transfer the card to his/her stock as long as the cardId is not -1. When -1 arrives, the player sends the READY_TO_PLAY.

Finally you may add some more status information, e.g. "Dealing out" during the deal out process.

Execute the solution you should obtain.

Next Cycle
Previous Cycle
Back to Exercise Overview