Spielprogrammierung mit Java
HomeAufgabenDruckenJava-Online

TCP Memory (Game-Server verwenden)



 


Die Tcp Memory Applikation ermöglicht ein Memory-Spiel über das Internet zu spielen. Jede Spielgruppe, bestehend aus einem TcpMemoryServer und zwei TcpMemoryPlayers muss eine eindeutige Session ID haben. Diese wird beim Starten des MemoryServers festgelegt. So können beliebig viele Spielgruppen (mit verschiedenen Session ID's) gleichzeitig spielen. Die Verwaltung der Session ID's übernimmt unser TcpRelay Server, der ebenfalls dafür sorgt, dass alle Daten über den Port 80 versendet und empfangen und somit nicht von Firewalls angehalten werden. Der Memory Server - eine Java-Applikation, die auf einen der beiden Benutzerrechnern gestartet werden kann, regelt die Spielreihenfolge, den Zugriff auf die Karten und zeigt die Spielergebnisse an.


Um das Programm zu testen gehen Sie wie folgt vor:

1) Memory Server starten
Einer der beiden Spieler muss vor dem Spielbeginn den TcpMemoryServer starten, d.h. die Applikation TcpMemoryServer.java ausführen. Nach der Eingabe der Session ID (eine beliebige Zahlen oder Buchstabengruppe) wird die Verbindung zum TcpRelay hergestellt.

Memory Server starten (TcpMemory.java)

 


2) Player starten
Die beiden Spieler starten das Spiel mit dem unten stehenden Link:

Memory Plaer starten (TcpMemory.java)

In der ersten Dialogbox muss die gleiche SessionID, wie beim Memory Server eingegeben werden.

 

 

In der zweiten Dialogbox gibt jeder Spieler sein Namen ein. Sobald die TCP/IP-Verbindung aufgebaut wurde, kann das Spiel beginnen.

 

Zum Testn kann die Applikation auch zweimal auf dem gleichen Computer gestartet werden.

 

 

4. Spielregeln

Das Spiel besteht aus 8 Kartenpaare. Die Rückseiten der Karten sind grau. Der aktive Spieler kehrt jeweils mit einem Mausklick zwei Karten um. Wenn sie gleich sind, erhält er 1 Punkt und kann noch einmal spielen, sonst kommt der zweite Spieler zu Zug. Wer am Schluss mehr Punkte hat, gewinnt.


TcpMemoryServer im Online-Editor bearbeiten

TcpMemoryPlayer im Online-Editor bearbeiten

 

Programmcode für lokale Bearbeitung downloaden: TcpMemory.zip

Erklärungen zum Programmcode finden Sie unter www.aplu.ch/TcpJLib


Programmcode Memory Server:
// TcpMemoryServer.java

import ch.aplu.tcp.*;
import ch.aplu.util.*;
import java.util.*;

public class TcpMemoryServer extends TcpBridge implements TcpBridgeListener
{
  private static String sessionID;
  private final static String myName = "MemoryServer";
  private Vector<String> players = new Vector<String>();
  private int nbCells = 16;  // Must be even
  private int[] state = new int[nbCells];
  private static Console = new Console();

  static
  {
    c.print("Enter a unique session ID: ");
    sessionID = c.readLine();
  }

  // Protocol tags
  private interface Command
  {
    int STATUS = 0;
    int INIT = 1;
    int SHOW = 2;
    int HIDE = 3;
    int SCORE = 4;
    int NAME_IN_USE = 5;
    int TOO_MANY_PLAYERS = 6;
    int PLAYER_NAMES = 7;
    int WAIT_REMOTE_CONNECT = 8;
    int REMOTE_MOVE = 9;
    int LOCAL_MOVE = 10;
    int END_OF_GAME = 11;
  }

  public TcpMemoryServer()
  {
    super(sessionID, myName);
    addTcpBridgeListener(this);
    c.println("Connecting to relay");
    ArrayList<String> connectList = connectToRelay(6000);
    if (connectList.isEmpty())
      errorQuit("Connection failed");
    if (!connectList.get(0).equals(myName))
      errorQuit("Only one instance of server allowed");
    c.println("Connection established. Service enabled.");
  }

  private void errorQuit(String msg)
  {
    c.println(msg);
    System.exit(1);
  }

  private void dealingOut()
  {
    ArrayList<Integer> numbers = new ArrayList<Integer>();
    for (int = 0; i < nbCells; i++)
      numbers.add(i);
    // Now take out one by one at arbitrary location and put it into state
    int = 0;
    while (!numbers.isEmpty())
    {
      int index = (int)(Math.random() * numbers.size());
      state[k] = numbers.get(index);
      numbers.remove(index);
      k++;
    }
  }

  public void notifyRelayConnection(boolean connected)
  {
  }

  public void notifyAgentConnection(String agentName, boolean connected)
  {
    if (connected)
    {
      c.println("Agent connecting. Name: " + agentName);
      if (players.size() < 2)
        players.add(agentName);
      else
      {
        c.println("Reporting 'Too many players'");
        send(myName, agentName, Command.TOO_MANY_PLAYERS);
        return;
      }
    }
    else // disconnect
    {
      c.println("Remove player. Name: " + agentName);
      if (!players.contains(agentName))
        return;
      else
        players.remove(agentName);
    }
    if (players.isEmpty())
      c.println("No players in game.");
    if (players.size() == 1)
    {
      c.println("One player in game. Name: " + players.get(0));
      send(myName, players.get(0), Command.WAIT_REMOTE_CONNECT);
    }

    if (players.size() == 2)
    {
      // Game may start now...
      c.println("Second player arrived. Name: " + agentName);

      // We transmit names of players
      String player0 = players.get(0);
      String player1 = players.get(1);
      c.println("Transmitting names of players:");
      c.println("First player: " + player0);
      c.println("Second player: " + player1);

      String text = player0 + "-->" + player1;
      sendCommand(myName, player0, Command.PLAYER_NAMES,
        TcpTools.stringToIntAry(text));

      text = player1 + "-->" + player0;
      sendCommand(myName, player1, Command.PLAYER_NAMES,
        TcpTools.stringToIntAry(text));

      // We transmit who begins
      send(myName, player1, Command.LOCAL_MOVE); // Second player begins
      send(myName, player0, Command.REMOTE_MOVE);

      // Now we transmit new game state
      c.println("Dealing out now.");
      dealingOut();
      sendGameState();
    }
  }

  private void sendGameState()
  {
    int[] data = new int[17];
    data[0] = Command.INIT;
    for (int = 0; i < 16; i++)
      data[i + 1] = state[i];
    send(myName, players.get(0), data);
    send(myName, players.get(1), data);
  }

  public void pipeRequest(String source, String destination, int[] indata)
  {
    if (indata[0] == Command.END_OF_GAME)
    {
      int winnerIndex = players.indexOf(source);
      int loserIndex = (winnerIndex + 1) % 2;
      String winner = source;
      String loser = players.get(loserIndex);
      c.println("Winner: " + source + "; loser: " + loser);
      // Next game will start in 10 s
      TcpTools.delay(10000);
      dealingOut();
      sendGameState();
      send(myName, loser, Command.LOCAL_MOVE); // loser begins
      send(myName, winner, Command.REMOTE_MOVE);
      return;
    }

    // destination is empty, we decide that the data goes to the partner
    String target = "";
    if (players.get(0).equals(source))
      target = players.get(1);
    if (players.get(1).equals(source))
      target = players.get(0);
    send(source, target, indata);
    reportPipeAction(source, target, indata);
  }

  private void reportPipeAction(String source, String destination, int[] data)
  {
    c.println("pipe: " + source + "-->" + destination);
    c.print("data: [");
    for (int = 0; i < data.length; i++)
    {
      c.print(data[i]);
      if (i < data.length - 1)
        c.print(", ");
    }
    c.println("]");
  }

  public static void main(String[] args)
  {
    new TcpMemoryServer();
  }
}

Programmcode Memory Player:
// TcpMemory.java

import javax.swing.JOptionPane;
import ch.aplu.jgamegrid.*;
import ch.aplu.tcp.*;
import java.awt.Color;
import ch.aplu.util.*;

public class TcpMemory extends GameGrid implements GGMouseListener
{
  private final String VERSION = "1.1";
  private final String serverName = "MemoryServer";
  private String sessionID;
  private TcpAgent tcpAgent;
  private MemoryCard firstCard;
  private MemoryCard secondCard;
  private boolean isFirstMove;
  private int myScore;
  private int rivalScore;
  private String playerNames;
  private static final int horzCells = 4;
  private static final int vertCells = 4;
  private static final int nbCells = horzCells * vertCells; // Must be even
  private MemoryCard[] cards = new MemoryCard[nbCells];

  // Protocol tags
  private interface Command
  {
    int STATUS = 0;
    int INIT = 1;
    int SHOW = 2;
    int HIDE = 3;
    int SCORE = 4;
    int NAME_IN_USE = 5;
    int TOO_MANY_PLAYERS = 6;
    int PLAYER_NAMES = 7;
    int WAIT_REMOTE_CONNECT = 8;
    int REMOTE_MOVE = 9;
    int LOCAL_MOVE = 10;
    int END_OF_GAME = 11;
  }

  public TcpMemory()
  {
    super(horzCells, vertCells, 115, Color.red, nullfalse);
    setTitle("Memory Game (V" + VERSION + ")");
    setBgColor(Color.white);
    for (int = 0; i < nbCells; i++)
    {
      if (i < nbCells / 2)
        cards[i] = new MemoryCard(i);
      else
        cards[i] = new MemoryCard(- nbCells / 2);
    }
    addMouseListener(thisGGMouse.lPress);
    setMouseEnabled(false)// Will be enabled when we get the move
    show();
    connect();

    // Main thread is responsible to turn back cards on a bad move
    while (true)
    {
      Monitor.putSleep();  // Wait until there is something to do
      delay(2000); // Sleep awhile
      firstCard.show(1);
      secondCard.show(1);
      refresh();
      tcpAgent.send(""Command.HIDE,
        firstCard.getLocation().x, firstCard.getLocation().y);
      tcpAgent.send(""Command.HIDE,
        secondCard.getLocation().x, secondCard.getLocation().y);
      tcpAgent.send(""Command.LOCAL_MOVE);
    }
  }

  public boolean mouseEvent(GGMouse mouse)
  {
    Location location = toLocation(mouse.getX(), mouse.getY());
    MemoryCard card = (MemoryCard)getOneActorAt(location);
    if (card.getIdVisible() == 0) // Card already flipped
      return true;
    card.show(0); // Show picture
    refresh();
    // Report to rival
    tcpAgent.send(""Command.SHOW, location.x, location.y);

    if (isFirstMove)
    {
      isFirstMove = false;
      firstCard = card;
    }
    else
    {
      isFirstMove = true;
      secondCard = card;
      if (firstCard.getId() == secondCard.getId()) // Successful move
      {
        myScore++;
        tcpAgent.send(""Command.SCORE, myScore);
        setTitle(playerNames + ". Score: " + myScore + "/" + rivalScore);
        if (isGameOver())
        {
          setMouseEnabled(false);
          tcpAgent.send(""Command.END_OF_GAME);
          showFinalScore();
          return true;
        }
      }
      else // Bad move
      {
        setMouseEnabled(false)// Disable mouse events until remote
        // gives move back
        setTitle(playerNames + ". Wait for remote player's move...");
        Monitor.wakeUp();
      }
    }
    return true;
  }

  private boolean isGameOver()
  {
    for (int = 0; i < nbCells; i++)
    {
      if (cards[i].getIdVisible() == 1)
        return false;
    }
    return true;
  }

  private String requestEntry(String prompt)
  {
    String entry = "";
    while (entry.length() < 3)
    {
      entry = JOptionPane.showInputDialog(nullprompt, "");
      if (entry == null)
        System.exit(0);
    }
    return entry.trim();
  }

  private void connect()
  {
    String sessionID =
      requestEntry("Enter the session ID (ASCII >3 chars):");
    String localName =
      requestEntry("Enter your name (ASCII >3 chars):");
    tcpAgent = new TcpAgent(sessionID, serverName);
    tcpAgent.addTcpAgentListener(new TcpAgentAdapter()
    {
      public void dataReceived(String source, int[] data)
      {
        switch (data[0])
        {
          case Command.TOO_MANY_PLAYERS:
            setTitle("Connected. But too many players.");
            break;

          case Command.WAIT_REMOTE_CONNECT:
            clear();
            setTitle("Wait until remote player connects...");
            break;

          case Command.PLAYER_NAMES:
            playerNames = TcpTools.intAryToString(data, 1);
            break;

          case Command.REMOTE_MOVE:
            setTitle(playerNames + ". Wait for remote player's move...");
            break;

          case Command.LOCAL_MOVE:
            isFirstMove = true;
            setTitle(playerNames + ". Please click on two cards!");
            setMouseEnabled(true);
            break;

          case Command.INIT:
            init(data);
            break;

          case Command.SHOW:
            showCard(data[1], data[2], true);
            break;

          case Command.HIDE:
            showCard(data[1], data[2], false);
            break;

          case Command.SCORE:
            rivalScore = data[1];
            if (isGameOver())
              showFinalScore();
            else
              setTitle(playerNames + ". Score: " + myScore + "/" + rivalScore);
            break;
        }
      }
    });
    setTitle("Connecting to relay...");
    if (tcpAgent.connectToRelay(localName, 6000).isEmpty())
    {
      setTitle("Connection to relay failed");
      return;
    }
    setTitle("Connection to relay established");
    if (!tcpAgent.isBridgeConnected())
    {
      setTitle("Game server not found.");
      return;
    }
  }

  private void showFinalScore()
  {
    String s = "";
    if (myScore > rivalScore)
      s = " (Winner)";
    if (myScore < rivalScore)
      s = " (Loser)";
    if (myScore == rivalScore)
      s = " (Tie)";
    setTitle(playerNames
      + ". Game over. Score: " + myScore + "/" + rivalScore + s
      + ". Wait...");
  }

  private void showCard(int x, int y, boolean show)
  {
    if (show)
      getOneActorAt(new Location(x, y)).show(0);
    else
      getOneActorAt(new Location(x, y)).show(1);
    refresh();
  }

  private void clear()
  {
    removeAllActors();
    refresh();
  }

  private void init(int[] data)
  {
    myScore = 0;
    rivalScore = 0;
    isFirstMove = true;
    removeAllActors();
    int k = 1;
    for (int i = 0; i < nbCells; i++)
      cards[i].show(1);  // show back
    for (int x = 0; x < nbHorzCells; x++)
      for (int y = 0; y < nbVertCells; y++)
        addActor(cards[data[k++]], new Location(x, y));
  }

  public static void main(String[] args)
  {
    new TcpMemory();
  }
}

// -------------------- class MemoryCard--------------------------
class MemoryCard extends Actor
{
  private int id;

  public MemoryCard(int id)
  {
    super("sprites/card" + id + ".gif""sprites/cardback.gif");
    this.id = id;
  }

  public int getId()
  {
    return id;
  }
}