Spielprogrammierung mit Java
HomeAufgabenDruckenJava-Online

Nim Spiel

Das Nim Spiel ist eines der beliebtesten Spiele der Spieltheorie, da sich die Gewinnstrategie relativ einfach herausfinden lässt. Es gibt mehrere verschiedene Varianten und es lässt sich als ein-Personen-Spiel (Benutzer gegen Computer) oder als zwei-Personen-Spiel (2 Benutzer kommunizieren während des Spieles via Bluetooth programmieren. Die zweiteVariante wird unter dem Menüpunkt Bluetooth-Kommunikation erklärt.

Beim der einfachsten Variante sind zu Beginn 15 Zündhölzer da. Der Benutzer entfernt jeweils durch Mausklick auf Zündhölzer 1, 2, oder 3 Zündhölzer, danach kommt der Computer zum Zug. Wer das letzte Hölzchen entfernt, hat verloren.

Programmcode für lokale Bearbeitung downloaden (NimEx1.zip)

Programmcode:

// NimEx1.java

import ch.aplu.jgamegrid.*;
import java.awt.Color;
import java.awt.Font;
import java.awt.Point;
import java.util.ArrayList;

public class NimEx1 extends GameGrid 
  implements GGMouseListener, GGButtonListener
{
  private int nbMatches;
  private int nbTakenMatches;
  private GGBackground bg;
  private GGButton okBtn = new GGButton("sprites/ok.gif"true);
  private GGButton newBtn = new GGButton("sprites/new.gif"true);

  public NimEx1()
  {
    super(56912false);
    bg = getBg();
    addMouseListener(this, GGMouse.lClick);
    addActor(okBtn, new Location(504));
    okBtn.addButtonListener(this);
    addActor(newBtn, new Location(504));
    newBtn.addButtonListener(this);
    init();
    show();
  }

  public void init()
  {
    nbMatches = 15;
    nbTakenMatches = 0;
    bg.clear();
    for (int i = 0; i < 15; i++)
      addActor(new Match()new Location(2 + 3 * i, 4));
    okBtn.show();
    newBtn.hide();
    refresh();
    setTitle(nbMatches + " matches. Click on 1, 2 or 3 matches to remove, then click 'OK'.");
  }

  public boolean mouseEvent(GGMouse mouse)
  {
    Location loc = toLocationInGrid(mouse.getX(), mouse.getY());
    Actor actor = null;
    for (int y = 2; y < 7; y++)
    {
      actor = getOneActorAt(new Location(loc.x, y), Match.class);
      if (actor != null)
        break;
    }
    if (actor != null)
    {
      if (nbTakenMatches == 3)
        setTitle("Take a maximum of 3. Click 'OK' to continue!");
      else
      {
        actor.removeSelf();
        nbMatches--;
        setTitle(nbMatches + " matches remaining. Click 'OK' to continue.");
        nbTakenMatches++;
        if (nbMatches == 0)
        {
          setTitle("Press 'new Game' to play again.");
          bg.setPaintColor(Color.red);
          bg.setFont(new Font("Arial"Font.BOLD, 90));
          bg.drawText("You lost!"new Point(toPoint(new Location(36))));
          okBtn.hide();
          newBtn.show();
        }
        refresh();
      }
    }
    return false;
  }

  public void computerMove()
  {
    int nbRemovedMatches = nbMatches - nbWin(nbMatches);
    if (nbRemovedMatches == 0)
      nbRemovedMatches = 1// optimal strategy impossible
    nbMatches = nbMatches - nbRemovedMatches;
    for (int x = 0; x < nbRemovedMatches; x++)
    {
      ArrayList<Actor> matches = getActors(Match.class);
      int k = (int)(Math.random() * matches.size());
      removeActor(matches.get(k))// take random match
    }
    setTitle(nbMatches + " matches remaining. Your move now.");

    if (nbMatches == 0)
    {
      bg.setPaintColor(Color.red);
      bg.setFont(new Font("Arial"Font.BOLD, 96));
      bg.drawText("You win!"new Point(toPoint(new Location(36))));
      okBtn.hide();
      newBtn.show();
      setTitle("You win, press 'New Game' to play again.");
    }
    refresh();
  }

  // returns next lower winning number
  private int nbWin(int n)
  {
    return ((- 1) / 4) * 4 + 1;
  }

  public void buttonClicked(GGButton button)
  {
    if (nbMatches == 0)
        init();
      else
      {
        if (nbTakenMatches == 0)
          setTitle("You have to remove at least 1 match!");
        else
        {
          nbTakenMatches = 0;
          computerMove();
        }
      }
  }

  public void buttonPressed(GGButton button)
  {
  }

  public void buttonReleased(GGButton button)
  {
  }

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

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

Erklärungen zum Programmcode:

init() Die Methode init() dient dazu, ein neues Spiel zu initialisieren (Zündhölzer und OK-Button zu erzeugen). Sie wird zu Beginn und bei der Spielwiederholung aufgerufen. Da die Zündhölzer lang und schmal sind, wird die Zellengrösse klein gewählt und das Zündholz wird vertikal in mehreren Zellen positioniert,
setTitle() Die Ergebnisse werden im Titel ausgegeben
for (int y = 2; y < 7; y++)
{
     actor = getOneActorAt(new Location(loc.x, y), Match.class);
   
 }
Bei der Erfassung des Mausklicks, muss man mehrere Felder, mit der gleichen x-Koordinate überprüfen
private int nbWin(int n)
 {
    return ((n - 1) / 4) * 4 + 1;
 }
Berechnet zur verbleibenden Anzahl Zündhölzer die nächste tiefere Gewinnzahl. Der Computer setzt danach die Gewinnstrategie ein und nimmt die optimale Anzahl Zündhölzer weg. Falls der Spieler bereits die Gewinnstrategie entdeckt hat, entfernt der Computer beim nächsten Zug nur ein Zündholz.
implements GGButtonListener
Impementiert einen ButtonListener mit dem Callbackmethoden buttonClicked() buttonPressed() und buttonReleased()
GGButton okBtn = new GGButton("sprites/ok.gif", true);
okBtn.addButtonListener(this);
Erzeugt einen Ok-Button und registriert den GGButtonListener. Der Parameter true ermöglicht drei verschiedene Ansichten des Button (normal, over, click). Die Sprite-Bilder ok_0.gif, ok_1.git, ok_2.gif müssen sich im Ordner sprites befinden
okBtn.hide()
 newBtn.show()
Die beiden Buttons okBtn und newBtn haben die gleiche Grösse und die gleiche Position. Zu Beginn des Spieles ist der okBtn sichtbar und dient zum Abschliessen eines Zuges und zur Übergabe des Spiels an den Computer. Wenn das Spiel fertig ist (nbMaches = 0), wird der okBtn versteckt und an seiner Stelle der newBtn angezeigt, mit dem ein neues Spiel gestartet werden kann.

 

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.

Spielregeln:
Der Spieler kann eine beliebige Anzahl Zündhölzer aus einer Reihe entfernen. Mit Klick auf ok wird der Zug beendet, dann kommt der Computer zum Zug.

Diesmal gewinnt derjenige, der das letzte Zündholz entfernen kann.

Finden Sie auch hier die Gewinnstrategie?

 

Programmcode für lokale Bearbeitung downloaden
(NimExtended.zip)