/*-
* Copyright Bogdan Mocanu, 2009
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 3
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import static java.lang.Math.floor;
/**
* The Alpha algorithm implementation of the Diego player for the sled.
*
* @author mocanu
*/
public class AlphaSledAlgorithm extends AbstractAlgorithm {
public AlphaAlgorithm parentAlgorithm;
private int planAbandonCount = -1;
private boolean currentPlan_wandering_lockedOn = false;
private double currentPlan_delta = 0;
private int currentPlan_grays = 0;
private double currentPlan_radius = 0;
public RealPoint currentPlan_center = new RealPoint();
private RealPoint currentPlan_startCoord = new RealPoint( Integer.MAX_VALUE, Integer.MAX_VALUE );
private int newPlan_grays = 0;
private double newPlan_radius = 0;
private RealPoint newPlan_center = new RealPoint();
// -------------------------------------------------------------------------------------------------
/**
* {@inheritDoc}
*/
@Override
public void execute() {
findRadarCircleRadius();
reAnalyzeCurrentPlan();
if ( distance( currentPlan_startCoord, stateManager.friendSled.coord ) < 5 ) {
// we created a closed area
if ( Const.DEBUG_SLED ) {
log.info( "sled", "BOOM! Closed area!" );
}
resetCurrentPlan();
}
if ( Const.DEBUG_SLED ) {
log.info( "sled", "CurrentPlan", "grays", currentPlan_grays, "rad", currentPlan_radius );
log.info( "sled", "NewPlan", "grays", newPlan_grays, "rad", newPlan_radius );
}
if ( newPlan_radius != 0 && planAbandonCount < Const.PARAM_RADAR_CIRCLE_ABANDON_LIMIT ) {
// ok, we have a possible new plan
// -------------------------------------------------------------------------------------------------
if ( parentAlgorithm.strategy == AlphaStrategy.OFFENSE ) {
if ( newPlan_grays > currentPlan_grays + 1 ) {
// the new plan is better than the old one
adoptNewPlan();
} else if ( newPlan_grays == currentPlan_grays ) {
// the new plan is just the same
if ( newPlanHasBiggerRadius() ) {
// let's favor this one
adoptNewPlan();
} else {
// keep the old plan
}
}
}
// -------------------------------------------------------------------------------------------------
if ( parentAlgorithm.strategy == AlphaStrategy.DEFENSE ) {
if ( newPlan_grays > currentPlan_grays ) {
// the new plan is just better than the old one
adoptNewPlan();
} else if ( newPlan_grays == currentPlan_grays ) {
// the new plan is just the same
if ( newPlanHasBiggerRadius() ) {
// let's favor this one
adoptNewPlan();
} else {
// keep the old plan
}
}
}
// -------------------------------------------------------------------------------------------------
} else if ( newPlan_radius == 0 ) {
// well, if we don't have a plan, just keep wandering on the table
if ( !currentPlan_wandering_lockedOn ) {
computeWanderingTarget();
currentPlan_grays = 0;
planAbandonCount = -1;
}
}
responseManager.sledDirectionDelta = currentPlan_delta;
}
// ------------------------------------------------------------------------------------------------------
/**
* Finds the radar circle that the sled can describe, in order to get the best combination of
* RED and GRAY pucks.
* <p>
* The absolute value represents the radius of the circle.
* <p>
* The sign of the value represents the side of the circle, based on the current direction of
* the sled. Positive value represents the circle from the "left" side of the
* direction (counter clockwise) and negative means on the "right" side of the sled's
* direction.
*/
private void findRadarCircleRadius() {
Sled sled = stateManager.friendSled;
double maxRadius = Const.PARAM_TRAIL_SAFE_LIMIT / TWO_PI;
double radiusStep = 5;
double reducedSledAngle = sled.direction - floor( sled.direction / TWO_PI ) * TWO_PI;
RealPoint circleCenter = new RealPoint();
double bestChoiceRadius = 0;
int bestChoiceGrays = 0;
RealPoint bestChoiceCenter = new RealPoint();
int[] sidesForRadar = new int[] { 1, -1 };
for ( int sideIndex = 0; sideIndex < sidesForRadar.length; sideIndex++ ) {
double currentRadius = radiusStep;
while ( currentRadius <= maxRadius ) {
movePointAt90degreesAndDistance( circleCenter, sled.coord, reducedSledAngle, currentRadius, sidesForRadar[sideIndex] );
// compute the situation of pucks inside the circle
int grayPucks = 0;
int friendPucks = 0;
int enemyPucks = 0;
for ( Puck puck : stateManager.pucks ) {
switch ( puck.type ) {
case GRAY: {
if ( distance( puck.coord, circleCenter ) < currentRadius
- Const.PARAM_FPUCK_TO_BORDER_DISTANCE_LIMIT ) {
grayPucks++;
}
break;
}
case FRIEND: {
if ( distance( puck.coord, circleCenter ) < currentRadius
- Const.PARAM_FPUCK_TO_BORDER_DISTANCE_LIMIT ) {
friendPucks++;
}
break;
}
case ENEMY: {
if ( distance( puck.coord, circleCenter ) < currentRadius
- Const.PARAM_EPUCK_TO_BORDER_DISTANCE_LIMIT ) {
enemyPucks++;
}
break;
}
}
}
if ( enemyPucks == 0 && grayPucks > bestChoiceGrays && friendPucks >= 1 ) {
bestChoiceGrays = grayPucks;
bestChoiceRadius = currentRadius * sidesForRadar[sideIndex];
bestChoiceCenter.x = circleCenter.x;
bestChoiceCenter.y = circleCenter.y;
}
currentRadius += radiusStep;
}
}
newPlan_radius = bestChoiceRadius;
newPlan_grays = bestChoiceGrays;
newPlan_center.x = bestChoiceCenter.x;
newPlan_center.y = bestChoiceCenter.y;
}
private void reAnalyzeCurrentPlan() {
for ( int puckIndex = 0; puckIndex < stateManager.pucks.length; puckIndex++ ) {
Puck currentPuck = stateManager.pucks[puckIndex];
int grayPucks = 0;
int friendPucks = 0;
int enemyPucks = 0;
switch ( currentPuck.type ) {
case GRAY: {
if ( distance( currentPuck.coord, currentPlan_center ) < currentPlan_radius
- Const.PARAM_FPUCK_TO_BORDER_DISTANCE_LIMIT ) {
grayPucks++;
}
break;
}
case FRIEND: {
if ( distance( currentPuck.coord, currentPlan_center ) < currentPlan_radius
- Const.PARAM_FPUCK_TO_BORDER_DISTANCE_LIMIT ) {
friendPucks++;
}
break;
}
case ENEMY: {
if ( distance( currentPuck.coord, currentPlan_center ) < currentPlan_radius
- 1
- Const.PARAM_EPUCK_TO_BORDER_DISTANCE_LIMIT ) {
enemyPucks++;
}
break;
}
}
if ( enemyPucks > 0 || friendPucks <= 0 ) {
currentPlan_grays = -1;
} else {
currentPlan_grays = grayPucks;
}
}
}
private void adoptNewPlan() {
planAbandonCount++;
double sledDelta = upperBound( Const.SLED_SPEED / newPlan_radius, Const.SLED_TURN_LIMIT );
if ( Const.DEBUG_SLED ) {
log.info( "sled", "DELTA", sledDelta );
}
currentPlan_wandering_lockedOn = false;
currentPlan_startCoord.x = stateManager.friendSled.coord.x;
currentPlan_startCoord.y = stateManager.friendSled.coord.y;
currentPlan_delta = sledDelta;
currentPlan_radius = newPlan_radius;
currentPlan_grays = newPlan_grays;
currentPlan_center.x = newPlan_center.x;
currentPlan_center.y = newPlan_center.y;
}
private void resetCurrentPlan() {
if ( Const.DEBUG_SLED ) {
log.info( "sled", "Reset current plan" );
}
planAbandonCount = -1;
currentPlan_delta = 0;
currentPlan_grays = 0;
currentPlan_radius = 0;
}
private boolean newPlanHasBiggerRadius() {
return Math.abs( newPlan_radius ) > Math.abs( currentPlan_radius );
}
private void computeWanderingTarget() {
// get the most distanced friend puck
double maxDistance = 0;
Puck maxPuck = null;
for ( int index = 0; index < stateManager.pucksNr; index++ ) {
Puck currentPuck = stateManager.pucks[index];
if ( currentPuck.type == PuckType.FRIEND ) {
double currentDistance = distance( currentPuck.coord, stateManager.friendSled.coord );
if ( currentDistance > maxDistance ) {
maxDistance = currentDistance;
maxPuck = currentPuck;
}
}
}
if ( maxPuck != null ) {
// get the angle needed to get there
double targetAngle = angleOf( maxPuck.coord, stateManager.friendSled.coord );
double reducedSledAngle = stateManager.friendSled.direction
- floor( stateManager.friendSled.direction / TWO_PI ) * TWO_PI;
if ( targetAngle < reducedSledAngle ) {
currentPlan_delta = -upperBound( reducedSledAngle - targetAngle, Const.SLED_TURN_LIMIT );
} else {
currentPlan_delta = upperBound( targetAngle - reducedSledAngle, Const.SLED_TURN_LIMIT );
}
currentPlan_wandering_lockedOn = true;
} else {
currentPlan_delta = Const.SLED_TURN_LIMIT * (randomNumberGenerator.nextInt( 20 ) < 14 ? 1 : -1);
}
}
}