Skip to content

Instantly share code, notes, and snippets.

@hube12
Last active March 27, 2019 10:53
Show Gist options
  • Save hube12/134711a82080894aec0606fb88ad9ab1 to your computer and use it in GitHub Desktop.
Save hube12/134711a82080894aec0606fb88ad9ab1 to your computer and use it in GitHub Desktop.
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Random;
public class FortressGen {
private static final Random rand = new Random();
private static final int NORTH = 0, EAST = 1, SOUTH = 2, WEST = 3;
private static final int START = 0;
private static final int BRIDGE_FIRST = 1,
BRIDGE_STRAIGHT = 1,
BRIDGE_CROSSING = 2,
BRIDGE_FORTIFIED_CROSSING = 3,
BRIDGE_STAIRS = 4,
BRIDGE_SPAWNER = 5,
BRIDGE_CORRIDOR_ENTRANCE = 6,
BRIDGE_PIECES_COUNT = 6;
private static final int CORRIDOR_FIRST = 7,
CORRIDOR_STRAIGHT = 7,
CORRIDOR_CROSSING = 8,
CORRIDOR_TURN_RIGHT = 9,
CORRIDOR_TURN_LEFT = 10,
CORRIDOR_STAIRS = 11,
CORRIDOR_T_CROSSING = 12,
CORRIDOR_NETHER_WART = 13,
CORRIDOR_PIECES_COUNT = 7;
private static final int END = 14;
private static final int PIECES_COUNT = 15;
private static final int[] BRIDGE_WEIGHTS = {30, 10, 10, 10, 5, 5};
private static final int[] BRIDGE_MAXS = {0, 4, 4, 3, 2, 1};
private static final boolean[] BRIDGE_ALLOW_CONSECUTIVE = {true, false, false, false, false, false};
private static final int[] CORRIDOR_WEIGHTS = {25, 15, 5, 5, 10, 7, 5};
private static final int[] CORRIDOR_MAXS = {0, 5, 10, 10, 3, 2, 2};
private static final boolean[] CORRIDOR_ALLOW_CONSECUTIVE = {true, false, false, false, true, false, false};
private static final Creator[] CREATORS = {
null,
FortressGen::createBridgeStraight,
FortressGen::createBridgeCrossing,
FortressGen::createBridgeFortifiedCrossing,
FortressGen::createBridgeStairs,
FortressGen::createBridgeSpawner,
FortressGen::createBridgeCorridorEntrance,
FortressGen::createCorridorStraight,
FortressGen::createCorridorCrossing,
FortressGen::createCorridorTurnRight,
FortressGen::createCorridorTurnLeft,
FortressGen::createCorridorStairs,
FortressGen::createCorridorTCrossing,
FortressGen::createCorridorNetherWart
};
private static final Extender[] EXTENDERS = {
null,
FortressGen::extendBridgeStraight,
FortressGen::extendBridgeCrossing,
FortressGen::extendBridgeFortifiedCrossing,
FortressGen::extendBridgeStairs,
FortressGen::extendBridgeSpawner,
FortressGen::extendBridgeCorridorEntrance,
FortressGen::extendCorridorStraight,
FortressGen::extendCorridorCrossing,
FortressGen::extendCorridorTurnRight,
FortressGen::extendCorridorTurnLeft,
FortressGen::extendCorridorStairs,
FortressGen::extendCorridorTCrossing,
FortressGen::extendCorridorNetherWart,
FortressGen::extendEnd
};
private static final Runnable[] POST_CREATORS = {
() -> {},
() -> {},
() -> {},
() -> {},
() -> {},
() -> {},
() -> {},
() -> {},
() -> {},
() -> rand.nextInt(3),
() -> rand.nextInt(3),
() -> {},
() -> {},
() -> {}
};
@SuppressWarnings("unchecked")
private static List<PieceInfo>[] placements = new List[PIECES_COUNT];
static {
Arrays.setAll(placements, i -> new ArrayList<>());
}
private static PieceInfo start;
private static List<PieceInfo> pieceQueue = new ArrayList<>();
private static int lastPlaced;
private static void genFortress(int chunkX, int chunkZ) {
start = createStart((chunkX << 4) + 2, 64, (chunkZ << 4) + 2);
placements[START].add(start);
extendBridgeCrossing(start);
while (!pieceQueue.isEmpty()) {
int i =rand.nextInt(pieceQueue.size());
PieceInfo piece = pieceQueue.remove(i);
EXTENDERS[piece.type].extend(piece);
}
}
// ===== CREATORS ===== //
private static PieceInfo createStart(int x, int y, int z) {
return new PieceInfo(START, 0, x, y, z, x + 19 - 1, 73, z + 19 - 1, rand.nextInt(4));
}
private static PieceInfo createBridgeStraight(int x, int y, int z, int depth, int facing) {
return createRotated(BRIDGE_STRAIGHT, depth, x, y, z, -1, -3, 0, 5, 10, 19, facing);
}
private static PieceInfo createBridgeCrossing(int x, int y, int z, int depth, int facing) {
return createRotated(BRIDGE_CROSSING, depth, x, y, z, -8, -3, 0, 19, 10, 19, facing);
}
private static PieceInfo createBridgeFortifiedCrossing(int x, int y, int z, int depth, int facing) {
return createRotated(BRIDGE_FORTIFIED_CROSSING, depth, x, y, z, -2, 0, 0, 7, 9, 7, facing);
}
private static PieceInfo createBridgeStairs(int x, int y, int z, int depth, int facing) {
return createRotated(BRIDGE_STAIRS, depth, x, y, z, -2, 0, 0, 7, 11, 7, facing);
}
private static PieceInfo createBridgeSpawner(int x, int y, int z, int depth, int facing) {
return createRotated(BRIDGE_SPAWNER, depth, x, y, z, -2, 0, 0, 7, 8,9, facing);
}
private static PieceInfo createBridgeCorridorEntrance(int x, int y, int z, int depth, int facing) {
return createRotated(BRIDGE_CORRIDOR_ENTRANCE, depth, x, y, z, -5, -3, 0, 13, 14, 13, facing);
}
private static PieceInfo createCorridorStraight(int x, int y, int z, int depth, int facing) {
return createRotated(CORRIDOR_STRAIGHT, depth, x, y, z, -1, 0, 0, 5, 7, 5, facing);
}
private static PieceInfo createCorridorCrossing(int x, int y, int z, int depth, int facing) {
return createRotated(CORRIDOR_CROSSING, depth, x, y, z, -1, 0, 0, 5, 7, 5, facing);
}
private static PieceInfo createCorridorTurnRight(int x, int y, int z, int depth, int facing) {
return createRotated(CORRIDOR_TURN_RIGHT, depth, x, y, z, -1, 0, 0, 5, 7, 5, facing);
}
private static PieceInfo createCorridorTurnLeft(int x, int y, int z, int depth, int facing) {
return createRotated(CORRIDOR_TURN_LEFT, depth, x, y, z, -1, 0, 0, 5, 7, 5, facing);
}
private static PieceInfo createCorridorStairs(int x, int y, int z, int depth, int facing) {
return createRotated(CORRIDOR_STAIRS, depth, x, y, z, -1, -7, 0, 5, 14, 10, facing);
}
private static PieceInfo createCorridorTCrossing(int x, int y, int z, int depth, int facing) {
return createRotated(CORRIDOR_T_CROSSING, depth, x, y, z, -3, 0, 0, 9, 7, 9, facing);
}
private static PieceInfo createCorridorNetherWart(int x, int y, int z, int depth, int facing) {
return createRotated(CORRIDOR_NETHER_WART, depth, x, y, z, -5, -3, 0, 13, 14, 13, facing);
}
private static PieceInfo createEnd(int x, int y, int z, int depth, int facing) {
return createRotated(END, depth, x, y, z, -1, -3, 0, 5, 10, 8, facing);
}
private static PieceInfo createRotated(int type, int depth, int x, int y, int z, int relXMin, int relYMin, int relZMin, int relXMax, int relYMax, int relZMax, int facing) {
int xMin, yMin, zMin, xMax, yMax, zMax;
switch (facing) {
case NORTH:
case SOUTH:
xMin = x + relXMin;
xMax = x + relXMax - 1 + relXMin;
break;
case WEST:
xMin = x - relZMax + 1 + relZMin;
xMax = x + relZMin;
break;
case EAST:
xMin = x + relZMin;
xMax = x + relZMax - 1 + relZMin;
break;
default:
throw new AssertionError();
}
yMin = y + relYMin;
yMax = y + relYMax - 1 + relYMin;
switch (facing) {
case NORTH:
zMin = z - relZMax + 1 + relZMin;
zMax = z + relZMin;
break;
case SOUTH:
zMin = z + relZMin;
zMax = z + relZMax - 1 + relZMin;
break;
case WEST:
case EAST:
zMin = z + relXMin;
zMax = z + relXMax - 1 + relXMin;
break;
default:
throw new AssertionError();
}
return new PieceInfo(type, depth, xMin, yMin, zMin, xMax, yMax, zMax, facing);
}
// ===== EXTENDERS ===== //
private static void extendBridgeStraight(PieceInfo pieceInfo) {
extendForwards(pieceInfo, 1, 3, false);
}
private static void extendBridgeCrossing(PieceInfo pieceInfo) {
extendForwards(pieceInfo, 8, 3, false);
extendLeft(pieceInfo, 8, 3, false);
extendRight(pieceInfo, 8, 3, false);
}
private static void extendBridgeFortifiedCrossing(PieceInfo pieceInfo) {
extendForwards(pieceInfo, 2, 0, false);
extendLeft(pieceInfo, 2, 0, false);
extendRight(pieceInfo, 2, 0, false);
}
private static void extendBridgeStairs(PieceInfo pieceInfo) {
extendRight(pieceInfo, 2, 6, false);
}
private static void extendBridgeSpawner(PieceInfo pieceInfo) {
}
private static void extendEnd(PieceInfo pieceInfo) {
}
private static void extendBridgeCorridorEntrance(PieceInfo pieceInfo) {
extendForwards(pieceInfo, 5, 3, true);
}
private static void extendCorridorStraight(PieceInfo pieceInfo) {
extendForwards(pieceInfo, 1, 0, true);
}
private static void extendCorridorCrossing(PieceInfo pieceInfo) {
extendForwards(pieceInfo, 1, 0, true);
extendLeft(pieceInfo, 1, 0, true);
extendRight(pieceInfo, 1, 0, true);
}
private static void extendCorridorTurnRight(PieceInfo pieceInfo) {
extendRight(pieceInfo, 1, 0, true);
}
private static void extendCorridorTurnLeft(PieceInfo pieceInfo) {
extendLeft(pieceInfo, 1, 0, true);
}
private static void extendCorridorStairs(PieceInfo pieceInfo) {
extendForwards(pieceInfo, 1, 0, true);
}
private static void extendCorridorTCrossing(PieceInfo pieceInfo) {
int horOffset;
if (pieceInfo.facing == WEST || pieceInfo.facing == NORTH)
horOffset = 5;
else
horOffset = 1;
extendLeft(pieceInfo, horOffset, 0, rand.nextInt(8) > 0);
extendRight(pieceInfo, horOffset, 0, rand.nextInt(8) > 0);
}
private static void extendCorridorNetherWart(PieceInfo pieceInfo) {
extendForwards(pieceInfo, 5, 3, true);
extendForwards(pieceInfo, 5, 11, true);
}
private static void extendForwards(PieceInfo pieceInfo, int horOffset, int vertOffset, boolean inCorridor) {
switch (pieceInfo.facing) {
case NORTH:
extend(pieceInfo.xMin + horOffset, pieceInfo.yMin + vertOffset, pieceInfo.zMin - 1, pieceInfo.facing, pieceInfo.depth + 1, inCorridor);
break;
case SOUTH:
extend(pieceInfo.xMin + horOffset, pieceInfo.yMin + vertOffset, pieceInfo.zMax + 1, pieceInfo.facing, pieceInfo.depth + 1, inCorridor);
break;
case WEST:
extend(pieceInfo.xMin - 1, pieceInfo.yMin + vertOffset, pieceInfo.zMin + horOffset, pieceInfo.facing, pieceInfo.depth + 1, inCorridor);
break;
case EAST:
extend(pieceInfo.xMax + 1, pieceInfo.yMin + vertOffset, pieceInfo.zMin + horOffset, pieceInfo.facing, pieceInfo.depth + 1, inCorridor);
break;
}
}
private static void extendLeft(PieceInfo pieceInfo, int horOffset, int vertOffset, boolean inCorridor) {
switch (pieceInfo.facing) {
case NORTH:
case SOUTH:
extend(pieceInfo.xMin - 1, pieceInfo.yMin + vertOffset, pieceInfo.zMin + horOffset, WEST, pieceInfo.depth + 1, inCorridor);
break;
case WEST:
case EAST:
extend(pieceInfo.xMin + horOffset, pieceInfo.yMin + vertOffset, pieceInfo.zMin - 1, NORTH, pieceInfo.depth + 1, inCorridor);
break;
}
}
private static void extendRight(PieceInfo pieceInfo, int horOffset, int vertOffset, boolean inCorridor) {
switch (pieceInfo.facing) {
case NORTH:
case SOUTH:
extend(pieceInfo.xMax + 1, pieceInfo.yMin + vertOffset, pieceInfo.zMin + horOffset, EAST, pieceInfo.depth + 1, inCorridor);
break;
case WEST:
case EAST:
extend(pieceInfo.xMin + horOffset, pieceInfo.yMin + vertOffset, pieceInfo.zMax + 1, SOUTH, pieceInfo.depth + 1, inCorridor);
break;
}
}
private static void extend(int x, int y, int z, int facing, int depth, boolean inCorridor) {
if (Math.abs(x - start.xMin) <= 112 && Math.abs(z - start.zMin) <= 112) {
int first;
int pieceCount;
int[] weights;
int[] maxs;
boolean[] allowConsecutives;
if (inCorridor) {
first = CORRIDOR_FIRST;
pieceCount = CORRIDOR_PIECES_COUNT;
weights = CORRIDOR_WEIGHTS;
maxs = CORRIDOR_MAXS;
allowConsecutives = CORRIDOR_ALLOW_CONSECUTIVE;
} else {
first = BRIDGE_FIRST;
pieceCount = BRIDGE_PIECES_COUNT;
weights = BRIDGE_WEIGHTS;
maxs = BRIDGE_MAXS;
allowConsecutives = BRIDGE_ALLOW_CONSECUTIVE;
}
boolean anyValid = false;
int totalWeight = 0;
for (int i = 0; i < pieceCount; i++) {
if (maxs[i] > 0 && placements[first + i].size() >= maxs[i])
continue;
if (maxs[i] > 0)
anyValid = true;
totalWeight += weights[i];
}
if (anyValid && totalWeight > 0 && depth <= 30) {
int tries = 0;
while (tries < 5) {
tries++;
int n = rand.nextInt(totalWeight);
for (int i = 0; i < pieceCount; i++) {
if (maxs[i] > 0 && placements[first + i].size() >= maxs[i])
continue;
n -= weights[i];
if (n < 0) {
if (lastPlaced == first + i && !allowConsecutives[i]){
break;
}
Creator creator = CREATORS[first + i];
System.out.println("Creating fortress piece " + (first + i) + " at (" + x + ", " + y + ", " + z + ") facing " + facing + " with depth " + depth + " queue size: " + pieceQueue.size() + " last placed: " + lastPlaced);
PieceInfo pieceInfo = creator.create(x, y, z, depth, facing);
if (!intersectsAny(pieceInfo.xMin, pieceInfo.yMin, pieceInfo.zMin, pieceInfo.xMax, pieceInfo.yMax, pieceInfo.zMax)) {
POST_CREATORS[first + i].run();
lastPlaced = first + i;
placements[first + i].add(pieceInfo);
pieceQueue.add(pieceInfo);
return;
}
}
}
}
}
}
PieceInfo pieceInfo = createEnd(x, y, z, depth, facing);
if (!intersectsAny(pieceInfo.xMin, pieceInfo.yMin, pieceInfo.zMin, pieceInfo.xMax, pieceInfo.yMax, pieceInfo.zMax)) {
rand.nextInt();
placements[END].add(pieceInfo);
pieceQueue.add(pieceInfo);
}
}
private static boolean intersectsAny(int xMin, int yMin, int zMin, int xMax, int yMax, int zMax) {
for (List<PieceInfo> pieceInfoList : placements) {
for (PieceInfo pieceInfo : pieceInfoList) {
if (pieceInfo.xMin <= xMax && pieceInfo.xMax >= xMin && pieceInfo.zMin <= zMax && pieceInfo.zMax >= zMin && pieceInfo.yMin <= yMax && pieceInfo.yMax >= yMin)
return true;
}
}
return false;
}
private static class PieceInfo {
private final int type;
private final int depth;
private final int xMin, yMin, zMin;
private final int xMax, yMax, zMax;
private final int facing;
public PieceInfo(int type, int depth, int xMin, int yMin, int zMin, int xMax, int yMax, int zMax, int facing) {
this.type = type;
this.depth = depth;
this.xMin = xMin;
this.yMin = yMin;
this.zMin = zMin;
this.xMax = xMax;
this.yMax = yMax;
this.zMax = zMax;
this.facing = facing;
}
}
@FunctionalInterface
private interface Creator {
PieceInfo create(int x, int y, int z, int depth, int facing);
}
@FunctionalInterface
private interface Extender {
void extend(PieceInfo pieceInfo);
}
private static void setSeed(long worldSeed, int chunkX, int chunkZ) {
rand.setSeed((chunkX >> 4) ^ (chunkZ >> 4 << 4) ^ worldSeed);
rand.nextInt();
rand.nextInt(3);
rand.nextInt(8);
rand.nextInt(8);
}
public static void main(String[] args) {
setSeed(5896870166552931055L, -21, -5);
genFortress(-21, -5);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment