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
Example #4
0
    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
Example #5
0
    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)
Example #8
0
    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