Spielprogrammierung mit Java
HomeAufgabenDruckenJava-Online

Sokoban

Die Sokoban-Spielfigur , die mit den Cursortasten 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)
Der Parameter kann 0, 1 oder 2 sein. Dem entsprechned wird dann die Startsituation Soko_0, Soko_1 bzw. Soko_2 erzeugt.

 

Programmcode downloaden: Sokoban.zip

 

Programmcode:

// Sokoban.java

import ch.aplu.jgamegrid.*;
import java.awt.event.KeyEvent;
import java.awt.*;

public class Sokoban extends GameGrid implements GGKeyListener
{
  private class SokobanActor extends Actor
  {
    public SokobanActor()
    {
      super(true"sprites/sokoban.gif");  // Rotatable
    }
  }

  private class SokobanTarget extends Actor
  {
    public SokobanTarget()
    {
      super("sprites/target.gif");
    }
  }

  private class SokobanStone extends Actor
  {
    public SokobanStone()
    {
      super("sprites/stone.gif"2)// Two sprites
    }
  }
  // ------------- End of inner classes ------
  
  private final static SokobanGrid grid = new SokobanGrid(0);
  private final static int nbHorzCells = grid.getNbHorzCells();
  private final static int nbVertCells = grid.getNbVertCells();
  private final Color borderColor = new Color(1301050);
  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, 30false);
    setTitle("Sokoban");
    GGBackground bg = getBg();
    drawBoard(bg);
    drawActors();
    addKeyListener(this);
    show();
    // Check if finished
    int nbTarget = 0;
    while (nbTarget < grid.getNbStones())
    {
      nbTarget = 0;
      for (int i = 0; i < grid.getNbStones(); i++)
      {
        if (stones[i].isVisible(1))
          nbTarget++;
      }
      setTitle("# Stones at Target: " + nbTarget);
      delay(500);
    }
    setTitle("Game over. Well done!");
    isFinished = true;
    playSound(this, GGSound.NOTIFY);
  }

  private void drawActors()
  {
    int stoneIndex = 0;
    int targetIndex = 0;

    for (int y = 0; y < nbVertCells; y++)
    {
      for (int x = 0; x < nbHorzCells; x++)
      {
        Location location = new Location(x, y);
        int a = grid.getCell(location);
        if (== 5) // Sokoban actor
        {
          sok = new SokobanActor();
          addActor(sok, location);
        }
        if (== 3) // Stones
        {
          stones[stoneIndex] = new SokobanStone();
          addActor(stones[stoneIndex], location);
          stoneIndex++;
        }
        if (== 4) // Targets
        {
          targets[targetIndex] = new SokobanTarget();
          addActor(targets[targetIndex], location);
          targetIndex++;
        }
      }
    }
    setPaintOrder(SokobanTarget.class);
  }

  private void drawBoard(GGBackground bg)
  {
    bg.clear(new Color(230230230));
    bg.setPaintColor(Color.darkGray);
    for (int y = 0; y < nbVertCells; y++)
    {
      for (int x = 0; x < nbHorzCells; x++)
      {
        Location location = new Location(x, y);
        int a = grid.getCell(location);
        if (== 1 || a == 3 || a == 4 || a == 5)
        {
          bg.fillCell(location, Color.lightGray);
          Point center = toPoint(location);
          bg.drawLine(new Point(center.x - 15, center.y - 15),
            new Point(center.x + 15, center.y + 15));
          bg.drawLine(new Point(center.x - 15, center.y + 15),
            new Point(center.x + 15, center.y - 15));
        }
        if (== 2)  // Border
          bg.fillCell(location, borderColor);
      }
    }
  }

  public boolean keyPressed(KeyEvent evt)
  {
    if (isFinished)
      return true;
    Location next = null;
    switch (evt.getKeyCode())
    {
      case KeyEvent.VK_LEFT:
        next = sok.getLocation().getNeighbourLocation(Location.WEST);
        sok.setDirection(Location.WEST);
        break;
      case KeyEvent.VK_UP:
        next = sok.getLocation().getNeighbourLocation(Location.NORTH);
        sok.setDirection(Location.NORTH);
        break;
      case KeyEvent.VK_RIGHT:
        next = sok.getLocation().getNeighbourLocation(Location.EAST);
        sok.setDirection(Location.EAST);
        break;
      case KeyEvent.VK_DOWN:
        next = sok.getLocation().getNeighbourLocation(Location.SOUTH);
        sok.setDirection(Location.SOUTH);
        break;
    }
    if (next != null && canMove(next))
    {
      sok.setLocation(next);
    }
    refresh();
    return true;
  }

  public boolean keyReleased(KeyEvent evt)
  {
    return true;
  }

  private boolean canMove(Location location)
  {
    // Test if try to move into border
    Color c = getBg().getColor(location);
    if (c.equals(borderColor))
      return false;
    else // Test if there is a stone
    {
      SokobanStone stone = (SokobanStone)getOneActorAt(location, SokobanStone.class);
      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();
    // Test if try to move into border
    Color c = getBg().getColor(next);
    if (c.equals(borderColor))
      return false;

    // Test if there is another stone
    SokobanStone neighbourStone =
      (SokobanStone)getOneActorAt(next, SokobanStone.class);
    if (neighbourStone != null)
      return false;

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

    // Check if we are at a target
    if (getOneActorAt(next, SokobanTarget.class) != null)
      stone.setVisible(1true);
    else
      stone.setVisible(0true);
    return true;
  }

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

class SokobanGr
{
  private final static int nbHorzCells = 19;
  private final static int nbVertCells = 11;
  private static int[][] a = new int[nbHorzCells][nbVertCells];
  private static 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 static 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] = 0;  // Empty outside
            break;
          case '.':
            a[i][k] = 1;  // Empty inside
            break;
          case 'x':
            a[i][k] = 2;  // Border
            break;
          case '*':
            a[i][k] = 3;  // Stones
            nbStones++;
            break;
          case 'o':
            a[i][k] = 4;  // Target positions
            break;
          case 'A':
            a[i][k] = 5;  // Sokoban actor
            break;
        }
      }
    }
  }

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

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

  public static int getNbStones()
  {
    return nbStones;
  }

  public static int getCell(Location location)
  {
    return a[location.x][location.y];
  }
}