Beispiel #1
0
        static void Main(string[] args)
        {
            Console.WriteLine("You are runnning the Algorithms example.");
            Console.WriteLine("======================================================");
            Console.WriteLine();

            #region Sorting
            {
                // Note: these functions are not restricted to array types. You can use the
                // overloads with "Get" and "Assign" delegates to use them on any int-indexed
                // data structure.

                Console.WriteLine("  Sorting Algorithms----------------------");
                Console.WriteLine();
                int[] dataSet = new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
                Console.Write("    Data Set:" + string.Join(", ", dataSet.Select(x => x.ToString())));
                Console.WriteLine();

                // Shuffling (Randomizing)
                Sort.Shuffle(dataSet);
                Console.Write("    Shuffle (Randomizing): " + string.Join(", ", dataSet.Select(x => x.ToString())));
                Console.WriteLine();

                // Bubble
                Sort.Bubble(dataSet);
                Console.Write("    Bubble: " + string.Join(", ", dataSet.Select(x => x.ToString())));
                Console.WriteLine();

                Console.WriteLine("    shuffling dataSet...");
                Sort.Shuffle(dataSet);

                // Selection
                Sort.Selection(dataSet);
                Console.Write("    Selection: " + string.Join(", ", dataSet.Select(x => x.ToString())));
                Console.WriteLine();

                Console.WriteLine("    shuffling dataSet...");
                Sort.Shuffle(dataSet);

                // Insertion
                Sort.Insertion(dataSet);
                Console.Write("    Insertion: " + string.Join(", ", dataSet.Select(x => x.ToString())));
                Console.WriteLine();

                Console.WriteLine("    shuffling dataSet...");
                Sort.Shuffle(dataSet);

                // Quick
                Sort.Quick(dataSet);
                Console.Write("    Quick: " + string.Join(", ", dataSet.Select(x => x.ToString())));
                Console.WriteLine();

                Console.WriteLine("    shuffling dataSet...");
                Sort.Shuffle(dataSet);

                // Merge
                Sort.Merge(Compute.Compare, dataSet);
                Console.Write("    Merge: " + string.Join(", ", dataSet.Select(x => x.ToString())));
                Console.WriteLine();

                Console.WriteLine("    shuffling dataSet...");
                Sort.Shuffle(dataSet);

                // Heap
                Sort.Heap(Compute.Compare, dataSet);
                Console.Write("    Heap: " + string.Join(", ", dataSet.Select(x => x.ToString())));
                Console.WriteLine();

                Console.WriteLine("    shuffling dataSet...");
                Sort.Shuffle(dataSet);

                // OddEven
                Sort.OddEven(Compute.Compare, dataSet);
                Console.Write("    OddEven: " + string.Join(", ", dataSet.Select(x => x.ToString())));
                Console.WriteLine();

                //Console.WriteLine("  shuffling dataSet...");
                //Sort<int>.Shuffle(dataSet);

                //// Slow
                //Sort<int>.Slow(Logic.compare, get, set, 0, dataSet.Length);
                //Console.Write("Slow: " + string.Join(", ", dataSet.Select(x => x.ToString())));
                //Console.WriteLine();

                //Console.WriteLine("  shuffling dataSet...");
                //Sort<int>.Shuffle(dataSet);

                // Bogo
                //Sort<int>.Bogo(Logic.compare, get, set, 0, dataSet.Length);
                Console.Write("    Bogo: Disabled (takes forever)"); //+ string.Join(", ", dataSet.Select(x => x.ToString())));
                //Console.WriteLine();

                Console.WriteLine();
                Console.WriteLine();
            }
            #endregion

            #region Graph Search (Using Graph Data Structure)
            {
                Console.WriteLine("  Graph Searching----------------------");
                Console.WriteLine();

                // make a graph
                IGraph <int> graph = new GraphSetOmnitree <int>()
                {
                    // add nodes
                    0,
                    1,
                    2,
                    3,
                    // add edges
                    { 0, 1 },
                    { 0, 2 },
                    { 1, 3 },
                    { 2, 3 }
                };

                // make a heuristic function
                int heuristic(int node)
                {
                    switch (node)
                    {
                    case 0:
                        return(3);

                    case 1:
                        return(6);

                    case 2:
                        return(1);

                    case 3:
                        return(0);

                    default:
                        throw new NotImplementedException();
                    }
                }

                // make a cost function
                int cost(int from, int to)
                {
                    if (from == 0 && to == 1)
                    {
                        return(1);
                    }
                    if (from == 0 && to == 2)
                    {
                        return(2);
                    }
                    if (from == 1 && to == 3)
                    {
                        return(5);
                    }
                    if (from == 2 && to == 3)
                    {
                        return(1);
                    }
                    if (from == 0 && to == 3)
                    {
                        return(99);
                    }
                    throw new Exception("invalid path cost computation");
                }

                // make a goal function
                bool goal(int node)
                {
                    if (node == 3)
                    {
                        return(true);
                    }
                    else
                    {
                        return(false);
                    }
                }

                // run A* the algorithm
                Stepper <int> aStar_path = Search.Graph <int, int>(0, graph, heuristic, cost, goal);
                Console.Write("    A* Path: ");
                if (aStar_path != null)
                {
                    aStar_path(i => Console.Write(i + " "));
                }
                else
                {
                    Console.Write("none");
                }

                Console.WriteLine();

                // run the Greedy algorithm
                Stepper <int> greedy_path = Search.Graph <int, int>(0, graph, heuristic, goal);
                Console.Write("    Greedy Path: ");
                if (greedy_path != null)
                {
                    greedy_path(i => Console.Write(i + " "));
                }
                else
                {
                    Console.Write("none");
                }

                Console.WriteLine();
                Console.WriteLine();
            }
            #endregion

            #region Graph Search (Vector Game-Style Example)
            {
                Console.WriteLine("  Graph Searching (Vector Game-Style Example)-------------------");
                Console.WriteLine();
                Console.WriteLine("    Debug the code. The path is to large to write to the console.");
                Console.WriteLine();

                // Lets say you are coding enemy AI and you want the AI to find a path towards the player
                // in order to attack them. Here are their starting positions:
                Vector <float> enemyLocation    = new Vector <float>(-100f, 0f, -50f);
                Vector <float> playerLocation   = new Vector <float>(200f, 0f, -50f);
                float          enemyAttackRange = 3f; // enemy has a melee attack with 3 range

                // Lets say most of the terrain is open, but there is a big rock in between them that they
                // must go around.
                Vector <float> rockLocation = new Vector <float>(15f, 0f, -40f);
                float          rockRadius   = 20f;

                // Make sure we don't re-use locations (must be wiped after running the algorithm)
                ISet <Vector <float> > alreadyUsed = new SetHashLinked <Vector <float> >();

                Vector <float> validationVectorStorage = null; // storage to prevent a ton of vectors from being allocated

                // So, we just need to validate movement locations (make sure the path finding algorithm
                // ignores locations inside the rock)
                bool validateMovementLocation(Vector <float> location)
                {
                    // if the location is inside the rock, it is not a valid movement
                    location.Subtract(rockLocation, ref validationVectorStorage);
                    float magnitude = validationVectorStorage.Magnitude;

                    if (magnitude <= rockRadius)
                    {
                        return(false);
                    }

                    // NOTE: If you are running a physics engine, you might be able to just call it to validate a location.

                    // if the location was already used, then let's consider it invalid, because
                    // another path (which is faster) has already reached that location
                    if (alreadyUsed.Contains(location))
                    {
                        return(false);
                    }

                    return(true);
                }

                // Now we need the neighbor function (getting the neighbors of the current location).
                void neighborFunction(Vector <float> currentLocation, Step <Vector <float> > neighbors)
                {
                    // NOTES:
                    // - This neighbor function has a 90 degree per-node resolution (360 / 4 [north/south/east/west] = 90).
                    // - This neighbor function has a 1 unit per-node distance resolution, because we went 1 unit in each direction.

                    // RECOMMENDATIONS:
                    // - If the path finding is failing, you may need to increase the resolution.
                    // - If the algorithm is running too slow, you may need to reduce the resolution.

                    float distanceResolution = 1;

                    float x = currentLocation.X;
                    float y = currentLocation.Y;
                    float z = currentLocation.Z;

                    // Note: I'm using the X-axis and Z-axis here, but which axis you need to use
                    // depends on your environment. Your "north" could be along the Y-axis for example.

                    Vector <float> north = new Vector <float>(x + distanceResolution, y, z);

                    if (validateMovementLocation(north))
                    {
                        alreadyUsed.Add(north); // mark location as used
                        neighbors(north);
                    }

                    Vector <float> east = new Vector <float>(x, y, z + distanceResolution);

                    if (validateMovementLocation(east))
                    {
                        alreadyUsed.Add(east); // mark location as used
                        neighbors(east);
                    }

                    Vector <float> south = new Vector <float>(x - distanceResolution, y, z);

                    if (validateMovementLocation(south))
                    {
                        alreadyUsed.Add(south); // mark location as used
                        neighbors(south);
                    }

                    Vector <float> west = new Vector <float>(x, y, z - distanceResolution);

                    if (validateMovementLocation(west))
                    {
                        alreadyUsed.Add(west); // mark location as used
                        neighbors(west);
                    }
                }

                Vector <float> heuristicVectorStorage = null; // storage to prevent a ton of vectors from being allocated

                // Heuristic function (how close are we to the goal)
                float heuristicFunction(Vector <float> currentLocation)
                {
                    // The goal is the player's location, so we just need our distance from the player.
                    currentLocation.Subtract(playerLocation, ref heuristicVectorStorage);
                    return(heuristicVectorStorage.Magnitude);
                }

                // Lets say there is a lot of mud around the rock, and the mud makes our player move at half their normal speed.
                // Our path finding needs to find the fastest route to the player, whether it be through the mud or not.
                Vector <float> mudLocation = new Vector <float>(15f, 0f, -70f);
                float          mudRadius   = 30f;

                Vector <float> costVectorStorage = null; // storage to prevent a ton of vectors from being allocated

                // Cost function
                float costFunction(Vector <float> from, Vector <float> to)
                {
                    // If the location we are moving to is in the mud, lets adjust the
                    // cost because mud makes us move slower.
                    to.Subtract(mudLocation, ref costVectorStorage);
                    float magnitude = costVectorStorage.Magnitude;

                    if (magnitude <= mudRadius)
                    {
                        return(2f);
                    }

                    // neither location is in the mud, it is just a standard movement at normal speed.
                    return(1f);
                }

                Vector <float> goalVectorStorage = null; // storage to prevent a ton of vectors from being allocated

                // Goal function
                bool goalFunction(Vector <float> currentLocation)
                {
                    // if the player is within the enemy's attack range WE FOUND A PATH! :)
                    currentLocation.Subtract(playerLocation, ref goalVectorStorage);
                    float magnitude = goalVectorStorage.Magnitude;

                    if (magnitude <= enemyAttackRange)
                    {
                        return(true);
                    }

                    // the enemy is not yet within attack range
                    return(false);
                }

                // We have all the necessary parameters. Run the pathfinding algorithms!
                Stepper <Vector <float> > aStarPath =
                    Search.Graph(
                        enemyLocation,
                        neighborFunction,
                        heuristicFunction,
                        costFunction,
                        goalFunction);

                // Flush the already used markers before running the Greedy algorithm.
                // Normally you won't run two algorithms for the same graph/location, but
                // we are running both algorithms in this example to demonstrate the
                // differences between them.
                alreadyUsed.Clear();

                Stepper <Vector <float> > greedyPath =
                    Search.Graph(
                        enemyLocation,
                        neighborFunction,
                        heuristicFunction,
                        goalFunction);

                // NOTE: If there is no valid path, then "Search.Graph" will return "null."
                // For this example, I know that there will be a valid path so I did not
                // include a null check.

                // Lets convert the paths into arrays so you can look at them in the debugger. :)
                Vector <float>[] aStarPathArray  = aStarPath.ToArray();
                Vector <float>[] greedyPathArray = greedyPath.ToArray();

                // lets calculate the movement cost of each path to see how they compare
                float astartTotalCost = Compute.Add <float>(step =>
                {
                    for (int i = 0; i < aStarPathArray.Length - 1; i++)
                    {
                        step(costFunction(aStarPathArray[i], aStarPathArray[i + 1]));
                    }
                });
                float greedyTotalCost = Compute.Add <float>(step =>
                {
                    for (int i = 0; i < greedyPathArray.Length - 1; i++)
                    {
                        step(costFunction(greedyPathArray[i], greedyPathArray[i + 1]));
                    }
                });

                // Notice that that the A* algorithm produces a less costly path than the Greedy,
                // meaning that it is faster. The Greedy path went through the mud, but the A* path
                // took the longer route around the other side of the rock, which ended up being faster
                // than running through the mud.
            }
            #endregion

            #region Random Generation
            {
                Console.WriteLine("  Random Generation---------------------");
                Console.WriteLine();

                int iterationsperrandom = 3;
                void testrandom(Random random)
                {
                    for (int i = 0; i < iterationsperrandom; i++)
                    {
                        Console.WriteLine("    " + i + ": " + random.Next());
                    }
                    Console.WriteLine();
                }

                Arbitrary mcg_2pow59_13pow13 = new Arbitrary.Algorithms.MultiplicativeCongruent_A();
                Console.WriteLine("    mcg_2pow59_13pow13 randoms:");
                testrandom(mcg_2pow59_13pow13);
                Arbitrary mcg_2pow31m1_1132489760 = new Arbitrary.Algorithms.MultiplicativeCongruent_B();
                Console.WriteLine("    mcg_2pow31m1_1132489760 randoms:");
                testrandom(mcg_2pow31m1_1132489760);
                Arbitrary mersenneTwister = new Arbitrary.Algorithms.MersenneTwister();
                Console.WriteLine("    mersenneTwister randoms:");
                testrandom(mersenneTwister);
                Arbitrary cmr32_c2_o3 = new Arbitrary.Algorithms.CombinedMultipleRecursive();
                Console.WriteLine("    mersenneTwister randoms:");
                testrandom(cmr32_c2_o3);
                Arbitrary wh1982cmcg = new Arbitrary.Algorithms.WichmannHills1982();
                Console.WriteLine("    mersenneTwister randoms:");
                testrandom(wh1982cmcg);
                Arbitrary wh2006cmcg = new Arbitrary.Algorithms.WichmannHills2006();
                Console.WriteLine("    mersenneTwister randoms:");
                testrandom(wh2006cmcg);
                Arbitrary mwcxorsg = new Arbitrary.Algorithms.MultiplyWithCarryXorshift();
                Console.WriteLine("    mwcxorsg randoms:");
                testrandom(mwcxorsg);
            }
            #endregion

            Console.WriteLine();
            Console.WriteLine("============================================");
            Console.WriteLine("Example Complete...");
            Console.ReadLine();
        }
Beispiel #2
0
        static void Main(string[] args)
        {
            Console.WriteLine("You are runnning the Algorithms tutorial.");
            Console.WriteLine("======================================================");
            Console.WriteLine();

            #region Sorting
            {
                Console.WriteLine(" Sorting Algorithms----------------------");
                Console.WriteLine();
                int[] dataSet = new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
                Console.Write("  Data Set:");
                Console.Write(dataSet[0]);
                for (int i = 1; i < dataSet.Length; i++)
                {
                    Console.Write(", " + dataSet[i]);
                }
                Console.WriteLine();

                // if you want to sort non-array types, see the overloads using Get<int> and Assign<int>
                // Delegates
                //Get<int> get = (int index) => { return dataSet[index]; };
                //Assign<int> assign = (int index, int value) => { dataSet[index] = value; };

                // Shuffling (Randomizing)
                Sort <int> .Shuffle(dataSet);

                Console.Write("  Shuffle (Randomizing): ");
                Console.Write(dataSet[0]);
                for (int i = 1; i < dataSet.Length; i++)
                {
                    Console.Write(", " + dataSet[i]);
                }
                Console.WriteLine();

                // Bubble
                Sort <int> .Bubble(dataSet);

                Console.Write("  Bubble: ");
                Console.Write(dataSet[0]);
                for (int i = 1; i < dataSet.Length; i++)
                {
                    Console.Write(", " + dataSet[i]);
                }
                Console.WriteLine();

                Console.WriteLine("  shuffling dataSet...");
                Sort <int> .Shuffle(dataSet);

                // Selection
                Sort <int> .Selection(dataSet);

                Console.Write("  Selection: ");
                Console.Write(dataSet[0]);
                for (int i = 1; i < dataSet.Length; i++)
                {
                    Console.Write(", " + dataSet[i]);
                }
                Console.WriteLine();

                Console.WriteLine("  shuffling dataSet...");
                Sort <int> .Shuffle(dataSet);

                // Insertion
                Sort <int> .Insertion(dataSet);

                Console.Write("  Insertion: ");
                Console.Write(dataSet[0]);
                for (int i = 1; i < dataSet.Length; i++)
                {
                    Console.Write(", " + dataSet[i]);
                }
                Console.WriteLine();

                Console.WriteLine("  shuffling dataSet...");
                Sort <int> .Shuffle(dataSet);

                // Quick
                Sort <int> .Quick(dataSet);

                Console.Write("  Quick: ");
                Console.Write(dataSet[0]);
                for (int i = 1; i < dataSet.Length; i++)
                {
                    Console.Write(", " + dataSet[i]);
                }
                Console.WriteLine();

                Console.WriteLine("  shuffling dataSet...");
                Sort <int> .Shuffle(dataSet);

                // Merge
                Sort <int> .Merge(Compute.Compare, dataSet);

                Console.Write("  Merge: ");
                Console.Write(dataSet[0]);
                for (int i = 1; i < dataSet.Length; i++)
                {
                    Console.Write(", " + dataSet[i]);
                }
                Console.WriteLine();

                Console.WriteLine("  shuffling dataSet...");
                Sort <int> .Shuffle(dataSet);

                // Heap
                Sort <int> .Heap(Compute.Compare, dataSet);

                Console.Write("  Heap: ");
                Console.Write(dataSet[0]);
                for (int i = 1; i < dataSet.Length; i++)
                {
                    Console.Write(", " + dataSet[i]);
                }
                Console.WriteLine();

                Console.WriteLine("  shuffling dataSet...");
                Sort <int> .Shuffle(dataSet);

                // OddEven
                Sort <int> .OddEven(Compute.Compare, dataSet);

                Console.Write("  OddEven: ");
                Console.Write(dataSet[0]);
                for (int i = 1; i < dataSet.Length; i++)
                {
                    Console.Write(", " + dataSet[i]);
                }
                Console.WriteLine();

                //Sort<int>.Shuffle(get, set, 0, dataSet.Length);

                //// Slow
                //Sort<int>.Slow(Logic.compare, get, set, 0, dataSet.Length);
                //Console.Write("Slow: ");
                //Console.Write(dataSet[0]);
                //for (int i = 1; i < dataSet.Length; i++)
                //	Console.Write(", " + dataSet[i]);
                //Console.WriteLine();

                Sort <int> .Shuffle(dataSet);

                // Bogo
                //Sort<int>.Bogo(Logic.compare, get, set, 0, dataSet.Length);
                Console.Write("  Bogo: Disabled (takes forever)");
                //Console.Write(dataSet[0]);
                //for (int i = 1; i < dataSet.Length; i++)
                //	Console.Write(", " + dataSet[i]);
                //Console.WriteLine();

                Console.WriteLine();
                Console.WriteLine();
            }
            #endregion

            #region Graph Search
            {
                Console.WriteLine(" Graph Searching----------------------");
                Console.WriteLine();

                // make a graph
                Graph <int> graph = new GraphSetOmnitree <int>(
                    Compare.Default,
                    Hash.Default);

                // add nodes
                graph.Add(0);
                graph.Add(1);
                graph.Add(2);
                graph.Add(3);

                // add edges
                graph.Add(0, 1);
                graph.Add(0, 2);
                graph.Add(1, 3);
                graph.Add(2, 3);

                //// represent a graph
                //// Note: can be any type  (doesn't have to be int?[,])
                //int?[,] adjacencyMatrix =
                //{
                //	{ null, 1, 2, null },
                //	{ null, null, null, 5 },
                //	{ null, null, null, 1 },
                //	{ null, null, null, null }
                //};

                // make a delegate for finding neighbor nodes
                Action <int, Step <int> > neighbors =
                    (int current, Step <int> step_function) =>
                {
                    //for (int i = 0; i < 4; i++)
                    //	if (adjacencyMatrix[current, i] != null)
                    //		step(i);
                    graph.Neighbors(current, step_function);
                };

                // make a delegate for computing heuristics
                Func <int, int> heuristic =
                    (int node) =>
                {
                    switch (node)
                    {
                    case 0:
                        return(3);

                    case 1:
                        return(6);

                    case 2:
                        return(1);

                    case 3:
                        return(0);

                    default:
                        throw new NotImplementedException();
                    }
                };

                // make a delegate for computing costs
                Func <int, int, int> cost =
                    (int from, int to) =>
                {
                    if (from == 0 && to == 1)
                    {
                        return(1);
                    }
                    if (from == 0 && to == 2)
                    {
                        return(2);
                    }
                    if (from == 1 && to == 3)
                    {
                        return(5);
                    }
                    if (from == 2 && to == 3)
                    {
                        return(1);
                    }
                    if (from == 0 && to == 3)
                    {
                        return(99);
                    }
                    throw new Exception("invalid path cost computation");
                };

                // make a delegate for determining if the goal is reached
                Func <int, bool> goal =
                    (int node) =>
                {
                    if (node == 3)
                    {
                        return(true);
                    }
                    else
                    {
                        return(false);
                    }
                };

                // run A* the algorithm
                Stepper <int> aStar_path = Search <int> .Graph <int> .Astar(
                    0,
                    graph,
                    new Search <int> .Graph <int> .Heuristic(heuristic),
                    new Search <int> .Graph <int> .Cost(cost),
                    new Search <int> .Graph <int> .Goal(goal));

                // run the Greedy algorithm
                Stepper <int> greedy_path = Search <int> .Graph <int> .Greedy(
                    0,
                    graph,
                    new Search <int> .Graph <int> .Heuristic(heuristic),
                    new Search <int> .Graph <int> .Goal(goal));

                Console.Write("  A* Path: ");
                if (aStar_path != null)
                {
                    aStar_path((int i) => { System.Console.Write(i + " "); });
                }
                else
                {
                    Console.Write("  none");
                }

                Console.WriteLine();

                Console.Write("  Greedy Path: ");
                if (greedy_path != null)
                {
                    greedy_path((int i) => { System.Console.Write(i + " "); });
                }
                else
                {
                    Console.Write("  none");
                }
                Console.WriteLine();
                Console.WriteLine();
            }
            #endregion

            #region Graph Search (Vector Game-Style Example)

            // Lets say you are coding enemy AI and you want the AI to find a path towards the player
            // in order to attack them. Here are their starting positions:
            Vector <float> enemy_location     = new Vector <float>(-100, 0, -50);
            Vector <float> player_location    = new Vector <float>(200, 0, -50);
            float          enemy_attack_range = 3; // enemy has a melee attack with 3 range

            // Lets say most of the terrain is open, but there is a big rock in between them that they
            // must go around.
            Vector <float> rock_location = new Vector <float>(15, 0, -40);
            float          rock_radius   = 20;

            // So, we just need to validate movement locations (make sure the path finding algorithm
            // ignores locations inside the rock)
            Func <Vector <float>, bool> validateMovementLocation = location =>
            {
                float mag = (location - rock_location).Magnitude;
                if (mag <= rock_radius)
                {
                    return(false); // inside rock (not valid)
                }
                return(true);      // not inside rock (valid)

                // NOTE:
                // This function will normally be handled by your physics engine if you are running one.
            };

            // Make sure we don't re-use locations (must be wiped after running the algorithm)
            Set <Vector <float> > already_used = new SetHashList <Vector <float> >();

            // Now we need the neighbor function (getting the neighbors of the current location).
            Search <Vector <float> > .Graph <float> .Neighbors neighborFunction = (currentLocation, neighbors) =>
            {
                // lets make a simple neighbor function that returns 4 locations (directly north, south, east, and west)
                // and the distance of each node in the graph will be 1
                Vector <float>
                north = new Vector <float>(currentLocation.X + 1, currentLocation.Y, currentLocation.Z),
                                                               east  = new Vector <float>(currentLocation.X, currentLocation.Y, currentLocation.Z + 1),
                                                               south = new Vector <float>(currentLocation.X - 1, currentLocation.Y, currentLocation.Z),
                                                               west  = new Vector <float>(currentLocation.X, currentLocation.Y, currentLocation.Z - 1);

                // validate the locations (not inside the rock) and make sure we have not already traversed the location
                if (validateMovementLocation(north) && !already_used.Contains(north))
                {
                    already_used.Add(north); // mark for usage so we do not use this location again
                    neighbors(north);
                }
                if (validateMovementLocation(east) && !already_used.Contains(east))
                {
                    already_used.Add(east); // mark for usage so we do not use this location again
                    neighbors(east);
                }
                if (validateMovementLocation(south) && !already_used.Contains(south))
                {
                    already_used.Add(south); // mark for usage so we do not use this location again
                    neighbors(south);
                }
                if (validateMovementLocation(west) && !already_used.Contains(west))
                {
                    already_used.Add(west); // mark for usage so we do not use this location again
                    neighbors(west);
                }

                // NOTES:
                // - This neighbor function has a 90 degree per-node resolution (360 / 4 [north/south/east/west] = 90).
                // - This neighbor function has a 1 unit per-node resolution, because we went 1 unit in each direction.

                // RECOMMENDATIONS:
                // - If the path finding is failing, you may need to increase the resolution.
                // - If the algorithm is running too slow, you may need to reduce the resolution.
            };

            // Now we need the heuristic function (how close are we to the goal).
            Search <Vector <float> > .Graph <float> .Heuristic heuristicFunction = currentLocation =>
            {
                // The goal is the player's location, so we just need our distance from the player.
                return((currentLocation - player_location).Magnitude);
            };

            // Lets say there is a lot of mud around the rock, and the mud makes our player move at half their normal speed.
            // Our path finding needs to find the fastest route to the player, whether it be through the mud or not.
            Vector <float> mud_location = new Vector <float>(15, 0, -70);
            float          mud_radius   = 30;

            // Now we need the cost function
            Search <Vector <float> > .Graph <float> .Cost costFunction = (location1, location2) =>
            {
                // If either locations are in the mud, lets increase the cost of moving to that spot.
                float mag1 = (location1 - mud_location).Magnitude;
                if (mag1 <= mud_radius)
                {
                    return(2);
                }
                float mag2 = (location2 - mud_location).Magnitude;
                if (mag2 <= mud_radius)
                {
                    return(2);
                }

                // neither location is in the mud, it is just a standard movement at normal speed.
                return(1);
            };

            // Now we need a goal function
            Search <Vector <float> > .Graph <float> .Goal goalFunction = currentLocation =>
            {
                float mag = (currentLocation - player_location).Magnitude;
                // if the player is within the enemy's attack range WE FOUND A PATH! :)
                if (mag <= enemy_attack_range)
                {
                    return(true);
                }

                // the enemy is not yet within attack range
                return(false);
            };

            // We have all the necessary parameters. Run the pathfinding algorithms!
            Stepper <Vector <float> > aStarPath = Search <Vector <float> > .Graph <float> .Astar(
                enemy_location,
                neighborFunction,
                heuristicFunction,
                costFunction,
                goalFunction);

            // NOTE:
            // if the "Astar" function returns "null" there is no valid path. (in this example there
            // are valid paths, so I didn't add a nul check)

            // Here is the path converted to an array (easier to read while debugging)
            Vector <float>[] aStarPath_array = aStarPath.ToArray();

            // flush the duplicate locations checker before running the Greedy algorithm
            already_used.Clear();

            Stepper <Vector <float> > greedyPath = Search <Vector <float> > .Graph <float> .Greedy(
                enemy_location,
                neighborFunction,
                heuristicFunction,
                goalFunction);

            // Here is the path converted to an array (easier to read while debugging)
            Vector <float>[] greedyPath_array = greedyPath.ToArray();


            // lets calculate the movement cost of each path

            float total_cost_astar = Compute.Add <float>(step =>
            {
                for (int i = 0; i < aStarPath_array.Length - 1; i++)
                {
                    float cost = costFunction(aStarPath_array[i], aStarPath_array[i + 1]);
                    step(cost);
                }
            });

            float total_cost_greedy = Compute.Add <float>(step =>
            {
                for (int i = 0; i < greedyPath_array.Length - 1; i++)
                {
                    float cost = costFunction(greedyPath_array[i], greedyPath_array[i + 1]);
                    step(cost);
                }
            });

            // Notice that that the A* algorithm produces a less costly path than the Greedy,
            // meaning that it is faster. The Greedy path went through the mud, but the A* path
            // took the longer route around the other side of the rock, which ended up being faster
            // than running through the mud.

            #endregion

            #region Random Generation

            Console.WriteLine(" Random Generation---------------------");
            Console.WriteLine();

            int             iterationsperrandom = 3;
            Action <Random> testrandom          = (Random random) =>
            {
                for (int i = 0; i < iterationsperrandom; i++)
                {
                    Console.WriteLine("    " + i + ": " + random.Next());
                }
                Console.WriteLine();
            };
            Arbitrary mcg_2pow59_13pow13 = new Arbitrary.Algorithms.MultiplicativeCongruent_A();
            Console.WriteLine("  mcg_2pow59_13pow13 randoms:");
            testrandom(mcg_2pow59_13pow13);
            Arbitrary mcg_2pow31m1_1132489760 = new Arbitrary.Algorithms.MultiplicativeCongruent_B();
            Console.WriteLine("  mcg_2pow31m1_1132489760 randoms:");
            testrandom(mcg_2pow31m1_1132489760);
            Arbitrary mersenneTwister = new Arbitrary.Algorithms.MersenneTwister();
            Console.WriteLine("  mersenneTwister randoms:");
            testrandom(mersenneTwister);
            Arbitrary cmr32_c2_o3 = new Arbitrary.Algorithms.CombinedMultipleRecursive();
            Console.WriteLine("  mersenneTwister randoms:");
            testrandom(cmr32_c2_o3);
            Arbitrary wh1982cmcg = new Arbitrary.Algorithms.WichmannHills1982();
            Console.WriteLine("  mersenneTwister randoms:");
            testrandom(wh1982cmcg);
            Arbitrary wh2006cmcg = new Arbitrary.Algorithms.WichmannHills2006();
            Console.WriteLine("  mersenneTwister randoms:");
            testrandom(wh2006cmcg);
            Arbitrary mwcxorsg = new Arbitrary.Algorithms.MultiplyWithCarryXorshift();
            Console.WriteLine("  mwcxorsg randoms:");
            testrandom(mwcxorsg);

            #endregion

            Console.WriteLine();
            Console.WriteLine("============================================");
            Console.WriteLine("Example Complete...");
            Console.ReadLine();
        }