Game-Apps für Smartphones und Tablets

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

Sokoban


Die Sokoban-Spielfigur , die mit Taps links, rechts, oberhalb bzw. unterhalb der Sokoban bewegt wird, kann die blauen Kisten verschieben. Ziel ist es, diese an die mit einem leeren blauen Quadrat markierten Stellen zu bringen.
Es ist eine einfache Sokoban-Impementierung mit drei Levels, die bei der Erzeugung des SokobanGrids festgelegt werden.
SokobanGrid grid = new SokobanGrid(0)
bzw. SokobanGrid grid = new SokobanGrid(1)
oder SokobanGrid grid = new SokobanGrid(2)


 

Beispiel im Online-Editor bearbeiten

App installieren auf Smartphone oder Tablet

QR-Code

Programmcode downloaden (Sokoban.zip)


Weitere Levels::

   


Programmcode:
// Sokoban.java

package app.sokoban;

import ch.aplu.android.*;
import ch.aplu.android.Location.CompassDirection;

public class Sokoban extends GameGrid implements GGTouchListener
{
  private final static SokobanGrid grid = new SokobanGrid(0); // 0, 1, or 2
  private final static int nbHorzCells = grid.getNbHorzCells();
  private final static int nbVertCells = grid.getNbVertCells();
  private final int borderColor = RED;
  private SokobanStone[] stones = new SokobanStone[grid.getNbStones()];
  private SokobanTarget[] targets = new SokobanTarget[grid.getNbStones()];
  private SokobanActor sok;
  private boolean isFinished = false;

  public Sokoban()
  {
    super(nbHorzCells, nbVertCells, cellZoom(20), LTGRAY);
    setScreenOrientation(LANDSCAPE);
  }

  public void main()
  {
    initializeBord();
    addTouchListener(thisGGTouch.press);
    showToast("Click next to Sokoban to move it into the desired direction.");
  }

  private void initializeBord()
  {
    GGBackground bg = getBg();
    bg.clear(WHITE);
    bg.setPaintColor(DKGRAY);

    int stoneIndex = 0;
    int targetIndex = 0;

    for (int = 0; y < nbVertCells; y++)
    {
      for (int = 0; x < nbHorzCells; x++)
      {
        Location location = new Location(x, y);
        switch (grid.getCellTypeAt(location))
        {
          case SOKOBAN:
            sok = new SokobanActor();
            addActorNoRefresh(sok, location);
            break;
          case STONE:
            stones[stoneIndex] = new SokobanStone();
            addActorNoRefresh(stones[stoneIndex], location);
            stoneIndex++;
            break;
          case TARGET_POSITION:
            targets[targetIndex] = new SokobanTarget();
            addActorNoRefresh(targets[targetIndex], location);
            targetIndex++;
            break;
          case EMPTY_OUTSIDE:
            bg.fillCell(location, LTGRAY);
            break;
          case BORDER:
            bg.fillCell(location, borderColor);
            break;
          case EMPTY_INSIDE:
            // leave it white
            break;
        }
      }
    }
    setPaintOrder(SokobanTarget.class);
    refresh();
  }

  private boolean canMove(Location nextLocation)
  {
    if (isBorder(nextLocation))
      return false;
    else // Test if there is a stone
    {
      SokobanStone stone = getStoneAt(nextLocation);
      if (stone != null)
      {
        // Try to move the stone
        stone.setDirection(sok.getDirection());
        if (moveStone(stone))
          return true;
        else
          return false;
      }
    }
    return true;
  }

  private boolean moveStone(SokobanStone stone)
  {
    Location next = stone.getNextMoveLocation();
    if (isBorder(next))
      return false;

    // Test if there is another stone
    SokobanStone neighbourStone = getStoneAt(next);
    if (neighbourStone != null)
      return false;

    // Move the stone
    stone.setLocation(next);

    stone.updateSprite();
    return true;
  }

  private boolean isBorder(Location loc)
  {
    int c = getBg().getColor(loc);
    return c == borderColor;
  }

  private SokobanStone getStoneAt(Location loc)
  {
    return (SokobanStone) getOneActorAt(loc, SokobanStone.class);
  }

  @Override
  public boolean touchEvent(GGTouch touch)
  {
    if (isFinished)
      return true;
    CompassDirection dir = sok.getLocation().get4CompassDirectionTo(toLocation(touch.getX(), touch.getY()));
    Location next = sok.getLocation().getNeighbourLocation(dir);
    sok.setDirection(dir);

    if (canMove(next))
    {
      sok.setLocation(next);
    }
    refresh();
    return true;
  }
}

// --------- SokobanActors-------------
class SokobanActor extends Actor
{
  public SokobanActor()
  {
    super(true"sokoban")// Rotatable
  }
}

class SokobanTarget extends Actor
{
  public SokobanTarget()
  {
    super("target");
  }
}

class SokobanStone extends Actor
{
  public SokobanStone()
  {
    super("sokobanstone"2);
  }

  /**
   * Changes appearance, if located at a target location.
   */
  public void updateSprite()
  {
    if (gameGrid.getOneActorAt(getLocation(), SokobanTarget.class) != null)
      show(1);
    else
      show(0);
  }
}

// ------- class SokobanGrid ------------
class SokobanGrid
{
  private final static int nbHorzCells = 19;
  private final static int nbVertCells = 11;
  private CellType[][] a = new CellType[nbHorzCells][nbVertCells];
  private int nbStones = 0;
  private final static String soko_0 = "    xxxxx          " + // 0 (19)
    "    x...x          " + // 1
    "    x*..x          " + // 2
    "  xxx..*xx         " + // 3
    "  x..*.*.x         " + // 4
    "xxx.x.xx.x   xxxxxx" + // 5
    "x...x.xx.xxxxx..oox" + // 6
    "x.*..*..........oox" + // 7
    "xxxxx.xxx.xAxx..oox" + // 8
    "    x.....xxxxxxxxx" + // 9
    "    xxxxxxx        "// 10
  private final static int nbHorzCells_0 = 19;
  private final static int nbVertCells_0 = 11;
  private final static String soko_1 = "xxxxxxxxxxxx  " + // 0 (14)
    "xoo..x.....xxx" + // 1
    "xoo..x.*..*..x" + // 2
    "xoo..x*xxxx..x" + // 3
    "xoo....A.xx..x" + // 4
    "xoo..x.x..*.xx" + // 5
    "xxxxxx.xx*.*.x" + // 6
    "  x.*..*.*.*.x" + // 7
    "  x....x.....x" + // 8
    "  xxxxxxxxxxxx"// 9
  private final static int nbHorzCells_1 = 14;
  private final static int nbVertCells_1 = 10;
  private final static String soko_2 = "        xxxxxxxx " + // 0 (17)
    "        x.....Ax " + // 1
    "        x.*x*.xx " + // 2
    "        x.*..*x  " + // 3
    "        xx*.*.x  " + // 4
    "xxxxxxxxx.*.x.xxx" + // 5
    "xoooo..xx.*..*..x" + // 6
    "xxooo....*..*...x" + // 7
    "xoooo..xxxxxxxxxx" + // 8
    "xxxxxxxx         "// 9
  private final static int nbHorzCells_2 = 17;
  private final static int nbVertCells_2 = 10;
  private final static String[] sokoModel =
  {
    soko_0, soko_1, soko_2
  };
  private final static int[] nbHorzCellsModel =
  {
    nbHorzCells_0,
    nbHorzCells_1, nbHorzCells_2
  };
  private final static int[] nbVertCellsModel =
  {
    nbVertCells_0,
    nbVertCells_1, nbVertCells_2
  };
  private int model;

  public SokobanGrid(int model)
  {
    this.model = model;

    // Copy structure into integer array
    for (int k = 0; k < nbVertCellsModel[model]; k++)
    {
      for (int i = 0; i < nbHorzCellsModel[model]; i++)
      {
        switch (sokoModel[model]
          .charAt(nbHorzCellsModel[model] * k + i))
        {
          case ' ':
            a[i][k] = CellType.EMPTY_OUTSIDE;
            break;
          case '.':
            a[i][k] = CellType.EMPTY_INSIDE;
            break;
          case 'x':
            a[i][k] = CellType.BORDER;
            break;
          case '*':
            a[i][k] = CellType.STONE;
            nbStones++;
            break;
          case 'o':
            a[i][k] = CellType.TARGET_POSITION;
            break;
          case 'A':
            a[i][k] = CellType.SOKOBAN;
            break;
        }
      }
    }
  }

  public int getNbHorzCells()
  {
    return nbHorzCellsModel[model];
  }

  public int getNbVertCells()
  {
    return nbVertCellsModel[model];
  }

  public int getNbStones()
  {
    return nbStones;
  }

  public CellType getCellTypeAt(Location location)
  {
    return a[location.x][location.y];
  }
}

// ------ class CellType -------
enum CellType
{
  EMPTY_OUTSIDE,
  EMPTY_INSIDE,
  BORDER,
  STONE,
  TARGET_POSITION,
  SOKOBAN;
}