Holy crap Batman! It's the beginnings of refactoring the basic puzzle
stuff into Narya. git-svn-id: svn+ssh://src.earth.threerings.net/narya/trunk@2876 542714f4-19e9-0310-aa3c-eee0fc999fb1
This commit is contained in:
@@ -0,0 +1,340 @@
|
||||
//
|
||||
// $Id: PieceDropper.java,v 1.1 2003/11/26 01:42:34 mdb Exp $
|
||||
|
||||
package com.threerings.puzzle.drop.util;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import com.samskivert.util.StringUtil;
|
||||
|
||||
import com.threerings.puzzle.Log;
|
||||
import com.threerings.puzzle.drop.data.DropBoard;
|
||||
import com.threerings.puzzle.drop.data.DropPieceCodes;
|
||||
|
||||
/**
|
||||
* Handles dropping pieces in a board.
|
||||
*/
|
||||
public class PieceDropper
|
||||
implements DropPieceCodes
|
||||
{
|
||||
/**
|
||||
* A class to hold information detailing the pieces to be dropped
|
||||
* in a particular column.
|
||||
*/
|
||||
public static class PieceDropInfo
|
||||
{
|
||||
/** The starting row of the bottom piece being dropped. */
|
||||
public int row;
|
||||
|
||||
/** The column number. */
|
||||
public int col;
|
||||
|
||||
/** The distance to drop the pieces. */
|
||||
public int dist;
|
||||
|
||||
/** The pieces to be dropped. */
|
||||
public int[] pieces;
|
||||
|
||||
/**
|
||||
* Constructs a piece drop info object.
|
||||
*/
|
||||
public PieceDropInfo (int col, int row, int dist)
|
||||
{
|
||||
this.col = col;
|
||||
this.row = row;
|
||||
this.dist = dist;
|
||||
}
|
||||
|
||||
/** Returns a string representation of this instance. */
|
||||
public String toString ()
|
||||
{
|
||||
return StringUtil.fieldsToString(this);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a piece dropper that uses the supplied piece drop logic
|
||||
* to specialise itself for a particular puzzle.
|
||||
*/
|
||||
public PieceDropper (PieceDropLogic logic)
|
||||
{
|
||||
_logic = logic;
|
||||
}
|
||||
|
||||
/**
|
||||
* Destructively modifies the supplied board to contain pieces dropped
|
||||
* in the first of potentially multiple drop positions and returns a
|
||||
* list of {@link PieceDropInfo} objects detailing all column segments
|
||||
* to be dropped. Note that a single list is used internally to store
|
||||
* the drop info objects, and so callers that care to do anything
|
||||
* long-term with the drop info should create their own copy of the
|
||||
* list or somesuch.
|
||||
*
|
||||
* @param DropPieceProvider if the board should always be filled
|
||||
* (as specified by the PieceDropLogic) this will provide
|
||||
* information on the newly filled in pieces.
|
||||
*/
|
||||
public List dropPieces (DropBoard board, DropPieceProvider provider)
|
||||
{
|
||||
int bhei = board.getHeight(), bwid = board.getWidth();
|
||||
_drops.clear();
|
||||
for (int yy = bhei - 1; yy >= 0; yy--) {
|
||||
for (int xx = 0; xx < bwid; xx++) {
|
||||
// find all drops in this column
|
||||
getColumnDrops(board, _drops, xx, yy);
|
||||
}
|
||||
}
|
||||
|
||||
if (_logic.boardAlwaysFilled()) {
|
||||
addFillingDrops(board, provider, _drops);
|
||||
}
|
||||
|
||||
return _drops;
|
||||
}
|
||||
|
||||
/**
|
||||
* Analyzes but does not modify the supplied board and returns a list
|
||||
* of {@link PieceDropInfo} objects detailing all column segments to
|
||||
* be dropped. Note that a single list is used internally to store
|
||||
* the drop info objects, and so callers that care to do anything
|
||||
* long-term with the drop info should create their own copy of the
|
||||
* list or somesuch.
|
||||
*
|
||||
* @param DropPieceProvider if the board should always be filled
|
||||
* (as specified by the PieceDropLogic) this will provide
|
||||
* information on the newly filled in pieces.
|
||||
*/
|
||||
public List getDroppedPieces (DropBoard board, DropPieceProvider provider)
|
||||
{
|
||||
// grab a snapshot of the board within which we're dropping
|
||||
// pieces to avoid modifying the source board directly while we're
|
||||
// figuring out what to drop where
|
||||
if (_board == null) {
|
||||
_board = (DropBoard)board.clone();
|
||||
} else {
|
||||
board.copyInto(_board);
|
||||
}
|
||||
return dropPieces(_board, provider);
|
||||
}
|
||||
|
||||
/**
|
||||
* Populates the <code>drops</code> list with {@link PieceDropInfo}
|
||||
* objects detailing the column segments to be dropped per empty space
|
||||
* below. Destructively modifies the given board to reflect the final
|
||||
* positions of the column segments once dropped.
|
||||
*/
|
||||
protected void getColumnDrops (DropBoard board, List drops, int x, int y)
|
||||
{
|
||||
// skip empty or fixed pieces
|
||||
int piece = board.getPiece(x, y);
|
||||
if (!_logic.isDroppablePiece(piece)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (_logic.isConstrainedPiece(piece)) {
|
||||
// get the distance to drop the block
|
||||
int dist = getBlockDropDistance(board, x, y);
|
||||
if (dist == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// scoot along the bottom edge of the block dropping each column
|
||||
int bwid = board.getWidth();
|
||||
int start = _logic.getConstrainedEdge(board, x, y, LEFT);
|
||||
int end = _logic.getConstrainedEdge(board, x, y, RIGHT);
|
||||
for (int xpos = start; xpos <= end; xpos++) {
|
||||
addDropInfo(board, drops, true, xpos, y, dist);
|
||||
}
|
||||
|
||||
} else {
|
||||
// get the distance to drop the pieces
|
||||
int dist = board.getDropDistance(x, y);
|
||||
if (dist == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// add the column segment to the list of drops
|
||||
addDropInfo(board, drops, false, x, y, dist);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a {@link PieceDropInfo} object to the drop list detailing
|
||||
* dropping of the column segment at the specified location.
|
||||
*
|
||||
* @param board the working board.
|
||||
* @param allowConst whether to allow dropping constrained pieces in
|
||||
* the specified column segment.
|
||||
* @param x the column x-coordinate.
|
||||
* @param y the column segment bottom y-coordinate.
|
||||
* @param dist the distance to drop the pieces.
|
||||
*/
|
||||
protected void addDropInfo (DropBoard board, List drops,
|
||||
boolean allowConst, int x, int y, int dist)
|
||||
{
|
||||
// sanity check our column
|
||||
if (x < 0 || x >= board.getWidth()) {
|
||||
Log.warning("Requested to add bogus drop info [board=" + board +
|
||||
", drops=" + StringUtil.toString(drops) +
|
||||
", allowConst=" + allowConst +
|
||||
", x=" + x + ", y=" + y + ", dist=" + dist + "].");
|
||||
Thread.dumpStack();
|
||||
}
|
||||
|
||||
// traverse up the column looking for an empty or block piece
|
||||
// that will terminate this column segment
|
||||
int height = getDropHeight(board, allowConst, x, y, dist);
|
||||
|
||||
// create the piece drop info object
|
||||
PieceDropInfo pdi = new PieceDropInfo(x, y, dist);
|
||||
|
||||
// copy in the relevant pieces
|
||||
pdi.pieces = new int[height];
|
||||
int idx = 0;
|
||||
for (int yy = y; yy > (y - height); yy--) {
|
||||
pdi.pieces[idx++] = board.getPiece(x, yy);
|
||||
}
|
||||
|
||||
// update the working copy of the board with the eventual
|
||||
// piece locations
|
||||
dropPieces(board, pdi);
|
||||
|
||||
// add the column segment to the pot
|
||||
drops.add(pdi);
|
||||
}
|
||||
|
||||
/**
|
||||
* If we want to keep the board filled at all times,
|
||||
* this method will be called to create drop objects for the
|
||||
* newly-filled in pieces.
|
||||
*/
|
||||
protected void addFillingDrops (
|
||||
DropBoard board, DropPieceProvider provider, List drops)
|
||||
{
|
||||
boolean out = false;
|
||||
// drop in new pieces for empty spaces at the top
|
||||
for (int xx=0, bwid=board.getWidth(); xx < bwid; xx++) {
|
||||
// get the distance to drop the pieces
|
||||
int dist = board.getDropDistance(xx, -1);
|
||||
if (dist != 0) {
|
||||
PieceDropInfo pdi = new PieceDropInfo(xx, -1, dist);
|
||||
pdi.pieces = new int[dist];
|
||||
for (int ii=0; ii < dist; ii++) {
|
||||
try {
|
||||
pdi.pieces[ii] = provider.getNextPiece();
|
||||
} catch (DropPieceProvider.OutOfPiecesException oop) {
|
||||
// well, how far did we get?
|
||||
int[] something = new int[ii];
|
||||
System.arraycopy(pdi.pieces, 0, something, 0, ii);
|
||||
pdi.pieces = something;
|
||||
out = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
dropPieces(board, pdi);
|
||||
drops.add(pdi);
|
||||
|
||||
// if we're outta pieces, bail.
|
||||
if (out) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the height of the piece segment to be dropped at the
|
||||
* given coordinates.
|
||||
*/
|
||||
protected int getDropHeight (
|
||||
DropBoard board, boolean allowConst, int x, int y, int dist)
|
||||
{
|
||||
int height = 0;
|
||||
for (int yy = y; yy >= 0; yy--) {
|
||||
int curpiece = board.getPiece(x, yy);
|
||||
if (!_logic.isClimbablePiece(allowConst, curpiece, true)) {
|
||||
return height;
|
||||
}
|
||||
|
||||
height++;
|
||||
|
||||
if (!_logic.isClimbablePiece(allowConst, curpiece, false)) {
|
||||
return height;
|
||||
}
|
||||
}
|
||||
|
||||
return height;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the given board to reflect the eventual destination of the
|
||||
* given pieces.
|
||||
*/
|
||||
protected void dropPieces (DropBoard board, PieceDropInfo pdi)
|
||||
{
|
||||
// clear out the original piece positions
|
||||
if (!board.setSegment(
|
||||
VERTICAL, pdi.col, pdi.row, pdi.pieces.length, PIECE_NONE)) {
|
||||
Log.warning("Bogosity encountered when clearing pieces for drop " +
|
||||
"[bwid=" + board.getWidth() +
|
||||
", bhei=" + board.getHeight() + ", pdi=" + pdi + "].");
|
||||
}
|
||||
// place the pieces in their destination positions
|
||||
applyPieces(board, pdi);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of spaces the block whose bottom edge
|
||||
* contains the specified coordinate can be dropped based on the
|
||||
* contents of the given board. The droppable distance for a
|
||||
* block is determined by finding the minimum drop distance across
|
||||
* all columns the block spans.
|
||||
*/
|
||||
protected int getBlockDropDistance (DropBoard board, int x, int y)
|
||||
{
|
||||
// get the smallest drop distance across all of the block columns
|
||||
int bwid = board.getWidth();
|
||||
int start = Math.max(_logic.getConstrainedEdge(board, x, y, LEFT), 0);
|
||||
int end = Math.min(
|
||||
_logic.getConstrainedEdge(board, x, y, RIGHT), bwid - 1);
|
||||
int dist = board.getHeight() - 1;
|
||||
|
||||
for (int xpos = start; xpos <= end; xpos++) {
|
||||
dist = Math.min(dist, board.getDropDistance(xpos, y));
|
||||
}
|
||||
|
||||
return dist;
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies the pieces in the given piece drop info object to the given
|
||||
* board at their eventual destination positions.
|
||||
*/
|
||||
protected void applyPieces (DropBoard board, PieceDropInfo pdi)
|
||||
{
|
||||
int start = pdi.row, end = (pdi.row - pdi.pieces.length);
|
||||
int idx = 0;
|
||||
boolean error = false;
|
||||
for (int yy = (start + pdi.dist); yy > (end + pdi.dist); yy--) {
|
||||
error = !board.setPiece(pdi.col, yy, pdi.pieces[idx++]) || error;
|
||||
}
|
||||
if (error) {
|
||||
Log.warning("Bogosity encountered while applying dropped " +
|
||||
"pieces to board [bwid=" + board.getWidth() +
|
||||
", bhei=" + board.getHeight() + ", pdi=" + pdi + "].");
|
||||
}
|
||||
}
|
||||
|
||||
/** The piece drop logic used to allow puzzle-specific piece dropping
|
||||
* hooks. */
|
||||
protected PieceDropLogic _logic;
|
||||
|
||||
/** The list of piece drop info objects detailing the piece drops
|
||||
* resulting from the last call to {@link #getDroppedPieces}. */
|
||||
protected ArrayList _drops = new ArrayList();
|
||||
|
||||
/** The board on which pieces are being dropped. */
|
||||
protected DropBoard _board;
|
||||
}
|
||||
Reference in New Issue
Block a user