Field report · Research & Development

Unit 7

Autonomous Extraction at the Face

Document
SIPSCO-RND-FR-007
Revision
Rev. C
Issued
September 2026
Prepared by
Research & Development
Approved
Yard Operations

Summary

Unit 7 levels a fifteen-by-fifteen pad to the elevation at which it is set down, and hauls what it removes to a chest at the edge of the work. It runs unattended. It has been cutting the north face since spring. This report covers what the unit does, what it refuses to do, how it takes a new version of itself, and how we satisfied ourselves that the difference holds.

Layers, not columns

The obvious way to level ground is column by column: find the high point, chase it down, move on. Unit 7 does not do this. It sweeps the entire area at its starting elevation, rises one block, sweeps it again, and repeats until nothing remains overhead.

This costs more walking. We accept the cost because a column scan walks underneath things — an overhang, a floating leaf canopy, anything whose support has already been removed. A column scan finds clear air above the column and calls it finished. The layer sweep does not have this failure mode, because it visits every cell at every elevation on the way up.

Within a layer the unit walks a serpentine path — down one column, up the next — so that consecutive cells are always neighbours and no move is wasted returning to a row start. Each layer reverses the direction of the one below it, so the unit begins the next layer where it finished the last rather than trekking back to the corner.

One layer's serpentine route, and the layer above it running the same route in reverse Two fifteen-by-fifteen grids side by side. In the first, the route runs down the first column, up the second, and so on, starting at the near corner and finishing at the far one. In the second, the identical route is walked backwards, so it starts at the corner where the layer below finished.Layer +0Layer +1 — reversed
Figure 1. The route through a single layer. Filled dot is where the unit starts, open ring where it finishes. Because the odd layer walks the same route backwards, the unit begins each layer in the corner where it ended the one below — it never returns to a fixed origin between layers.

Knowing when to stop

This is the interesting part of the problem, and the part that took the longest.

An extraction unit can see exactly one block above itself. It cannot survey the site. It cannot know how tall the hill is. What it can do is notice, for free, whether the cell above the one it is standing in is occupied — and it stands in every cell of every layer. Finishing a layer therefore tells the unit whether the next layer has anything in it, which is how a tree keeps the unit climbing even when the leaf layers sit over open air.

Where one block of lookahead carries the unit up, and where it does not Two side elevations, nine cells wide and seven layers tall. In the first, a tree trunk occupies every layer from the floor to the canopy, so the unit finds something overhead at each step and keeps climbing. In the second, debris sits five layers up with nothing beneath it; the unit sweeps the first four layers regardless, finds the fifth empty with nothing above it, and stops below the debris.Connected+5+0Detached+5+0
Figure 2. Left: the trunk occupies every layer, so the unit finds a block overhead at each step and keeps going. Right: the same debris five layers up with nothing beneath it. The shaded band is the four layers swept unconditionally; above it the unit finds an empty layer with nothing overhead and stops. The gap defeats the lookahead. This is a stated limit of the method, not a fault in it.

That lookahead reaches one block. Debris floating with a wider gap beneath it would be missed, so the first four layers are swept unconditionally before the unit is permitted to trust an empty result. A ceiling of thirty-two layers above the starting elevation bounds the job in the event that something above the pad is taller than anyone expected.

An empty layer is only evidence if the unit actually reached all of it. A cell it could not enter — a unit stuck against a block it has failed to cut, out of the six attempts it allows itself — is not a cell known to be empty, and a layer holding one is no longer grounds for calling the job done. The unit carries on upward and reports the skipped cells rather than quietly finishing beneath them.

-- Stop once this layer came up empty AND nothing was spotted overhead,
-- but never before SCAN_MIN layers have actually been walked. A cell we
-- could not reach is not a cell we know to be empty, so a layer with
-- skipped cells is never grounds for calling the job done.
if z + 1 >= SCAN_MIN and layerDigs == 0 and not sawAbove and skipped == 0 then
  print("Nothing left up here. Done.")
  break
end
Listing 1. The stop condition, verbatim from turtles/flattener.lua. sawAbove is the free lookahead; SCAN_MIN is the four-layer floor beneath it; skipped is the count of cells the unit could not reach. All three conditions must hold, and none is reachable from an operator setting.

What the unit will not do

The guarantees below are structural. With one exception, noted where it falls, they are not settings, and an operator cannot switch them off by accident.

It will not cut below the floor. The elevation at which the unit is placed is the floor. The unit never aims a dig at or beneath it. A crew can set the floor by hand, walk away, and find it intact.

It will not leave the pad. Every leg of every route is axis-aligned and every destination lies inside the work area, so there is no sequence of moves that carries the unit outside the fifteen-by-fifteen. This includes the chest, which sits one block behind the near edge and has never been mined.

It will not scatter the haul. A dig with nowhere to put the block drops it on the ground, so the unit refuses to begin a cell with a full inventory — it returns and empties first. The delivery instruction is worse than it looks: told to deposit into nothing, it throws the material on the floor and reports success. The unit therefore confirms a real container is present before it hands anything over, and holds its load rather than dumping it into the dirt if one is not.

It will not hoard its own fuel. Coal, charcoal and blaze rods are recognised as fuel. The unit keeps sixty-four of them aboard and posts the remainder to the chest with everything else it has cut.

Withdrawn at Rev. C. Rev. B of this report stated that fuel was never delivered to the chest. That was the defect, not the guarantee. Coal is ordinary spoil at the face, and a unit that treats every piece of it as reserve fills all sixteen slots with material it refuses to put down. Observed under test: a collection unit on a coal-littered yard seized after sixteen cells with 5,376 coal on the ground and 1,024 aboard, and halted there. An extraction unit driven through a coal seam abandoned 838 blocks mid-job and delivered nothing. Sixty-four is a working reserve; the rest is cargo. The figure is the exception noted above: it is an operator setting, and it is the only one in this section.

It will not spin. If the unit comes home full and leaves still full, it would bounce off the chest once per cell indefinitely. It stops instead, and says why.

It will not lose to gravel. Sand and gravel fall into the gap the instant it is cleared. Each block is re-cut up to eight times before the unit concedes the square.

Endurance

The unit refuels itself, topping up when its reserve falls below two hundred blocks of travel. It continuously computes the cost of returning home from wherever it currently stands, and keeps a margin of sixteen above that figure. When the margin is gone it comes home under its own power rather than stopping in the field.

When the inventory fills, the unit walks to the chest, empties its load down to the fuel reserve described in section 4, and resumes at the exact cell it left. This accounts for essentially all of Unit 7's downtime. The 94% uptime figure quoted elsewhere is not a reliability measure; it is the fraction of the shift the unit is not standing at a chest, waiting for someone to come and take the dirt away.

The second unit

A companion unit patrols a ten-by-ten area collecting loose material. It cruises one block above the litter rather than walking through it, because a unit cannot recover items sharing its own position; clearing each cell downward also makes coverage independent of which way the unit happens to be facing. Under a low roof it drops to ground level and sweeps sideways instead — coverage is then partial, and it reports this rather than implying a clean pass. It runs a full circuit, sleeps an hour, and runs another.

The sideways sweep is the one direction that can reach outside the pad, and at ground level it is the only one doing any work. It is therefore taken only when the cell in front belongs to the ten-by-ten — which excludes the row behind the near edge, where the chest stands. Rev. B described the unit as staying inside the area. At ground level that was an aspiration rather than a fact, and a marker placed one row outside the pad was collected during testing. It is now bounded, and the case is in the harness.

A single cell can hold more litter than the unit can carry. Rather than take what fits and move on, it returns to the chest and comes back to the same cell, up to eight times, until the ground is clear. Before this the unit resumed at the next cell along and counted the pass complete: 400 items of 2,000 were left behind, one per cell, against a report of a clean sweep.

Verification

Both units are exercised against a stubbed voxel world that implements the movement and inspection interface without requiring a live site. The update subsystem is exercised against a stubbed network and filesystem on the same principle. The three harnesses hold 58 cases between them. Their names are the guarantees restated, and they are reproduced here in full.

Extraction unit

  1. clears every block at and above the start layer inside the 15x15
  2. leaves the layer it started on intact
  3. never aims a dig below the start layer
  4. leaves blocks outside the 15x15 alone
  5. does not mine the chest
  6. clears an overhang floating above empty air
  7. delivers the dirt and the stone to the chest
  8. never scatters items on the ground
  9. comes home to the start cell facing the way it started
  10. empties into the chest partway through a big job
  11. halts instead of spinning when the chest fills up
  12. holds its haul rather than scattering it when no chest is there
  13. mines nothing on ground that is already flat
  14. posts mined fuel to the chest instead of seizing up on it
  15. runs perfectly well with no updater installed
  16. checks for a new version once, before it starts digging
  17. installs a new version and restarts before touching the ground
  18. takes an update without restarting when it cannot come back up
  19. gets on with the job when the update check cannot reach the network
  20. refuses a broken release and flattens with the version it has

test/flattener_test.lua

Collection unit

  1. picks up every loose stack inside the 10x10
  2. delivers the haul to the chest
  3. leaves litter outside the 10x10 alone
  4. never scatters items on the ground
  5. never digs anything
  6. empties into the chest partway through and resumes the pass
  7. halts instead of spinning when the chest fills up
  8. holds its haul rather than scattering it when no chest is there
  9. keeps its fuel instead of posting it into the chest
  10. comes home to the start cell facing the way it started
  11. warns and keeps sweeping when there is no headroom
  12. finishes a pass on a yard with nothing lying about
  13. posts swept fuel to the chest instead of seizing up on it
  14. leaves the row behind the start edge alone at ground level
  15. comes back for litter it had no room for
  16. runs perfectly well with no updater installed
  17. checks for a new version at startup and again between passes
  18. installs a new version published between passes and restarts
  19. leaves a startup file, but only when asked to
  20. writes nothing to the computer with autostart off, as it ships
  21. takes an update without restarting when it cannot come back up
  22. carries on sweeping when the update check cannot reach the network
  23. refuses a broken release and keeps sweeping with the version it has

test/sweeper_test.lua

Update subsystem

  1. installs a strictly newer version
  2. leaves an identical version alone
  3. refuses to install an older version
  4. compares version numbers numerically, not as text
  5. refuses a download that does not compile, and keeps the old file
  6. refuses a download with no VERSION line
  7. refuses a download that compiles but is a fraction of the size
  8. reports being offline rather than throwing
  9. copes with the HTTP API being switched off entirely
  10. does not know how to update a program it has no source for
  11. keeps a backup that restore puts back
  12. can update itself
  13. writes a startup file that relaunches the program
  14. rewrites its own startup file without complaint
  15. never clobbers a startup file somebody else wrote

test/updater_test.lua

We would rather find out on a workbench than on the north face.

Taking a new version

A fleet is only as current as its slowest visit. Units may now fetch their own replacement from the published repository and restart into it, which removes the walk to sixteen turtles and the transcription errors that come with it.

Rollout is driven by one line. Each program carries its own version number, and the copy on the server is compared against it component by component — numerically, so 1.10 follows 1.9 rather than preceding it. Publishing a change without raising that number rolls out nothing. Raising it rolls out to every unit that asks.

An update lands only where stopping is free. Neither unit writes its position to disk, so a restart partway through a job would bring the unit back up believing it stood at the corner and set it to work on the wrong ground. The extraction unit therefore checks once, at startup, before its first move. The collection unit checks at startup and again between passes, parked on its start cell with an empty hold — the state a fresh run expects to boot into anyway. There is no mid-job check, and adding one would be a defect.

A release is refused unless it is fit to install.The realistic failure is not a bad program but a truncated download, and a unit that writes half a file over itself is a unit somebody has to walk out to. Every download is therefore parsed before it is written, and one that will not compile is discarded. So is one carrying no version line, and one that arrives at less than half the size of the file it would replace — a cut that lands on a statement boundary can still compile, and the length is what gives it away. The previous version is kept alongside the new one. Rollback is deliberate and manual.

Nothing here is load-bearing. A unit with no network, no update library, or a server returning nonsense carries on running the program it already has, and the harness holds a case for each of those. A unit also restarts only when it knows it can come back up running; otherwise it installs the new version and defers to the next manual start. That guarantee ships switched off, because switching it on writes a startup file to the computer, and that is the operator's decision rather than ours.

Availability

Unit 7's routines are not a trade secret. Both programs, the update library and the test harnesses are published in full. Run them on your own fleet — we ask only that you tell us what we got wrong.

Read the source on GitHub (opens in a new tab)

Appendix A — Specifications

Figures below are the shipped defaults. Each is set at the head of the program named beneath it and may be changed by the operator; the identifier column gives the name it is set under.

A.1 Extraction unit

ParameterValueIdentifier
Working area15 × 15 cellsWIDTH × LENGTH
Vertical ceiling32 layers above the floorMAX_HEIGHT
Unconditional sweep depth4 layersSCAN_MIN
Re-cuts per block8DIG_RETRY
Move attempts before a cell is abandoned6MAX_STUCK
Refuelling threshold200 blocks of travelFUEL_MIN
Reserve held over the trip home16 blocks of travelFUEL_MARGIN
Onboard capacity16 slots—
Accepted fuelCoal, charcoal, coal block, blaze rodFUEL_ITEMS
Fuel kept aboard; the surplus is posted64 itemsFUEL_KEEP
Program version1.0.0VERSION
Restarts itself after an updatefalse as shippedAUTOSTART

turtles/flattener.lua

A.2 Collection unit

ParameterValueIdentifier
Working area10 × 10 cellsWIDTH × LENGTH
Cruise altitude1 block above the litter layerSWEEP_ALT
Interval between passes3,600 s (one hour)PATROL_DELAY
Recovery attempts per cell64SUCK_LIMIT
Chest runs allowed for a single cell8CELL_RETRY
Climb allowance3 blocksCLIMB_LIMIT
Move attempts before a cell is abandoned3MAX_STUCK
Refuelling threshold200 blocks of travelFUEL_MIN
Reserve held over the trip home16 blocks of travelFUEL_MARGIN
Fuel kept aboard; the surplus is posted64 itemsFUEL_KEEP
Program version1.0.0VERSION
Restarts itself after an updatefalse as shippedAUTOSTART

turtles/sweeper.lua

A.3 Update subsystem

ParameterValueIdentifier
Library version1.0.0VERSION
Repository consultederinlkolp/computercraft-scriptsREPO
Branch consultedmainBRANCH
Smallest accepted release, against the file it replaces0.5SHRINK_LIMIT
Previous version retained as.bak alongside the programBACKUP_SUFFIX
RollbackManual—
Required of a release before it is writtenCompiles, and carries a version line—

lib/updater.lua