def getClusterForce(singuities: List[Singuity]) -> float: clusterStd = arrays.mad([s.position for s in singuities], axis=0, minMad=PhysicsEstimator.getMinimumStd( len(singuities))) averageHealth = np.mean([s.healthPoints for s in singuities]).item() return PhysicsEstimator.getClusterForce(len(singuities), np.linalg.norm(clusterStd), averageHealth, 0)
def executeMovement(self, move: DiscreteMove, targetPosition: np.ndarray, restrictedDuration: int = None, acceptableSinguityCount: int = None): def getLastingMoveDuration(moveDuration: int) -> int: minimumBoundDuration = moveDuration if move.minimumMoveTime is None else max( moveDuration, move.minimumMoveTime) maximumBoundDuration = minimumBoundDuration if restrictedDuration is None else min( minimumBoundDuration, restrictedDuration) return maximumBoundDuration originalMovementDuration = PhysicsEstimator.estimateMovementDuration( self.singuitiesMeanPosition, targetPosition, clusterStd=self.singuitiesStd, acceptableSinguityCount=None if acceptableSinguityCount is None else (self.singuityCount, acceptableSinguityCount)) constrainedMovementDuration = getLastingMoveDuration( originalMovementDuration) if constrainedMovementDuration == 0: return 0, self targetIsWithinStd = PhysicsEstimator.distance2( self.singuitiesMeanPosition, targetPosition) <= self.singuitiesStd**2 if targetIsWithinStd: ratioOfTimeSpentInside = min( constrainedMovementDuration * Singuity.MAXIMUM_SPEED_UNITS_PER_FRAME, self.singuitiesStd * 2) / (self.singuitiesStd * 2) newStd = min( PhysicsEstimator.getMinimumStd(self.singuityCount) * ratioOfTimeSpentInside + self.singuitiesStd * (1 - ratioOfTimeSpentInside), self.singuitiesStd) else: # maximum duration on a 5000 unit map newStd = self.singuitiesStd * (1 + constrainedMovementDuration / 1768) return constrainedMovementDuration,\ DiscretePlayer( self.isCurrentPlayer, self.id, self.singuityCount, self.singuitiesMeanPosition + (targetPosition - self.singuitiesMeanPosition) * (constrainedMovementDuration / constrainedMovementDuration), newStd, self.singuitiesAverageHealth )
def quickAttackDuration(gameState: DiscreteGameState, playerId: str) -> int: ownPlayer = gameState.playerDictionary[playerId] ownSpawners = [ s for s in gameState.spawners if s.isClaimed() and s.isAllegedToPlayer(playerId) ] if len(ownSpawners) == 0: return -1 ownGestationFrames = [ s.frameCountBeforeGestationIsDone(gameState.frameCount) for s in ownSpawners ] claimedEnemySpawners = [ s for s in gameState.spawners if s.isClaimed() and not s.isAllegedToPlayer(playerId) ] totalEnemySpawnerHealthPoints = np.sum( [s.getHealthPoints() for s in claimedEnemySpawners]) totalEnemyCount = np.sum([ p.singuityCount for p in gameState.playerDictionary.values() if p.id != playerId ]) virtualSinguityCount = np.max( [ownPlayer.singuityCount - totalEnemyCount * 1.1, 1]) if ownPlayer.singuityCount == 0: ownForceMeanPosition = np.mean([s.position for s in ownSpawners], axis=0) ownForceStd = 1000 else: ownForceMeanPosition = ownPlayer.singuitiesMeanPosition ownForceStd = ownPlayer.singuitiesStd return PhysicsEstimator.estimateSpawnerToZeroHealthDurationIntegral(virtualSinguityCount, totalEnemySpawnerHealthPoints, [], False, 1.0) * 5\ + np.sum([PhysicsEstimator.estimateMovementDuration(ownForceMeanPosition, s.position, ownForceStd) for s in claimedEnemySpawners]) * 5\ + gameState.frameCount
def getClusterInteractions( self, clusters: List[DiscretePlayer] ) -> List[List[Tuple[DiscretePlayer, float]]]: interactions: List[List[Tuple[DiscretePlayer, float]]] = [] if len(clusters) <= 1: return interactions clusterCollisionDetector = KDTree( np.array([c.singuitiesMeanPosition for c in clusters])) interactingClusters = set() for cluster in clusters: if cluster in interactingClusters: continue [closest, secondClosest] = clusterCollisionDetector.query( cluster.singuitiesMeanPosition.reshape(1, 2), 2, return_distance=False)[0] if clusters[closest].id != cluster.id: closestCluster = clusters[closest] else: closestCluster = clusters[secondClosest] isColliding, distance2 = PhysicsEstimator.areSinguitiesColliding( clusters[secondClosest].singuitiesMeanPosition, cluster.singuitiesMeanPosition, clusters[secondClosest].singuitiesStd + cluster.singuitiesStd, returnDistance=True) if isColliding and closestCluster.id not in interactingClusters: interactingClusters.update([cluster.id, closestCluster.id]) interactions.append([(cluster, distance2), (closestCluster, distance2)]) return interactions
def getSpawnerInteractions( self, spawners: List[DiscreteSpawner], clusters: List[DiscretePlayer] ) -> Tuple[Dict[str, List[Tuple[DiscretePlayer, float]]], Dict[str, str]]: spawnerInteractions: Dict[str, List[Tuple[DiscretePlayer, float]]] = {} spawnerInteractedClusters: Dict[str, str] = {} def addSpawnerInteraction(spawner: DiscreteSpawner, cluster: DiscretePlayer, addedDistance2: float): spawnerInteractedClusters[cluster.id] = spawner.id if spawner.id in spawnerInteractions: spawnerInteractions[spawner.id].append( (cluster, addedDistance2)) else: spawnerInteractions[spawner.id] = [(cluster, addedDistance2)] clusterPositions = np.array( [cluster.singuitiesMeanPosition for cluster in clusters]) closestSpawnerIndices = DiscreteGameState.spawnerCollisionDetector.query( clusterPositions, return_distance=False).reshape(-1) for cluster, closestSpawnerIndex in zip(clusters, closestSpawnerIndices): closestSpawner = spawners[closestSpawnerIndex] isColliding, distance2 = PhysicsEstimator.areSinguitiesColliding( cluster.singuitiesMeanPosition, closestSpawner.position, cluster.singuitiesStd, returnDistance=True) if isColliding: addSpawnerInteraction(closestSpawner, cluster, distance2) return spawnerInteractions, spawnerInteractedClusters
def fromGameState(gameState: GameState, playerId: str, currentPlayerId: str) -> DiscretePlayer: playerSinguities = [ s for s in gameState.singuities if s.playerId == playerId ] if len(playerSinguities) == 0: return DiscretePlayer(currentPlayerId == playerId, playerId, 0, np.zeros(2), PhysicsEstimator.getMinimumStd(0), 0, []) singuityPositions = [s.position for s in playerSinguities] singuityClusterIds = DBSCAN( eps=100, min_samples=25).fit_predict(singuityPositions) singuityClusters: List[List[Singuity]] = [ [] for _ in range(np.max(singuityClusterIds) + 1) ] for index, singuity in [ (index, singuity) for index, singuity in enumerate(playerSinguities) if singuityClusterIds[index] != -1 ]: singuityClusters[singuityClusterIds[index]].append(singuity) def getClusterForce(singuities: List[Singuity]) -> float: clusterStd = arrays.mad([s.position for s in singuities], axis=0, minMad=PhysicsEstimator.getMinimumStd( len(singuities))) averageHealth = np.mean([s.healthPoints for s in singuities]).item() return PhysicsEstimator.getClusterForce(len(singuities), np.linalg.norm(clusterStd), averageHealth, 0) clusterForces = [ getClusterForce(cluster) for cluster in singuityClusters ] if len(clusterForces) == 0: singuityStd, singuityCenter = arrays.mad( singuityPositions, axis=0, returnMedian=True, minMad=PhysicsEstimator.getMinimumStd(len(singuityPositions))) singuityAverageHealth = np.mean( [s.healthPoints for s in playerSinguities]).item() return DiscretePlayer(currentPlayerId == playerId, playerId, len(playerSinguities), singuityCenter, np.linalg.norm(singuityStd), singuityAverageHealth, [s.id for s in playerSinguities]) else: keptCluster = singuityClusters[np.argmax(clusterForces)] singuityStd, singuityCenter = arrays.mad( [s.position for s in keptCluster], axis=0, returnMedian=True, minMad=PhysicsEstimator.getMinimumStd(len(keptCluster))) singuityAverageHealth = np.mean( [s.healthPoints for s in keptCluster]).item() return DiscretePlayer(currentPlayerId == playerId, playerId, len(keptCluster), singuityCenter, np.linalg.norm(singuityStd), singuityAverageHealth, [s.id for s in keptCluster])
def appendNewSpawned(self, spawners: List[Tuple[np.ndarray, int]], anteMoveFrameCount: int, postMoveFrameCount: int) -> DiscretePlayer: spawnerLastClaimedFrames = np.array([s[1] for s in spawners]) spawnerPositions = np.array([s[0] for s in spawners]) matureSpawnerDurations = np.maximum( 0, postMoveFrameCount - np.maximum(anteMoveFrameCount, spawnerLastClaimedFrames + Spawner.GESTATION_FRAME_LAG)) newSinguityCounts = np.round(matureSpawnerDurations / Spawner.SPAWN_FRAME_LAG) usefulSpawnersMask = newSinguityCounts > 0 if np.count_nonzero(usefulSpawnersMask) <= 0: return self spawnerPositions = spawnerPositions[usefulSpawnersMask] matureSpawnerDurations = matureSpawnerDurations[usefulSpawnersMask] newSinguityCounts = newSinguityCounts[usefulSpawnersMask] distanceFromSpawnersToMeanPosition = PhysicsEstimator.distance( self.singuitiesMeanPosition, spawnerPositions) durationsToTarget = distanceFromSpawnersToMeanPosition / Singuity.MAXIMUM_SPEED_UNITS_PER_FRAME atTargetCounts = np.round( np.maximum((matureSpawnerDurations - durationsToTarget) * PhysicsEstimator.SPAWNER_SPAWN_PER_FRAME, 0)) atTargetCount = np.sum(atTargetCounts) furthestSinguityRatios = np.ones_like( distanceFromSpawnersToMeanPosition) matureSpawnerDurationIsLowerThanTargetDurationMask = matureSpawnerDurations < atTargetCounts furthestSinguityRatios[ matureSpawnerDurationIsLowerThanTargetDurationMask] = matureSpawnerDurations[ matureSpawnerDurationIsLowerThanTargetDurationMask] / atTargetCounts[ matureSpawnerDurationIsLowerThanTargetDurationMask] inLineCounts = newSinguityCounts - atTargetCounts inLineMeanPositions = spawnerPositions + ( spawnerPositions - self.singuitiesMeanPosition ) * furthestSinguityRatios[:, np.newaxis] / 2 inLineStds = distanceFromSpawnersToMeanPosition * furthestSinguityRatios / 4 lineUsageMask = PhysicsEstimator.distance2( spawnerPositions, self.singuitiesMeanPosition) <= (self.singuitiesStd * 2)**2 means = np.concatenate( [[self.singuitiesMeanPosition, self.singuitiesMeanPosition], inLineMeanPositions[lineUsageMask]]) stds = np.concatenate([[ self.singuitiesStd, PhysicsEstimator.getMinimumStd(atTargetCount) ], inLineStds[lineUsageMask]]) counts = np.concatenate([[self.singuityCount, atTargetCount], inLineCounts[lineUsageMask]]) mean, std, count = arrays.combineMeanStdAndCount(means, stds, counts, minStd=np.ones(2)) return DiscretePlayer( self.isCurrentPlayer, self.id, count, mean, np.mean(std), 0 if count == 0 else (self.singuitiesAverageHealth * self.singuityCount + Singuity.MAX_HEALTH_POINT * (count - self.singuityCount)) / count)
def executeInteractions( self, spawners: List[DiscreteSpawner], players: List[DiscretePlayer], restrictedDuration: Optional[int], restrictDurationToPlayer: Optional[str] = None ) -> Tuple[List[DiscreteSpawner], List[DiscretePlayer], int]: if len(players) == 0: return [], [], 0 updatedSpawners = [] updatedPlayers = [] oldSpawnersById: Dict[str, DiscreteSpawner] = {s.id: s for s in spawners} spawnerInteractionSubjects, clusterIdToInteractedSpawner = self.getSpawnerInteractions( spawners, players) def updateSpawnersAndPlayers(spawnerId: str, spawnerHealthPoints: float, originalPlayers: List[DiscretePlayer], remainingSinguityCounts: List[int]): updatedSpawners.append( oldSpawnersById[spawnerId].loseHealthPointsTo( spawnerHealthPoints)) for originalPlayer, remainingSinguityCount in zip( originalPlayers, remainingSinguityCounts): updatedPlayers.append( originalPlayer.fought(remainingSinguityCount)) def updatePlayers(originalPlayers: List[DiscretePlayer], remainingSinguityCount: List[int]): for originalPlayer, remainingSinguityCount in zip( originalPlayers, remainingSinguityCount): updatedPlayers.append( originalPlayer.fought(remainingSinguityCount)) interactionDuration = restrictedDuration if restrictDurationToPlayer is not None and restrictDurationToPlayer in clusterIdToInteractedSpawner: interactingSpawnerId = clusterIdToInteractedSpawner[ restrictDurationToPlayer] spawner = oldSpawnersById[interactingSpawnerId] spawnerHealthPoints, interactionDuration, remainingCounts = PhysicsEstimator.estimateFightOverSpawner( None if spawner.allegence is None else spawner.allegence.playerId, spawner.getHealthPoints(), restrictedDuration, [(cluster.id, cluster.singuityCount, cluster.singuitiesStd, cluster.singuitiesAverageHealth, distance2) for (cluster, distance2 ) in spawnerInteractionSubjects[interactingSpawnerId]]) updateSpawnersAndPlayers( interactingSpawnerId, spawnerHealthPoints, list( list( zip(*spawnerInteractionSubjects[interactingSpawnerId])) [0]), remainingCounts) del spawnerInteractionSubjects[interactingSpawnerId] # No interaction duration restriction elif restrictDurationToPlayer is not None and restrictedDuration is None: return [], [], 0 for spawnerId, interactionSubjects in spawnerInteractionSubjects.items( ): spawner = oldSpawnersById[spawnerId] spawnerHealthPoints, _, remainingCounts = PhysicsEstimator.estimateFightOverSpawner( None if spawner.allegence is None else spawner.allegence.playerId, spawner.getHealthPoints(), interactionDuration, [(cluster.id, cluster.singuityCount, cluster.singuitiesStd, cluster.singuitiesAverageHealth, distance2) for (cluster, distance2) in interactionSubjects]) updateSpawnersAndPlayers(spawnerId, spawnerHealthPoints, list(list(zip(*interactionSubjects))[0]), remainingCounts) clusterInteractions = self.getClusterInteractions([ cluster for cluster in players if cluster.id not in clusterIdToInteractedSpawner ]) for interactingClusters in clusterInteractions: remainingCounts = PhysicsEstimator.estimateVoidFight([ (cluster.singuityCount, cluster.singuitiesStd, cluster.singuitiesAverageHealth, distance2) for (cluster, distance2) in interactingClusters ]) updatePlayers(list(list(zip(*interactingClusters))[0]), remainingCounts) return updatedSpawners, updatedPlayers, interactionDuration