/// <summary> /// Only if ball is near the rod new location will be sent to communication layer. /// Method is based on distance, ball speed and BALL_DISTANCE_FACTOR, BALL_MAX_SPEED /// This method added to support Amit's requirement, to be able to set new position of rod based on distance to it. /// </summary> /// <param name="ballCoords">Ball Future Coordinates to respond to</param> /// <param name="currentRod">Current responding rod</param> /// <returns>[True] if we are near the rod or high speed, [False] otherwise</returns> protected bool IsBallNearTheRod(BallCoordinates ballCoords, IRod currentRod, int stopperPosition) { //By changing this parameter we set system sensitivity const double BALL_DISTANCE_FACTOR = 3000; const double BALL_MAX_SPEED = 100; if (!ballCoords.IsDefined) { return(false); } double distanceFactor = Convert.ToDouble(TABLE_WIDTH) / Math.Abs(ballCoords.X - currentRod.RodXCoordinate); double speedFactor = 1; if (ballCoords.Vector != null && ballCoords.Vector.IsDefined) { speedFactor += Math.Abs(ballCoords.Vector.X / BALL_MAX_SPEED); } double diff = (double)Math.Abs(currentRod.State.DcPosition - stopperPosition) / (double)(currentRod.PlayerCount); bool result = (distanceFactor * diff * speedFactor > BALL_DISTANCE_FACTOR); return(result); }
/// <summary> /// Calculate actual linear movement (Y Axe) for current rod to perform /// Is based on desired linear move type /// </summary> /// <param name="rod">Current rod</param> /// <param name="respondingPlayer">Responding player in current rod (1 based index)</param> /// <param name="bfc">Ball Future coordinates</param> /// <param name="desiredLinearMove">Desired Linear Move Type</param> /// <returns>New rod coordinate to move to (Axe Y)</returns> protected int CalculateNewRodCoordinate(IRod rod, int respondingPlayer, BallCoordinates bfc, eLinearMove desiredLinearMove) { //Define actual desired rod coordinate to move to //NOTE: responding player might be undefined will be -1 switch (desiredLinearMove) { case eLinearMove.BALL_Y: return(_helper.LocateRespondingPlayer(rod, bfc.Y, respondingPlayer)); // return bfc.Y - _helper.CalculateCurrentPlayerYCoordinate(rod, _currentRodYCoordinate[rod.RodType], respondingPlayer); //case eLinearMove.LEFT_BALL_DIAMETER: // return (-1) * 2 * BALL_RADIUS; //case eLinearMove.RIGHT_BALL_DIAMETER: // return 2 * BALL_RADIUS; case eLinearMove.VECTOR_BASED: if (rod.Intersection.IsDefined) { int stopperPosition = _helper.LocateRespondingPlayer(rod, rod.Intersection.Y, respondingPlayer); return((IsBallNearTheRod(bfc, rod, stopperPosition)) ? stopperPosition : rod.State.DcPosition); } //return rod.IntersectionY - _helper.CalculateCurrentPlayerYCoordinate(rod, _currentRodYCoordinate[rod.RodType], respondingPlayer); return(rod.State.DcPosition); //case eLinearMove.BEST_EFFORT: // return rod.BestEffort; default: return(rod.State.DcPosition); } }
/// <summary> /// Verify Player Count on given rod is beetween 1 to 5. /// </summary> /// <param name="rod">Current rod</param> /// <exception cref="ArgumentException">Thrown in case player count on rod is not in range of 1 to 5</exception> private void VerifyPlayerCountOnRod(IRod rod) { if (rod.PlayerCount < 1 || rod.PlayerCount > 5) { throw new ArgumentException(String.Format("[{0}] Number of players on rod {1} is incorrect {2}!", MethodBase.GetCurrentMethod().Name, rod.RodType.ToString(), rod.PlayerCount)); } }
/// <summary> /// Choose player to respond on current rod and action to perform /// </summary> /// <param name="rod">Current rod</param> /// <param name="bfc">Ball Future Coordinates</param> /// <param name="respondingPlayer">Responding Player index (1 based) on current rod [out]</param> /// <returns>Rod Action to be performed</returns> protected RodAction DefineActionAndRespondingPlayer(IRod rod, BallCoordinates bfc, out int respondingPlayer) { if (rod == null) { throw new ArgumentException(String.Format( "[{0}] Unable to define action and responding player while rod argument is NULL!", MethodBase.GetCurrentMethod().Name)); } if (bfc == null || !bfc.IsDefined) { throw new ArgumentException(String.Format( "[{0}] Unable to define action and responding player while ball coordinates are NULL or UNDEFINED!", MethodBase.GetCurrentMethod().Name)); } RodAction action = null; respondingPlayer = -1; switch (_helper.IsBallInSector(bfc.X, rod.RodXCoordinate, rod.DynamicSector)) { //Ball is in Current Rod Sector case eXPositionSectorRelative.IN_SECTOR: action = SubTree.Decide(rod, bfc); respondingPlayer = SubTree.RespondingPlayer; break; /* OLD : * //The Big Sub Tree * action = EnterDecisionTreeBallInSector(rod, bfc, out respondingPlayer); */ //Ball is ahead of Current Rod Sector case eXPositionSectorRelative.AHEAD_SECTOR: //Ball Vector Direction is TO Current Rod and we have intersection point if (_helper.IsBallVectorToRod(bfc.Vector) && rod.Intersection.IsDefined) { action = new RodAction(rod.RodType, eRotationalMove.DEFENCE, eLinearMove.VECTOR_BASED); //Define responding player index BallYPositionToPlayerYCoordinate(bfc.Y, rod); respondingPlayer = this.RespondingPlayer; } else { //Ball Vector Direction is FROM Current Rod action = new RodAction(rod.RodType, eRotationalMove.DEFENCE, eLinearMove.BEST_EFFORT); } break; //Ball is behind Current Rod Sector case eXPositionSectorRelative.BEHIND_SECTOR: action = new RodAction(rod.RodType, eRotationalMove.RISE, eLinearMove.BEST_EFFORT); break; } return(action); }
/* * Currently not in use methods. Need to verify if needed before TESTING */ /// <summary> /// Calculate current Player Y coordinate /// </summary> /// <param name="rod">Current rod</param> /// <param name="currentRodYCoordinate">Current Rod Y coordinates (stopper)</param> /// <param name="playerIndex">Chosen player index to perform action (index 1 based)</param> /// <returns>Chosen player Y coordinate</returns> /// <exception cref="ArgumentOutOfRangeException">Thrown in case player index is out of range</exception> public int CalculateCurrentPlayerYCoordinate(IRod rod, int currentRodYCoordinate, int playerIndex) { if (playerIndex > rod.PlayerCount || playerIndex < 1) { throw new ArgumentOutOfRangeException(String.Format( "Player index {0} for rod type {1} is wrong! Players count is {2}", playerIndex, rod.RodType, rod.PlayerCount)); } return(rod.OffsetY + currentRodYCoordinate + rod.PlayerDistance * (playerIndex - 1)); }
/// <summary> /// Choose player to respond on current rod and action to perform /// </summary> /// <param name="rod">Current rod</param> /// <param name="bfc">Ball Future Coordinates</param> /// <param name="respondingPlayer">Responding Player index (1 based) on current rod [out]</param> /// <returns>Rod Action to be performed</returns> protected RodAction DefineActionAndRespondingPlayer(IRod rod, BallCoordinates bfc, out int respondingPlayer) { if (rod == null) throw new ArgumentException(String.Format( "[{0}] Unable to define action and responding player while rod argument is NULL!", MethodBase.GetCurrentMethod().Name)); if (bfc == null || !bfc.IsDefined) throw new ArgumentException(String.Format( "[{0}] Unable to define action and responding player while ball coordinates are NULL or UNDEFINED!", MethodBase.GetCurrentMethod().Name)); RodAction action = null; respondingPlayer = -1; switch (_helper.IsBallInSector(bfc.X, rod.RodXCoordinate, rod.DynamicSector)) { //Ball is in Current Rod Sector case eXPositionSectorRelative.IN_SECTOR: action = SubTree.Decide(rod, bfc); respondingPlayer = SubTree.RespondingPlayer; break; /* OLD : * //The Big Sub Tree * action = EnterDecisionTreeBallInSector(rod, bfc, out respondingPlayer); */ //Ball is ahead of Current Rod Sector case eXPositionSectorRelative.AHEAD_SECTOR: //Ball Vector Direction is TO Current Rod and we have intersection point if (_helper.IsBallVectorToRod(bfc.Vector) && rod.Intersection.IsDefined) { action = new RodAction(rod.RodType, eRotationalMove.DEFENCE, eLinearMove.VECTOR_BASED); //Define responding player index BallYPositionToPlayerYCoordinate(bfc.Y, rod); respondingPlayer = this.RespondingPlayer; } else { //Ball Vector Direction is FROM Current Rod action = new RodAction(rod.RodType, eRotationalMove.DEFENCE, eLinearMove.BEST_EFFORT); } break; //Ball is behind Current Rod Sector case eXPositionSectorRelative.BEHIND_SECTOR: action = new RodAction(rod.RodType, eRotationalMove.RISE, eLinearMove.BEST_EFFORT); break; } return action; }
/// <summary> /// Verify if there is enough space to move the rod from current rod Y coordinate to new Y coordinate /// New Y coordinate is rod Y coordinate with provided movement (negative or positive) /// </summary> /// <param name="rod">Current rod</param> /// <param name="currentRodYCoordinate">Current rod Y coordinate to move from, including ROD_START_Y </param> /// <param name="movement">Y delta to move from current rod Y coordinate (could be negative)</param> /// <returns>[True] in case there is enough space to move, [False] otherwise</returns> public bool IsEnoughSpaceToMove(IRod rod, int currentRodYCoordinate, int movement) { //Check if potential start of rod stopper is in range int potentialStartY = currentRodYCoordinate + movement; if (potentialStartY < ROD_START_Y) return false; //Check if potential end of rod stopper is in range int potentialEndY = potentialStartY + rod.StopperDistance; if (potentialEndY > ROD_END_Y) return false; //We are good, we have space to move! return true; }
/// <summary> /// Get all players Y coordinates per current rod /// </summary> /// <param name="rodType">Current rod</param> /// <param name="rodCoordinate">Y Coordinate of current rod (stopper coordinate)</param> /// <returns>Array contains player Y in index of player number + 1 /// <example>Player 1 Y coordinate is stored in array[0]</example> /// </returns> /// <exception cref="ArgumentNullException">Thrown in case rod is null</exception> public int[] AllCurrentPlayersYCoordinates(IRod rod, int rodCoordinate) { if (rod == null) throw new ArgumentNullException(String.Format( "[{0}] Unable to calculate all players Y coordinates on rod because rod is NULL!", MethodBase.GetCurrentMethod().Name)); VerifyPlayerCountOnRod(rod); VerifyYRodCoordinate(rod.StopperDistance, rodCoordinate); int[] players = new int[rod.PlayerCount]; for (int i = 0; i < rod.PlayerCount; i++) { players[i] = rodCoordinate + rod.OffsetY + i * rod.PlayerDistance; } return players; }
/// <summary> /// Main Decision Flow Method /// </summary> /// <param name="rod">Rod to use for decision</param> /// <param name="bfc">Ball Future coordinates</param> /// <returns>Rod Action to perform</returns> public override RodAction Decide(IRod rod, BallCoordinates bfc) { //Player to respond (index base is 0) int respondingPlayer = -1; //Chose responding player on rod and define action to perform RodAction action = DefineActionAndRespondingPlayer(rod, bfc, out respondingPlayer); //Define actual desired rod coordinate to move to int startStopperDesiredY = CalculateNewRodCoordinate(rod, respondingPlayer, bfc, action.Linear); action.DcCoordinate = rod.NearestPossibleDcPosition(startStopperDesiredY); //Set last decided rod and player coordinates rod.State.DcPosition = action.DcCoordinate; rod.State.ServoPosition = action.Rotation; return(action); }
/// <summary> /// Main Decision Flow Method /// </summary> /// <param name="rod">Rod to use for decision</param> /// <param name="bfc">Ball Future coordinates</param> /// <returns>Rod Action to perform</returns> public override RodAction Decide(IRod rod, BallCoordinates bfc) { //Player to respond (index base is 0) int respondingPlayer = -1; //Chose responding player on rod and define action to perform RodAction action = DefineActionAndRespondingPlayer(rod, bfc, out respondingPlayer); //Define actual desired rod coordinate to move to int startStopperDesiredY = CalculateNewRodCoordinate(rod, respondingPlayer, bfc, action.Linear); action.DcCoordinate = rod.NearestPossibleDcPosition(startStopperDesiredY); //Set last decided rod and player coordinates rod.State.DcPosition = action.DcCoordinate; if (_helper.ShouldSetServoStateFromTree(rod.RodType)) rod.State.ServoPosition = action.Rotation; return action; }
/// <summary> /// Get all players Y coordinates per current rod /// </summary> /// <param name="rodType">Current rod</param> /// <param name="rodCoordinate">Y Coordinate of current rod (stopper coordinate)</param> /// <returns>Array contains player Y in index of player number + 1 /// <example>Player 1 Y coordinate is stored in array[0]</example> /// </returns> /// <exception cref="ArgumentNullException">Thrown in case rod is null</exception> public int[] AllCurrentPlayersYCoordinates(IRod rod, int rodCoordinate) { if (rod == null) { throw new ArgumentNullException(String.Format( "[{0}] Unable to calculate all players Y coordinates on rod because rod is NULL!", MethodBase.GetCurrentMethod().Name)); } VerifyPlayerCountOnRod(rod); VerifyYRodCoordinate(rod.StopperDistance, rodCoordinate); int[] players = new int[rod.PlayerCount]; for (int i = 0; i < rod.PlayerCount; i++) { players[i] = rodCoordinate + rod.OffsetY + i * rod.PlayerDistance; } return(players); }
/// <summary> /// Verify if there is enough space to move the rod from current rod Y coordinate to new Y coordinate /// New Y coordinate is rod Y coordinate with provided movement (negative or positive) /// </summary> /// <param name="rod">Current rod</param> /// <param name="currentRodYCoordinate">Current rod Y coordinate to move from, including ROD_START_Y </param> /// <param name="movement">Y delta to move from current rod Y coordinate (could be negative)</param> /// <returns>[True] in case there is enough space to move, [False] otherwise</returns> public bool IsEnoughSpaceToMove(IRod rod, int currentRodYCoordinate, int movement) { //Check if potential start of rod stopper is in range int potentialStartY = currentRodYCoordinate + movement; if (potentialStartY < ROD_START_Y) { return(false); } //Check if potential end of rod stopper is in range int potentialEndY = potentialStartY + rod.StopperDistance; if (potentialEndY > ROD_END_Y) { return(false); } //We are good, we have space to move! return(true); }
/// <summary> /// Main Decision Flow Method desides on action and sets property of responding player /// </summary> /// <param name="rod">Rod to use for decision</param> /// <param name="bfc">Ball Future coordinates</param> /// <returns>Rod Action to perform</returns> public override RodAction Decide(IRod rod, BallCoordinates bfc) { //Get relative Y position and set Responding Player eYPositionPlayerRelative relativeY = BallYPositionToPlayerYCoordinate(bfc.Y, rod); //Get relative X position eXPositionRodRelative relativeX = BallXPositionToRodXPosition(bfc.X, rod); RodAction action = new RodAction(rod.RodType); /* * For Alpha this is good enough to make a kick. * For Beta need to define the actual sub tree. */ if (relativeX.Equals(eXPositionRodRelative.FRONT)) { action = new RodAction(rod.RodType, eRotationalMove.KICK, eLinearMove.VECTOR_BASED); } if (relativeX.Equals(eXPositionRodRelative.BACK)) { action = new RodAction(rod.RodType, eRotationalMove.RISE, eLinearMove.VECTOR_BASED); } if (relativeX.Equals(eXPositionRodRelative.CENTER)) { action = new RodAction(rod.RodType, eRotationalMove.KICK, eLinearMove.BALL_Y); } //Define actual desired rod coordinate to move to int startStopperDesiredY = CalculateNewRodCoordinate(rod, RespondingPlayer, bfc, action.Linear); action.DcCoordinate = rod.NearestPossibleDcPosition(startStopperDesiredY); //Set last decided rod and player coordinates rod.State.DcPosition = action.DcCoordinate; rod.State.ServoPosition = action.Rotation; return(action); }
/// <summary> /// Main Decision Flow Method /// </summary> /// <param name="rod">Rod to use for decision</param> /// <param name="bfc">Ball Future coordinates</param> /// <returns>Rod Action to perform</returns> public abstract RodAction Decide(IRod rod, BallCoordinates bfc);
/// <summary> /// Get Current Ball Position relative to current rod in Axe X /// </summary> /// <param name="xBallPosition">X ball coordinate</param> /// <param name="currentRod">Current rod</param> /// <returns>X position relative to current rod</returns> protected eXPositionRodRelative BallXPositionToRodXPosition(int xBallPosition, IRod currentRod) { if (xBallPosition - BALL_RADIUS > currentRod.RodXCoordinate) { return(eXPositionRodRelative.FRONT); } if (xBallPosition + BALL_RADIUS < currentRod.RodXCoordinate) { return(eXPositionRodRelative.BACK); } return(eXPositionRodRelative.CENTER); }
/// <summary> /// Get Y coordinate of rod stopper to bring responding player to desired Y coordinate /// </summary> /// <param name="rod">Current rod</param> /// <param name="desiredY">Desired Y coordinate to reach by responding player</param> /// <param name="respondingPlayer">One-Based Player Index in rod</param> /// <returns>Y coordinate of rod stopper to bring palyer to desired Y coordinate</returns> public int LocateRespondingPlayer(IRod rod, int desiredY, int respondingPlayer) { return(desiredY - rod.OffsetY - rod.PlayerDistance * (respondingPlayer - 1)); }
/// <summary> /// Get Y coordinate of rod stopper to bring responding player to desired Y coordinate /// </summary> /// <param name="rod">Current rod</param> /// <param name="desiredY">Desired Y coordinate to reach by responding player</param> /// <param name="respondingPlayer">One-Based Player Index in rod</param> /// <returns>Y coordinate of rod stopper to bring palyer to desired Y coordinate</returns> public int LocateRespondingPlayer(IRod rod, int desiredY, int respondingPlayer) { return desiredY - rod.OffsetY - rod.PlayerDistance * (respondingPlayer - 1); }
/// <summary> /// Verify Player Count on given rod is beetween 1 to 5. /// </summary> /// <param name="rod">Current rod</param> /// <exception cref="ArgumentException">Thrown in case player count on rod is not in range of 1 to 5</exception> private void VerifyPlayerCountOnRod(IRod rod) { if (rod.PlayerCount < 1 || rod.PlayerCount > 5) throw new ArgumentException(String.Format("[{0}] Number of players on rod {1} is incorrect {2}!", MethodBase.GetCurrentMethod().Name, rod.RodType.ToString(), rod.PlayerCount)); }
/// <summary> /// Get Current Ball Position relative to current rod in Axe X /// </summary> /// <param name="xBallPosition">X ball coordinate</param> /// <param name="currentRod">Current rod</param> /// <returns>X position relative to current rod</returns> protected eXPositionRodRelative BallXPositionToRodXPosition(int xBallPosition, IRod currentRod) { if (xBallPosition - BALL_RADIUS > currentRod.RodXCoordinate) return eXPositionRodRelative.FRONT; if (xBallPosition + BALL_RADIUS < currentRod.RodXCoordinate) return eXPositionRodRelative.BACK; return eXPositionRodRelative.CENTER; }
/// <summary> /// Only if ball is near the rod new location will be sent to communication layer. /// Method is based on distance, ball speed and BALL_DISTANCE_FACTOR, BALL_MAX_SPEED /// This method added to support Amit's requirement, to be able to set new position of rod based on distance to it. /// </summary> /// <param name="ballCoords">Ball Future Coordinates to respond to</param> /// <param name="currentRod">Current responding rod</param> /// <returns>[True] if we are near the rod or high speed, [False] otherwise</returns> protected bool IsBallNearTheRod(BallCoordinates ballCoords, IRod currentRod, int stopperPosition) { //By changing this parameter we set system sensitivity const double BALL_DISTANCE_FACTOR = 3000; const double BALL_MAX_SPEED = 100; if (!ballCoords.IsDefined) return false; double distanceFactor = Convert.ToDouble(TABLE_WIDTH) / Math.Abs(ballCoords.X - currentRod.RodXCoordinate); double speedFactor = 1; if (Vector2D.NotNullAndDefined(ballCoords.Vector)) { speedFactor += Math.Abs(ballCoords.Vector.X / BALL_MAX_SPEED); } double diff = (double)Math.Abs(currentRod.State.DcPosition - stopperPosition) / (double)(currentRod.PlayerCount); bool result = (distanceFactor * diff * speedFactor > BALL_DISTANCE_FACTOR); return result; }
/// <summary> /// Calculate actual linear movement (Y Axe) for current rod to perform /// Is based on desired linear move type /// </summary> /// <param name="rod">Current rod</param> /// <param name="respondingPlayer">Responding player in current rod (1 based index)</param> /// <param name="bfc">Ball Future coordinates</param> /// <param name="desiredLinearMove">Desired Linear Move Type</param> /// <returns>New rod coordinate to move to (Axe Y)</returns> protected int CalculateNewRodCoordinate(IRod rod, int respondingPlayer, BallCoordinates bfc, eLinearMove desiredLinearMove) { int pos; //Define actual desired rod coordinate to move to //NOTE: responding player might be undefined will be -1 switch (desiredLinearMove) { case eLinearMove.BALL_Y: return _helper.LocateRespondingPlayer(rod, bfc.Y, respondingPlayer); // return bfc.Y - _helper.CalculateCurrentPlayerYCoordinate(rod, _currentRodYCoordinate[rod.RodType], respondingPlayer); case eLinearMove.LEFT_BALL_DIAMETER: pos = rod.NearestPossibleDcPosition(_helper.LocateRespondingPlayer(rod, bfc.Y, respondingPlayer)); return pos + (-1) * 2 * BALL_RADIUS; case eLinearMove.RIGHT_BALL_DIAMETER: pos = rod.NearestPossibleDcPosition(_helper.LocateRespondingPlayer(rod, bfc.Y, respondingPlayer)); return pos + 2 * BALL_RADIUS; case eLinearMove.VECTOR_BASED: if (rod.Intersection.IsDefined) { int stopperPosition = _helper.LocateRespondingPlayer(rod, rod.Intersection.Y, respondingPlayer); return (IsBallNearTheRod(bfc, rod, stopperPosition)) ? stopperPosition : rod.State.DcPosition; } //return rod.IntersectionY - _helper.CalculateCurrentPlayerYCoordinate(rod, _currentRodYCoordinate[rod.RodType], respondingPlayer); return rod.State.DcPosition; //case eLinearMove.BEST_EFFORT: // return rod.BestEffort; default: return rod.State.DcPosition; } }
/// <summary> /// Define Ball Y position relative to responding player of current rod and /// re define index of responding rod setting the property Responding Player /// </summary> /// <param name="yBallCoordinate">Current ball Y coordinate</param> /// <param name="currentRod">Current rod</param> /// <param name="playerToResponse">Responding player[out] (index base is 0)</param> /// <returns>Ball Y position relative to responding player</returns> protected eYPositionPlayerRelative BallYPositionToPlayerYCoordinate(int yBallCoordinate, IRod currentRod) { //set default responding player RespondingPlayer = -1; //get array of players and their Y coordinates (player i stored in array index i - 1) int[] currentPlayerYs = _helper.AllCurrentPlayersYCoordinates(currentRod, currentRod.State.DcPosition); //calculate movements for each player to reach the ball (Y Axe only) int[] movements = _helper.CalculateYMovementForAllPlayers(currentPlayerYs, yBallCoordinate); //convert movement to distance on Y Axe (Absolute value) int[] absoluteDistance = movements.Select(x => Math.Abs(x)).ToArray(); //get index of fist minimal distance (movement is here: movements[minIndexFirst]) int minIndexFirst = Array.IndexOf(absoluteDistance, absoluteDistance.Min()); //eliminate the first minimal distance absoluteDistance[minIndexFirst] = TABLE_WIDTH * 100; //get index of second minimal distance (movement is here: movements[minIndexSecond]) int minIndexSecond = Array.IndexOf(absoluteDistance, absoluteDistance.Min()); //chosen movement to perform int movement = 0; //Define actual player to response if (_helper.IsEnoughSpaceToMove(currentRod, currentRod.State.DcPosition, movements[minIndexFirst])) { //as index starts from 0 => first one is 1 RespondingPlayer = minIndexFirst + 1; movement = movements[minIndexFirst]; } else if (_helper.IsEnoughSpaceToMove(currentRod, currentRod.State.DcPosition, movements[minIndexSecond])) { //as index starts from 0 => first one is 1 RespondingPlayer = minIndexSecond + 1; movement = movements[minIndexSecond]; } else //In case we have no way to move for all the players { RespondingPlayer = minIndexFirst + 1; movement = 0; } //In case we reach the ball - no move needed if (Math.Abs(movement) < PLAYER_WIDTH) return eYPositionPlayerRelative.CENTER; else if (movement > 0) return eYPositionPlayerRelative.RIGHT; else // (movement < 0) return eYPositionPlayerRelative.LEFT; }
/// <summary> /// Define Ball Y position relative to responding player of current rod and /// re define index of responding rod setting the property Responding Player /// </summary> /// <param name="yBallCoordinate">Current ball Y coordinate</param> /// <param name="currentRod">Current rod</param> /// <param name="playerToResponse">Responding player[out] (index base is 0)</param> /// <returns>Ball Y position relative to responding player</returns> protected eYPositionPlayerRelative BallYPositionToPlayerYCoordinate(int yBallCoordinate, IRod currentRod) { //set default responding player RespondingPlayer = -1; //get array of players and their Y coordinates (player i stored in array index i - 1) int[] currentPlayerYs = _helper.AllCurrentPlayersYCoordinates(currentRod, currentRod.State.DcPosition); //calculate movements for each player to reach the ball (Y Axe only) int[] movements = _helper.CalculateYMovementForAllPlayers(currentPlayerYs, yBallCoordinate); //convert movement to distance on Y Axe (Absolute value) int[] absoluteDistance = movements.Select(x => Math.Abs(x)).ToArray(); //get index of fist minimal distance (movement is here: movements[minIndexFirst]) int minIndexFirst = Array.IndexOf(absoluteDistance, absoluteDistance.Min()); //eliminate the first minimal distance absoluteDistance[minIndexFirst] = TABLE_WIDTH * 100; //get index of second minimal distance (movement is here: movements[minIndexSecond]) int minIndexSecond = Array.IndexOf(absoluteDistance, absoluteDistance.Min()); //chosen movement to perform int movement = 0; //Define actual player to response if (_helper.IsEnoughSpaceToMove(currentRod, currentRod.State.DcPosition, movements[minIndexFirst])) { //as index starts from 0 => first one is 1 RespondingPlayer = minIndexFirst + 1; movement = movements[minIndexFirst]; } else { //as index starts from 0 => first one is 1 RespondingPlayer = minIndexSecond + 1; movement = movements[minIndexSecond]; } //In case we reach the ball - no move needed if (Math.Abs(movement) < PLAYER_WIDTH) { return(eYPositionPlayerRelative.CENTER); } else if (movement > 0) { return(eYPositionPlayerRelative.RIGHT); } else // (movement < 0) { return(eYPositionPlayerRelative.LEFT); } }
/* * Currently not in use methods. Need to verify if needed before TESTING */ /// <summary> /// Calculate current Player Y coordinate /// </summary> /// <param name="rod">Current rod</param> /// <param name="currentRodYCoordinate">Current Rod Y coordinates (stopper)</param> /// <param name="playerIndex">Chosen player index to perform action (index 1 based)</param> /// <returns>Chosen player Y coordinate</returns> /// <exception cref="ArgumentOutOfRangeException">Thrown in case player index is out of range</exception> public int CalculateCurrentPlayerYCoordinate(IRod rod, int currentRodYCoordinate, int playerIndex) { if (playerIndex > rod.PlayerCount || playerIndex < 1) throw new ArgumentOutOfRangeException(String.Format( "Player index {0} for rod type {1} is wrong! Players count is {2}", playerIndex, rod.RodType, rod.PlayerCount)); return rod.OffsetY + currentRodYCoordinate + rod.PlayerDistance * (playerIndex - 1); }
/// <summary> /// Main Decision Flow Method decides on action and sets property of responding player /// </summary> /// <param name="rod">Rod to use for decision</param> /// <param name="bfc">Ball Future coordinates</param> /// <returns>Rod Action to perform</returns> public override RodAction Decide(IRod rod, BallCoordinates bfc) { RodAction action = new RodAction(rod.RodType); //Action will be ignored if not enough time passed since last request was made inside sector if (!IgnoreDecision(rod.RodType)) { //Get relative Y position and set Responding Player eYPositionPlayerRelative relativeY = BallYPositionToPlayerYCoordinate(bfc.Y, rod); //Get relative X position eXPositionRodRelative relativeX = BallXPositionToRodXPosition(bfc.X, rod); /* * Beta Version of inner DECISION TREE */ switch (relativeX) { case eXPositionRodRelative.FRONT: switch (relativeY) { case eYPositionPlayerRelative.RIGHT: case eYPositionPlayerRelative.LEFT: switch (rod.State.ServoPosition) { case eRotationalMove.RISE: case eRotationalMove.DEFENCE: action = new RodAction(rod.RodType, eRotationalMove.DEFENCE, eLinearMove.BALL_Y); break; case eRotationalMove.KICK: action = new RodAction(rod.RodType, eRotationalMove.DEFENCE, eLinearMove.NA); break; } break; case eYPositionPlayerRelative.CENTER: switch (rod.State.ServoPosition) { case eRotationalMove.RISE: case eRotationalMove.DEFENCE: action = new RodAction(rod.RodType, eRotationalMove.KICK, eLinearMove.NA); break; case eRotationalMove.KICK: if (_helper.IsEnoughSpaceToMove(rod, rod.State.DcPosition, BALL_RADIUS)) { action = new RodAction(rod.RodType, eRotationalMove.NA, eLinearMove.RIGHT_BALL_DIAMETER); } else { action = new RodAction(rod.RodType, eRotationalMove.NA, eLinearMove.LEFT_BALL_DIAMETER); } break; } break; } break; case eXPositionRodRelative.CENTER: case eXPositionRodRelative.BACK: switch (relativeY) { case eYPositionPlayerRelative.RIGHT: case eYPositionPlayerRelative.LEFT: switch (rod.State.ServoPosition) { case eRotationalMove.RISE: action = new RodAction(rod.RodType, eRotationalMove.RISE, eLinearMove.BALL_Y); break; case eRotationalMove.DEFENCE: action = new RodAction(rod.RodType, eRotationalMove.RISE, eLinearMove.NA); break; case eRotationalMove.KICK: action = new RodAction(rod.RodType, eRotationalMove.DEFENCE, eLinearMove.NA); break; } break; case eYPositionPlayerRelative.CENTER: switch (rod.State.ServoPosition) { case eRotationalMove.RISE: action = new RodAction(rod.RodType, eRotationalMove.KICK, eLinearMove.NA); break; case eRotationalMove.DEFENCE: case eRotationalMove.KICK: if (_helper.IsEnoughSpaceToMove(rod, rod.State.DcPosition, BALL_RADIUS)) { action = new RodAction(rod.RodType, eRotationalMove.NA, eLinearMove.RIGHT_BALL_DIAMETER); } else { action = new RodAction(rod.RodType, eRotationalMove.NA, eLinearMove.LEFT_BALL_DIAMETER); } break; } break; } break; } Log.Print(String.Format("Defined action for {0}: [{1}] [{2}]", rod.RodType, action.Rotation, action.Linear), eCategory.Info, LogTag.DECISION); ActivateDelay(action); //Define actual desired rod coordinate to move to int startStopperDesiredY = CalculateNewRodCoordinate(rod, RespondingPlayer, bfc, action.Linear); action.DcCoordinate = rod.NearestPossibleDcPosition(startStopperDesiredY); } else { Log.Print(String.Format("Ignoring inner tree of {0} for {1} milliseconds", rod.RodType, (ACTION_DELAY - _sectorWatch[rod.RodType].Elapsed).TotalMilliseconds), eCategory.Debug, LogTag.DECISION); } //Set last decided rod and player coordinates if it was defined if (action.Linear!=eLinearMove.NA) rod.State.DcPosition = action.DcCoordinate; if (_helper.ShouldSetServoStateFromTree(rod.RodType)) if (action.Rotation != eRotationalMove.NA) rod.State.ServoPosition = action.Rotation; return action; }