Create: Computing AgesDoc Brass
Recettes : vrais projets

Tri automatique des objets

Vider un coffre d'entrée dans des coffres de rangement sur un Câble de données, par objet, par mod ou par fin d'identifiant, avec un coffre de surplus et un écran d'état.

Toute base finit par avoir un coffre plein de tout : ce que les perceuses mécaniques ont creusé, ce que les fermes ont lâché, ce que vous avez rapporté de voyage. Create sait trier avec des filtres sur les entonnoirs et les chutes intelligentes, mais chaque filtre prend un seul objet, et changer l'organisation oblige à reconstruire. Un ordinateur sur un Câble de données peut trier avec des règles : « tout ce qui finit par _ore va au coffre des minerais », « tout ce qui vient de Create va au coffre des pièces », et les changer en modifiant une table.

Le programme vide un coffre d'entrée dans des coffres de rangement, une pile à la fois, en choisissant le coffre de la première règle qui correspond à l'objet. Ce qu'aucune règle ne veut, et ce qui ne tient plus dans un coffre plein, part dans un coffre de surplus. Un écran montre chaque coffre avec ses règles, le nombre d'objets reçus et s'il est plein.

Ce qu'il vous faut

  • Un Mini-ordinateur ou plus récent. Les coffres de rangement sont atteints par un Câble de données, et seuls le Mini-ordinateur, le Micro-ordinateur, le Microcontrôleur et l'Ordinateur moderne ont un port réseau. Un Micro-ordinateur affiche les couleurs de l'écran ci-dessous ; un Mini-ordinateur le montre en ambre.
  • Du Câble de données, qui longe tous les coffres. Chaque bloc qui touche le câble devient un périphérique nommé d'après son type et sa position : inventory@12,64,-3 pour un coffre en x 12, y 64, z -3 (F3 affiche les coordonnées du bloc que vous regardez).
  • Un coffre d'entrée qui touche le câble (ou l'ordinateur), rempli à la main, par un entonnoir de Minecraft ou par un entonnoir de Create au bout d'un tapis.
  • Des coffres de rangement, un par catégorie, plus un coffre de surplus. Les tonneaux sont pratiques : deux coffres côte à côte fusionneraient en un grand coffre. Un Coffre à objet renforcé de Create fait un grand surplus.

Vu de dessus, un bloc par case :

  +--------+--------+--------+--------+--------+
  |minerais|lingots | create |  bois  |surplus |   tonneaux de rangement
  +--------+--------+--------+--------+--------+
  |   =    |   =    |   =    |   =    |   =    |   Câble de données, contre chaque tonneau
  +--------+--------+--------+--------+--------+
  |  Mini  | ENTRÉE |                              Mini-ordinateur et coffre d'entrée,
  +--------+--------+                              tous deux contre le câble

Le programme, étape par étape

1. Les coffres et les règles

COFFRES donne à chaque coffre de rangement un nom court pour l'écran, et son nom de périphérique sur le câble. REGLES se lit de haut en bas, et la première règle qui correspond l'emporte. Une règle a l'une de ces trois formes :

règlecorrespond à
"minecraft:iron_ingot"cet objet seulement
"create:*"tout identifiant qui commence par create: : tout le mod Create
"*_ore"tout identifiant qui finit par _ore : minerai de fer, minerai d'or des abîmes, minerai de zinc...
Brass
local ENTREE = "inventory@11,64,-2"  -- le coffre que vous remplissez : nom de câble ou côté

local COFFRES = {                    -- nom court = nom de câble du coffre
  minerais = "inventory@10,64,-4",
  lingots = "inventory@11,64,-4",
  create = "inventory@12,64,-4",
  bois = "inventory@13,64,-4",
  surplus = "inventory@14,64,-4",
}
local SURPLUS = "surplus"            -- où vont les objets sans règle

local REGLES = {
  {motif = "minecraft:raw_*", vers = "minerais"},
  {motif = "*_ore", vers = "minerais"},
  {motif = "*_ingot", vers = "lingots"},
  {motif = "*_nugget", vers = "lingots"},
  {motif = "create:*", vers = "create"},
  {motif = "*_log", vers = "bois"},
  {motif = "*_planks", vers = "bois"},
}

local REPOS = 1      -- secondes entre deux coups d'œil à un coffre d'entrée vide
local ACTIF = 0.25   -- secondes entre deux passes tant que des objets arrivent

L'ordre compte : create:brass_ingot correspond à la fois à *_ingot et à create:*, et va dans lingots parce que cette règle vient d'abord. Mettez les règles précises en haut et les règles larges en dessous.

2. Reconnaître un objet

string.starts et string.ends font le travail. Une étoile au début d'une règle veut dire « finit par », une étoile à la fin « commence par » :

Brass
local function correspond(motif, id)
  if motif:starts("*") then return id:ends(motif:sub(2)) end
  if motif:ends("*") then return id:starts(motif:sub(1, -2)) end
  return id == motif
end

local function destination(id)
  for _, regle in ipairs(REGLES) do
    if correspond(regle.motif, id) then return regle.vers end
  end
  return SURPLUS
end

motif:sub(2) est la règle sans son premier caractère, motif:sub(1, -2) sans son dernier (une position négative se compte depuis la fin).

Brass
local function correspond(motif, id)
  if motif:starts("*") then return id:ends(motif:sub(2)) end
  if motif:ends("*") then return id:starts(motif:sub(1, -2)) end
  return id == motif
end
print(correspond("*_ore", "minecraft:deepslate_iron_ore"))
print(correspond("create:*", "create:andesite_alloy"))
print(correspond("create:*", "minecraft:andesite"))
Écran
true
true
false

3. Vérifier les réglages

Une faute de frappe dans une table doit arrêter le programme tout de suite avec un message clair, pas envoyer la moitié des objets nulle part. preparer prend le coffre d'entrée, vérifie que chaque règle désigne un coffre connu, note les coffres absents et prépare le texte des règles pour l'écran :

Brass
local entree = nil
local deplaces = {}   -- nom court -> objets envoyés là depuis le démarrage
local etat = {}       -- nom court -> "ok", "PLEIN" ou "ABSENT"
local regles_de = {}  -- nom court -> ses règles, en texte pour l'écran

local function preparer()
  entree = peripheral.wrap(ENTREE)
  if entree == nil or entree.push == nil then
    error("pas de coffre d'entrée '" .. ENTREE .. "'")
  end
  if COFFRES[SURPLUS] == nil then
    error("le coffre de surplus '" .. SURPLUS .. "' n'est pas dans COFFRES")
  end
  for _, regle in ipairs(REGLES) do
    if COFFRES[regle.vers] == nil then
      error("la règle '" .. regle.motif .. "' envoie vers '" .. regle.vers .. "', absent de COFFRES")
    end
    if regles_de[regle.vers] == nil then
      regles_de[regle.vers] = regle.motif
    else
      regles_de[regle.vers] = regles_de[regle.vers] .. ", " .. regle.motif
    end
  end
  for cle, nom in pairs(COFFRES) do
    deplaces[cle] = 0
    if peripheral.type(nom) == nil then etat[cle] = "ABSENT" else etat[cle] = "ok" end
    if regles_de[cle] == nil then regles_de[cle] = "(aucune règle)" end
  end
  if regles_de[SURPLUS] == "(aucune règle)" then regles_de[SURPLUS] = "tout le reste" end
end

pairs parcourt les entrées nommées d'une table dans l'ordre où elles ont été écrites : l'écran liste donc les coffres dans l'ordre de COFFRES.

4. Déplacer une pile : push

Le cœur du trieur est entree.push(cible, emplacement, quantite) (inventory.push()). Ses règles valent la peine d'être bien connues :

  • Il s'appelle sur l'inventaire source, et la cible est le nom d'un autre périphérique du même ordinateur (un côté comme "right", ou un nom de câble), pas un objet obtenu par wrap.
  • emplacement limite le déplacement à cet emplacement de la source ; quantite est le maximum d'objets à déplacer (64 si on l'omet).
  • Il renvoie le nombre d'objets réellement déplacés : 0 quand la cible est pleine, moins que demandé quand elle se remplit en route. Rien ne se perd : ce qui ne rentre pas reste dans la source.
  • Quand le nom ne mène à aucun inventaire (coffre cassé, faute de frappe, chunk déchargé), il lève l'erreur no inventory 'inventory@14,64,-4'.

envoyer l'appelle donc à travers pcall, et traduit le résultat en état du coffre :

Brass
local function envoyer(cle, emplacement, quantite)
  local r = pcall(entree.push, COFFRES[cle], emplacement, quantite)
  if not r.ok then
    etat[cle] = "ABSENT"
    return 0
  end
  if r.value < quantite then etat[cle] = "PLEIN" else etat[cle] = "ok" end
  deplaces[cle] = deplaces[cle] + r.value
  return r.value
end

5. Une passe de tri

Une passe lit le coffre d'entrée une fois avec entree.list(), puis envoie chaque pile vers son coffre. Ce que le coffre choisi refuse (plein ou absent) a une seconde chance dans le surplus. Les piles qui restent quand même sont comptées comme « bloquées » : leur coffre et le surplus sont pleins.

Brass
local total = 0           -- objets triés depuis le démarrage
local bloquees = 0        -- piles restées en entrée après la dernière passe
local entree_ok = true
local dernier = "rien pour l'instant"

local function passe_de_tri()
  local r = pcall(entree.list)
  entree_ok = r.ok
  if not r.ok then return 0 end
  local fait = 0
  bloquees = 0
  for emplacement, pile in pairs(r.value) do
    local cle = destination(pile.name)
    local n = envoyer(cle, emplacement, pile.count)
    local ou = cle
    if n < pile.count and cle ~= SURPLUS then
      local en_plus = envoyer(SURPLUS, emplacement, pile.count - n)
      if en_plus > 0 then ou = SURPLUS end
      n = n + en_plus
    end
    if n > 0 then dernier = n .. " x " .. pile.name .. " -> " .. ou end
    if n < pile.count then bloquees = bloquees + 1 end
    fait = fait + n
  end
  total = total + fait
  return fait
end

entree.list() renvoie les piles indexées par numéro d'emplacement (les emplacements vides manquent) : c'est pourquoi la boucle utilise pairs, et pourquoi emplacement peut être passé tel quel à push.

6. L'écran et la boucle

L'écran liste les coffres, puis l'état de l'entrée et le dernier déplacement. Un clic n'importe où suspend ou reprend le tri, pratique pendant que vous réorganisez les coffres.

Brass
local en_pause = false

local function dessiner()
  term.set_bg(term.colors.black)
  term.clear()
  term.set_cursor(1, 1)
  term.set_bg(term.colors.blue)
  term.set_fg(term.colors.white)
  local titre = "TRI DES OBJETS"
  if en_pause then titre = "TRI DES OBJETS (EN PAUSE)" end
  term.write(string.format(" %-34s%15s ", titre, total .. " triés"))
  term.set_bg(term.colors.black)
  term.set_fg(term.colors.light_gray)
  term.set_cursor(1, 3)
  term.write(string.format(" %-9s %-25s%7s %-7s", "Coffre", "Règles", "Objets", "État"))
  local y = 4
  for cle, _ in pairs(COFFRES) do
    term.set_cursor(1, y)
    term.set_fg(term.colors.white)
    term.write(string.format(" %-9s %-25s%7d ", cle:sub(1, 9), regles_de[cle]:sub(1, 25), deplaces[cle]))
    if etat[cle] == "ok" then
      term.set_fg(term.colors.green)
    elseif etat[cle] == "PLEIN" then
      term.set_fg(term.colors.yellow)
    else
      term.set_fg(term.colors.red)
    end
    term.write(etat[cle])
    y = y + 1
  end
  term.set_cursor(2, 16)
  if not entree_ok then
    term.set_fg(term.colors.red)
    term.write("Coffre d'entrée introuvable : " .. ENTREE)
  elseif bloquees > 0 then
    term.set_fg(term.colors.yellow)
    term.write(bloquees .. " pile(s) bloquée(s) en entrée : coffres pleins")
  else
    term.set_fg(term.colors.green)
    term.write("Coffre d'entrée vide, en attente d'objets.")
  end
  term.set_cursor(2, 17)
  term.set_fg(term.colors.light_gray)
  term.write("Dernier : " .. dernier)
  term.set_cursor(2, 19)
  term.set_fg(term.colors.gray)
  term.write("Cliquez sur l'écran pour suspendre ou reprendre.")
end

La boucle attend son minuteur. Après une passe qui a déplacé quelque chose, elle regarde de nouveau bientôt (ACTIF), car un tapis alimente sans doute encore le coffre ; après une passe vide, elle attend plus longtemps (REPOS) : un trieur au repos ne coûte presque rien.

Brass
preparer()
dessiner()
local minuteur = os.start_timer(REPOS)
while true do
  local e = os.pull_event()
  if e.name == "timer" and e.id == minuteur then
    local fait = 0
    if not en_pause then fait = passe_de_tri() end
    dessiner()
    if fait > 0 then minuteur = os.start_timer(ACTIF) else minuteur = os.start_timer(REPOS) end
  elseif e.name == "click" then
    en_pause = not en_pause
    dessiner()
  end
end

Le programme complet

startup
-- Tri des objets : vide un coffre d'entrée dans des coffres de rangement sur
-- un Câble de données, selon des règles ; ce qu'aucune règle ne veut part au
-- coffre de surplus.

local ENTREE = "inventory@11,64,-2"  -- le coffre que vous remplissez : nom de câble ou côté

local COFFRES = {                    -- nom court = nom de câble du coffre
  minerais = "inventory@10,64,-4",
  lingots = "inventory@11,64,-4",
  create = "inventory@12,64,-4",
  bois = "inventory@13,64,-4",
  surplus = "inventory@14,64,-4",
}
local SURPLUS = "surplus"            -- où vont les objets sans règle

-- Lues de haut en bas, la première qui correspond l'emporte :
--   "minecraft:iron_ingot"  cet objet seulement
--   "create:*"              tout identifiant qui commence par "create:"
--   "*_ore"                 tout identifiant qui finit par "_ore"
local REGLES = {
  {motif = "minecraft:raw_*", vers = "minerais"},
  {motif = "*_ore", vers = "minerais"},
  {motif = "*_ingot", vers = "lingots"},
  {motif = "*_nugget", vers = "lingots"},
  {motif = "create:*", vers = "create"},
  {motif = "*_log", vers = "bois"},
  {motif = "*_planks", vers = "bois"},
}

local REPOS = 1      -- secondes entre deux coups d'œil à un coffre d'entrée vide
local ACTIF = 0.25   -- secondes entre deux passes tant que des objets arrivent

local entree = nil
local deplaces = {}   -- nom court -> objets envoyés là depuis le démarrage
local etat = {}       -- nom court -> "ok", "PLEIN" ou "ABSENT"
local regles_de = {}  -- nom court -> ses règles, en texte pour l'écran
local total = 0       -- objets triés depuis le démarrage
local bloquees = 0    -- piles restées en entrée après la dernière passe
local entree_ok = true
local dernier = "rien pour l'instant"
local en_pause = false

-- Règles ---------------------------------------------------------------------------

local function correspond(motif, id)
  if motif:starts("*") then return id:ends(motif:sub(2)) end
  if motif:ends("*") then return id:starts(motif:sub(1, -2)) end
  return id == motif
end

local function destination(id)
  for _, regle in ipairs(REGLES) do
    if correspond(regle.motif, id) then return regle.vers end
  end
  return SURPLUS
end

local function preparer()
  entree = peripheral.wrap(ENTREE)
  if entree == nil or entree.push == nil then
    error("pas de coffre d'entrée '" .. ENTREE .. "'")
  end
  if COFFRES[SURPLUS] == nil then
    error("le coffre de surplus '" .. SURPLUS .. "' n'est pas dans COFFRES")
  end
  for _, regle in ipairs(REGLES) do
    if COFFRES[regle.vers] == nil then
      error("la règle '" .. regle.motif .. "' envoie vers '" .. regle.vers .. "', absent de COFFRES")
    end
    if regles_de[regle.vers] == nil then
      regles_de[regle.vers] = regle.motif
    else
      regles_de[regle.vers] = regles_de[regle.vers] .. ", " .. regle.motif
    end
  end
  for cle, nom in pairs(COFFRES) do
    deplaces[cle] = 0
    if peripheral.type(nom) == nil then etat[cle] = "ABSENT" else etat[cle] = "ok" end
    if regles_de[cle] == nil then regles_de[cle] = "(aucune règle)" end
  end
  if regles_de[SURPLUS] == "(aucune règle)" then regles_de[SURPLUS] = "tout le reste" end
end

-- Déplacer les objets -------------------------------------------------------------

local function envoyer(cle, emplacement, quantite)
  local r = pcall(entree.push, COFFRES[cle], emplacement, quantite)
  if not r.ok then
    etat[cle] = "ABSENT"
    return 0
  end
  if r.value < quantite then etat[cle] = "PLEIN" else etat[cle] = "ok" end
  deplaces[cle] = deplaces[cle] + r.value
  return r.value
end

local function passe_de_tri()
  local r = pcall(entree.list)
  entree_ok = r.ok
  if not r.ok then return 0 end
  local fait = 0
  bloquees = 0
  for emplacement, pile in pairs(r.value) do
    local cle = destination(pile.name)
    local n = envoyer(cle, emplacement, pile.count)
    local ou = cle
    if n < pile.count and cle ~= SURPLUS then
      local en_plus = envoyer(SURPLUS, emplacement, pile.count - n)
      if en_plus > 0 then ou = SURPLUS end
      n = n + en_plus
    end
    if n > 0 then dernier = n .. " x " .. pile.name .. " -> " .. ou end
    if n < pile.count then bloquees = bloquees + 1 end
    fait = fait + n
  end
  total = total + fait
  return fait
end

-- Écran ------------------------------------------------------------------------------

local function dessiner()
  term.set_bg(term.colors.black)
  term.clear()
  term.set_cursor(1, 1)
  term.set_bg(term.colors.blue)
  term.set_fg(term.colors.white)
  local titre = "TRI DES OBJETS"
  if en_pause then titre = "TRI DES OBJETS (EN PAUSE)" end
  term.write(string.format(" %-34s%15s ", titre, total .. " triés"))
  term.set_bg(term.colors.black)
  term.set_fg(term.colors.light_gray)
  term.set_cursor(1, 3)
  term.write(string.format(" %-9s %-25s%7s %-7s", "Coffre", "Règles", "Objets", "État"))
  local y = 4
  for cle, _ in pairs(COFFRES) do
    term.set_cursor(1, y)
    term.set_fg(term.colors.white)
    term.write(string.format(" %-9s %-25s%7d ", cle:sub(1, 9), regles_de[cle]:sub(1, 25), deplaces[cle]))
    if etat[cle] == "ok" then
      term.set_fg(term.colors.green)
    elseif etat[cle] == "PLEIN" then
      term.set_fg(term.colors.yellow)
    else
      term.set_fg(term.colors.red)
    end
    term.write(etat[cle])
    y = y + 1
  end
  term.set_cursor(2, 16)
  if not entree_ok then
    term.set_fg(term.colors.red)
    term.write("Coffre d'entrée introuvable : " .. ENTREE)
  elseif bloquees > 0 then
    term.set_fg(term.colors.yellow)
    term.write(bloquees .. " pile(s) bloquée(s) en entrée : coffres pleins")
  else
    term.set_fg(term.colors.green)
    term.write("Coffre d'entrée vide, en attente d'objets.")
  end
  term.set_cursor(2, 17)
  term.set_fg(term.colors.light_gray)
  term.write("Dernier : " .. dernier)
  term.set_cursor(2, 19)
  term.set_fg(term.colors.gray)
  term.write("Cliquez sur l'écran pour suspendre ou reprendre.")
end

-- Boucle principale --------------------------------------------------------------

preparer()
dessiner()
local minuteur = os.start_timer(REPOS)
while true do
  local e = os.pull_event()
  if e.name == "timer" and e.id == minuteur then
    local fait = 0
    if not en_pause then fait = passe_de_tri() end
    dessiner()
    if fait > 0 then minuteur = os.start_timer(ACTIF) else minuteur = os.start_timer(REPOS) end
  elseif e.name == "click" then
    en_pause = not en_pause
    dessiner()
  end
end

À quoi ressemble l'écran

La même fonction dessiner, avec les compteurs d'un trieur qui tourne depuis un moment : le tonneau du bois est plein, si bien que des planches sont parties au surplus.

Brass
local ENTREE = "inventory@11,64,-2"
local COFFRES = {
  minerais = "inventory@10,64,-4",
  lingots = "inventory@11,64,-4",
  create = "inventory@12,64,-4",
  bois = "inventory@13,64,-4",
  surplus = "inventory@14,64,-4",
}
local regles_de = {
  minerais = "minecraft:raw_*, *_ore",
  lingots = "*_ingot, *_nugget",
  create = "create:*",
  bois = "*_log, *_planks",
  surplus = "tout le reste",
}
local deplaces = {minerais = 1408, lingots = 896, create = 517, bois = 1728, surplus = 75}
local etat = {minerais = "ok", lingots = "ok", create = "ok", bois = "PLEIN", surplus = "ok"}
local total = 4624
local bloquees = 0
local entree_ok = true
local dernier = "11 x minecraft:oak_planks -> surplus"
local en_pause = false

local function dessiner()
  term.set_bg(term.colors.black)
  term.clear()
  term.set_cursor(1, 1)
  term.set_bg(term.colors.blue)
  term.set_fg(term.colors.white)
  local titre = "TRI DES OBJETS"
  if en_pause then titre = "TRI DES OBJETS (EN PAUSE)" end
  term.write(string.format(" %-34s%15s ", titre, total .. " triés"))
  term.set_bg(term.colors.black)
  term.set_fg(term.colors.light_gray)
  term.set_cursor(1, 3)
  term.write(string.format(" %-9s %-25s%7s %-7s", "Coffre", "Règles", "Objets", "État"))
  local y = 4
  for cle, _ in pairs(COFFRES) do
    term.set_cursor(1, y)
    term.set_fg(term.colors.white)
    term.write(string.format(" %-9s %-25s%7d ", cle:sub(1, 9), regles_de[cle]:sub(1, 25), deplaces[cle]))
    if etat[cle] == "ok" then
      term.set_fg(term.colors.green)
    elseif etat[cle] == "PLEIN" then
      term.set_fg(term.colors.yellow)
    else
      term.set_fg(term.colors.red)
    end
    term.write(etat[cle])
    y = y + 1
  end
  term.set_cursor(2, 16)
  if not entree_ok then
    term.set_fg(term.colors.red)
    term.write("Coffre d'entrée introuvable : " .. ENTREE)
  elseif bloquees > 0 then
    term.set_fg(term.colors.yellow)
    term.write(bloquees .. " pile(s) bloquée(s) en entrée : coffres pleins")
  else
    term.set_fg(term.colors.green)
    term.write("Coffre d'entrée vide, en attente d'objets.")
  end
  term.set_cursor(2, 17)
  term.set_fg(term.colors.light_gray)
  term.write("Dernier : " .. dernier)
  term.set_cursor(2, 19)
  term.set_fg(term.colors.gray)
  term.write("Cliquez sur l'écran pour suspendre ou reprendre.")
end

dessiner()
Écran
Écran

L'essayer

  1. Trouvez les noms de vos coffres. Dans le shell, l'interpréteur interactif liste tous les inventaires du câble :

    Terminal
    > brass
    Brass 1.0 - type 'exit' to leave.
    brass> for _, n in ipairs(peripheral.list("inventory")) do print(n) end
    inventory@11,64,-2
    inventory@10,64,-4
    inventory@11,64,-4
    inventory@12,64,-4
    inventory@13,64,-4
    inventory@14,64,-4
    brass> exit

    Regardez chaque coffre avec F3 pour savoir quelles coordonnées sont les siennes, ou lancez l'exemple devices (devices), qui dessine l'ordinateur, son câble et chaque bloc branché dessus.

  2. Recopiez les noms dans ENTREE et COFFRES, enregistrez sous startup et lancez. Un mauvais nom arrête le programme aussitôt avec pas de coffre d'entrée '...', ou affiche ABSENT à côté du coffre.
  3. Jetez un mélange dans le coffre d'entrée : minerai de fer, cuivre brut, bûches de chêne, roues dentées de Create, terre. En une seconde, tout part vers son coffre ; la terre va au surplus.
  4. Remplissez le tonneau du bois jusqu'en haut, et ajoutez des bûches : la ligne du bois passe à PLEIN et les bûches partent au surplus.
  5. Cassez le tonneau de surplus et remplissez celui du bois : les bûches restent en entrée, et la ligne d'état compte les piles bloquées.

Variantes

  • Arrêter le tapis en cas de blocage. Quand bloquees > 0, allumez une sortie redstone qui atteint l'entonnoir de Create qui remplit le coffre d'entrée : un entonnoir alimenté ne déplace plus rien, et le tapis s'accumule au lieu de déborder. rs.set("top", bloquees > 0) après chaque passe.
  • Trier par nom. Chaque pile a aussi un champ display, son nom tel qu'il s'affiche en jeu. Renommez des objets sur une enclume (« Retour à la base ») et ajoutez une sorte de règle qui compare pile.display.
  • Garder les compteurs. Enregistrez deplaces dans un fichier avec fs.write chaque minute et relisez-le dans preparer : les compteurs survivent à un redémarrage.
  • Ressortir des objets. Prenez un coffre de rangement et poussez de lui vers un coffre de sortie : peripheral.wrap(COFFRES.lingots) puis .push(SORTIE, emplacement, 64). Un écran tactile avec un bouton par objet fait une petite boutique : voir Panneau de commande tactile pour les boutons.
  • Beaucoup de coffres. Sur un Ordinateur moderne, l'écran a 24 lignes : il y a la place pour 15 coffres. Pour toute une salle de stockage, faites de chaque entrée de COFFRES une liste de noms (minerais = {"inventory@10,64,-4", "inventory@10,65,-4"}), et laissez envoyer les essayer tour à tour jusqu'à ce que l'un prenne la pile.