---(( Types ))---
---@class ItemInfoX : ItemInfo
---@field slot integer
---(( Functions ))---
---@param inv inventory
---@return table<integer, ItemInfoX>
local function listItems(inv)
local items = inv.list()
for key, item in pairs(items) do
item.slot = key
end
return items
end
---@param item ItemInfo
---@return string
local function getItemNormalizedName(item)
if item.displayName then
return item.displayName:lower()
end
return select(1, item.name:gsub("[^:]+:", ""))
end
---@param a ItemInfoX
---@param b ItemInfoX
---@return boolean
local function compareItems(a, b)
local nameA = getItemNormalizedName(a)
local nameB = getItemNormalizedName(b)
if nameA ~= nameB then
return nameA < nameB
end
if a.count ~= b.count then
return a.count > b.count
end
return a.slot < b.slot
end
---@param unsorted table<integer,ItemInfoX>
local function sortItemsTable(unsorted)
---@type ItemInfoX[]
local items = {}
for _, item in pairs(unsorted) do
table.insert(items, item)
end
table.sort(items, compareItems)
return items
end
---@param inv inventory
---@param state table<integer, ItemInfoX>
local function findEmptySlot(inv, state)
local nextEmpty = #state + 1
if nextEmpty > inv.size() then
error("Failed to find empty slot in inventory")
end
return nextEmpty
end
---@param inv inventory
---@param state table<integer, ItemInfoX>
---@param fromSlot integer
---@param toSlot integer
local function moveItems(inv, state, fromSlot, toSlot)
if not state[fromSlot] then
error(string.format("move items from slot %d to %d: source slot is empty", fromSlot, toSlot))
end
if state[toSlot] then
error(string.format("move items from slot %d to %d: target slot is not empty", fromSlot, toSlot))
end
local invName = peripheral.getName(inv)
inv.pushItems(invName, fromSlot, nil, toSlot)
state[toSlot] = state[fromSlot]
state[fromSlot] = nil
state[toSlot].slot = toSlot
end
---@param inv inventory
---@param state table<integer, ItemInfoX>
---@param item ItemInfoX
---@param toSlot integer
local function performSortItem(inv, state, item, toSlot)
if state[toSlot] then
local empty = findEmptySlot(inv, state)
moveItems(inv, state, toSlot, empty)
end
moveItems(inv, state, item.slot, toSlot)
end
---@param key integer
---@param item ItemInfoX
---@return boolean
local function shouldSort(key, item)
return key ~= item.slot
end
---@param inv inventory
---@param state table<integer, ItemInfoX>
---@param sorted ItemInfoX[]
---@param sortOps integer
local function performSort(inv, state, sorted, sortOps)
local fgColor = term.getTextColor()
term.setTextColor(colors.gray)
local lines = write(string.format("|%s|", string.rep("-", sortOps)))
term.setTextColor(colors.yellow)
local _, y = term.getCursorPos()
term.setCursorPos(1, y - lines)
write("|")
for key, item in ipairs(sorted) do
if shouldSort(key, item) then
performSortItem(inv, state, item, key)
write("=")
end
end
write("|")
print()
term.setTextColor(fgColor)
end
---@param side string?
---@return inventory
local function findChest(side)
if side then
local p = peripheral.wrap(side)
if not p then
error(string.format('No peripheral at "%s"', side))
end
if not peripheral.hasType(p, "inventory") then
error(string.format('Peripheral at "%s" is not an "inventory" type', side))
end
return p
end
if peripheral.hasType("front", "inventory") then
return peripheral.wrap("front")
end
local chest = peripheral.find("inventory")
return chest
end
---(( Program ))---
local chest = findChest(...)
if chest == nil then
print("No inventory peripheral found.")
return
end
local origItems = listItems(chest)
local sortedItems = sortItemsTable(origItems)
print(string.format('Sorting "%s" with %d items...', peripheral.getName(chest), #sortedItems))
local sortOps = 0
for key, item in ipairs(sortedItems) do
if shouldSort(key, item) then
sortOps = sortOps + 1
end
end
if sortOps == 0 then
print("Already sorted.")
return
end
performSort(chest, origItems, sortedItems, sortOps)
print("Sorting done")
Comments