// NOTE: Duration needs to be a multiple of 2 since both the upwards motion and the downwards motion
        //       count as 1 instructionSent
        public static IBallControlStrategy TwoStepBouncing(int duration, ITiltController tiltController,
                                                           Vector2?target = null, float lowPos = 0.05f, float highPos = 0.058f, float moveTime = 0.1f,
                                                           Action action  = null)
        {
            var currentPositionIsUp = false;

            return(new BallControlStrategy((ballData, machineController, instructionCount) =>
            {
                if (!ballData.BallIsMovingUp && ballData.CurrentPositionVector.z < 140f && !currentPositionIsUp)
                {
                    // if we're in here, the ball is coming downwards

                    BallControlling.MoveToHeightWithAlternatingXYTiltCorrection(machineController, tiltController,
                                                                                ballData, highPos, moveTime, instructionCount, target);

                    currentPositionIsUp = true;
                    return true;
                }

                if (ballData.BallIsMovingUp && currentPositionIsUp)
                {
                    // if we're in here, the ball is moving upwards
                    // so if the ball IS moving up, and the last thing we did was hitting the ball, then we should move down again

                    BallControlling.MoveToHeightWithAlternatingXYTiltCorrection(machineController, tiltController,
                                                                                ballData, lowPos, moveTime, instructionCount, target);

                    currentPositionIsUp = false;
                    return true;
                }

                // instructionSent: false
                return false;
            }, duration, true, onStrategyExecutionStart: action));
        }
        public static IBallControlStrategy StepBouncing_Up(ITiltController tiltController,
                                                           Vector2?target = null, float highPos = 0.058f, float moveTime = 0.1f,
                                                           Action action  = null)
        {
            return(new BallControlStrategy((ballData, machineController, instructionCount) =>
            {
                if (!ballData.BallIsMovingUp && ballData.CurrentPositionVector.z < 140f)
                {
                    // if we're in here, the ball is coming downwards
                    BallControlling.MoveToHeightWithXYTiltCorrection(machineController, tiltController,
                                                                     ballData, highPos, moveTime, target);

                    return true;
                }
                return false;
            }, 1, true, onStrategyExecutionStart: action));
        }
        public static IBallControlStrategy StepBouncing_Down(ITiltController tiltController,
                                                             Vector2?target = null, float lowPos = 0.05f, float moveTime = 0.1f,
                                                             Action action  = null)
        {
            return(new BallControlStrategy((ballData, machineController, instructionCount) =>
            {
                if (ballData.BallIsMovingUp)
                {
                    // if we're in here, the ball is moving upwards
                    // so if the ball IS moving up, and the last thing we did was hitting the ball, then we should move down again
                    BallControlling.MoveToHeightWithXYTiltCorrection(machineController, tiltController,
                                                                     ballData, lowPos, moveTime, target);

                    return true;
                }
                return false;
            }, 1, true, onStrategyExecutionStart: action));
        }