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

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

There is a newer version: 0.4.20
Show 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 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 Array of booleans indicating if placement is possible. */ List placement(List queries); /** * Calculates expansion locations, this call can take on the order of 100ms since it makes blocking queries to SC2 * so call it once and cache the results. * * @param debug If filled out CalculateExpansionLocations will render spheres to show what it calculated. */ default List calculateExpansionLocations( ObservationInterface observation, DebugInterface debug, ExpansionParameters parameters) { List resources = observation.getUnits(unitInPool -> { Set fields = 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 fields.contains(unitInPool.unit().getType()); }); List expansionLocations = new ArrayList<>(); Map> clusters = cluster(resources, parameters.getClusterDistance()); Map querySize = new LinkedHashMap<>(); List queries = new ArrayList<>(); for (Map.Entry> cluster : clusters.entrySet()) { if (debug != null) { for (double r : parameters.getRadiuses()) { debug.debugSphereOut(cluster.getKey(), (float) r, Color.GREEN); } } // Get the required queries for this cluster. int queryCount = 0; for (double r : parameters.getRadiuses()) { List calculatedQueries = calculateQueries( r, parameters.getCircleStepSize(), cluster.getKey().toPoint2d()); queries.addAll(calculatedQueries); queryCount += calculatedQueries.size(); } querySize.put(cluster.getKey(), queryCount); } List results = this.placement(queries); int startIndex = 0; for (Map.Entry> cluster : clusters.entrySet()) { double distance = Double.MAX_VALUE; Point2d closest = null; // For each query for the cluster minimum distance location that is valid. for (int j = startIndex, e = startIndex + querySize.get(cluster.getKey()); j < e; ++j) { if (!results.get(j)) { continue; } Point2d p = queries.get(j).getTarget(); double d = p.distance(cluster.getKey().toPoint2d()); if (d < distance) { distance = d; closest = p; } } if (closest != null) { Point expansion = Point.of( closest.getX(), closest.getY(), cluster.getValue().get(0).unit().getPosition().getZ()); if (debug != null) { debug.debugSphereOut(expansion, 0.35f, Color.RED); } expansionLocations.add(expansion); } startIndex += querySize.get(cluster.getKey()); } return expansionLocations; } default List calculateExpansionLocations(ObservationInterface observation) { return calculateExpansionLocations(observation, null, ExpansionParameters.preset()); } default List calculateExpansionLocations(ObservationInterface observation, DebugInterface debug) { return calculateExpansionLocations(observation, debug, ExpansionParameters.preset()); } private List calculateQueries(double radius, double stepSize, Point2d center) { List queries = new ArrayList<>(); Point2d previousGrid = Point2d.of(Float.MAX_VALUE, Float.MAX_VALUE); // Find a buildable location on the circumference of the sphere for (double degree = 0.0; degree < 360.0; degree += stepSize) { Point2d point = pointOnCircle(radius, center, degree); QueryBuildingPlacement query = QueryBuildingPlacement .placeBuilding() .useAbility(Abilities.BUILD_COMMAND_CENTER) .on(point) .build(); Point2d currentGrid = Point2d.of((float) Math.floor(point.getX()), (float) Math.floor(point.getY())); if (!previousGrid.equals(currentGrid)) { queries.add(query); } previousGrid = currentGrid; } return queries; } private static Point2d pointOnCircle(double radius, Point2d center, double degree) { return Point2d.of( (float) (radius * Math.cos(Math.toRadians(degree)) + center.getX()), (float) (radius * Math.sin(Math.toRadians(degree)) + center.getY())); } /** * Clusters units within some distance of each other and returns a list of them and their center of mass. */ static Map> cluster(List units, double distanceApart) { Map> clusters = new LinkedHashMap<>(); for (UnitInPool u : units) { double distance = Double.MAX_VALUE; Map.Entry> targetCluster = null; // Find the cluster this mineral patch is closest to. for (Map.Entry> cluster : clusters.entrySet()) { double d = u.unit().getPosition().distance(cluster.getKey()); if (d < distance) { 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(u.unit().getPosition(), 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); int size = targetCluster.getValue().size(); Point centerOfMass = targetCluster.getKey().mul(size - 1.0f).add(u.unit().getPosition()).div(size); clusters.put(centerOfMass, clusters.remove(targetCluster.getKey())); } return clusters; } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy