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

com.github.ocraft.s2client.bot.gateway.QueryInterface Maven / Gradle / Ivy

The newest version!
package com.github.ocraft.s2client.bot.gateway;

/*-
 * #%L
 * ocraft-s2client-bot
 * %%
 * Copyright (C) 2017 - 2018 Ocraft Project
 * %%
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 * #L%
 */

import com.github.ocraft.s2client.protocol.data.Abilities;
import com.github.ocraft.s2client.protocol.data.Ability;
import com.github.ocraft.s2client.protocol.data.UnitType;
import com.github.ocraft.s2client.protocol.data.Units;
import com.github.ocraft.s2client.protocol.debug.Color;
import com.github.ocraft.s2client.protocol.query.AvailableAbilities;
import com.github.ocraft.s2client.protocol.query.QueryBuildingPlacement;
import com.github.ocraft.s2client.protocol.query.QueryPathing;
import com.github.ocraft.s2client.protocol.spatial.Point;
import com.github.ocraft.s2client.protocol.spatial.Point2d;
import com.github.ocraft.s2client.protocol.unit.Unit;

import java.util.*;
import java.util.stream.Collectors;

import static java.util.Arrays.asList;

/**
 * The QueryInterface provides additional data not contained in the observation.
 * 

* Performance note: * - Always try and batch things up. These queries are effectively synchronous and will block until returned. */ public interface QueryInterface { /** * Returns a list of abilities represented as a AvailableAbilities see the Abilities enum for their corresponding, * named, representations. * * @param unit Unit. * @param ignoreResourceRequirements Ignores food, mineral and gas costs, as well as cooldowns. * @return Abilities for the unit. */ AvailableAbilities getAbilitiesForUnit(Unit unit, boolean ignoreResourceRequirements); /** * Issues multiple available abilities queries. * Batch version. * * @param units Units. * @param ignoreResourceRequirements Ignores food, mineral and gas costs, as well as cooldowns. * @return Abilities for the units. */ List getAbilitiesForUnits(List units, boolean ignoreResourceRequirements); /** * Returns pathing distance between two locations. Takes into account unit movement properties (e.g. Flying). * * @param start Starting point. * @param end End point. * @return Distance between the two points. */ float pathingDistance(Point2d start, Point2d end); /** * Returns pathing distance between a unit and a target location. Takes into account unit movement properties * (e.g. Flying). * Batch version. * * @param start Starting points. * @param end End points. * @return Distances between the two points. */ float pathingDistance(Unit start, Point2d end); /** * Issues multiple pathing queries. */ List pathingDistance(List queries); /** * @see #placement(Ability, Point2d, Unit) */ boolean placement(Ability ability, Point2d target); /** * Returns whether a building can be placed at a location. * The placing unit field is optional. This is only used for cases where the placing unit plays a role in the * placement grid test (e.g. A flying barracks building an add-on requires room for both the barracks and add-on). * * @param ability Ability for building or moving a structure. * @param target Position to attempt placement on. * @param unit (Optional) The unit that is moving, if moving a structure. * @return If placement is possible. */ boolean placement(Ability ability, Point2d target, Unit unit); /** * A batch version of the above Placement query. Takes an array of abilities, positions and * optional unit tags and returns a matching array of booleans indicating if placement is possible. * * @param queries Placement queries. * @return List of booleans indicating if placement is possible. */ List placement(List queries); /** * Calculates expansion locations * Note: bases that are blocked by destructible rocks or small minerals are included in this list * * @param debug If provided CalculateExpansionLocations will render boxes to show what it calculated. */ default List calculateExpansionLocations(ObservationInterface observation, DebugInterface debug) { List resources = observation.getUnits(unitInPool -> { Set nodes = new HashSet<>(asList( Units.NEUTRAL_MINERAL_FIELD, Units.NEUTRAL_MINERAL_FIELD750, Units.NEUTRAL_RICH_MINERAL_FIELD, Units.NEUTRAL_RICH_MINERAL_FIELD750, Units.NEUTRAL_PURIFIER_MINERAL_FIELD, Units.NEUTRAL_PURIFIER_MINERAL_FIELD750, Units.NEUTRAL_PURIFIER_RICH_MINERAL_FIELD, Units.NEUTRAL_PURIFIER_RICH_MINERAL_FIELD750, Units.NEUTRAL_LAB_MINERAL_FIELD, Units.NEUTRAL_LAB_MINERAL_FIELD750, Units.NEUTRAL_BATTLE_STATION_MINERAL_FIELD, Units.NEUTRAL_BATTLE_STATION_MINERAL_FIELD750, Units.NEUTRAL_VESPENE_GEYSER, Units.NEUTRAL_PROTOSS_VESPENE_GEYSER, Units.NEUTRAL_SPACE_PLATFORM_GEYSER, Units.NEUTRAL_PURIFIER_VESPENE_GEYSER, Units.NEUTRAL_SHAKURAS_VESPENE_GEYSER, Units.NEUTRAL_RICH_VESPENE_GEYSER )); return nodes.contains(unitInPool.unit().getType()); }); List expansionLocations = new ArrayList<>(); Map> clusters = cluster(resources, 15); for (Map.Entry> cluster : clusters.entrySet()) { Point2d basePos = cluster.getKey(); List nodes = cluster.getValue(); //estimate base position basePos = estimateBasePos(basePos, nodes); //adjust basePos by grid restraints on each resource node in the cluster while (true) { Point2d finalBasePos = basePos; nodes = nodes.stream() .sorted(Comparator.comparing(u -> u.unit().getPosition().toPoint2d().distance(finalBasePos))) .collect(Collectors.toList()); Point2d adjustedPoint = pushAwayFromNodes(basePos, nodes); if (adjustedPoint != null) { basePos = adjustedPoint; continue; } adjustedPoint = pullTowardsNodes(basePos, nodes); if (adjustedPoint != null) { basePos = adjustedPoint; continue; } break; } Point basePoint = basePos.toPoint((float)Math.ceil(observation.terrainHeight(basePos))); if (debug != null) { debug.debugBoxOut(basePoint.add(2.5f, 2.5f, 0), basePoint.sub(2.5f, 2.5f, 0), Color.RED); } expansionLocations.add(basePoint); } return expansionLocations; } /** * Calculates expansion locations * Note: bases that are blocked by destructible rocks or small minerals are included in this list * * @param debug If provided CalculateExpansionLocations will render boxes to show what it calculated. * @deprecated use {@link #calculateExpansionLocations(ObservationInterface observation, DebugInterface debug)} instead. */ @Deprecated default List calculateExpansionLocations( ObservationInterface observation, DebugInterface debug, ExpansionParameters parameters) { return calculateExpansionLocations(observation, debug); } /** * Calculates expansion locations * Note: bases that are blocked by destructible rocks or small minerals are included in this list */ default List calculateExpansionLocations(ObservationInterface observation) { return calculateExpansionLocations(observation, null); } /** * Clusters units within some distance of each other and returns a list of them and their center of mass. * @param units List of minerals and vespian geyser units * @param distanceApart Range to consider as apart of the same base * @return Map of minerals/gas clusters (one entry for each base) */ static Map> cluster(List units, double distanceApart) { return cluster(units, distanceApart,true); } /** * Clusters units within some distance of each other and returns a list of them and their center of mass. * @param units List of minerals and vespian geyser units * @param distanceApart Range to consider as apart of the same base * @param isElevationResticted if true, minerals/gas will only be apart of the same cluster if they are at the same elevation * @return Map of minerals/gas clusters (one entry for each base) */ static Map> cluster(List units, double distanceApart, boolean isElevationResticted) { Map> clusters = new LinkedHashMap<>(); for (UnitInPool u : units) { double distance = Double.MAX_VALUE; Point2d unitPos = u.unit().getPosition().toPoint2d(); Map.Entry> targetCluster = null; // Find the cluster this mineral patch is closest to. for (Map.Entry> cluster : clusters.entrySet()) { double d = unitPos.distance(cluster.getKey()); if (d < distance && (!isElevationResticted || isSameElevation(u.unit().getPosition(), cluster))) { distance = d; targetCluster = cluster; } } // If the target cluster is some distance away don't use it. if (targetCluster == null || distance > distanceApart) { ArrayList unitsInCluster = new ArrayList<>(); unitsInCluster.add(u); clusters.put(unitPos, unitsInCluster); continue; } // Otherwise append to that cluster and update it's center of mass. if (targetCluster.getValue() == null) { targetCluster.setValue(new ArrayList<>()); } targetCluster.getValue().add(u); Point2d centerOfCluster = getCenterPos(targetCluster.getValue()); clusters.put(centerOfCluster, clusters.remove(targetCluster.getKey())); } return clusters; } private static Point2d getCenterPos(List unitList) { float minX, maxX, minY, maxY; minX = minY = Float.MAX_VALUE; maxX = maxY = 0; for (UnitInPool u : unitList) { Point2d p = u.unit().getPosition().toPoint2d(); minX = Math.min(p.getX(), minX); maxX = Math.max(p.getX(), maxX); minY = Math.min(p.getY(), minY); maxY = Math.max(p.getY(), maxY); } return Point2d.of((minX+maxX)/2f, (minY+maxY)/2f); } private static Point2d estimateBasePos(Point2d basePos, List nodes) { for (int i=0; i<6; i++) { Point2d finalBestGuess = basePos; Point2d closestNodePos = nodes.stream() .min(Comparator.comparing(node -> node.unit().getPosition().toPoint2d().distance(finalBestGuess))).get() .unit().getPosition().toPoint2d(); basePos = closestNodePos.towards(basePos, 6.2f); } basePos = Point2d.of(basePos.getX(), basePos.getY()).toNearestHalfPoint(); return basePos; } private static Point2d pullTowardsNodes(Point2d basePos, List nodes) { for (int i = nodes.size() - 1; i >= 0; i--) { UnitInPool node = nodes.get(i); Point2d nodePos = node.unit().getPosition().toPoint2d().roundToHalfPointAccuracy(); boolean isMineralNode = node.unit().getType().toString().contains("MINERAL"); float xMinDistCenter = isMineralNode ? 6.5f : 7; float yMinDistCenter = isMineralNode ? 6f : 7; float xMaxDist = isMineralNode ? 7.5f : 7; float yMaxDist = 7; float xDist = Math.abs(nodePos.getX() - basePos.getX()); float yDist = Math.abs(nodePos.getY() - basePos.getY()); if (xDist < yDist) { if (xDist < xMinDistCenter && yDist > yMaxDist) { return moveYFromNodeBy(basePos, nodePos, yMaxDist); } } else { if (yDist < yMinDistCenter && xDist > xMaxDist) { return moveXFromNodeBy(basePos, nodePos, xMaxDist); } } } return null; } private static Point2d pushAwayFromNodes(Point2d basePos, List nodes) { for (UnitInPool node : nodes) { Point2d nodePos = node.unit().getPosition().toPoint2d().roundToHalfPointAccuracy(); boolean isMineralNode = node.unit().getType().toString().contains("MINERAL"); float xMinDistCenter = isMineralNode ? 6.5f : 7; float xMinDistCorner = isMineralNode ? 5.5f : 6; float yMinDistCenter = isMineralNode ? 6f : 7; float yMinDistCorner = isMineralNode ? 5f : 6; float xDist = Math.abs(nodePos.getX() - basePos.getX()); float yDist = Math.abs(nodePos.getY() - basePos.getY()); if (xDist < yDist) { if (xDist < xMinDistCorner && yDist < yMinDistCenter) { return moveYFromNodeBy(basePos, nodePos, yMinDistCenter); } else if (xDist < xMinDistCenter && yDist < yMinDistCorner) { return moveYFromNodeBy(basePos, nodePos, yMinDistCorner); } } else { if (yDist < yMinDistCorner && xDist < xMinDistCenter) { return moveXFromNodeBy(basePos, nodePos, xMinDistCenter); } else if (yDist < yMinDistCenter && xDist < xMinDistCorner) { return moveXFromNodeBy(basePos, nodePos, xMinDistCorner); } } } return null; } private static Point2d moveXFromNodeBy(Point2d origin, Point2d nodePos, float distance) { float newX; if (origin.getX() < nodePos.getX()) { newX = nodePos.getX() - distance; } else { newX = nodePos.getX() + distance; } return Point2d.of(newX, origin.getY()); } private static Point2d moveYFromNodeBy(Point2d origin, Point2d nodePos, float distance) { float newY; if (origin.getY() < nodePos.getY()) { newY = nodePos.getY() - distance; } else { newY = nodePos.getY() + distance; } return Point2d.of(origin.getX(), newY); } private static boolean isSameElevation(Point newNode, Map.Entry> cluster) { return cluster.getValue().stream().allMatch(node -> Math.abs(node.unit().getPosition().getZ() - newNode.getZ()) < 1.2); } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy