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 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)