def goToBall(self): if (self.agentType == "keeper"): V = kUtil.getVector(self.noisy_pos, self.onReceiveDecision[1]) else: #you have a taker V = kUtil.getVector(self.noisy_pos, self.noisyBallPos) self.worldRef.moveAttempt(self, (V, self.maxPlayerSpeed))
def test_rotated_vectors(self): import keepAway keepAwayWorld = keepAway.keepAway(0) #intialize agent to position 25,25 with no noise/error, as a keeper, with (357/2, 550/2) as center of field, #and 2 as agent speed, and 3 as ball speed Agent = agent(keepAwayWorld,(25, 25), 0.0, "keeper", (357/2, 550/2), 2, 3) cos5 = math.cos(5.0 * math.pi / 180) #cosine of 3 degress. angle needs to be in radians #Agent.receiveListOfOtherPlayers(self.keeperArray, self.takerArray, i) testBall = ball.ball((25,25), 3, True) Agent.receiveBallReference(testBall) v = kUtil.getVector((25,25), (30, 30)) #a vector whose angle is 45 degrees answer = Agent.__getRotatedVectors(v, cos5) correctAnswer1 = [((math.cos(50 * math.pi / 180)), (math.sin(50 * math.pi/180))), ((math.cos(40 * math.pi / 180)), (math.sin(40 * math.pi/180)))] """ print "answer" print answer print "correct" print correctAnswer1 """ for i in range(len(correctAnswer1)): for j in range(len(correctAnswer1[i])): str1 = "failed at: i = %d "%i str2 = "j = %d" %j strend = str1 + str2 self.assertAlmostEqual(answer[i][j], correctAnswer1[i][j], msg=strend)
def __blockPass(self, kIndex): """ This function will take a keeper to block as a parameter. The keeper that this taker is trying to block should NOT have the ball. This function works by taking the position of the keeper with the ball, and the position of the keeper to block, and making the taker run to the midpoint of these 2 positions. In 3V2 keepaway, this function should be implemented by the taker farther from the ball. :param kIndex: the index of the keeper to take, sorted by distance. so kIndex = 1 means the keeper who is closest to the ball besides the keeper who has the ball. kIndex = 2 means the 2nd closest keeper, and so on. :type kIndex: integer :returns: nothing """ #kIndex is ordered based on who is the closest keeper keeperActual = sorted(self.keeperArray) #use the current keeper 0 position to block pass midPoint = kUtil.getMidPoint(keeperActual[0].get_noisy_pos(), keeperActual[kIndex].get_noisy_pos()) #use the predicted keeper 0 position to block pass #midPoint = kUtil.getMidPoint(self.onReceiveDecision[1], keeperActual[kIndex].noisy_pos) vector = kUtil.getVector(self.__noisy_pos, midPoint) minDist = min(self.maxPlayerSpeed, kUtil.getDist(self.__noisy_pos, midPoint)) self.worldRef.moveAttempt(self, (vector, minDist))
def _passBall(self, pointToPassTo): ''' If a keeper currently has the ball, then it has the option to hold the ball, or pass it. Call this function to pass the ball. pointToPassTo is the coordinate that the keeper can pass to. these are pixel values :param pointToPassTo: the tile that the agent is passing to. It is an XY coordinate defined to be the top left corner of the tile you're trying to pass to :type pointToPassTo: typle of int :returns: no return ''' if self.fieldBall == None: print("ERROR: trying to hold ball without actually having ball") return #pass to team mate integerK. if integerK = 1, then it's the 2nd element in array selfToTargetDirection = kUtil.unitVector(kUtil.getVector(self.__getBallCenter(self.noisyBallPos), pointToPassTo)) selfToTargetVector = (kUtil.scalarMultiply(self.fieldBall.maxBallSpeed, selfToTargetDirection)) #at this point, you've determined the angle you wanna pass, and you pass. #set ball's possesion to false, and update to new point. ball class handles direction vector self.fieldBall.updatePosession(False) self.inPosession = False self.isKicking = True #kUtil.addVectorToPoint(self.fieldBall.trueBallPos, selfToTeammateVector) self.fieldBall.updateDirection(kUtil.getNoisyVals(selfToTargetVector, self.getSigma()))
def __goToBall(self): """ If a keeper is calling this method, then this method will make the keeper run directly towards the ball if the ball is stationary. If the ball is not stationary, then the simulator will use the calcReceieve methods in order to calculate the optimal intersection point that the keeper can run to. The keeper will then run towards that intercept point using this function. The only keeper that should be running to the ball is the keeper that can get to the ball the fastest. all other keepers should be implementing the getOpen() function. For a taker, the taker will simply run towards the ball. The taker that is closest to the ball should call this function, while the taker that is farther should try to block a pass with blockPass method. :returns: no return .. note:: For keepers, this function will use information that is set when the simulator calls the agent method receiveDecision. Do not call __goToBall for a keeper unless that simulator function has been called. """ if (self.__agentType == "keeper"): V = kUtil.getVector(self.__noisy_pos, self.onReceiveDecision[1]) minDist = min(self.maxPlayerSpeed, kUtil.getDist(self.__noisy_pos, self.onReceiveDecision[1])) else: #you have a taker. have him be stupider by going to the ball rather than the intersection point V = kUtil.getVector(self.__noisy_pos, self.noisyBallPos) minDist = min(self.maxPlayerSpeed, kUtil.getDist(self.__noisy_pos, self.noisyBallPos)) #these 2 lines of code are for if you want keepers and takers to go to same location #this is the more challenging case as the taker will predict where the ball intersects """ V = kUtil.getVector(self.__noisy_pos, self.onReceiveDecision[1]) minDist = min(self.maxPlayerSpeed, kUtil.getDist(self.__noisy_pos, self.onReceiveDecision[1])) """ self.worldRef.moveAttempt(self, (V, minDist))
def getOpen(self, respectivePos): keeper = self stepIncrease = 5 maximum = -9999 #the threshold is compared to cosTheta. so 0 means the windows has to be at least 90 degrees. #1.0 is most flexible value as it's saying the angle has to be at least 0 threshold = 0.9 maxPoint = None tempmaximum = -9999 tempmaxPoint = None playersToIterate = [] for isKeeper in self.keeperArray: if isKeeper != self: playersToIterate.append(isKeeper) for taker in self.takerArray: playersToIterate.append(taker) pointsToIterate = [(self.true_pos[0],self.true_pos[1]+stepIncrease), (self.true_pos[0]+stepIncrease,self.true_pos[1]), (self.true_pos[0]+stepIncrease,self.true_pos[1]+stepIncrease), (self.true_pos[0]-stepIncrease,self.true_pos[1]-stepIncrease), (self.true_pos[0]-stepIncrease,self.true_pos[1]), (self.true_pos[0],self.true_pos[1]-stepIncrease), (self.true_pos[0]-stepIncrease,self.true_pos[1]+stepIncrease), (self.true_pos[0]+stepIncrease,self.true_pos[1]-stepIncrease),] for point in pointsToIterate: if kUtil.cosTheta(point, respectivePos, self.takerArray[0].true_pos)>threshold and kUtil.cosTheta(point, respectivePos, self.takerArray[1].true_pos)>threshold: spar = 0 for player in playersToIterate: spar += kUtil.getDist(player.true_pos, point) if spar>tempmaximum: tempmaximum = spar tempmaxPoint = point continue else: spar = 0 for player in playersToIterate: spar += kUtil.getDist(player.true_pos, point) if spar>maximum: maximum = spar maxPoint = point if maxPoint == None: #print("no open position available for agent #", self.agentListIndex + 1) maxPoint = tempmaxPoint #return (kUtil.getVector(keeper.true_pos, maxPoint),stepIncrease) self.worldRef.moveAttempt(self, (kUtil.getVector(keeper.true_pos, maxPoint), self.maxPlayerSpeed))
def __getOpen(self): """ This function implements a hand coded procedure to go and place individual agents in an optimal position to receive a pass. The code for this function is based heavily on "Algorithm 2 GetOpen:Hand-coded", which is the pseudo-code for the getOpen function used by some other researchers and was published here: http://www.cs.utexas.edu/users/pstone/Papers/bib2html-links/LNAI09-kalyanakrishnan-1.pdf Only keeper who are not trying to go after the ball should call this method. The decision for who goes after the ball or gets open is deterministic. :returns: nothing """ #note: safety constant is cos(18.4) degrees safetyConstant = 0.94887601164449654493424118056447 curMax = float("-inf") argMax = self.getOpenPoints[12] #default case if self.worldRef.fieldBall.trueBallDirection == (0.0, 0.0): predictedBallPos = self.noisyBallPos else: predictedBallPos = self.onReceiveDecision[1] for point in self.getOpenPoints: safety = max(kUtil.cosTheta(point, predictedBallPos, self.takerArray[0].get_noisy_pos()), kUtil.cosTheta(point, predictedBallPos, self.takerArray[1].get_noisy_pos())) if (safety > safetyConstant): #angle is too narrow, a taker can easily steal continue #if you're at this point, then then point is a safe point to consider teamCongestion = 0.0 oppCongestion = 0.0 totalCongestion = 0.0 value = 0.0 for i in range(len(self.keeperArray)): if i != self.agentListIndex: teamCongestion += 1.0 / (kUtil.getDist(self.keeperArray[i].get_noisy_pos(), point)) for i in range(len(self.takerArray)): oppCongestion += 1.0 / (kUtil.getDist(self.takerArray[i].get_noisy_pos(), point)) totalCongestion = teamCongestion + oppCongestion value = -1.0 * totalCongestion if (value > curMax): curMax = value argMax = point #At this point, just run towards argMax at max speed minDist = min(self.maxPlayerSpeed, kUtil.getDist(self.__noisy_pos, argMax)) self.worldRef.moveAttempt(self, (kUtil.getVector(self.__noisy_pos, argMax), minDist) )
def calcOptimal(self, agentList, i, intersect): V = kUtil.getVector(self.fieldBall.trueBallPos, intersect) UV = kUtil.unitVector(V) stepVector = kUtil.scalarMultiply(self.maxBallSpeed, UV) #keep adding the step vector to the optimal point optimalPoint = self.fieldBall.trueBallPos maxNumSteps = int(kUtil.getDist(self.fieldBall.trueBallPos, intersect)/ self.maxBallSpeed) stepCount = 0 for k in range(maxNumSteps): optimalPoint = kUtil.addVectorToPoint(optimalPoint, stepVector) stepCount += 1 currPd = kUtil.getDist(optimalPoint,agentList[i].true_pos) currBd = kUtil.getDist(self.fieldBall.trueBallPos, optimalPoint) currPt = currPd / self.maxPlayerSpeed currBt = currBd / self.maxBallSpeed if currPt < currBt: #found the optimal, so return it return optimalPoint #if you get here, then no closer optimal was found, so just return the intersect return intersect
def passBall(self, integerK): #if you're passing to integerK = 1, then that means pass to the keeper that's closer to you #if you're passing ot integerK = 2, then that means pass to the keeper that's farther to you #0 is an invalid input sortedKeepers = sorted(self.keeperArray) if integerK == 0: print("Invalid input for integerK! integerK not allowed to be", integerK) return if self.fieldBall == None: print("ERROR: trying to hold ball without actually having ball") return #pass to team mate integerK. if integerK = 1, then it's the 2nd element in array selfToTeammateDirection = kUtil.unitVector(kUtil.getVector(self.noisyBallPos, sortedKeepers[integerK].noisy_pos)) selfToTeammateVector = (kUtil.scalarMultiply(self.fieldBall.maxBallSpeed, selfToTeammateDirection)) #set ball's possesion to false, and update to new point. ball class handles direction vector self.fieldBall.updatePosession(False) self.inPosession = False self.isKicking = True #kUtil.addVectorToPoint(self.fieldBall.trueBallPos, selfToTeammateVector) self.fieldBall.update(selfToTeammateVector) #self.fieldBall.updatepos( kUtil.addVectorToPoint(self.fieldBall.true_pos, selfToTeammateVector ) ) return (selfToTeammateDirection, self.maxBallSpeed)
def _passBall(self, integerK): """ If a keeper currently has the ball, then it has the option to hold the ball, or pass it. Call this function to pass the ball. integerK represents the keeper that the ball holder is passing to. integerK = 1 means pass to the keeper that is closest to the ball besides the ball holder. integerK = 2 means pass to the 2nd closest, and so on. Only subclasses of agent should be calling this function. :param integerK: this represents the keeper that the ball holder is passing to. integerK = 1 means pass to the keeper that is closest to the ball besides the ball holder. integerK = 2 means pass to the 2nd closest, and so on. :type integerK: integer :returns: no return """ #print("passing to keeper " + str(integerK) + ", indexed by distance. ") #if you're passing to integerK = 1, then that means pass to the keeper that's closer to you #if you're passing ot integerK = 2, then that means pass to the keeper that's farther to you #0 is an invalid input sortedKeepers = sorted(self.keeperArray) if integerK == 0: print("Invalid input for integerK! integerK not allowed to be", integerK) return if self.fieldBall == None: print("ERROR: trying to hold ball without actually having ball") return #pass to team mate integerK. if integerK = 1, then it's the 2nd element in array selfToTeammateDirection = kUtil.unitVector(kUtil.getVector(self.noisyBallPos, sortedKeepers[integerK].get_noisy_pos())) selfToTeammateVector = (kUtil.scalarMultiply(self.fieldBall.maxBallSpeed, selfToTeammateDirection)) """ #this code segment is for determining which angle you'd rather pass to. it's all deterministic ballPos = self.noisyBallPos passVectorsToConsider = [] passVectorsToConsider.append(selfToTeammateDirection) for i in range(len(self.cosinesOfInterest)): passVectorsToConsider = passVectorsToConsider + self.__getRotatedVectors(selfToTeammateDirection, self.cosinesOfInterest[i]) #now sort the vectors based on how open they are: self.worldRef.debugPassVectors(ballPos, passVectorsToConsider) passVectorsToConsider = sorted(passVectorsToConsider, key = lambda vector: max ( kUtil.cosTheta(self.worldRef.takerArray[0].getNoisyMidPoint(), ballPos, kUtil.addVectorToPoint(ballPos, vector)), kUtil.cosTheta(self.worldRef.takerArray[1].getNoisyMidPoint(), ballPos, kUtil.addVectorToPoint(ballPos, vector))), ) #iterate over the sorted list until you find a pass that you can do for i in range(len(passVectorsToConsider)): if( calcReceive.calc_receive(self.worldRef, passVectorsToConsider[i]) != None): selfToTeammateDirection = passVectorsToConsider[i] selfToTeammateVector = (kUtil.scalarMultiply(self.fieldBall.maxBallSpeed, selfToTeammateDirection)) print "PASS USING ANGLE ACHEIVED AT i=", i break """ #at this point, you've determined the angle you wanna pass, and you pass. #set ball's possesion to false, and update to new point. ball class handles direction vector self.fieldBall.updatePosession(False) self.inPosession = False self.isKicking = True #kUtil.addVectorToPoint(self.fieldBall.trueBallPos, selfToTeammateVector) self.fieldBall.updateDirection(kUtil.getNoisyVals(selfToTeammateVector, self.__sigma))
def __calcOptimal(worldRef, agentList, i, intersect): """ This function is a private function meant to assist another private function called __calc_receive_ball_moving. once __calc_receive_ball_moving has calculated the intersection point, there's one last step: to make sure that the intersection point isn't too close to the out of bounds area. If the intersection point too close to out of bounds, then return an intersection point along the path that the ball is traveling, but is just still safely away from the out of bounds areas. .. note:: This is a private function that the user shouldn't worry about calling. Only the calc_receive function should be called. :param worldRef: a reference to the simulator class which is calling this function :param agentList: a list provided by the simulator of all agents :param i: the index of the agent running to the ball for the list agentList :param intersect: the intersection point that has been calculated, and might be too close to out of bounds :type worldRef: keepAway class :type agentList: a list where each element is an agent class :type i: integer :type intersect: a tuple of floats :returns: the intersection coordinate which is safely within bounds, or the original intersection point if no such point is found :rtype: tuple of floats """ #if the intersect is in bounds, just go to it. no calculations needed if __isPointOutOfPlayRegion(worldRef, intersect, agentList, i) == False: #print("point in bounds, return intersect") return intersect #V = vector from agent's perpendicular intercept to the ball V = kUtil.getVector(intersect, worldRef.fieldBall.trueBallPos) #turn V into a unit vector and multipy it by the speed of the ball to get velocity vector UV = kUtil.unitVector(V) stepVector = kUtil.scalarMultiply(worldRef.maxBallSpeed, UV) #the optimal point is intialized to the intersect, and #the intersect is currently out of bounds. #keep adding the step vector to the optimal point until #the intersect is no longer out of bounds optimalPoint = intersect maxNumSteps = int(kUtil.getDist(worldRef.fieldBall.trueBallPos, intersect)/ worldRef.maxBallSpeed) stepCount = 0 #if you can't get to the ball in maxNumSteps, then it's hopeless. simply #return the intersection point. Your agent will fail and the ball will #go out of bounds, but there's nothing that can be done for k in range(maxNumSteps): optimalPoint = kUtil.addVectorToPoint(optimalPoint, stepVector) stepCount += 1 if __isPointOutOfPlayRegion(worldRef, optimalPoint, agentList, i) == False: #print("Optimal found, returning optimal point:", optimalPoint) return optimalPoint #if you get here, then no closer optimal was found #print("no optimal found, returning intersect", intersect) return intersect
def blockPass(self, kIndex): #kIndex is ordered based on who is the closest keeper keeperActual = sorted(self.keeperArray) midPoint = kUtil.getMidPoint(keeperActual[0].noisy_pos, keeperActual[kIndex].noisy_pos) vector = kUtil.getVector(self.noisy_pos, midPoint) self.worldRef.moveAttempt(self, (vector, self.maxPlayerSpeed))