Game-Apps für Smartphones und Tablets

Bern University oh Teacher Education  
HomeOnline-Editor startenDruckenAndroid-TurtlegrafikJava-Online

Box - Game


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 gezogen hat. Dieser färbt sie mit seiner Farbe aus und darf dann noch einmal ziehen. Ziel ist es, möglichst viele Boxes mit der eigenen Farbe auszufärben.

 

Beispiel im Online-Editor bearbeiten

App installieren auf Smartphone oder Tablet

QR-Code

Programmcode downloaden (BoxGame.zip)

 

// BoxGame.java

package app.boxgame;

import java.util.Hashtable;
import java.util.LinkedList;
import android.graphics.*;
import ch.aplu.android.*;

public class BoxGame extends GameGrid implements GGActorTouchListener
{
  private static final boolean customizableGrid = false;
  
  Hashtable<Location, LinkedList<Stroke>> BoxMap = new Hashtable<Location, LinkedList<Stroke>>();
  private Player currentPlayer;
  private Player[] players = new Player[2];
  private GGStatusBar status;
  private static int playerCounter = 0;
  public static final int INIT_CELL_SIZE = 75;

  public BoxGame()
  {
    super(+ 2, 3 + 2, cellZoom(INIT_CELL_SIZE), Color.WHITE);
    status = addStatusBar(30);
  }

  public void main()
  {
    players[0] = new Player(Color.BLUE, "Blue");
    players[1] = new Player(Color.RED, "Red");
    currentPlayer = players[0]; //blue begins;
    getBg().clear(Color.WHITE);
    refresh(); //so user doesn't sit in front of black screen
    for (int x = 1; x < getNbHorzCells(); x++)
    {
      for (int y = 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() - && d == StrokeDirection.VERTICAL
            || x == getNbHorzCells() - && d == StrokeDirection.HORIZONTAL)
            continue;
          Stroke s = new Stroke(this, d);
          addActorNoRefresh(s, new Location(x, y));
          s.addActorTouchListener(this, GGTouch.click);
          for (Location l : s.getPossibleFillLocations())
            BoxMap.get(l).add(s);
        }
      }
    }
    status.setText("Click on an edge to start");
    setTitle("The box game -- www.java-online.ch");
    refresh();
  }

  @Override
  public void actorTouched(Actor actor, GGTouch mouse, Point spot)
  {
    Stroke s = (Stroke) actor;
    if (s.isDrawn())
      return;
    switch (mouse.getEvent())
    {
      case GGTouch.click:
        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;
    status.setText(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 int color;
    private int score;
    private String name;

    public Player(int blue, String name)
    {
      this.name = name;
      this.id = playerCounter++;
      this.color = blue;
      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"strokeboarder"3);
    strokeCounter++;
    this.gg = gg;
    this.direction = d;
    this.drawn = false;
  }

  public void reset()
  {
    this.turn(direction.ordinal() * 90);
    this.setLocationOffset(scaleOffset(direction.getOffset()));
    L.d("Cell size: " + gg.getCellSize());
    this.setActorTouchCircle(new Point(0, 0), BoxGame.INIT_CELL_SIZE / 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;
  }
}

// --------- class StrokeDirection ---------------
enum StrokeDirection
{
  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.