public void ProcessHit(ASurfaceExpression surface, double horizontalHitCoeff, double verticalHitCoeff) { var surfaceNormal = surface.Normal; var projection = ProjectToSurface(surface); projection.Position.Vertical = 2 * Constants.BallRadius * surfaceNormal - projection.Position.Vertical; projection.Speed.Vertical *= -verticalHitCoeff; var ballPoint = -Constants.BallRadius * surfaceNormal; var fullPerpendicularSpeed = projection.Speed.Horizontal + Point3DExpression.VectorMult(ballPoint, AngularSpeed); var force = -horizontalHitCoeff * fullPerpendicularSpeed; projection.Speed.Horizontal += force; AngularSpeed += Point3DExpression.VectorMult(force, ballPoint.Normal) / Constants.BallRadius; RestoreFromSurfaceProjection(surface, projection); }
public bool Initialize(State state, Player player) { initialState = state; state = state.Clone(false); this.player = player.Clone(); isServing = state.GameState == GameState.Serving; maxHeightLimitation = isServing ? Constants.MaxBallServeMaxHeight : Constants.MaxBallMaxHeight; playerSide = player.Side; if (state.GameState == GameState.Failed || player.Index != state.HitSide) { return(false); } var states = new List <State>(); if (isServing) { while (state.Ball.Speed.Y >= 0 || state.Ball.Position.Y >= Constants.MaxBallServeY) { state.DoStep(); } var attemptState = state.Clone(false); while (attemptState.Ball.Position.Y >= Constants.MinBallServeY) { states.Add(attemptState.Clone(false)); attemptState.DoStep(); } states.Add(attemptState.Clone(false)); hitTimeVar.MinValue = state.Time; hitTimeVar.MaxValue = attemptState.Time; } else { if (!state.DoStepsUntilGameState(GameState.FlyingToBat)) { return(false); } var attemptState = state.Clone(false); // Calculate time till max height while (attemptState.GameState != GameState.Failed) { states.Add(attemptState.Clone(false)); if (attemptState.DoStep().HasOneOfEvents(Event.MaxHeight)) { break; } } double ballMaxHeightTime = attemptState.Time - state.Time; // Calculate time till ball fall while (attemptState.GameState != GameState.Failed) { states.Add(attemptState.Clone(false)); if (attemptState.DoStep().HasOneOfEvents(Event.LowHit)) { break; } } double ballFallTime = attemptState.Time - state.Time; states.Add(attemptState.Clone(false)); hitTimeVar.MinValue = state.Time + player.Strategy.GetMinBackHitTime(ballMaxHeightTime, ballFallTime); hitTimeVar.MaxValue = state.Time + player.Strategy.GetMaxBackHitTime(ballMaxHeightTime, ballFallTime); } var statesCount = states.Count; if (statesCount < 2) { return(false); } var ballPositions = new Point3D[statesCount]; var ballSpeeds = new Point3D[statesCount]; var ballForces = new Point3D[statesCount]; var ballAngularSpeeds = new Point3D[statesCount]; var ballAngularForces = new Point3D[statesCount]; for (var index = 0; index < statesCount; index++) { var ball = states[index].Ball; ballPositions[index] = ball.Position; ballSpeeds[index] = ball.Speed; ballForces[index] = ball.Force; ballAngularSpeeds[index] = ball.AngularSpeed; ballAngularForces[index] = ball.AngularForce; } optimizationVariables = new Variable[] { hitTimeVar, hitSpeedVar, attackPitchVar, attackYawVar, velocityAttackPitchVar, velocityAttackYawVar }; optimizationVariablesCount = optimizationVariables.Length; var ballForceExpression = FunctionByPoints.Create3DFunctionByPoints("BallForce", hitTimeVar, ballForces, state.Time, Constants.SimulationFrameTime); var ballSpeedExpression = FunctionByPoints.Create3DFunctionByPoints("BallSpeed", hitTimeVar, ballSpeeds, state.Time, Constants.SimulationFrameTime, ballForceExpression); var ballPositionExpression = FunctionByPoints.Create3DFunctionByPoints("BallPosition", hitTimeVar, ballPositions, state.Time, Constants.SimulationFrameTime, ballSpeedExpression); var ballAngularForceExpression = FunctionByPoints.Create3DFunctionByPoints("BallAngularForce", hitTimeVar, ballAngularForces, state.Time, Constants.SimulationFrameTime); var ballAngularSpeedExpression = FunctionByPoints.Create3DFunctionByPoints("BallAngularSpeed", hitTimeVar, ballAngularSpeeds, state.Time, Constants.SimulationFrameTime, ballAngularForceExpression); initialBallExpression = new BallExpression(ballPositionExpression, ballSpeedExpression, ballAngularSpeedExpression); var ballReverseSpeed = -initialBallExpression.Speed; var reverseBallSpeedPitch = ballReverseSpeed.Pitch; var reverseBallSpeedYaw = ballReverseSpeed.Yaw; if (isServing) { // Yaw for vertical vector is undefined, so use default bat direction instead reverseBallSpeedYaw = player.DefaultNormal.Yaw; } Expression minVelocityAttackPitch, maxVelocityAttackPitch; double minHitSpeed, maxHitSpeed, minAttackPitch, maxAttackPitch, maxAttackYaw, maxVelocityAttackYaw; if (isServing) { var ballX = Math.Abs(state.Ball.Position.X); minHitSpeed = Constants.MaxPlayerSpeed * player.Strategy.GetMinServeHitSpeed(ballX); maxHitSpeed = Constants.MaxPlayerSpeed * player.Strategy.GetMaxServeHitSpeed(ballX); minAttackPitch = player.Strategy.GetMinServeAttackAngle(); maxAttackPitch = player.Strategy.GetMaxServeAttackAngle(); minVelocityAttackPitch = player.Strategy.GetMinServeVelocityAttackAngle(); maxVelocityAttackPitch = player.Strategy.GetMaxServeVelocityAttackAngle(); maxAttackYaw = Misc.FromDegrees(40); maxVelocityAttackYaw = Misc.FromDegrees(70); } else { minHitSpeed = Constants.MaxPlayerSpeed * player.Strategy.GetMinHitSpeed(); maxHitSpeed = Constants.MaxPlayerSpeed * player.Strategy.GetMaxHitSpeed(); minAttackPitch = player.Strategy.GetMinAttackAngle(); maxAttackPitch = player.Strategy.GetMaxAttackAngle(); minVelocityAttackPitch = player.Strategy.GetMinVelocityAttackAngle(); maxVelocityAttackPitch = player.Strategy.GetMaxVelocityAttackAngle(); maxAttackYaw = Misc.FromDegrees(50); maxVelocityAttackYaw = Misc.FromDegrees(60); } //maxAttackYaw = maxVelocityAttackYaw = 0.0001; var hitSpeed = minHitSpeed + hitSpeedVar * (maxHitSpeed - minHitSpeed); var attackPitch = minAttackPitch + attackPitchVar * (maxAttackPitch - minAttackPitch); if (!isServing) { minVelocityAttackPitch = Expression.Max(minVelocityAttackPitch, attackPitch - Constants.MaxAttackAngleDifference); maxVelocityAttackPitch = Expression.Min(maxVelocityAttackPitch, attackPitch + Constants.MaxAttackAngleDifference); } var velocityAttackPitch = minVelocityAttackPitch + velocityAttackPitchVar * (maxVelocityAttackPitch - minVelocityAttackPitch); var attackYaw = attackYawVar * maxAttackYaw; var velocityAttackYaw = velocityAttackYawVar * maxVelocityAttackYaw; playerExpression = new PlayerExpression() { Index = player.Index, AnglePitch = reverseBallSpeedPitch + attackPitch, AngleYaw = reverseBallSpeedYaw + attackYaw, Speed = hitSpeed * Point3DExpression.FromAngles(reverseBallSpeedPitch + velocityAttackPitch, reverseBallSpeedYaw + attackYaw + velocityAttackYaw) }; playerExpression.Position = initialBallExpression.Position - Constants.BallRadius * playerExpression.Normal; playerExpression.Alias = "Bat"; initialBallExpression.ProcessHit(playerExpression, Constants.BallHitHorizontalCoeff, Constants.BallHitVerticalCoeff); initialBallExpressionDerivatives = new BallExpression[optimizationVariablesCount]; playerExpressionDerivatives = new PlayerExpression[optimizationVariablesCount]; for (var i = 0; i < optimizationVariablesCount; i++) { initialBallExpressionDerivatives[i] = initialBallExpression.Derivate(optimizationVariables[i]); playerExpressionDerivatives[i] = playerExpression.Derivate(optimizationVariables[i]); } return(true); }
public void RestoreFromSurfaceProjection(ASurfaceExpression surface, ProjectionToSurfaceExpression projection) { Position = surface.Position + projection.Position.Full; Speed = surface.Speed + projection.Speed.Full; }
public BallExpression(Point3DExpression position, Point3DExpression speed, Point3DExpression angularSpeed) { Position = position; Speed = speed; AngularSpeed = angularSpeed; }