Spielprogrammierung mit Java
HomeAufgabenDruckenJava-Online

Erweitertes Nim-Spiel

Beim erweiterten NimSpiel werden die Zündhölzer in 6 Reihen angeordnet. Jede Reihe enthält zufällig 1 - 15 Zündhölzer. Die Verteilung wird bei jeder Spielwiederholung neu festgelegt. Nach der Aufbau der Bluetooth-Verbindung wird beim Bluetooh-Client die zufällige Zündhölzer-Verteilung erzeugt. Da die beiden Spieler über die gleiche Startsituation verfügen müssen. Der spielberechtigte Spieler nimmt jeweils mit einem Mausklick beliebig viele Zündhölzer aus einer Reihe weg. Dabei werden via Bluetooth jeweils zwei Integer (x-Koordinate und die Reihe des entfernten Zündhölz) zum anderen Computer gesendet und das entprechende Zündholz auch aus seiner Ansicht entfernt. Mit Klick auf OK wird der Zug beendet und als Information der Integer -1 gesendet.
Wer das letzte Zündholz wegnehmen muss, hat verloren.

 
Bluetooth Server
 
Bluetooth Client

Programmcode:

// BtNimExtended.java

import ch.aplu.jgamegrid.*;

import java.awt.Color;
import java.awt.Font;
import java.awt.Point;
import ch.aplu.bluetooth.*;
import javax.swing.JOptionPane;

public class BtNimExtended extends GameGrid
  implements GGMouseListener, GGButtonListener, BtPeerListener, GGExitListener
{
  private int nbMatches = 0;
  private int nbTakenMatches = 0;
  private GGBackground bg;
  private GGButton okBtn = new GGButton("sprites/ok.gif"true);
  private GGButton newBtn = new GGButton("sprites/new.gif"true);
  private BluetoothPeer bp;
  private boolean isMyMove;
  private int row; 
  private int nbRows = 6;
  private int[] a = new int[nbRows + 1]// Number of matches at row
  // Example: a[3] = 12 (3th row has 12 matches)

  public BtNimExtended()
  {
    super(524412false);
    bg = getBg();
    addActor(okBtn, new Location(474));
    okBtn.addButtonListener(this);
    addActor(newBtn, new Location(474));
    newBtn.addButtonListener(this);
    addMouseListener(this, GGMouse.lPress);
    init();
    show();
    connect();
    addExitListener(this);
  }

  private void initGame()
  {
    okBtn.show();
    a[0] = -3;
    for (int k = 0; k < nbRows; k++)
    {
      int nb = (int)(15 * Math.random() + 1);
      a[+ 1] = nb;
      nbMatches += nb;
      for (int i = 0; i < nb; i++)
        addActor(new Match()new Location(2 + 3 * i, 4 + 7 * k));
    }
    if (isMyMove)
      setTitle("Remove any number of matches from the same row, then click 'ok'. It is your to play");
    else
      setTitle("Remove any number of matches from the same row, then click on 'ok'. Wait to play");
  }

  private void init()
  {
    bg.clear();
    okBtn.hide();
    newBtn.hide();
    refresh();
  }

  public boolean mouseEvent(GGMouse mouse)
  {
    if (!isMyMove)
      return true;
    Location loc = toLocationInGrid(mouse.getX(), mouse.getY());
    Actor actor = null;
    // You can also click on the edge of the matches
    for (int y = -3; y < 4; y++)
    {
      actor = getOneActorAt(new Location(loc.x, loc.y + y), Match.class);
      if (actor != null)
        break;
    }
    if (actor != null)
    {
      if (row != 0 && actor.getY() != row)
        setTitle("You have to take matches from the same row");
      else
      {
        row = actor.getY()// y-coordinate of the center of the Match
        actor.removeSelf();
        int[] data = new int[2];
        data[0] = actor.getLocation().x;
        data[1] = row;
        bp.sendDataBlock(data);
        nbMatches--;
        nbTakenMatches++;
        if (nbMatches == 0)
        {
          setTitle("You lost");
          bg.setPaintColor(Color.red);
          bg.setFont(new Font("Arial"Font.BOLD, 90));
          bg.drawText("you lost!"new Point(toPoint(new Location(36))));
          isMyMove = false;
          data = new int[1];
          data[0] = -2;
          bp.sendDataBlock(data);
          okBtn.hide();
          newBtn.show();
        }
        refresh();
      }
    }
    return false;
  }

  public void buttonClicked(GGButton button)
  {
    if (nbMatches == 0)
      init();
    else
    {
      if (nbTakenMatches == 0)
        setTitle("You have to remove at least 1 match");
      else
      {
        isMyMove = false;
        int[] data = new int[1];
        data[0] = -1;
        bp.sendDataBlock(data);
        setTitle(nbMatches + " matches remaining. Wait to play");
        row = 0;
      }
    }
  }

  public void buttonPressed(GGButton button)
  {
  }

  public void buttonReleased(GGButton button)
  {
  }

  private void connect()
  {
    String serverName;
    String prompt = "Enter Bluetooth Name";
    serverName = JOptionPane.showInputDialog(null, prompt, "");
    if (serverName == null)
      System.exit(0);
    serverName = serverName.trim();
    if (serverName.equals(""))
    {
      setTitle("Waiting as Server. Bluetooth Name: " + BluetoothFinder.getLocalBluetoothName());
      bp = new BluetoothPeer(serverName, "nim"thistrue);
    }
    else
    {
      setTitle("Connecting to " + serverName);
      bp = new BluetoothPeer(serverName, "nim"thistrue);
      if (bp.isConnected())
      {
        initGame();
        bp.sendDataBlock(a);
        setTitle("Client connected. Remove any number of matches from the same row.");
        isMyMove = true// Client hat first move
      }
      else
        setTitle("Waiting as server");
    }
  }

  public void notifyConnection(boolean connected)
  {
    System.out.println("Notify " + (connected ? "connected" : "connection lost"));
    if (connected)
    {
      setTitle("Server connected, wait to play");
      isMyMove = false;
    }
  }

  public void receiveDataBlock(int[] data)
  {
    int x = data[0];
    if (== -1)
    {
      isMyMove = true;
      setTitle(nbMatches + " matches. It is your to play");
      nbTakenMatches = 0;
    }
    else if (== -2)
    {
      setTitle("You won");
      bg.setPaintColor(Color.red);
      bg.setFont(new Font("Arial"Font.BOLD, 90));
      bg.drawText("You won!"new Point(toPoint(new Location(36))));
      okBtn.hide();
      newBtn.show();
      setTitle("You won, press 'New Game' to play again");
      isMyMove = true;
      refresh();
    }
    else if (== -3) // initializing game configuration recived from client
    {
      okBtn.show();
      for (int k = 0; k < nbRows; k++)
      {
        int nb = data[+ 1];
        nbMatches += nb;
        for (int i = 0; i < nb; i++)
          addActor(new Match()new Location(2 + 3 * i, 4 + 7 * k));
      }
    }
    else
    {
      int y = data[1];
      Location loc = new Location(x, y);
      getOneActorAt(loc).removeSelf();
      nbMatches--;
      setTitle(nbMatches + " matches. Wait to play");
      refresh();
    }
  }

  public boolean notifyExit()
  {
    bp.releaseConnection();
    return true;
  }

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

// ----------------- class Match ------------------------
class Match extends Actor
{
  public Match()
  {
    super("sprites/match.gif");
  }
}

Erklärungen zum Programmcode:
connect()
sendDataBlock(data)
Der Aufbau der Bluetooth-Verbindung, Senden und Empfangen der Daten wird im Beispiel BtCom.java erklärt
int[] a Die Zahl der Zündhölzer in den einzelnen Reihen wird in einem Array gespreichert.
a[3] = 12 bedeutet, dass in der dritten Reihe 12 Zündhölzer vorhande sind.
Das erste Element des arrays a[0] = -3. Falls das Array mit sendDataBlock(a) verschickt wird, weiss der Empfänger, dass diese Daten zur Initialisierung des Spiels notwendig sind
initGame() Die Methode initGame() dient dazu, ein neues Spiel zu initialisieren (Zündhölzer erzeugen). Das Spiel wird initialisiert erst nach dem die Verbindung aufgebaut ist. Dabei wird das Array a[] an den Spielpartner geschickt, wo die gleiche Spielsituation angezeigt wird
new Location(2 + 3 * i, 4 + 7 * k)) Die y-Koordinate der Zündhölzer muss berechnet werden, da ein Zündholz über 7 vertikalen Zellen positioniert ist

receiveDataBlock(int[] data)

 

Es gibt grundsätzlich drei Möglichkeiten, die man nach der ersten Zahl im gesendeten Array unterscheiden kann:
data[0] ist eine positive Zahl: x Koordinate des entfernten Zündhölzes
data[0] = -2: Anzahl der Zündhölter ist gleich 0, das Spiel ist fertig
data[0] = -3: Es wird ein Array mit der Zündolz-Verteilung geschickt, dient zur Initialisierung eines neuen Spiels