Set cleanup

This commit is contained in:
Anuken 2020-04-12 17:42:09 -04:00
parent 603d8b16d0
commit a240c00ebf
7 changed files with 196 additions and 31 deletions

View File

@ -4,6 +4,7 @@ import arc.*;
import arc.func.*;
import arc.math.*;
import arc.math.geom.*;
import arc.struct.EnumSet;
import arc.struct.*;
import arc.util.*;
import mindustry.content.*;
@ -15,6 +16,8 @@ import mindustry.world.*;
import mindustry.world.blocks.*;
import mindustry.world.meta.*;
import java.util.*;
import static mindustry.Vars.*;
/** Class used for indexing special target blocks for AI. */
@ -28,22 +31,21 @@ public class BlockIndexer{
private final IntSet intSet = new IntSet();
private final ObjectSet<Item> itemSet = new ObjectSet<>();
/** Stores all ore quadtrants on the map. */
private ObjectMap<Item, ObjectSet<Tile>> ores = new ObjectMap<>();
private ObjectMap<Item, TileArray> ores = new ObjectMap<>();
/** Maps each team ID to a quarant. A quadrant is a grid of bits, where each bit is set if and only if there is a block of that team in that quadrant. */
private GridBits[] structQuadrants;
/** Stores all damaged tile entities by team. */
private ObjectSet<Tile>[] damagedTiles = new ObjectSet[Team.all().length];
private TileArray[] damagedTiles = new TileArray[Team.all().length];
/**All ores available on this map.*/
private ObjectSet<Item> allOres = new ObjectSet<>();
/**Stores teams that are present here as tiles.*/
private ObjectSet<Team> activeTeams = new ObjectSet<>();
private Array<Team> activeTeams = new Array<>();
/** Maps teams to a map of flagged tiles by type. */
private ObjectSet<Tile>[][] flagMap = new ObjectSet[Team.all().length][BlockFlag.all.length];
private TileArray[][] flagMap = new TileArray[Team.all().length][BlockFlag.all.length];
/** Maps tile positions to their last known tile index data. */
private IntMap<TileIndex> typeMap = new IntMap<>();
/** Empty set used for returning. */
private ObjectSet<Tile> emptySet = new ObjectSet<>();
private TileArray emptySet = new TileArray();
/** Array used for returning and reusing. */
private Array<Tile> returnArray = new Array<>();
@ -62,12 +64,12 @@ public class BlockIndexer{
Events.on(WorldLoadEvent.class, event -> {
scanOres.clear();
scanOres.addAll(Item.getAllOres());
damagedTiles = new ObjectSet[Team.all().length];
flagMap = new ObjectSet[Team.all().length][BlockFlag.all.length];
damagedTiles = new TileArray[Team.all().length];
flagMap = new TileArray[Team.all().length][BlockFlag.all.length];
for(int i = 0; i < flagMap.length; i++){
for(int j = 0; j < BlockFlag.all.length; j++){
flagMap[i][j] = new ObjectSet<>();
flagMap[i][j] = new TileArray();
}
}
@ -98,7 +100,7 @@ public class BlockIndexer{
});
}
private ObjectSet<Tile>[] getFlagged(Team team){
private TileArray[] getFlagged(Team team){
return flagMap[team.id];
}
@ -130,14 +132,14 @@ public class BlockIndexer{
}
/** Returns all damaged tiles by team. */
public ObjectSet<Tile> getDamaged(Team team){
public TileArray getDamaged(Team team){
returnArray.clear();
if(damagedTiles[team.id] == null){
damagedTiles[team.id] = new ObjectSet<>();
damagedTiles[team.id] = new TileArray();
}
ObjectSet<Tile> set = damagedTiles[team.id];
TileArray set = damagedTiles[team.id];
for(Tile tile : set){
if((tile.entity == null || tile.entity.team() != team || !tile.entity.damaged()) || tile.block() instanceof BuildBlock){
returnArray.add(tile);
@ -152,7 +154,7 @@ public class BlockIndexer{
}
/** Get all allied blocks with a flag. */
public ObjectSet<Tile> getAllied(Team team, BlockFlag type){
public TileArray getAllied(Team team, BlockFlag type){
return flagMap[team.id][type.ordinal()];
}
@ -194,7 +196,7 @@ public class BlockIndexer{
returnArray.clear();
for(Team enemy : team.enemies()){
if(state.teams.isActive(enemy)){
ObjectSet<Tile> set = getFlagged(enemy)[type.ordinal()];
TileArray set = getFlagged(enemy)[type.ordinal()];
if(set != null){
for(Tile tile : set){
returnArray.add(tile);
@ -207,10 +209,10 @@ public class BlockIndexer{
public void notifyTileDamaged(Tilec entity){
if(damagedTiles[(int)entity.team().id] == null){
damagedTiles[(int)entity.team().id] = new ObjectSet<>();
damagedTiles[(int)entity.team().id] = new TileArray();
}
ObjectSet<Tile> set = damagedTiles[(int)entity.team().id];
TileArray set = damagedTiles[(int)entity.team().id];
set.add(entity.tile());
}
@ -273,7 +275,7 @@ public class BlockIndexer{
* each tile will at least have an ore within {@link #quadrantSize} / 2 blocks of it.
* Only specific ore types are scanned. See {@link #scanOres}.
*/
public ObjectSet<Tile> getOrePositions(Item item){
public TileArray getOrePositions(Item item){
return ores.get(item, emptySet);
}
@ -297,11 +299,11 @@ public class BlockIndexer{
private void process(Tile tile){
if(tile.block().flags.size() > 0 && tile.team() != Team.derelict){
ObjectSet<Tile>[] map = getFlagged(tile.team());
TileArray[] map = getFlagged(tile.team());
for(BlockFlag flag : tile.block().flags){
ObjectSet<Tile> arr = map[flag.ordinal()];
TileArray arr = map[flag.ordinal()];
arr.add(tile);
@ -309,7 +311,9 @@ public class BlockIndexer{
}
typeMap.put(tile.pos(), new TileIndex(tile.block().flags, tile.team()));
}
activeTeams.add(tile.team());
if(!activeTeams.contains(tile.team())){
activeTeams.add(tile.team());
}
if(ores == null) return;
@ -331,7 +335,7 @@ public class BlockIndexer{
//update quadrant at this position
for(Item item : scanOres){
ObjectSet<Tile> set = ores.get(item);
TileArray set = ores.get(item);
//update quadrant status depending on whether the item is in it
if(!itemSet.contains(item)){
@ -391,7 +395,7 @@ public class BlockIndexer{
//initialize ore map with empty sets
for(Item item : scanOres){
ores.put(item, new ObjectSet<>());
ores.put(item, new TileArray());
}
for(Tile tile : world.tiles){
@ -417,4 +421,34 @@ public class BlockIndexer{
this.team = team;
}
}
public static class TileArray implements Iterable<Tile>{
private Array<Tile> tiles = new Array<>(false, 16);
private IntSet contained = new IntSet();
public void add(Tile tile){
if(contained.add(tile.pos())){
tiles.add(tile);
}
}
public void remove(Tile tile){
if(contained.remove(tile.pos())){
tiles.remove(tile);
}
}
public int size(){
return tiles.size;
}
public Tile first(){
return tiles.first();
}
@Override
public Iterator<Tile> iterator(){
return tiles.iterator();
}
}
}

View File

@ -0,0 +1,50 @@
package mindustry.ai;
//new indexer implementation, uses quadtrees
public class NewBlockIndexer{
/*
public ObjectSet<Tile> getOrePositions(Item item){
}
public Tile findClosestOre(float xp, float yp, Item item){
}
public ObjectSet<Tile> getDamaged(Team team){
}
public ObjectSet<Tile> getAllied(Team team, BlockFlag type){
}
public boolean eachBlock(Teamc team, float range, Boolf<Tilec> pred, Cons<Tilec> cons){
return eachBlock(team.team(), team.getX(), team.getY(), range, pred, cons);
}
public boolean eachBlock(Team team, float wx, float wy, float range, Boolf<Tilec> pred, Cons<Tilec> cons){
}
public Array<Tile> getEnemy(Team team, BlockFlag type){
}
public void notifyTileDamaged(Tilec entity){
}
public Tilec findEnemyTile(Team team, float x, float y, float range, Boolf<Tilec> pred){
}
public Tilec findTile(Team team, float x, float y, float range, Boolf<Tilec> pred, boolean usePriority){
}
public Tilec findTile(Team team, float x, float y, float range, Boolf<Tilec> pred){
return findTile(team, x, y, range, pred, false);
}*/
}

View File

@ -8,6 +8,7 @@ import arc.graphics.*;
import arc.graphics.g2d.*;
import arc.math.*;
import arc.math.geom.*;
import arc.math.geom.QuadTree.*;
import arc.scene.ui.layout.*;
import arc.struct.*;
import arc.util.*;
@ -34,7 +35,7 @@ import static mindustry.Vars.*;
@EntityDef(value = {Tilec.class}, isFinal = false, genio = false, serialize = false)
@Component
abstract class TileComp implements Posc, Teamc, Healthc, Tilec, Timerc{
abstract class TileComp implements Posc, Teamc, Healthc, Tilec, Timerc, QuadTreeObject{
//region vars and initialization
static final float timeToSleep = 60f * 1;
static final ObjectSet<Tilec> tmpTiles = new ObjectSet<>();
@ -996,5 +997,10 @@ abstract class TileComp implements Posc, Teamc, Healthc, Tilec, Timerc{
updateFlow = false;
}
@Override
public void hitbox(Rect out){
out.setCentered(x, y, block.size * tilesize, block.size * tilesize);
}
//endregion
}

View File

@ -39,7 +39,7 @@ public class Team implements Comparable<Team>{
}
public static Team get(int id){
return all[Pack.u((byte)id)];
return all[((byte)id) & 0xff];
}
/** @return the 6 base team colors. */

View File

@ -3,6 +3,7 @@ package mindustry.world;
import arc.func.*;
import arc.math.*;
import arc.math.geom.*;
import arc.math.geom.QuadTree.*;
import arc.struct.*;
import arc.util.ArcAnnotate.*;
import mindustry.annotations.Annotations.*;
@ -15,7 +16,7 @@ import mindustry.world.modules.*;
import static mindustry.Vars.*;
public class Tile implements Position{
public class Tile implements Position, QuadTreeObject{
/** Tile traversal cost. */
public byte cost = 1;
/** Tile entity, usually null. */
@ -377,7 +378,12 @@ public class Tile implements Position{
}
public Rect getHitbox(Rect rect){
return rect.setSize(block().size * tilesize).setCenter(drawx(), drawy());
return rect.setCentered(drawx(), drawy(), block.size * tilesize, block.size * tilesize);
}
@Override
public void hitbox(Rect rect){
getHitbox(rect);
}
public Tile getNearby(Point2 relative){

View File

@ -9,6 +9,7 @@ import arc.scene.ui.layout.*;
import arc.struct.*;
import arc.util.*;
import arc.util.io.*;
import mindustry.ai.BlockIndexer.*;
import mindustry.content.*;
import mindustry.entities.*;
import mindustry.entities.units.*;
@ -94,9 +95,9 @@ public class CommandCenter extends Block{
@Override
public void placed(){
super.placed();
ObjectSet<Tile> set = indexer.getAllied(team, BlockFlag.comandCenter);
TileArray set = indexer.getAllied(team, BlockFlag.comandCenter);
if(set.size > 0){
if(set.size() > 0){
CommandCenterEntity oe = set.first().ent();
command = oe.command;
}
@ -106,9 +107,9 @@ public class CommandCenter extends Block{
public void onRemoved(){
super.onRemoved();
ObjectSet<Tile> set = indexer.getAllied(team, BlockFlag.comandCenter);
TileArray set = indexer.getAllied(team, BlockFlag.comandCenter);
if(set.size == 1){
if(set.size() == 1){
Groups.unit.each(t -> t.team() == team, u -> u.controller().command(UnitCommand.all[0]));
}
}

View File

@ -1,5 +1,7 @@
import arc.*;
import arc.backend.headless.*;
import arc.func.*;
import arc.math.*;
import arc.math.geom.*;
import arc.struct.*;
import arc.util.*;
@ -277,6 +279,72 @@ public class ApplicationTests{
world.tile(0, 0).entity.acceptStack(Items.copper, 1000, null);
}
@Test
void indexingBasic(){
resetWorld();
SaveIO.load(Core.files.internal("77.msav"));
//test basic method.
Rand r = new Rand(0);
Tilec[] res = {null};
Cons<Tilec> assigner = t -> res[0] = t;
int iterations = 100;
r.setSeed(0);
//warmup.
for(int i = 0; i < iterations; i++){
int x = r.random(0, world.width()), y = r.random(0, world.height());
float range = r.random(tilesize * 30);
indexer.eachBlock(Team.sharded, x * tilesize, y * tilesize, range, t -> true, assigner);
}
//TODO impl
/*
r.setSeed(0);
for(int i = 0; i < iterations; i++){
int x = r.random(0, world.width()), y = r.random(0, world.height());
float range = r.random(tilesize * 30);
indexer.eachBlock2(Team.sharded, x * tilesize, y * tilesize, range, t -> true, assigner);
}*/
//benchmark.
r.setSeed(0);
Time.mark();
for(int i = 0; i < iterations; i++){
int x = r.random(0, world.width()), y = r.random(0, world.height());
float range = r.random(tilesize * 30);
indexer.eachBlock(Team.sharded, x * tilesize, y * tilesize, range, t -> true, assigner);
}
Log.info("Time for basic indexing: {0}", Time.elapsed());
r.setSeed(0);
/*
Time.mark();
for(int i = 0; i < iterations; i++){
int x = r.random(0, world.width()), y = r.random(0, world.height());
float range = r.random(tilesize * 30);
indexer.eachBlock2(Team.sharded, x * tilesize, y * tilesize, range, t -> true, assigner);
}
Log.info("Time for quad: {0}", Time.elapsed());
*/
}
@Test
void conveyorBench(){
int[] itemsa = {0};