Exemplo n.º 1
0
        public void TestWeightVariance()
        {
            var box   = Factory.CreateBox("Box", 10, 10, 10, 0, 10, 10, 10, 100);
            var itemA = Factory.CreateItem("Item A", 5, 10, 10, 10, true);
            var itemB = Factory.CreateItem("Item B", 5, 10, 10, 20, true);

            var packedItemA = new PackedItem(itemA, 0, 0, 0, 5, 10, 10);
            var packedItemB = new PackedItem(itemB, 0, 0, 0, 5, 10, 10);

            var packedItemListA = new PackedItemList();

            packedItemListA.Insert(packedItemA);
            var packedBoxA = new PackedBox(box, packedItemListA);

            var packedItemListB = new PackedItemList();

            packedItemListB.Insert(packedItemB);
            var packedBoxB = new PackedBox(box, packedItemListB);

            var pBoxArray = new PackedBox[] { packedBoxA, packedBoxB };

            var packedBoxList = new PackedBoxList();

            packedBoxList.Insert(packedBoxA);
            packedBoxList.Insert(packedBoxB);

            Assert.Equal(25, packedBoxList.GetWeightVariance());
        }
        /// <summary>
        /// Not every attempted repack is actually helpful - sometimes moving an item between two
        /// otherwise identical boxes, or sometimes the box used for the now lighter set of items
        /// actually weighs more when empty causing an increase in total weight.
        /// </summary>
        /// <returns></returns>
        private bool DidRepackActuallyHelp(PackedBox oldBoxA, PackedBox oldBoxB, PackedBox newBoxA, PackedBox newBoxB)
        {
            PackedBoxList oldList = new PackedBoxList();

            oldList.InsertFromArray(new PackedBox[] { oldBoxA, oldBoxB });

            PackedBoxList newList = new PackedBoxList();

            newList.InsertFromArray(new PackedBox[] { newBoxA, newBoxB });

            return(newList.GetWeightVariance() < oldList.GetWeightVariance());
        }
        /// <summary>
        /// Given a solution set of packed boxes, repack them to achieve optimum weight distribution
        /// </summary>
        /// <param name="originalPackedBoxList"></param>
        /// <returns></returns>
        public PackedBoxList RedistributeWeight(PackedBoxList originalPackedBoxList)
        {
            var targetWeight = originalPackedBoxList.GetMeanWeight();

            _logger.Log(LogLevel.Debug, "Repacking for weight distribution, weight variance {0}, target weight {1}", originalPackedBoxList.GetWeightVariance(), targetWeight);

            var packedBoxes      = new PackedBoxList();
            var overWeightBoxes  = new List <PackedBox>();
            var underWeightBoxes = new List <PackedBox>();

            var originalPackedBoxes = originalPackedBoxList.ShallowCopy().GetContent().Cast <PackedBox>();

            foreach (var originalPackedBox in originalPackedBoxes)
            {
                var boxWeight = originalPackedBox.GetWeight();

                if (boxWeight > targetWeight)
                {
                    overWeightBoxes.Add(originalPackedBox);
                }
                else if (boxWeight < targetWeight)
                {
                    underWeightBoxes.Add(originalPackedBox);
                }
                else
                {
                    packedBoxes.Insert(originalPackedBox); // Target weight, so we'll keep these
                }
            }


            // Keep moving items from most overweight box to most underweight box
            var tryRepack = false;

            do
            {
                _logger.Log(LogLevel.Debug, "Boxes under/over target: {0}/{1}", underWeightBoxes.Count, overWeightBoxes.Count);

                for (var underWeightBoxIndex = 0; underWeightBoxIndex < underWeightBoxes.Count; underWeightBoxIndex++)
                {
                    var underWeightBox = underWeightBoxes[underWeightBoxIndex];

                    for (var overWeightBoxIndex = 0; overWeightBoxIndex < overWeightBoxes.Count; overWeightBoxIndex++)
                    {
                        var overWeightBox      = overWeightBoxes[overWeightBoxIndex];
                        var overWeightBoxItems = overWeightBox.GetItems().GetContent().Cast <Item>().ToList();

                        foreach (var overWeightBoxItem in overWeightBoxItems)
                        {
                            // If over target weight, just continue as it would hinder rather than help weight distribution
                            var overTargetWeight = (underWeightBox.GetWeight() + overWeightBoxItem.Weight) > targetWeight;
                            if (overTargetWeight)
                            {
                                continue;
                            }

                            var newItemsForLighterBox = underWeightBox.GetItems().ShallowCopy();
                            newItemsForLighterBox.Insert(overWeightBoxItem);

                            // We may need a bigger box
                            var newLighterBoxPacker = new Packer();
                            newLighterBoxPacker.AddBoxes(Boxes);
                            newLighterBoxPacker.AddItems(newItemsForLighterBox);
                            var newLighterBox = newLighterBoxPacker.PackByVolume().ExtractMin();

                            // New item fits!
                            if (newLighterBox.GetItems().GetCount() == newItemsForLighterBox.GetCount())
                            {
                                // Remove from overWeightBoxItems as it is now packed in a different box
                                overWeightBoxItems.Remove(overWeightBoxItem);

                                // We may be able to use a smaller box
                                var newHeavierBoxPacker = new Packer();
                                newHeavierBoxPacker.AddBoxes(Boxes);
                                newHeavierBoxPacker.AddItems(overWeightBoxItems);

                                var newHeavierBoxes = newHeavierBoxPacker.PackByVolume();

                                // Found an edge case in packing algorithm that *increased* box count
                                if (newHeavierBoxes.GetCount() > 1)
                                {
                                    _logger.Log(LogLevel.Info, "[REDISTRIBUTING WEIGHT] Abandoning redistribution, because new packing is less effciient than original");
                                    return(originalPackedBoxList);
                                }

                                // TODO: INDEX BASED ARRAY INSERTION FOR BOTH
                                overWeightBoxes[overWeightBoxIndex]   = newHeavierBoxes.ExtractMin();
                                underWeightBoxes[underWeightBoxIndex] = newLighterBox;

                                // We did some work, so see if we can do even better
                                tryRepack = true;
                                overWeightBoxes.Sort(packedBoxes.ReverseCompareTo);
                                underWeightBoxes.Sort(packedBoxes.ReverseCompareTo);

                                // The devil, but ported from PHP, was originally break 3, but .NET doesn't have a break x command
                                // so we're using a goto statement. It is, however, more readable than break 3.
                                goto MOVINGON;
                            }
                        }
                    }

                    MOVINGON : _logger.Log(LogLevel.Info, "Trying to repack");
                }
            } while (tryRepack);

            packedBoxes.InsertAll(overWeightBoxes);
            packedBoxes.InsertAll(underWeightBoxes);

            return(packedBoxes);
        }