TcpJLib
 
 

Under construction

10 Card Game Starting Player Selection Using an Embedded Server

10.1 Problem definition

Many multi-user card games start by selecting the starting player. A wide-spread practice is the following: The dealer shuffles the card deck and lays down all or a selection of the cards face-down on the game table normally in a fan-shaped arragement. Each player choses a card and turns it face-up. The player getting the card with the highest rank will have the first draw. If several players get cards with the same highest rank, but different suits, these players continue to take cards in the same or a reshuffled deck until the player with the highest card rank is unique.

10.2 Solution

Because the procedure may be repeated until the starting player is unique, the program design and the implementation are not totally trivial. For the reasons given in TcpJLib Tutorial Lesson 5 (see), we use the Embedded Server Pattern and is it assumed that you have learned from lesson 5, how to implement an embedded server.

Again we must make a design decision which state information is known to the clients and which is known to the server. As stated several times in this tutorial, against to common sense, it is better to hold state information in the client applications to avoid excessive exchange of data between the client and the server. This is especially true in this situation because when a player picks a card by double-clicking on one of the card of the presented card deck, the card is moved by a graphical animation to the players hand and the movement is shown in every other players view. So every player application is automatically aware of all selected cards.

The following commands will be used, declared separately in the class CommandA:

// CommandA.java

public interface CommandA
{
  int REPORT_NAMES = 0;
  int DISCONNECT = 1;
  int GAME_STARTING = 2;
  int MY_TURN = 3;
  int OTHER_TURN = 4;
  int READY_FOR_FAN = 5;
  int FAN_DATA = 6;
  int FAN_SELECTED_CARD = 7;
}

The connecting phase is almost the same as class CardPlay in lession 5, with the only difference that at the end of the constructor we ask not for a talon, but for a fan:

agent.sendCommand("", CommandA.READY_FOR_FAN, nbPlayers);

In the dataReceived() callback the REPORT_NAMES DISCONNECT, MY_TURN, OTHER_TURN cases remains the same, but other cases reflects our new problem. FAN_DATA... (to be continued)

case CommandA.FAN_DATA:
  int size = data.length - 1;
  int[] cardNumbers = new int[size];
  System.arraycopy(data, 1, cardNumbers, 0, size);
  cardTable.initFan(cardNumbers);
  nbEngaged = 0;
  nbSelected = 0;
  String in = "In: ";
  String out = "Out: ";
  for (int i = 0; i < nbPlayers; i++)
  {
    if (selectedCardNumbers[i] != -2)  // not out
    {
      selectedCardNumbers[i] = -1;
      nbEngaged++;
      in += playerNames[i] + ", ";
    }  
    else
      out += playerNames[i] + ", ";
   if (selectedCardNumbers[myPlayerId] != -2)
     cardTable.enableFan();
  }
  cardTable.setStatusText(in + "  -   " + out);
  break;

case CommandA.FAN_SELECTED_CARD:
  int playerId = data[1];
  int cardNumber = data[2];
  cardTable.moveSelectedCard(playerId, cardNumber);
  if (selectedCardNumbers[playerId] == -1)  // Not yet selected
  {
    nbSelected++;
    selectedCardNumbers[playerId] = cardNumber;
  }
  if (nbSelected == nbEngaged)
  {  
    bestRank();
    if (cardServer.isConnected())  // Client with running card server
      cardServer.checkStart(selectedCardNumbers);
  }
  break;

case CommandA.GAME_STARTING:
  cardTable.removeFan();
  cardTable.addStartInfo(playerNames[data[1]]);
  break;

asdf

abc

asdf

abc

Execute the program for each player on the same or different computers using WebStart.
Download
the source code.

(to be continued after holidays)