我的堆叠算法有什么问题?

问题描述 投票:0回答:3

My algorithm

 private void UpdateInventory()
{
    List<byte> indexes = new();
    for (int x = 0; x < Inventory.Count; x++)
    {
        if (!indexes.Contains(Inventory[x].ID))
        {
            indexes.Add(Inventory[x].ID);
            for (int i = x + 1; i < Inventory.Count; i++)
            {
                if (x != i && Inventory[x].ID == Inventory[i].ID && Inventory[x].amount < 99)
                {
                    if (Inventory[x].amount + Inventory[i].amount > 99)
                    {
                        Inventory[i].amount -= 99 - Inventory[x].amount;
                        Inventory[x].amount = 99;
                    }
                    else
                    {
                        Inventory[x].amount += Inventory[i].amount;
                        Inventory[i].amount = 0;
                    }
                }
            }
        }
    }

    Inventory.RemoveAll(item => item.amount <= 0);
}

所以我试图让物品在库存中具有相同 ID 的其他物品之后进行检查,然后检查当前堆栈的数量以确保每个堆栈的最大数量为 99,然后将其他堆栈的数量添加到第一个堆栈,然后删除转移的金额,之后它会检查库存,以便如果任何堆栈的金额为 0,则会将其删除,但由于某种原因,它会以每个堆栈都为 0 结束。

我尝试过一个循环,其中每个堆栈检查除自身之外的所有堆栈,我尝试过一个循环,其中每个堆栈检查列表中自身之上的所有堆栈,然后图像是我最新的尝试,但没有任何效果。

c# unity-game-engine
3个回答
1
投票

我认为在第一个循环中,您正在相互检查每个库存。

您唯一要跳过的是

 x != i

但我相信你宁愿只检查那些你还没有见过的条目,所以例如只从以下条目开始:

for(var i = x + 1; i < Inventory.Count; i++)

因此,假设前面的项目的情况已经由

x
的先前迭代处理过,则您只执行“向前看”而不是“向后看”。

然后,一旦到达

99
,您将根据
indexes
跳过具有相同索引的任何其他条目(顺便说一句,正确的复数是
indices
)。这也是不正确的。

您仍然想使用相同的 ID 检查以下条目

在最后,如此处所述,您的循环会跳过列表末尾的项目,因为当您迭代列表并从中删除项目时,列表计数会减少。

但是,您不需要任何

Linq
也不需要创建新的列表实例。

List<T>.RemoveAll
完美满足您的目的:

Inventory.RemoveAll(item => item.amount <= 0);

总的来说,我想它应该是这样的

private void UpdateInventory()
{
    for (var x = 0; x < Inventory.Count; x++)
    {
        var current = Inventory[x];
        // only deal for entries that have items - the 0 you can already skip right away as it will be removed in the end
        // and 99 are already full
        if (current.amount > 0 && current.amount < 99)
        {
            // only check items after the current one
            for (var i = x + 1; i < Inventory.Count; i++)
            {
                var other = Inventory[i];

                if (other.ID == current.ID && other.amount > 0)
                {
                    if (current.amount + other.amount > 99)
                    {
                        other.amount -= 99 - current.amount;
                        current.amount = 99;
                        // in this case you can immediately exit the inner loop since your slot is already full!
                        break;
                    }

                    current.amount += other.amount;
                    other.amount = 0;
                }
            }
        }
    }

    // remove all 0 entries
    Inventory.RemoveAll(item => item.amount <= 0);
}

0
投票

第 144-150 行中的循环在迭代列表时从列表中删除项目,这就是剩余项目的原因。 让我们看一下更简单的伪代码并运行它:

var list = {1,2,3,4,5};
for (int i = 0; i < list.Count; i++) {
  if (list[i] >= 0) {
    list.RemoveAt(i);
  }
}

现在让我们运行代码 -

i = 0
list = {1,2,3,4,5};
list[i] = 1 // >=0 , so it is removed
list = {2,3,4,5}
i = 1
list[i] = 3 // we skipped 2!!!!

因为您在修改列表时迭代列表,所以您跳过了项目。 避免这种情况的最简单方法是,不从列表中删除项目,而是创建一个仅包含您要保留的项目的新列表。您可以使用 Linq 的

Where
函数,或显式执行此操作。

无论如何,最好的做法是将逻辑分成更小的块,并通过单元测试单独测试每个块,以便了解代码出了什么问题。


0
投票

也许你应该尝试以不同的方式思考这个问题。您的代码示例有点混乱,我不想成为在它损坏时必须尝试调试它的编码员。 (讽刺的是,这正是你要求人们做的事情......)

让我们这样想:角色的库存中有 N 个物品,并且您希望在添加新物品时聚合它们。每个项目都有一个堆栈限制,虽然您没有明确说明,但我假设角色库存中的项目堆栈数量是固定的。

与其尝试管理内存中的堆栈(这看起来像皇家 PITA),不如重新考虑如何存储数据:

public enum eItemType
{
    Arrow = 1,
    Bullet = 2,
    Scroll = 3,
    //etc
}

private Dictionary<eItemType,int> _Inventory = new();

有了这个,任何添加或删除都是简单的字典查找,并且进行数学运算也很简单,否则可能会跨越堆栈。要确定在屏幕上渲染多少个堆栈,您可以循环遍历集合并渲染字典值/99 并向上舍入以获得堆栈计数。枚举只是锦上添花,使阅读代码中的逻辑检查变得简单:

if (_Inventory[eItemType.Arrow] > 1)
{
     // do stuff here
}

不要将渲染多少堆栈的问题与将数据存储在内存中的问题混为一谈。他们没有联系。

© www.soinside.com 2019 - 2024. All rights reserved.