forked from SteamWar/SteamWar
Add initial MultiTileArea
This commit is contained in:
+188
@@ -0,0 +1,188 @@
|
||||
/*
|
||||
* This file is a part of the SteamWar software.
|
||||
*
|
||||
* Copyright (C) 2026 SteamWar.de-Serverteam
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package de.steamwar.bausystem.region.dynamic;
|
||||
|
||||
import com.sk89q.worldedit.extent.clipboard.Clipboard;
|
||||
import de.steamwar.bausystem.region.Point;
|
||||
import de.steamwar.bausystem.region.Region;
|
||||
import de.steamwar.bausystem.shared.Pair;
|
||||
import lombok.NonNull;
|
||||
import org.bukkit.Location;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.function.BiConsumer;
|
||||
|
||||
public abstract class MultiTileArea implements Region.Area {
|
||||
|
||||
private final Set<Tile> tiles = new HashSet<>();
|
||||
|
||||
public boolean addTile(Tile tile) {
|
||||
boolean result = tiles.add(tile);
|
||||
quantize();
|
||||
return result;
|
||||
}
|
||||
|
||||
public boolean removeTile(Tile tile) {
|
||||
boolean result = tiles.remove(tile);
|
||||
quantize();
|
||||
return result;
|
||||
}
|
||||
|
||||
public void quantize() {
|
||||
long time = System.currentTimeMillis();
|
||||
Quantizer quantizer = new Quantizer(tiles);
|
||||
List<Pair<Tile, Tile>> results = new ArrayList<>();
|
||||
while (true) {
|
||||
Pair<Tile, Tile> pair = quantizer.next();
|
||||
if (pair == null) break;
|
||||
results.add(pair);
|
||||
}
|
||||
// [{"min_tile_x":0,"min_tile_z":0,"max_tile_x":0,"max_tile_z":0}]
|
||||
time = System.currentTimeMillis() - time;
|
||||
System.out.println(tiles.size() + ": " + results.size() + " in " + time + "ms");
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NonNull Point getMinPoint(boolean extension) {
|
||||
return Point.ZERO;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NonNull Point getMaxPoint(boolean extension) {
|
||||
return Point.ZERO;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NonNull Point getCopyPoint() {
|
||||
return Point.ZERO;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean inRegion(Location location, boolean extension) {
|
||||
return inRegion(location.getBlockX(), location.getBlockZ(), extension);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean inRegion(int x, int z, boolean extension) {
|
||||
Tile tile = Tile.fromXZ(x, z).orElse(null);
|
||||
return tiles.contains(tile);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable Clipboard copy(boolean extension) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void forEachChunk(BiConsumer<Integer, Integer> executor) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isChunkOutside(int chunkX, int chunkZ) {
|
||||
return true;
|
||||
}
|
||||
|
||||
private static class Quantizer {
|
||||
|
||||
private Map<Tile, Integer> tileToNeighbourCount = new HashMap<>();
|
||||
|
||||
public Quantizer(Set<Tile> tiles) {
|
||||
for (Tile tile : tiles) {
|
||||
int count = (int) tile.neighboursRing()
|
||||
.filter(tiles::contains)
|
||||
.count();
|
||||
tileToNeighbourCount.put(tile, count);
|
||||
}
|
||||
}
|
||||
|
||||
public Pair<Tile, Tile> next() {
|
||||
Map.Entry<Tile, Integer> entry = tileToNeighbourCount.entrySet()
|
||||
.stream()
|
||||
.max(Map.Entry.comparingByValue())
|
||||
.orElse(null);
|
||||
if (entry == null) return null;
|
||||
|
||||
Tile minTile = entry.getKey();
|
||||
Tile maxTile = entry.getKey();
|
||||
tileToNeighbourCount.remove(entry.getKey());
|
||||
|
||||
while (true) {
|
||||
Pair<Set<Tile>, Integer> neg_x = tryExpand(minTile, maxTile, -1, 0);
|
||||
Pair<Set<Tile>, Integer> pos_x = tryExpand(minTile, maxTile, 1, 0);
|
||||
Pair<Set<Tile>, Integer> neg_z = tryExpand(minTile, maxTile, 0, -1);
|
||||
Pair<Set<Tile>, Integer> pos_z = tryExpand(minTile, maxTile, 0, 1);
|
||||
|
||||
if (neg_x.getValue() == 0 && pos_x.getValue() == 0 && neg_z.getValue() == 0 && pos_z.getValue() == 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
Set<Tile> remove;
|
||||
if (neg_x.getValue() >= pos_x.getValue() && neg_x.getValue() >= neg_z.getValue() && neg_x.getValue() >= pos_z.getValue()) {
|
||||
minTile = minTile.add(-1, 0).orElseThrow();
|
||||
remove = neg_x.getKey();
|
||||
} else if (pos_x.getValue() >= neg_x.getValue() && pos_x.getValue() >= neg_z.getValue() && pos_x.getValue() >= pos_z.getValue()) {
|
||||
maxTile = maxTile.add(1, 0).orElseThrow();
|
||||
remove = pos_x.getKey();
|
||||
} else if (neg_z.getValue() >= neg_x.getValue() && neg_z.getValue() >= pos_x.getValue() && neg_z.getValue() >= pos_z.getValue()) {
|
||||
minTile = minTile.add(0, -1).orElseThrow();
|
||||
remove = neg_z.getKey();
|
||||
} else {
|
||||
maxTile = maxTile.add(0, 1).orElseThrow();
|
||||
remove = pos_z.getKey();
|
||||
}
|
||||
|
||||
remove.forEach(tileToNeighbourCount::remove);
|
||||
}
|
||||
|
||||
return new Pair<>(minTile, minTile.equals(maxTile) ? null : maxTile);
|
||||
}
|
||||
|
||||
private static final Pair<Set<Tile>, Integer> EMPTY = new Pair<>(Collections.emptySet(), 0);
|
||||
|
||||
private Pair<Set<Tile>, Integer> tryExpand(Tile minTile, Tile maxTile, int dx, int dz) {
|
||||
Set<Tile> expaned = new HashSet<>();
|
||||
if (dx == 0) {
|
||||
for (int x = minTile.getTileX(); x <= maxTile.getTileX(); x++) {
|
||||
int z = (dz < 0 ? minTile.getTileZ() : maxTile.getTileZ()) + dz;
|
||||
Tile tile = Tile.fromTile(x, z).orElse(null);
|
||||
if (tile == null) return EMPTY;
|
||||
if (!tileToNeighbourCount.containsKey(tile)) return EMPTY;
|
||||
expaned.add(tile);
|
||||
}
|
||||
} else if (dz == 0) {
|
||||
for (int z = minTile.getTileZ(); z <= maxTile.getTileZ(); z++) {
|
||||
int x = (dx < 0 ? minTile.getTileX() : maxTile.getTileX()) + dx;
|
||||
Tile tile = Tile.fromTile(x, z).orElse(null);
|
||||
if (tile == null) return EMPTY;
|
||||
if (!tileToNeighbourCount.containsKey(tile)) return EMPTY;
|
||||
expaned.add(tile);
|
||||
}
|
||||
} else {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
|
||||
int total = expaned.stream()
|
||||
.mapToInt(tileToNeighbourCount::get)
|
||||
.sum();
|
||||
return new Pair<>(expaned, total);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user