All Downloads are FREE. Search and download functionalities are using the official Maven repository.

squidpony.squidgrid.mapping.RectangleRoomFinder Maven / Gradle / Ivy

Go to download

SquidLib platform-independent logic and utility code. Please refer to https://github.com/SquidPony/SquidLib .

There is a newer version: 3.0.6
Show newest version
package squidpony.squidgrid.mapping;

import squidpony.squidgrid.Direction;
import squidpony.squidgrid.iterator.SquidIterators;
import squidpony.squidmath.Coord;

import java.util.*;

/**
 * An algorithm to find rectangle areas in dungeons. It is a simpler and faster
 * alternative to {@link RoomFinder}. You can execute
 * {@code RectangleRoomsFinderTest} to see how it performs.
 * 
 * @author smelC
 * 
 * @see RoomFinder A fancier room finder
 */
public class RectangleRoomFinder {

	protected final char[][] dungeon;
	protected final int dungeonWidth;
	protected final int dungeonHeight;

	protected final Set floors;

	/**
	 * The minimum number of cells that the diagonal of a room must have. Having
	 * 3 here means that, by default, only rooms at most 3x3 are considered.
	 */
	public int minimumDiagonal = 3;

	/** {@code true} to restrict {@code this} to find square rooms */
	public boolean onlySquareRooms = false;

	public RectangleRoomFinder(char[][] dungeon) {
		this.dungeon = dungeon;
		this.dungeonWidth = dungeon.length;
		this.dungeonHeight = dungeonWidth == 0 ? 0 : dungeon[0].length;
		this.floors = new HashSet<>();
		this.floors.add('.');
	}

	/**
	 * Adds a character considered as a floor.
	 * 
	 * @param c
	 * @return {@code true} if {@code c} wasn't a floor character.
	 */
	public boolean addFloorCharacter(char c) {
		return floors.add(c);
	}

	/**
	 * Removes a character from being considered as a floor.
	 * 
	 * @param c
	 * @return {@code true} if {@code c} was a floor character.
	 */
	public boolean removeFloorCharacter(char c) {
		return floors.remove(c);
	}

	/**
	 * @return The rectangles of the dungeon given at creation time.
	 */
	public List findRectangles() {
		final List result = new ArrayList<>();

		/*
		 * Every cell containing true indicates that this cell is included in an
		 * already-found room.
		 */
		final boolean[][] assigneds = new boolean[dungeonWidth][dungeonHeight];

		final Iterator it = new SquidIterators.BottomLeftToTopRight(dungeonWidth, dungeonHeight);

		nextBottomLeft: while (it.hasNext()) {
			final Coord c = it.next();
			/*
			 * Try to find the room's diagonal, from its bottom left to its top
			 * right
			 */
			Coord current = c;
			int steps = 0;
			while (!assigneds[c.x][c.y] && isFloor(dungeon[current.x][current.y])) {
				current = current.translate(Direction.UP_RIGHT);
				steps++;
			}
			if (steps < minimumDiagonal)
				continue;
			/*
			 * We have the diagonal. Let's check that this tentative room only
			 * contains (room-unassigned) floors.
			 */
			Rectangle r = new Rectangle.Impl(c, steps, steps);
			Iterator cells = Rectangle.Utils.cells(r);
			while (cells.hasNext()) {
				final Coord inr = cells.next();
				assert isInDungeon(inr);
				if (!isFloor(dungeon[inr.x][inr.y]) || assigneds[inr.x][inr.y])
					continue nextBottomLeft;
			}
			if (!onlySquareRooms) {
				/* Try to extend it */
				r = extendRoom(assigneds, r, Direction.LEFT);
				r = extendRoom(assigneds, r, Direction.RIGHT);
				r = extendRoom(assigneds, r, Direction.UP);
				r = extendRoom(assigneds, r, Direction.DOWN);
			}
			/* Found a room! Let's record the cells. */
			result.add(r);
			cells = Rectangle.Utils.cells(r);
			while (cells.hasNext()) {
				final Coord inr = cells.next();
				assigneds[inr.x][inr.y] = true;
			}
		}

		return result;
	}

	/**
	 * @param assigneds
	 *            Cells already in a room.
	 * @param d
	 *            A cardinal direction.
	 * @return A variant of {@code r} extended to the direction {@code d}, if
	 *         possible. {@code r} itself if unaffected.
	 */
	protected Rectangle extendRoom(boolean[][] assigneds, Rectangle r, Direction d) {
		assert !d.isDiagonal();
		Rectangle result = r;
		while (true) {
			Rectangle next = extendRoomOnce(assigneds, result, d);
			if (next == result)
				/* No change */
				break;
			else
				result = next;
		}
		return result;
	}

	/**
	 * @param assigneds
	 *            Cells already in a room. This array is muted by this call.
	 */
	protected Rectangle extendRoomOnce(boolean[][] assigneds, Rectangle r, Direction d) {
		final Coord bl = r.getBottomLeft();
		Coord first = null;
		Direction way = null;
		int steps = -1;
		switch (d) {
		case DOWN_LEFT:
		case DOWN_RIGHT:
		case NONE:
		case UP_LEFT:
		case UP_RIGHT:
			throw new IllegalStateException(
					"Unexpected direction in " + getClass().getSimpleName() + "::extendRoomOnce: " + d);
		case DOWN:
			first = bl.translate(Direction.DOWN);
			way = Direction.RIGHT;
			steps = r.getWidth();
			break;
		case LEFT:
			first = bl.translate(Direction.LEFT);
			way = Direction.UP;
			steps = r.getHeight();
			break;
		case RIGHT:
			first = bl.translate(r.getWidth() - 1, 0).translate(Direction.RIGHT);
			way = Direction.UP;
			steps = r.getHeight();
			break;
		case UP:
			first = bl.translate(0, -r.getHeight() + 1).translate(Direction.UP);
			way = Direction.RIGHT;
			steps = r.getWidth();
			break;
		}

		assert first != null;

		Coord current = first;

		assert 0 <= steps;
		assert way != null;

		while (0 < steps) {
			if (!isInDungeon(current) || !isFloor(dungeon[current.x][current.y])
					|| assigneds[current.x][current.y])
				/* Cannot extend */
				return r;
			current = current.translate(way);
			steps--;
		}

		final Rectangle result = Rectangle.Utils.extend(r, d);
		assert validRoomCells(Rectangle.Utils.cells(result));
		return result;
	}

	protected boolean isFloor(char c) {
		return floors.contains(c);
	}

	protected boolean isInDungeon(Coord c) {
		return 0 <= c.x && c.x < dungeonWidth && 0 <= c.y && c.y < dungeonHeight;
	}

	private boolean validRoomCells(Iterator cs) {
		while (cs.hasNext()) {
			final Coord c = cs.next();
			if (!isInDungeon(c) || !isFloor(dungeon[c.x][c.y]))
				return false;
		}
		return true;
	}
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy