Spielprogrammierung mit Java
HomeAufgabenDruckenJava-Online

BoxGame

 

Auch bekannt als Käsekästchen oder Dots and Boxes im Englischen. Zwei Spieler können abwechselnd jeweils eine Kante anfärben. Wird eine Box geschlossen, gehört sie dem Spieler, der die letzte Kante gelegt hat. Dieser färbt die Box mit seiner Farbe aus und darf dann noch einmal spielen. Ziel ist es, möglichst viele Boxes mit der eigenen Farbe auszufärben.

 

Programmcode für lokale Bearbeitung Downloaden: BoxGame.zip

 

Programmcode:

// BoxGame

import ch.aplu.jgamegrid.*;
import java.awt.Color;
import java.awt.Point;
import java.util.Hashtable;
import java.util.LinkedList;

public class BoxGame extends GameGrid implements GGMouseTouchListener
{
  private static final boolean customizableGrid = false;
  Hashtable<LocationLinkedList<Stroke>> BoxMap = new Hashtable<LocationLinkedList<Stroke>>();
  private Player currentPlayer;
  private Player[] players = new Player[2];
  private static int playerCounter = 0;

  public BoxGame(int height, int width)
  {
    super(width + 2, height + 2, 75, Color.WHITE, false);
    getBg().clear(Color.WHITE);
    players[0] = new Player(Color.BLUE, "Blue");
    players[1] = new Player(Color.RED, "Red");
    currentPlayer = players[0]; //blue begins;
    for (int = 1; x < getNbHorzCells(); x++)
    {
      for (int = 1; y < getNbVertCells(); y++)
      {
        Location loc = new Location(x, y);
        BoxMap.put(loc, new LinkedList<Stroke>());
        for (StrokeDirection d : StrokeDirection.values())
        {
          //prevent loop from drawing unnecessary strokes
          if (y == getNbVertCells() - && == StrokeDirection.VERTICAL
            || == getNbHorzCells() - && == StrokeDirection.HORIZONTAL)
            continue;
          Stroke = new Stroke(this, d);
          addActorNoRefresh(s, new Location(x, y));
          s.addMouseTouchListener(thisGGMouse.lClick | GGMouse.enter | GGMouse.leave);
          for (Location l : s.getPossibleFillLocations())
            BoxMap.get(l).add(s);
        }
      }
    }
    addStatusBar(20);
    setStatusText("Click on an edge to start");
    setTitle("The box game -- www.java-online.ch");
    show();
  }

  public static void main(String[] args)
  {
    int height = 3;
    int width = 3;
    if (customizableGrid)
    {
      height = new GGInputInt("Height""Choose the height of the grid.").show();
      width = new GGInputInt("Width""Choose the width of the grid.").show();
    }

    new BoxGame(height, width);
  }

  public void mouseTouched(Actor actor, GGMouse mouse, Point spot)
  {
    Stroke = (Stroke) actor;
    if (s.isDrawn())
      return;
    switch (mouse.getEvent())
    {
      case GGMouse.enter:
        s.show(1 + currentPlayer.id); //important, that not s.draw is called!
        break;
      case GGMouse.leave:
        s.show(0);
        break;
      case GGMouse.lClick:
        s.draw(currentPlayer.id);
        boolean nextPlayer = true;
        for (Location loc : s.getPossibleFillLocations())
        {
          if (players[currentPlayer.id].tryToFillBoxes(loc))
            nextPlayer = false;
        }
        if (nextPlayer)
          currentPlayer = currentPlayer.nextPlayer();
        updateStatusText();
        break;
    }
    refresh();
  }

  private void updateStatusText()
  {
    String msg = players[0].getLabelledScore() + " vs " + players[1].getLabelledScore();
    if (Stroke.allDrawn())
      msg = "Final Score -- " + msg;
    else
      msg = msg + " -- current Player is " + currentPlayer;
    setStatusText(msg);
  }

  private boolean outOfValidGrid(Location loc)
  {
    return loc.y >= getNbVertCells() - || loc.x >= getNbHorzCells() - 1
      || loc.y < || loc.x < 1;
  }

  class Player
  {
    private int id;
    private Color color;
    private int score;
    private String name;

    public Player(Color color, String name)
    {
      this.name = name;
      this.id = playerCounter++;
      this.color = color;
      this.score = 0;
    }

    public String toString()
    {
      return name;
    }

    public Player nextPlayer()
    {
      return players[(id + 1) % playerCounter];
    }

    public String getLabelledScore()
    {
      return name + ": " + score;
    }

    private boolean tryToFillBoxes(Location loc)
    {
      if (outOfValidGrid(loc))
        return false;
      for (Stroke s : BoxMap.get(loc))
        if (!s.isDrawn())
          return false;
      getPanel().fillCell(loc, color);
      score++;
      return true;
    }
  }
}

class Stroke extends Actor
{
  private BoxGame gg;
  private StrokeDirection direction;
  private boolean drawn;
  private static int drawnStrokes;
  private static int strokeCounter = 0;
 
  public Stroke(BoxGame gg, StrokeDirection d)
  {
    super(true"sprites/strokeBoarder.png"3);
    strokeCounter++;
    this.gg = gg;
    this.direction = d;
    this.drawn = false;
  }

  public void reset()
  {
    this.turn(direction.ordinal() * 90);
    this.setLocationOffset(scaleOffset(direction.getOffset()));
    this.setMouseTouchCircle(new Point(0, 0), gg.getCellSize() / 3);
  }

  private Point scaleOffset(GGVector offset)
  {
    int scaleFactor = gg.getCellSize() / 2;
    return new Point((int) (offset.x * scaleFactor), (int) (offset.y * scaleFactor));
  }

  public LinkedList<Location> getPossibleFillLocations()
  {
    LinkedList<Location> fillLocs = new LinkedList<Location>();
    Location loc = getLocation();
    fillLocs.add(loc);
    if (loc.y != && direction == StrokeDirection.HORIZONTAL)
      fillLocs.add(new Location(loc.x, loc.y - 1));
    if (loc.x != && direction == StrokeDirection.VERTICAL)
      fillLocs.add(new Location(loc.x - 1, loc.y));
    return fillLocs;
  }

  public StrokeDirection getStrokeDirection()
  {
    return direction;
  }

  public void draw(int playerId)
  {
    drawnStrokes++;
    drawn = true;
    show(1 + playerId);
  }

  public boolean isDrawn()
  {
    return drawn;
  }

  public static boolean allDrawn()
  {
    return strokeCounter == drawnStrokes;
  }
}

//
 enum 
{
  HORIZONTAL(new GGVector(0, -1)), VERTICAL(new GGVector(-1, 0));
  private GGVector offset;

  StrokeDirection(GGVector offset)
  {
    this.offset = offset;
  }

  public GGVector getOffset()
  {
    return offset;
  }
}

Erklärungen zum Programmcode:
Hashtable<Location, LinkedList<Stroke>> BoxMap Wir möchten für jede Location die umgebenden Striche einfach abrufen können. Deshalb speichern wir sie in einer Hashtable.
BoxMap.put(loc, new LinkedList<Stroke>()); Initialisiert eine leere Liste von Strokes für eine Location, in die später die dazugehörigen Striche abgefüllt werden.
BoxMap.get(loc) Gibt eine Liste mit den vier umgebenden Striche dieser Location zurück. Falls die Location ungültig ist, wird eine Exception geworfen.

s.show(1 + currentPlayer.id);

s.draw();

Es ist wichtig, für die beiden sichtbaren Zustände (bei mouseover oder wenn richtig gezeichnet) unterscheiden zu können. Deshalb wird in der draw() Methode isDrawn auf true gesetzt, während beim mouseover nur show() aufgerufen wird.