Exemplo n.º 1
0
        /// <summary>
        /// Gets a random ball direction which does not end up in a hole within the first direction.
        /// If no valid direction is found within the defined calculation steps, the last calculated direction is returned.
        /// </summary>
        /// <param name="window">The window.</param>
        /// <param name="ballPosition">The ball position.</param>
        /// <param name="maxCalculationSteps">The maximum calculation steps.</param>
        /// <returns>
        /// The direction
        /// </returns>
        public static Vector GetRandomBallDirection(Window window, Point ballPosition, int maxCalculationSteps = 2000)
        {
            if (window == null)
            {
                throw new ArgumentNullException("window");
            }

            var directionsCalculated = 0;
            var random = new Random(DateTime.Now.GetHashCode());

            while (true)
            {
                var clickPosition = new Point(
                    random.Next(0, GameConfiguration.GameWindowWidth),
                    random.Next(0, GameConfiguration.GameWindowHeight));

                // Set the direction and negate it, because the ball has to move away from the queue
                var direction = new Vector(clickPosition.X, clickPosition.Y)
                                - new Vector(ballPosition.X, ballPosition.Y);
                direction.Negate();
                directionsCalculated++;

                // The direction is valid if there are no holes in the window
                if (!window.Holes.Any())
                {
                    return(direction);
                }

                // Create a fake point outside of the window in the ball direction
                var intersectionTestPoint = ballPosition + (direction * 10);
                var intersectionFound     = false;

                // Check for an intersection between the ball and a hole in the set direction
                foreach (var hole in window.Holes)
                {
                    Point?firstIntersection;
                    Point?secondIntersection;

                    var intersection = CalculationHelpers.CalculateLineSphereIntersection(
                        hole.CenterPosition,
                        hole.Radius,
                        ballPosition,
                        intersectionTestPoint,
                        out firstIntersection,
                        out secondIntersection);

                    // Cancel the check because we have an intersection
                    if (intersection > 0)
                    {
                        Tracer.Debug(string.Format("GameHelpers :: GetRandomBallDirection :: Found an intersection between click position {0}, ball position {1}, direction {2} and hole position {3}", clickPosition, ballPosition, direction, hole.CenterPosition));
                        intersectionFound = true;
                        break;
                    }
                }

                // Return the current direction if there was no valid direction within the defined tries
                if (directionsCalculated == maxCalculationSteps)
                {
                    Tracer.Debug(string.Format("GameHelpers :: GetRandomBallDirection :: Returning direction {0} because the amount of tries is reached", direction));
                    return(direction);
                }

                // If we have a intersection, we need another try to find a valid direction
                if (intersectionFound)
                {
                    continue;
                }

                Tracer.Debug(string.Format("GameHelpers :: GetRandomBallDirection :: Returning direction {0} after {1} tries", direction, directionsCalculated));
                return(direction);
            }
        }
Exemplo n.º 2
0
        /// <summary>
        /// Calculates the ball animation tasks.
        /// </summary>
        /// <param name="initialWindow">The initial window.</param>
        /// <param name="startPosition">The start position.</param>
        /// <param name="initialDirection">The initial direction.</param>
        /// <returns>The calculated animation queue</returns>
        private Queue <BallAnimationTask> CalculateBallAnimationTasks(Models.Window initialWindow, Point startPosition, Vector initialDirection)
        {
            var animationQueue      = new Queue <BallAnimationTask>();
            var isLastAnimation     = false;
            var currentWindow       = initialWindow;
            var currentBallPosition = startPosition;
            var currentDirection    = initialDirection;
            var stepCounter         = 0;
            var currentTask         = new BallAnimationTask {
                Window = currentWindow
            };
            var          previousIntersection    = Intersection.None;
            var          windowPreviouslyChanged = false;
            const double BallRadius = GameConfiguration.BallDiameter / 2;

            while (!isLastAnimation)
            {
                // Ensure an end of the animation
                stepCounter++;
                if (stepCounter == 30)
                {
                    isLastAnimation                = true;
                    currentTask.IsLastAnimation    = true;
                    currentTask.IntersectsWithHole = false;
                }

                var borderPositionCorrection = new Point();

                // Calculate the ignorable intersection
                var ignoredIntersection = Intersection.None;
                if (previousIntersection != Intersection.None)
                {
                    if (windowPreviouslyChanged)
                    {
                        switch (previousIntersection)
                        {
                        case Intersection.TopWall:
                            ignoredIntersection = Intersection.BottomWall;
                            break;

                        case Intersection.LeftWall:
                            ignoredIntersection = Intersection.RightWall;
                            break;

                        case Intersection.RightWall:
                            ignoredIntersection = Intersection.LeftWall;
                            break;

                        case Intersection.BottomWall:
                            ignoredIntersection = Intersection.TopWall;
                            break;
                        }
                    }
                    else
                    {
                        ignoredIntersection = previousIntersection;
                    }
                }

                var           currentIntersection  = Intersection.None;
                Point?        intersectionPosition = null;
                Models.Window neighbourWindow      = null;

                // Generate a point outside of the window
                var intersectionTestPoint = currentBallPosition + (currentDirection * 1000);

                // Check for hole intersection
                if (currentWindow.Holes != null)
                {
                    foreach (var hole in currentWindow.Holes)
                    {
                        Point?firstIntersection;
                        Point?secondIntersection;

                        var intersection = CalculationHelpers.CalculateLineSphereIntersection(
                            hole.CenterPosition,
                            hole.Radius,
                            currentBallPosition,
                            intersectionTestPoint,
                            out firstIntersection,
                            out secondIntersection);

                        if (intersection > 0 && firstIntersection != null)
                        {
                            currentIntersection = Intersection.Hole;

                            // This hole intersection is only relevant, if the distance between this intersection and the ball is the shortest
                            if (intersectionPosition == null ||
                                currentBallPosition.DistanceTo((Point)firstIntersection)
                                < currentBallPosition.DistanceTo((Point)intersectionPosition))
                            {
                                intersectionPosition = firstIntersection;
                            }
                        }
                    }
                }

                // Check for top wall hit
                if (intersectionPosition == null && ignoredIntersection != Intersection.TopWall)
                {
                    intersectionPosition = CalculationHelpers.GetLineIntersection(
                        currentBallPosition,
                        intersectionTestPoint,
                        new Point(0, 0),
                        new Point(GameConfiguration.GameWindowWidth, 0));

                    if (intersectionPosition != null)
                    {
                        previousIntersection = currentIntersection = Intersection.TopWall;

                        var upperWindow = this.CurrentGame.Map.Windows.FirstOrDefault(w => w.X == currentWindow.X && w.Y == currentWindow.Y - 1);
                        if (upperWindow != null)
                        {
                            neighbourWindow = upperWindow;
                        }
                        else
                        {
                            borderPositionCorrection.Y += BallRadius;
                        }
                    }
                }

                if (intersectionPosition == null && ignoredIntersection != Intersection.LeftWall)
                {
                    // Check for left wall hit
                    intersectionPosition = CalculationHelpers.GetLineIntersection(
                        currentBallPosition,
                        intersectionTestPoint,
                        new Point(0, 0),
                        new Point(0, GameConfiguration.GameWindowWidth));

                    if (intersectionPosition != null)
                    {
                        previousIntersection = currentIntersection = Intersection.LeftWall;

                        var leftWindow = this.CurrentGame.Map.Windows.FirstOrDefault(w => w.X == currentWindow.X - 1 && w.Y == currentWindow.Y);
                        if (leftWindow != null)
                        {
                            neighbourWindow = leftWindow;
                        }
                        else
                        {
                            borderPositionCorrection.X += BallRadius;
                        }
                    }
                }

                if (intersectionPosition == null && ignoredIntersection != Intersection.RightWall)
                {
                    // Check for right wall hit
                    intersectionPosition = CalculationHelpers.GetLineIntersection(
                        currentBallPosition,
                        intersectionTestPoint,
                        new Point(GameConfiguration.GameWindowWidth, 0),
                        new Point(GameConfiguration.GameWindowWidth, GameConfiguration.GameWindowWidth));

                    if (intersectionPosition != null)
                    {
                        previousIntersection = currentIntersection = Intersection.RightWall;

                        var rightWindow = this.CurrentGame.Map.Windows.FirstOrDefault(w => w.X == currentWindow.X + 1 && w.Y == currentWindow.Y);
                        if (rightWindow != null)
                        {
                            neighbourWindow = rightWindow;
                        }
                        else
                        {
                            borderPositionCorrection.X -= BallRadius;
                        }
                    }
                }

                if (intersectionPosition == null && ignoredIntersection != Intersection.BottomWall)
                {
                    // Check for bottom wall hit
                    intersectionPosition = CalculationHelpers.GetLineIntersection(
                        currentBallPosition,
                        intersectionTestPoint,
                        new Point(0, GameConfiguration.GameWindowWidth),
                        new Point(GameConfiguration.GameWindowWidth, GameConfiguration.GameWindowWidth));

                    if (intersectionPosition != null)
                    {
                        previousIntersection = currentIntersection = Intersection.BottomWall;

                        var bottomWindow = this.CurrentGame.Map.Windows.FirstOrDefault(w => w.X == currentWindow.X && w.Y == currentWindow.Y + 1);
                        if (bottomWindow != null)
                        {
                            neighbourWindow = bottomWindow;
                        }
                        else
                        {
                            borderPositionCorrection.Y -= BallRadius;
                        }
                    }
                }

                if (currentIntersection != Intersection.None && intersectionPosition != null)
                {
                    var newPosition = (Point)intersectionPosition;

                    // Apply correction
                    newPosition.X += borderPositionCorrection.X;
                    newPosition.Y += borderPositionCorrection.Y;

                    currentTask.Steps.Add(AnimationHelpers.GetPointAnimation(currentBallPosition, newPosition));

                    // If the ball intersects a hole, end the calculation after this step
                    if (currentIntersection == Intersection.Hole)
                    {
                        currentTask.IsLastAnimation    = true;
                        currentTask.IntersectsWithHole = true;
                        animationQueue.Enqueue(currentTask);
                        return(animationQueue);
                    }

                    currentBallPosition = newPosition;

                    // Ball will leave the window
                    if (neighbourWindow != null)
                    {
                        animationQueue.Enqueue(currentTask);
                        currentWindow = neighbourWindow;
                        currentTask   = new BallAnimationTask {
                            Window = currentWindow
                        };
                        windowPreviouslyChanged = true;

                        // Set new initial position for the next window
                        switch (currentIntersection)
                        {
                        case Intersection.TopWall:
                            currentBallPosition.Y = GameConfiguration.GameWindowWidth;
                            break;

                        case Intersection.LeftWall:
                            currentBallPosition.X = GameConfiguration.GameWindowWidth;
                            break;

                        case Intersection.RightWall:
                            currentBallPosition.X = 0;
                            break;

                        case Intersection.BottomWall:
                            currentBallPosition.Y = 0;
                            break;
                        }
                    }
                    else
                    {
                        // Change ball direction within the current window
                        if (currentIntersection == Intersection.TopWall || currentIntersection == Intersection.BottomWall)
                        {
                            currentDirection.Y *= -1;
                        }
                        else
                        {
                            currentDirection.X *= -1;
                        }

                        windowPreviouslyChanged = false;
                    }
                }
            }

            // Add the last animation task to the queue because we had no wall hit (timeout)
            if (currentTask.Steps.Count > 0)
            {
                animationQueue.Enqueue(currentTask);
            }

            return(animationQueue);
        }