Files
StructureLib/src/main/java/com/gtnewhorizon/structurelib/structure/StructureDefinition.java
T

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

249 lines
9.5 KiB
Java
Raw Normal View History

package com.gtnewhorizon.structurelib.structure;
2024-07-06 12:13:22 -07:00
import static com.gtnewhorizon.structurelib.structure.StructureUtility.isAir;
import static com.gtnewhorizon.structurelib.structure.StructureUtility.notAir;
import static com.gtnewhorizon.structurelib.structure.StructureUtility.step;
2024-07-06 12:13:22 -07:00
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Set;
2021-05-27 13:32:14 +08:00
import java.util.stream.Collectors;
2022-08-08 17:40:12 +08:00
import com.gtnewhorizon.structurelib.util.Vec3Impl;
public class StructureDefinition<T> implements IStructureDefinition<T> {
2022-07-08 17:14:18 +08:00
private final Map<Character, IStructureElement<T>> elements;
private final Map<String, String> shapes;
private final Map<String, IStructureElement<T>[]> structures;
2022-09-11 02:54:11 +08:00
private final Map<String, Set<Vec3Impl>> occupiedSpaces;
2022-07-08 17:14:18 +08:00
public static <B> Builder<B> builder() {
return new Builder<>();
}
2022-08-08 17:40:12 +08:00
private StructureDefinition(Map<Character, IStructureElement<T>> elements, Map<String, String> shapes,
2022-09-11 02:54:11 +08:00
Map<String, IStructureElement<T>[]> structures, Map<String, Set<Vec3Impl>> occupiedSpaces) {
2022-07-08 17:14:18 +08:00
this.elements = elements;
this.shapes = shapes;
this.structures = structures;
2022-09-11 02:54:11 +08:00
this.occupiedSpaces = occupiedSpaces;
2022-07-08 17:14:18 +08:00
}
public static class Builder<T> {
2022-07-08 17:14:18 +08:00
private static final char A = '\uA000';
private static final char B = '\uB000';
private static final char C = '\uC000';
private char d = '\uD000';
private final Map<Vec3Impl, Character> navigates;
private final Map<Character, IStructureElement<T>> elements;
private final Map<String, String> shapes;
2022-09-11 02:54:11 +08:00
private final Map<String, Set<Vec3Impl>> occupiedSpaces;
2022-07-08 17:14:18 +08:00
private Builder() {
navigates = new HashMap<>();
elements = new HashMap<>();
shapes = new HashMap<>();
2022-09-11 02:54:11 +08:00
occupiedSpaces = new HashMap<>();
2022-07-08 17:14:18 +08:00
}
public Map<Character, IStructureElement<T>> getElements() {
return elements;
}
public Map<String, String> getShapes() {
return shapes;
}
/**
2022-09-15 16:15:12 +08:00
* Adds shape. Shape is a two-dimensional string array. Each <b>character</b> inside any of these strings will
* be later mapped to a particular type of {@link IStructureElement} as indicated by
* {@link #addElement(Character, IStructureElement)}.
2022-07-08 17:14:18 +08:00
* <p>
* next char is next block(a) next string is next line(b) next string[] is next slice(c)
* <p>
2022-09-15 16:15:12 +08:00
* There are a few special reserved characters that are pre-mapped to some common structure elements
* <ul>
* <li>+ is anything but air</li>
* <li>- is air checks</li>
* <li>space bar is skip</li>
* <li>~ is also skip (but marks controller position, not optional)</li>
* <li>char {@code A000-FFFF} range is reserved for generated skips</li>
* </ul>
* <p>
2022-07-08 17:14:18 +08:00
*
* @param name unlocalized/code name
* @param structurePiece generated or written struct - DO NOT STORE IT ANYWHERE, or at least set them to null
* afterwards
* @return this builder
*/
public Builder<T> addShape(String name, String[][] structurePiece) {
StringBuilder builder = new StringBuilder();
if (structurePiece.length > 0) {
for (String[] strings : structurePiece) {
if (strings.length > 0) {
for (String string : strings) {
builder.append(string).append(B);
}
builder.setLength(builder.length() - 1);
}
builder.append(C);
}
builder.setLength(builder.length() - 1);
}
2022-09-11 02:54:11 +08:00
Set<Vec3Impl> occupiedSpace = new HashSet<>();
// these track the global current location
int aa = 0, bb = 0, cc = 0;
// these track the vec3 towards next meaningful element
2022-07-08 17:14:18 +08:00
int a = 0, b = 0, c = 0;
2022-09-11 02:54:11 +08:00
// I do know these are pretty bad variable naming, but I have no better idea
2022-07-08 17:14:18 +08:00
for (int i = 0; i < builder.length(); i++) {
char ch = builder.charAt(i);
2022-09-11 02:54:11 +08:00
if (ch == ' ') {
2022-07-08 17:14:18 +08:00
builder.setCharAt(i, A);
ch = A;
2022-09-11 02:54:11 +08:00
} else if (ch == '~') {
builder.setCharAt(i, A);
ch = A;
occupiedSpace.add(Vec3Impl.getFromPool(aa, bb, cc));
2022-07-08 17:14:18 +08:00
}
if (ch == A) {
2022-09-11 02:54:11 +08:00
aa++;
2022-07-08 17:14:18 +08:00
a++;
} else if (ch == B) {
2022-09-11 02:54:11 +08:00
aa = 0;
2022-07-08 17:14:18 +08:00
a = 0;
2022-09-11 02:54:11 +08:00
bb++;
2022-07-08 17:14:18 +08:00
b++;
} else if (ch == C) {
2022-09-11 02:54:11 +08:00
aa = 0;
bb = 0;
2022-07-08 17:14:18 +08:00
a = 0;
b = 0;
c++;
2022-09-11 02:54:11 +08:00
cc++;
2022-07-08 17:14:18 +08:00
} else if (a != 0 || b != 0 || c != 0) {
Vec3Impl vec3 = new Vec3Impl(a, b, c);
Character navigate = navigates.get(vec3);
if (navigate == null) {
navigate = d++;
navigates.put(vec3, navigate);
addElement(navigate, step(vec3));
}
builder.setCharAt(i - 1, navigate);
2022-09-11 02:54:11 +08:00
occupiedSpace.add(Vec3Impl.getFromPool(aa, bb, cc));
aa++;
2022-07-08 17:14:18 +08:00
a = 0;
b = 0;
c = 0;
2022-09-11 02:54:11 +08:00
} else {
occupiedSpace.add(Vec3Impl.getFromPool(aa, bb, cc));
aa++;
2022-07-08 17:14:18 +08:00
}
}
2022-09-11 02:54:11 +08:00
occupiedSpaces.put(name, occupiedSpace);
2022-07-08 17:14:18 +08:00
String built = builder.toString().replaceAll("[\\uA000\\uB000\\uC000]", "");
if (built.contains("+")) {
addElement('+', notAir());
}
if (built.contains("-")) {
addElement('-', isAir());
}
shapes.put(name, built);
return this;
}
2022-09-15 16:15:12 +08:00
/**
* @deprecated use the unboxed version
*/
@Deprecated
2022-07-08 17:14:18 +08:00
public Builder<T> addElement(Character name, IStructureElement<T> structurePiece) {
elements.putIfAbsent(name, structurePiece);
return this;
}
2022-09-15 16:15:12 +08:00
public Builder<T> addElement(char name, IStructureElement<T> structurePiece) {
elements.putIfAbsent(name, structurePiece);
return this;
}
2022-07-08 17:14:18 +08:00
public IStructureDefinition<T> build() {
Map<String, IStructureElement<T>[]> structures = compileStructureMap();
2022-09-11 02:54:11 +08:00
return new StructureDefinition<>(
new HashMap<>(elements),
new HashMap<>(shapes),
structures,
occupiedSpaces);
2022-07-08 17:14:18 +08:00
}
@SuppressWarnings("unchecked")
private Map<String, IStructureElement<T>[]> compileElementSetMap() {
Set<Integer> missing = findMissing();
if (missing.isEmpty()) {
2022-08-08 17:40:12 +08:00
return shapes.entrySet().stream().collect(
Collectors.toMap(
Map.Entry::getKey,
e -> e.getValue().chars().mapToObj(c -> (char) c).distinct().map(elements::get)
.toArray(IStructureElement[]::new)));
2022-07-08 17:14:18 +08:00
} else {
2022-08-08 17:40:12 +08:00
throw new RuntimeException(
"Missing Structure Element bindings for (chars as integers): "
+ Arrays.toString(missing.toArray()));
2022-07-08 17:14:18 +08:00
}
}
@SuppressWarnings("unchecked")
private Map<String, IStructureElement<T>[]> compileStructureMap() {
Set<Integer> missing = findMissing();
if (missing.isEmpty()) {
2022-08-08 17:40:12 +08:00
return shapes.entrySet().stream().collect(
Collectors.toMap(
Map.Entry::getKey,
e -> e.getValue().chars().mapToObj(c -> elements.get((char) c))
.toArray(IStructureElement[]::new)));
2022-07-08 17:14:18 +08:00
} else {
2022-08-08 17:40:12 +08:00
throw new RuntimeException(
"Missing Structure Element bindings for (chars as integers): "
+ Arrays.toString(missing.toArray()));
2022-07-08 17:14:18 +08:00
}
}
private Set<Integer> findMissing() {
2022-08-08 17:40:12 +08:00
return shapes.values().stream().flatMapToInt(CharSequence::chars)
.filter(i -> !elements.containsKey((char) i)).boxed().collect(Collectors.toSet());
2022-07-08 17:14:18 +08:00
}
}
public Map<Character, IStructureElement<T>> getElements() {
return elements;
}
public Map<String, String> getShapes() {
return shapes;
}
public Map<String, IStructureElement<T>[]> getStructures() {
return structures;
}
@Override
public IStructureElement<T>[] getStructureFor(String name) {
IStructureElement<T>[] elements = structures.get(name);
2022-08-08 17:40:12 +08:00
if (elements == null) throw new NoSuchElementException(name);
2022-07-08 17:14:18 +08:00
return elements;
}
2022-09-11 02:54:11 +08:00
@Override
public boolean isContainedInStructure(String name, int offsetA, int offsetB, int offsetC) {
Set<Vec3Impl> occupiedSpace = occupiedSpaces.get(name);
if (occupiedSpace == null) throw new NoSuchElementException(name);
return occupiedSpace.contains(new Vec3Impl(offsetA, offsetB, offsetC));
}
2022-07-08 17:14:18 +08:00
}