public void FirstOrDefaultOnFourTuple()
 {
     const int expected = 42;
       var t = new Tuple<int, int, int, int>(42, 1, 1, 1);
       int actual = t.FirstOrDefault();
       Assert.AreEqual(expected, actual);
 }
Example #2
0
            private AsteroidOrMineralDefinition[] DetermineDestroyedChildrenMinerals(HullVoronoiExploder_Response shards, double overDamage, Vector3D parentRadius, double minChildRadius, Func<double, ITriangleIndexed[], double> getMassByRadius, Func<double, MineralDNA[]> getMineralsByDestroyedMass)
            {
                const double MAXOVERDMG = 13;       // overdamage of 1 is the smallest value (the asteroid was barely destroyed).  Larger values are overkill, and the asteroid becomes more fully destroyed

                if (shards == null || shards.Shards == null || shards.Shards.Length == 0)
                {
                    // There was problem, so just pop out some minerals
                    return DetermineDestroyedChildrenMinerals_Minerals(parentRadius, getMassByRadius, getMineralsByDestroyedMass);
                }

                Random rand = StaticRandom.GetRandomForThread();

                #region calculate volumes

                var shardVolumes = shards.Shards.
                    Select(o =>
                        {
                            Vector3D radius = GetEllipsoidRadius(o.Hull_Centered);
                            return new { Radius = radius, Volume = GetEllipsoidVolume(radius) };
                        }).
                    ToArray();

                // Figure out how much volume should permanently be destroyed
                double parentVolume = GetEllipsoidVolume(parentRadius);
                double volumeToDestroy = GetVolumeToDestroy(parentVolume, overDamage, MAXOVERDMG);

                #endregion

                double destroyedVolume = 0;
                bool[] shouldSelfDestruct = new bool[shards.Shards.Length];

                #region detect too small

                // Get rid of any that are too small
                //TODO: Also get rid of any that are too thin
                for (int cntr = 0; cntr < shards.Shards.Length; cntr++)
                {
                    if (shards.Shards[cntr].Radius < minChildRadius)
                    {
                        shouldSelfDestruct[cntr] = true;
                        destroyedVolume += shardVolumes[cntr].Volume;
                    }
                }

                #endregion

                if (destroyedVolume < volumeToDestroy)
                {
                    #region remove more

                    // Find the shards that could be removed
                    var candidates = Enumerable.Range(0, shards.Shards.Length).
                        Select(o => new
                        {
                            Index = o,
                            Shard = shards.Shards[o],
                            Volume = shardVolumes[o]
                        }).
                        Where(o => !shouldSelfDestruct[o.Index] && o.Volume.Volume < volumeToDestroy - destroyedVolume).
                        OrderBy(o => o.Volume.Volume).
                        ToList();

                    while (candidates.Count > 0 && destroyedVolume < volumeToDestroy)
                    {
                        // Figure out which to self destruct (the rand power will favor inicies closer to zero)
                        int index = UtilityCore.GetIndexIntoList(rand.NextPow(2), candidates.Count);

                        // Remove it
                        shouldSelfDestruct[candidates[index].Index] = true;
                        destroyedVolume += candidates[index].Volume.Volume;
                        candidates.RemoveAt(index);

                        // Remove the items at the end that are now too large
                        index = candidates.Count - 1;
                        while (index >= 0)
                        {
                            if (candidates[index].Volume.Volume < volumeToDestroy - destroyedVolume)
                            {
                                break;      // it's sorted, so the rest will also be under
                            }
                            else
                            {
                                candidates.RemoveAt(index);
                                index--;
                            }
                        }
                    }

                    #endregion
                }

                #region distribute minerals

                // Figure out the mineral value of the destroyed volume
                MineralDNA[] mineralDefinitions = null;
                if (destroyedVolume > 0 && getMineralsByDestroyedMass != null)
                {
                    double inferredRadius = GetEllipsoidRadius(destroyedVolume);
                    double destroyedMass = getMassByRadius(inferredRadius, null);

                    if (destroyedMass > 0)
                    {
                        mineralDefinitions = getMineralsByDestroyedMass(destroyedMass);
                    }
                }

                // Figure out which of the temp asteroids should contain minerals
                var packedMinerals = new Tuple<int, MineralDNA[]>[0];

                if (mineralDefinitions != null && mineralDefinitions.Length > 0)
                {
                    int[] destroyedIndicies = Enumerable.Range(0, shouldSelfDestruct.Length).
                        Where(o => shouldSelfDestruct[o]).
                        ToArray();

                    packedMinerals = DistributeMinerals(mineralDefinitions, destroyedIndicies);
                }

                #endregion

                #region final array

                AsteroidOrMineralDefinition[] retVal = new AsteroidOrMineralDefinition[shards.Shards.Length];

                for (int cntr = 0; cntr < retVal.Length; cntr++)
                {
                    Vector3D velocity = new Vector3D(0, 0, 0);
                    if (shards.Velocities != null && shards.Velocities.Length == shards.Shards.Length)
                    {
                        velocity = shards.Velocities[cntr];
                    }

                    //NOTE: Only position is needed (The first attempt created random asteroids and pulled them apart.  This second attempt
                    //doesn't need to pull them apart)
                    PartSeparator_Part part = new PartSeparator_Part(new[] { new Point3D() }, 0, shards.Shards[cntr].Center_ParentCoords, Quaternion.Identity);

                    //MineralDNA[] mineralsAfter = packedMinerals?.FirstOrDefault(o => o.Item1 == cntr)?.Item2;
                    MineralDNA[] mineralsAfter = null;
                    if (packedMinerals != null)
                    {
                        var found = packedMinerals.FirstOrDefault(o => o.Item1 == cntr);
                        if (found != null)
                        {
                            mineralsAfter = found.Item2;
                        }
                    }

                    retVal[cntr] = new AsteroidOrMineralDefinition(part, shards.Shards[cntr].Hull_Centered, shards.Shards[cntr].Radius, velocity, shouldSelfDestruct[cntr], mineralsAfter);
                }

                #endregion

                return retVal;
            }
            /// <summary>
            /// This adds ioIndex to one of finalLinks
            /// </summary>
            /// <param name="closestBrainIndex">Index of the brain that is closest to the IO.  There is no extra burden for linking to this one</param>
            /// <param name="brainBrainBurdens">
            /// Item1=Index of other brain
            /// Item2=Link resistance (burden) between closestBrainIndex and this brain
            /// </param>
            private static void AddIOLink(BrainBurden[] finalLinks, int ioIndex, double ioSize, int closestBrainIndex, Tuple<int, double>[] brainBrainBurdens)
            {
                // Figure out the cost of adding the link to the various brains
                Tuple<int, double>[] burdens = new Tuple<int, double>[finalLinks.Length];

                for (int cntr = 0; cntr < finalLinks.Length; cntr++)
                {
                    int brainIndex = finalLinks[cntr].Index;        // this is likely always the same as cntr, but since that object has brainIndex as a property, I feel safer using it

                    // Adding to the closest brain has no exta cost.  Adding to any other brain has a cost based on the
                    // distance between the closest brain and that other brain
                    double linkCost = 0d;
                    if (brainIndex != closestBrainIndex)
                    {
                        //TODO: Link every brain to every other brain, then get rid of this if statement
                        var matchingBrain = brainBrainBurdens.FirstOrDefault(o => o.Item1 == brainIndex);
                        if (matchingBrain == null)
                        {
                            continue;
                        }

                        linkCost = matchingBrain.Item2;
                    }

                    // LinkCost + IOStorageCost
                    burdens[cntr] = Tuple.Create(cntr, linkCost + BrainBurden.CalculateBurden(finalLinks[cntr].IOSize + ioSize, finalLinks[cntr].Size));
                }

                int cheapestIndex = burdens.
                    Where(o => o != null).
                    OrderBy(o => o.Item2).First().Item1;

                finalLinks[cheapestIndex].AddIOLink(ioIndex);
            }
Example #4
0
        /// <summary>
        /// This adds ioIndex to one of finalLinks
        /// </summary>
        /// <param name="closestBrainIndex">Index of the brain that is closest to the IO.  There is no extra burden for linking to this one</param>
        /// <param name="brainBrainBurdens">
        /// Item1=Index of other brain
        /// Item2=Link resistance (burden) between closestBrainIndex and this brain
        /// </param>
        private static void AddIOLink(BrainBurden[] finalLinks, int ioIndex, double ioSize, int closestBrainIndex, Tuple<int, double>[] brainBrainBurdens)
        {
            // Figure out the cost of adding the link to the various brains
            List<Tuple<int, double>> burdens = new List<Tuple<int, double>>();

            for (int cntr = 0; cntr < finalLinks.Length; cntr++)
            {
                if (finalLinks[cntr].IOLinks.Contains(ioIndex))
                {
                    // This only happens when extra links are requested
                    continue;
                }

                int brainIndex = finalLinks[cntr].Index;        // this is likely always the same as cntr, but since that object has brainIndex as a property, I feel safer using it

                // Adding to the closest brain has no exta cost.  Adding to any other brain has a cost based on the
                // distance between the closest brain and that other brain
                double linkCost = 0d;
                if (brainIndex != closestBrainIndex)
                {
                    var matchingBrain = brainBrainBurdens.FirstOrDefault(o => o.Item1 == brainIndex);
                    if (matchingBrain == null)
                    {
                        //NOTE: All brain-brain distances should be passed in, so this should never happen
                        continue;
                    }

                    linkCost = matchingBrain.Item2;
                }

                // LinkCost + IOStorageCost
                burdens.Add(Tuple.Create(cntr, linkCost + BrainBurden.CalculateBurden(finalLinks[cntr].IOSize + ioSize, finalLinks[cntr].Size)));
            }

            if (burdens.Count == 0)
            {
                // This io has already been added to all brains
                return;
            }

            int cheapestIndex = burdens.
                OrderBy(o => o.Item2).First().Item1;

            finalLinks[cheapestIndex].AddIOLink(ioIndex);
        }