builder improvements

This commit is contained in:
kepler155c@gmail.com 2017-06-15 03:25:55 -04:00
parent 71cbf34ade
commit 3f66a9397c
5 changed files with 245 additions and 262 deletions

View File

@ -347,7 +347,7 @@ function standardBlockDB:seedDB()
[ '163:0' ] = 'stairs', [ '163:0' ] = 'stairs',
[ '164:0' ] = 'stairs', [ '164:0' ] = 'stairs',
[ '167:0' ] = 'trapdoor', [ '167:0' ] = 'trapdoor',
[ '170:0' ] = 'flatten', [ '170:0' ] = 'quartz-pillar', -- hay bale
[ '175:0' ] = 'largeplant', [ '175:0' ] = 'largeplant',
[ '175:1' ] = 'largeplant', [ '175:1' ] = 'largeplant',
[ '175:2' ] = 'largeplant', -- double tallgrass - an alternative would be to use grass as the bottom part, bonemeal as top part [ '175:2' ] = 'largeplant', -- double tallgrass - an alternative would be to use grass as the bottom part, bonemeal as top part

View File

@ -29,20 +29,25 @@ function ChestProvider:refresh()
entry = self.cache[key] entry = self.cache[key]
if not entry then if not entry then
local meta = self.p.getItemMeta(k) -- slow method.. cache for speed local meta = self.p.getItemMeta(k) -- slow method.. cache for speed
entry = { if meta then
id = s.name, entry = {
dmg = s.damage, id = s.name,
name = meta.displayName, dmg = s.damage,
max_size = meta.maxCount, name = meta.displayName,
} max_size = meta.maxCount,
self.cache[key] = entry }
self.cache[key] = entry
end
end
if entry then
entry = Util.shallowCopy(entry)
self.items[key] = entry
entry.qty = 0
end end
entry = Util.shallowCopy(entry)
self.items[key] = entry
entry.qty = 0
end end
if entry then
entry.qty = entry.qty + s.count entry.qty = entry.qty + s.count
end
end end
end end
return self.items return self.items

View File

@ -1,8 +1,6 @@
local class = require('class') local class = require('class')
local DEFLATE = require('deflatelua') local DEFLATE = require('deflatelua')
local UI = require('ui') local UI = require('ui')
local Logger = require('logger')
local Profile = require('profile')
local Point = require('point') local Point = require('point')
--[[ --[[
@ -17,7 +15,6 @@ function Schematic:init(args)
self.blocks = { } self.blocks = { }
self.damages = { } self.damages = { }
self.originalBlocks = { } self.originalBlocks = { }
self.placementChains = { }
self.x, self.y, self.z = 0, 0, 0 self.x, self.y, self.z = 0, 0, 0
self.height = 0 self.height = 0
self.index = 1 self.index = 1
@ -29,7 +26,7 @@ end
Some parts of the file reader code was modified from the original Some parts of the file reader code was modified from the original
--]] --]]
function Schematic:discardBytes(h, n, spinner) function Schematic:discardBytes(h, n, spinner)
for i = 1,n do for i = 1,n do
h:readbyte() h:readbyte()
@ -38,17 +35,17 @@ function Schematic:discardBytes(h, n, spinner)
end end
end end
end end
function Schematic:readname(h) function Schematic:readname(h)
local n1 = h:readbyte(h) local n1 = h:readbyte(h)
local n2 = h:readbyte(h) local n2 = h:readbyte(h)
if(n1 == nil or n2 == nil) then if(n1 == nil or n2 == nil) then
return "" return ""
end end
local n = n1*256 + n2 local n = n1*256 + n2
local str = "" local str = ""
for i=1,n do for i=1,n do
local c = h:readbyte(h) local c = h:readbyte(h)
@ -59,13 +56,13 @@ function Schematic:readname(h)
end end
return str return str
end end
function Schematic:parse(a, h, containsName, spinner) function Schematic:parse(a, h, containsName, spinner)
if a==0 then if a==0 then
return return
end end
local name local name
if containsName then if containsName then
name = self:readname(h) name = self:readname(h)
@ -150,9 +147,8 @@ function Schematic:parse(a, h, containsName, spinner)
end end
end end
-- end http://www.computercraft.info/forums2/index.php?/topic/1949-turtle-schematic-file-builder/ -- end http://www.computercraft.info/forums2/index.php?/topic/1949-turtle-schematic-file-builder/
function Schematic:copyBlocks(iblocks, oblocks, spinner) function Schematic:copyBlocks(iblocks, oblocks, spinner)
Profile.start('copyBlocks')
for k,b in ipairs(iblocks) do for k,b in ipairs(iblocks) do
oblocks[k] = Util.shallowCopy(b) oblocks[k] = Util.shallowCopy(b)
if spinner then if spinner then
@ -161,29 +157,20 @@ function Schematic:copyBlocks(iblocks, oblocks, spinner)
end end
end end
end end
Profile.stop('copyBlocks')
end end
function Schematic:reload() function Schematic:reload()
self.placementChains = {}
self.blocks = { } self.blocks = { }
self:copyBlocks(self.originalBlocks, self.blocks) self:copyBlocks(self.originalBlocks, self.blocks)
--[[
self.planes = { } for _,ri in pairs(self.rowIndex) do
for i = 0, self.height - 1 do ri.loaded = false
self.planes[i] = { }
end end
for k,b in ipairs(self.blocks) do
if not self.planes[b.y].start then
self.planes[b.y].start = k
end
end
--]]
end end
function Schematic:getMagic(fh) function Schematic:getMagic(fh)
fh:open() fh:open()
local magic = fh:readbyte() * 256 + fh:readbyte() local magic = fh:readbyte() * 256 + fh:readbyte()
fh:close() fh:close()
@ -192,12 +179,12 @@ function Schematic:getMagic(fh)
end end
function Schematic:isCompressed(filename) function Schematic:isCompressed(filename)
local h = fs.open(filename, "rb") local h = fs.open(filename, "rb")
if not h then if not h then
error('unable to open: ' .. filename) error('unable to open: ' .. filename)
end end
local magic = h.read() * 256 + h.read() local magic = h.read() * 256 + h.read()
h.close() h.close()
@ -206,7 +193,7 @@ function Schematic:isCompressed(filename)
end end
function Schematic:checkFileType(fh) function Schematic:checkFileType(fh)
local magic = self:getMagic(fh) local magic = self:getMagic(fh)
if magic ~= schematicMagic then if magic ~= schematicMagic then
error('Unknown file type') error('Unknown file type')
@ -253,8 +240,6 @@ end
function Schematic:decompress(ifname, spinner) function Schematic:decompress(ifname, spinner)
Profile.start('decompress')
local ifh = fs.open(ifname, "rb") local ifh = fs.open(ifname, "rb")
if not ifh then if not ifh then
error('Unable to open ' .. ifname) error('Unable to open ' .. ifname)
@ -271,20 +256,17 @@ function Schematic:decompress(ifname, spinner)
ifh.close() ifh.close()
spinner:stop() spinner:stop()
Profile.stop('decompress')
return mh return mh
end end
function Schematic:loadpass(fh, spinner) function Schematic:loadpass(fh, spinner)
Profile.start('load')
fh:open() fh:open()
while true do while true do
local a = fh:readbyte() local a = fh:readbyte()
if not a then if not a then
break break
end end
@ -297,13 +279,11 @@ function Schematic:loadpass(fh, spinner)
end end
fh:close() fh:close()
Profile.stop('load')
self:assignDamages(spinner) self:assignDamages(spinner)
self.damages = nil self.damages = nil
self:copyBlocks(self.blocks, self.originalBlocks, spinner) self:copyBlocks(self.blocks, self.originalBlocks, spinner)
Profile.display()
spinner:stop() spinner:stop()
end end
@ -316,7 +296,7 @@ function Schematic:load(filename)
y = cursorY - 1 y = cursorY - 1
}) })
local f local f
if self:isCompressed(filename) then if self:isCompressed(filename) then
local originalFile = filename local originalFile = filename
filename = originalFile .. '.uncompressed' filename = originalFile .. '.uncompressed'
@ -335,28 +315,6 @@ function Schematic:load(filename)
self:checkFileType(f) self:checkFileType(f)
--[[
local size = fs.getSize(filename)
local buffer = {
h = h,
i = 1,
s = { },
l = size,
}
for i = 1,size do
buffer.s[i] = h.read()
end
function buffer:readbyte()
--return self.h.read()
local b = self.s[self.i]
self.i = self.i + 1
return b
end
]]--
print('Initial pass ') print('Initial pass ')
self:loadpass(f, spinner) self:loadpass(f, spinner)
@ -365,7 +323,6 @@ function Schematic:load(filename)
self.blocks = { } self.blocks = { }
self.damages = { } self.damages = { }
self.originalBlocks = { } self.originalBlocks = { }
self.placementChains = { }
self.x, self.y, self.z = 0, 0, 0 self.x, self.y, self.z = 0, 0, 0
self.height = 0 self.height = 0
self.index = 1 self.index = 1
@ -373,6 +330,18 @@ function Schematic:load(filename)
print('Second pass ') print('Second pass ')
self:loadpass(f, spinner) self:loadpass(f, spinner)
end end
self.rowIndex = { }
for k,b in ipairs(self.blocks) do
local ri = self.rowIndex[b.y]
if not ri then
self.rowIndex[b.y] = { s = k, e = k }
else
ri.e = k
end
end
self.cache = Util.readTable('usr/builder/' .. self.filename .. '.cache') or { }
end end
function Schematic:assignCoord(i, id) function Schematic:assignCoord(i, id)
@ -400,13 +369,11 @@ function Schematic:assignCoord(i, id)
self.height = self.y + 1 self.height = self.y + 1
end end
end end
function Schematic:assignDamages(spinner) function Schematic:assignDamages(spinner)
local i = 0
Profile.start('assignDamages')
print('Assigning damages') print('Assigning damages')
local i = 0
for _,b in pairs(self.blocks) do for _,b in pairs(self.blocks) do
b.dmg = self.damages[b.index] or 0 b.dmg = self.damages[b.index] or 0
i = i + 1 i = i + 1
@ -414,7 +381,6 @@ function Schematic:assignDamages(spinner)
spinner:spin() spinner:spin()
end end
end end
Profile.stop('assignDamages')
end end
function Schematic:findIndexAt(x, z, y) function Schematic:findIndexAt(x, z, y)
@ -445,8 +411,8 @@ function Schematic:findBlockAtSide(b, side)
return self.blocks[index] -- could be better return self.blocks[index] -- could be better
end end
end end
function Schematic:addPlacementChain(chain) function Schematic:addPlacementChain(chains, chain)
local t = { } local t = { }
for _,v in ipairs(chain) do for _,v in ipairs(chain) do
local k = self:findIndexAt(v.x, v.z, v.y) local k = self:findIndexAt(v.x, v.z, v.y)
@ -461,17 +427,17 @@ function Schematic:addPlacementChain(chain)
for _,b in pairs(t) do for _,b in pairs(t) do
keys[b.index] = true keys[b.index] = true
end end
table.insert(self.placementChains, { table.insert(chains, {
blocks = t, blocks = t,
keys = keys keys = keys
}) })
end end
end end
function Schematic:bestSide(b, ...) function Schematic:bestSide(b, chains, ...)
local directions = { ... } local directions = { ... }
local blocks = { } local blocks = { }
for k,d in pairs(directions) do for k,d in pairs(directions) do
local hi = turtle.getHeadingInfo(d) local hi = turtle.getHeadingInfo(d)
local sb = self:findIndexAt(b.x - hi.xd, b.z - hi.zd, b.y) local sb = self:findIndexAt(b.x - hi.xd, b.z - hi.zd, b.y)
@ -486,7 +452,7 @@ function Schematic:bestSide(b, ...)
d = d d = d
} }
end end
local bestBlock local bestBlock
for _,sb in ipairs(blocks) do for _,sb in ipairs(blocks) do
if not sb.b.direction then -- could be better if not sb.b.direction then -- could be better
@ -494,7 +460,7 @@ function Schematic:bestSide(b, ...)
break break
end end
end end
if not bestBlock then if not bestBlock then
local sideDirections = { local sideDirections = {
[ 'east-block' ] = 'east', [ 'east-block' ] = 'east',
@ -512,17 +478,17 @@ function Schematic:bestSide(b, ...)
end end
end end
end end
local hi = bestBlock.hi local hi = bestBlock.hi
b.heading = hi.heading -- ????????????????????????????????? b.heading = hi.heading -- ?????????????????????????????????
b.direction = bestBlock.d .. '-block' b.direction = bestBlock.d .. '-block'
self:addPlacementChain({ self:addPlacementChain(chains, {
{ x = b.x, z = b.z, y = b.y }, { x = b.x, z = b.z, y = b.y },
{ x = b.x - hi.xd, z = b.z - hi.zd, y = b.y } { x = b.x - hi.xd, z = b.z - hi.zd, y = b.y }
}) })
end end
function Schematic:bestOfTwoSides(b, side1, side2) -- could be better function Schematic:bestOfTwoSides(b, chains, side1, side2) -- could be better
local sb local sb
local fb = b -- first block local fb = b -- first block
@ -567,7 +533,7 @@ function Schematic:bestOfTwoSides(b, side1, side2) -- could be better
b = self:findBlockAtSide(b, side2) b = self:findBlockAtSide(b, side2)
end end
self:addPlacementChain(pc) self:addPlacementChain(chains, pc)
end end
-- can we place the first block from the side (instead of using piston) ? -- can we place the first block from the side (instead of using piston) ?
@ -599,12 +565,14 @@ function Schematic:bestOfTwoSides(b, side1, side2) -- could be better
end end
end end
-- Determine the best way to place each block -- Determine the best way to place each block
function Schematic:determineBlockPlacement(row) function Schematic:determineBlockPlacement(y)
-- NOTE: blocks are evaluated top to bottom -- NOTE: blocks are evaluated top to bottom
print('Processing level ' .. y)
local spinner = UI.Spinner({ local spinner = UI.Spinner({
x = 1, x = 1,
spinSymbols = { 'o.....', '.o....', '..o...', '...o..', '....o.', '.....o' } spinSymbols = { 'o.....', '.o....', '..o...', '...o..', '....o.', '.....o' }
@ -649,21 +617,14 @@ function Schematic:determineBlockPlacement(row)
[ 'west-block-vine' ] = 'west-block', [ 'west-block-vine' ] = 'west-block',
[ 'north-block-vine' ] = 'north-block' [ 'north-block-vine' ] = 'north-block'
} }
local dirtyBlocks = {} local dirtyBlocks = {}
local dirtyBlocks2 = {} local dirtyBlocks2 = {}
local chains = {}
self.rowIndex = { } local ri = self.rowIndex[y]
for k,b in ipairs(self.blocks) do for k = ri.s, ri.e do
local ri = self.rowIndex[b.y] local b = self.blocks[k]
if not ri then
self.rowIndex[b.y] = { s = k, e = k }
else
ri.e = k
end
end
for k,b in ipairs(self.blocks) do
local d = b.direction local d = b.direction
if d then if d then
@ -724,23 +685,23 @@ function Schematic:determineBlockPlacement(row)
local d = b.direction or '' local d = b.direction or ''
spinner:spin(#dirtyBlocks + #dirtyBlocks2 .. ' blocks remaining ') spinner:spin(#dirtyBlocks + #dirtyBlocks2 .. ' blocks remaining ')
if directions[d] then if directions[d] then
b.heading = turtle.getHeadingInfo(directions[d]).heading b.heading = turtle.getHeadingInfo(directions[d]).heading
end end
if doorDirections[d] then if doorDirections[d] then
local hi = turtle.getHeadingInfo(doorDirections[d]) local hi = turtle.getHeadingInfo(doorDirections[d])
b.heading = hi.heading b.heading = hi.heading
b.twoHigh = true b.twoHigh = true
self:addPlacementChain({ self:addPlacementChain(chains, {
{ x = b.x, z = b.z, y = b.y }, { x = b.x, z = b.z, y = b.y },
{ x = b.x - hi.xd, z = b.z - hi.zd, y = b.y }, { x = b.x - hi.xd, z = b.z - hi.zd, y = b.y },
}) })
end end
if stairDownDirections[d] then if stairDownDirections[d] then
if not self:findIndexAt(b.x, b.z, b.y-1) then if not self:findIndexAt(b.x, b.z, b.y-1) then
b.direction = stairDownDirections[b.direction] b.direction = stairDownDirections[b.direction]
@ -749,7 +710,7 @@ function Schematic:determineBlockPlacement(row)
b.heading = turtle.getHeadingInfo(stairDownDirections[b.direction]).heading b.heading = turtle.getHeadingInfo(stairDownDirections[b.direction]).heading
end end
end end
if d == 'bottom' then if d == 'bottom' then
-- slab occupying bottom of voxel -- slab occupying bottom of voxel
-- can be placed from top if a block is below -- can be placed from top if a block is below
@ -761,7 +722,7 @@ function Schematic:determineBlockPlacement(row)
-- no block below, place from side -- no block below, place from side
-- took care of all other cases above -- took care of all other cases above
self:bestSide(b, 'east', 'south', 'west', 'north') self:bestSide(b, chains, 'east', 'south', 'west', 'north')
-- elseif not db.direction or db.direction ~= 'bottom' then -- elseif not db.direction or db.direction ~= 'bottom' then
-- not a slab below, ok to place from above -- not a slab below, ok to place from above
@ -781,7 +742,7 @@ function Schematic:determineBlockPlacement(row)
-- all other directions are fine -- all other directions are fine
-- any stair southwards that can't be placed against another block must be pistoned -- any stair southwards that can't be placed against another block must be pistoned
local sd = stairUpDirections[d] local sd = stairUpDirections[d]
if self:findIndexAt(b.x, b.z, b.y-1) then if self:findIndexAt(b.x, b.z, b.y-1) then
-- there's a block below -- there's a block below
b.direction = sd[1] b.direction = sd[1]
@ -802,14 +763,14 @@ function Schematic:determineBlockPlacement(row)
-- placing a block from the side -- placing a block from the side
local hi = turtle.getHeadingInfo(blockDirections[d]) local hi = turtle.getHeadingInfo(blockDirections[d])
b.heading = hi.heading b.heading = hi.heading
self:addPlacementChain({ self:addPlacementChain(chains, {
{ x = b.x + hi.xd, z = b.z + hi.zd, y = b.y }, -- block we are placing against { x = b.x + hi.xd, z = b.z + hi.zd, y = b.y }, -- block we are placing against
{ x = b.x, z = b.z, y = b.y }, -- the block (or torch, etc) { x = b.x, z = b.z, y = b.y }, -- the block (or torch, etc)
{ x = b.x - hi.xd, z = b.z - hi.zd, y = b.y } -- room for the turtle { x = b.x - hi.xd, z = b.z - hi.zd, y = b.y } -- room for the turtle
}) })
end end
end end
-- pass 3 -- pass 3
while #dirtyBlocks2 > 0 do while #dirtyBlocks2 > 0 do
local b = table.remove(dirtyBlocks2) local b = table.remove(dirtyBlocks2)
@ -818,26 +779,50 @@ function Schematic:determineBlockPlacement(row)
spinner:spin(#dirtyBlocks2 .. ' blocks remaining ') spinner:spin(#dirtyBlocks2 .. ' blocks remaining ')
if d == 'east-west-block' then if d == 'east-west-block' then
self:bestOfTwoSides(b, 'east', 'west') self:bestOfTwoSides(b, chains, 'east', 'west')
elseif d == 'north-south-block' then elseif d == 'north-south-block' then
self:bestOfTwoSides(b, 'north', 'south') self:bestOfTwoSides(b, chains, 'north', 'south')
end end
end end
spinner:stop()
term.clearLine() term.clearLine()
self:setPlacementOrder(spinner, chains)
local plane = self:optimizeRoute(spinner, y)
spinner:stop()
for k,b in ipairs(plane) do
self.blocks[ri.s + k - 1] = b
end
end end
function Schematic:getComputedBlock(i)
local b = self.blocks[i]
-- has this level been computed ?
if not self.rowIndex[b.y].loaded then
-- compute each level up til this one (unless saved in cache)
for y = 0, b.y - 1 do
if not self.cache[y] then
self:determineBlockPlacement(y)
end
end
self:determineBlockPlacement(b.y)
-- get the block now at the computed location
b = self.blocks[i]
end
return b
end
-- set the order for block dependencies -- set the order for block dependencies
function Schematic:setPlacementOrder() function Schematic:setPlacementOrder(spinner, placementChains)
local cursorX, cursorY = term.getCursorPos() local cursorX, cursorY = term.getCursorPos()
local spinner = UI.Spinner({
x = 1
})
Profile.start('overlapping')
-- optimize for overlapping check -- optimize for overlapping check
for _,chain in pairs(self.placementChains) do for _,chain in pairs(placementChains) do
for index,_ in pairs(chain.keys) do for index,_ in pairs(chain.keys) do
if not chain.startRow or (index < chain.startRow) then if not chain.startRow or (index < chain.startRow) then
chain.startRow = index chain.startRow = index
@ -847,10 +832,10 @@ function Schematic:setPlacementOrder()
end end
end end
end end
local function groupOverlappingChains(t, groupedChain, chain, spinner) local function groupOverlappingChains(t, groupedChain, chain, spinner)
local found = true local found = true
local function overlaps(chain1, chain2) local function overlaps(chain1, chain2)
if chain1.startRow > chain2.endRow or if chain1.startRow > chain2.endRow or
chain2.startRow > chain1.endRow then chain2.startRow > chain1.endRow then
@ -862,7 +847,7 @@ function Schematic:setPlacementOrder()
end end
end end
end end
while found do while found do
found = false found = false
for k, v in pairs(t) do for k, v in pairs(t) do
@ -881,16 +866,15 @@ function Schematic:setPlacementOrder()
-- group together any placement chains with overlapping blocks -- group together any placement chains with overlapping blocks
local groupedChains = {} local groupedChains = {}
while #self.placementChains > 0 do while #placementChains > 0 do
local groupedChain = {} local groupedChain = {}
local chain = table.remove(self.placementChains) local chain = table.remove(placementChains)
table.insert(groupedChain, chain) table.insert(groupedChain, chain)
table.insert(groupedChains, groupedChain) table.insert(groupedChains, groupedChain)
groupOverlappingChains(self.placementChains, groupedChain, chain, spinner) groupOverlappingChains(placementChains, groupedChain, chain, spinner)
spinner:spin('chains: ' .. #groupedChains .. ' ' .. #self.placementChains .. ' ') spinner:spin('chains: ' .. #groupedChains .. ' ' .. #placementChains .. ' ')
end end
Profile.stop('overlapping')
--Logger.log('schematic', 'groups: ' .. #groupedChains) --Logger.log('schematic', 'groups: ' .. #groupedChains)
--Logger.setFileLogging('chains') --Logger.setFileLogging('chains')
@ -961,7 +945,7 @@ function Schematic:setPlacementOrder()
end end
end end
end end
while #chains > 0 do while #chains > 0 do
for k,chain in pairs(chains) do for k,chain in pairs(chains) do
if splice(masterChain.blocks, chain.blocks) then if splice(masterChain.blocks, chain.blocks) then
@ -983,16 +967,14 @@ function Schematic:setPlacementOrder()
return masterChain return masterChain
end end
-- combine the individual overlapping placement chains into 1 long master chain -- combine the individual overlapping placement chains into 1 long master chain
Profile.start('masterchains')
local masterChains = {} local masterChains = {}
for _,group in pairs(groupedChains) do for _,group in pairs(groupedChains) do
spinner:spin('chains: ' .. #masterChains) spinner:spin('chains: ' .. #masterChains)
table.insert(masterChains, mergeChains(group)) table.insert(masterChains, mergeChains(group))
end end
Profile.stop('masterchains')
local function removeDuplicates(chain) local function removeDuplicates(chain)
for k,v in ipairs(chain) do for k,v in ipairs(chain) do
for i = #chain, k+1, -1 do for i = #chain, k+1, -1 do
@ -1003,10 +985,9 @@ v.info = 'Unplaceable'
end end
end end
end end
-- any chains with duplicates cannot be placed correctly -- any chains with duplicates cannot be placed correctly
-- there are some cases where a turtle cannot place blocks the same as a player -- there are some cases where a turtle cannot place blocks the same as a player
Profile.start('duplicates')
for _,chain in pairs(masterChains) do for _,chain in pairs(masterChains) do
removeDuplicates(chain.blocks) removeDuplicates(chain.blocks)
spinner:spin('chains: ' .. #masterChains) spinner:spin('chains: ' .. #masterChains)
@ -1020,52 +1001,26 @@ v.info = 'Unplaceable'
--]] --]]
end end
Profile.stop('duplicates')
term.clearLine() term.clearLine()
-- adjust row indices as blocks are being moved -- set dependent blocks for optimize routine
Profile.start('reordering')
for k,chain in pairs(masterChains) do for k,chain in pairs(masterChains) do
spinner:spin('chains: ' .. #masterChains - k) spinner:spin('chains: ' .. #masterChains - k)
local startBlock = table.remove(chain.blocks, 1) local prev
startBlock.movedBlocks = chain.blocks for k,b in ipairs(chain.blocks) do
b.prev = prev
for _,b in pairs(chain.blocks) do b.next = chain.blocks[k + 1]
b.moved = true prev = b
end end
end end
local t = { }
for k,b in ipairs(self.blocks) do
-- adjust y so the turtle travels above the two high blocks
if b.twoHigh then
b.y = b.y + 1
end
spinner:spin('blocks: ' .. #self.blocks - k .. ' ')
if not b.moved then
table.insert(t, b)
end
if b.movedBlocks then
for _,mb in ipairs(b.movedBlocks) do
table.insert(t, mb)
end
end
end
self.blocks = t
Profile.stop('reordering')
--Logger.setWirelessLogging()
term.clearLine() term.clearLine()
spinner:stop()
return t
end end
function Schematic:optimizeRoute() function Schematic:optimizeRoute(spinner, y)
local function getNearestNeighbor(p, pt, maxDistance) local function getNearestNeighbor(p, pt, maxDistance)
local key, block, heading local key, block, heading
@ -1085,14 +1040,24 @@ function Schematic:optimizeRoute()
end end
end end
end end
local function blockReady(b)
if b.u then
return false
end
if b.prev and not b.prev.u then
return false
end
return true
end
local mid = pt.index local mid = pt.index
local forward = mid + 1 local forward = mid + 1
local backward = mid - 1 local backward = mid - 1
while forward <= #p or backward > 0 do while forward <= #p or backward > 0 do
if forward <= #p then if forward <= #p then
local b = p[forward] local b = p[forward]
if not b.u then if blockReady(b) then
getMoves(b, forward) getMoves(b, forward)
if moves <= 1 then if moves <= 1 then
break break
@ -1105,7 +1070,7 @@ function Schematic:optimizeRoute()
end end
if backward > 0 then if backward > 0 then
local b = p[backward] local b = p[backward]
if not b.u then if blockReady(b) then
getMoves(b, backward) getMoves(b, backward)
if moves <= 1 then if moves <= 1 then
break break
@ -1125,54 +1090,62 @@ function Schematic:optimizeRoute()
block.u = true block.u = true
return block return block
end end
local pt = { x = -1, z = -1, y = 0, heading = 0 } local pt = Util.shallowCopy(self.cache[y - 1] or turtle.point)
local t = {} local t = {}
local cursorX, cursorY = term.getCursorPos() local ri = self.rowIndex[y]
local spinner = UI.Spinner({ local blockCount = ri.e - ri.s + 1
x = 0,
y = cursorY local function extractPlane()
})
local function extractPlane(y)
local t = {} local t = {}
local dt = {} local dt = {}
for _, b in pairs(self.blocks) do for i = ri.s, ri.e do
if b.y == y then local b = self.blocks[i]
if b.twoHigh then if b.twoHigh then
table.insert(dt, b) b.last = true
else while b.next do
table.insert(t, b) b = b.next
b.last = true
end end
end end
end end
for i = ri.s, ri.e do
local b = self.blocks[i]
if b.last then
table.insert(dt, b)
else
table.insert(t, b)
end
end
return t, dt return t, dt
end end
local maxDistance = self.width*self.length local maxDistance = self.width*self.length
Profile.start('optimize') local plane, doors = extractPlane(y)
for y = 0, self.height do spinner:spin(percent)
local percent = math.floor(#t * 100 / #self.blocks) .. '%' pt.index = 0
spinner:spin(percent) for i = 1, #plane do
local plane, doors = extractPlane(y) local b = getNearestNeighbor(plane, pt, maxDistance)
pt.index = 0 table.insert(t, b)
for i = 1, #plane do local percent = math.floor(#t * 100 / blockCount) .. '%'
local b = getNearestNeighbor(plane, pt, maxDistance) spinner:spin(percent .. ' ' .. blockCount - i .. ' ')
table.insert(t, b) end
spinner:spin(percent .. ' ' .. #plane - i .. ' ') -- all two high blocks are placed last on each plane
end pt.index = 0
-- all two high blocks are placed last on each plane for i = 1, #doors do
pt.index = 0 local b = getNearestNeighbor(doors, pt, maxDistance)
for i = 1, #doors do table.insert(t, b)
local b = getNearestNeighbor(doors, pt, maxDistance) local percent = math.floor(#t * 100 / blockCount) .. '%'
table.insert(t, b) spinner:spin(percent .. ' ' .. blockCount - #plane - i .. ' ')
spinner:spin(percent .. ' ' .. #doors - i .. ' ')
end
end end
Profile.stop('optimize') self.rowIndex[y].loaded = true
self.blocks = t if not self.cache[y] then
spinner:stop(' ') self.cache[y] = Util.shallowCopy(pt)
Util.writeTable('usr/builder/' .. self.filename .. '.cache', self.cache)
end
return t
end end
return Schematic return Schematic

View File

@ -9,7 +9,6 @@ local Message = require('message')
local Logger = require('logger') local Logger = require('logger')
local UI = require('ui') local UI = require('ui')
local Schematic = require('schematic') local Schematic = require('schematic')
local Profile = require('profile')
local TableDB = require('tableDB') local TableDB = require('tableDB')
local MEProvider = require('meProvider') local MEProvider = require('meProvider')
local Blocks = require('blocks') local Blocks = require('blocks')
@ -520,7 +519,7 @@ function Builder:getGenericSupplyList(blockIndex)
local lastBlock = blockIndex local lastBlock = blockIndex
for k = blockIndex, #schematic.blocks do for k = blockIndex, #schematic.blocks do
lastBlock = k lastBlock = k
local b = schematic.blocks[k] local b = schematic:getComputedBlock(k)
if b.id ~= 'minecraft:air' then if b.id ~= 'minecraft:air' then
local slot = getSlot(b.id, b.dmg) local slot = getSlot(b.id, b.dmg)
@ -1164,16 +1163,8 @@ function Builder:placeDirectionalBlock(b, slot, travelPlane)
} }
if doorDirections[d] then if doorDirections[d] then
local hi = turtle.getHeadingInfo(doorDirections[d]) local hi = turtle.getHeadingInfo(doorDirections[d])
self:gotoEx(b.x - hi.xd, b.z - hi.zd, b.y - 2, hi.heading, travelPlane) self:gotoEx(b.x - hi.xd, b.z - hi.zd, b.y - 1, hi.heading, travelPlane)
-- if not turtle.detectDown() then b.placed = self:place(slot)
-- if turtle.down() then
-- if not turtle.detectDown() then
-- if turtle.down() then
b.placed = self:place(slot)
-- end
-- end
-- end
-- end
end end
local blockDirections = { local blockDirections = {
@ -1251,15 +1242,19 @@ function Builder:findTravelPlane(index)
for i = index, 1, -1 do for i = index, 1, -1 do
local b = schematic.blocks[i] local b = schematic.blocks[i]
if not travelPlane or b.y > travelPlane then local y = b.y
if b.twoHigh then
y = y + 1
end
if not travelPlane or y > travelPlane then
-- if not b.twoHigh then -- if not b.twoHigh then
travelPlane = b.y travelPlane = y
Logger.log('builder', 'adjusting travelPlane') Logger.log('builder', 'adjusting travelPlane')
Logger.log('builder', b) Logger.log('builder', b)
--read() --read()
-- end -- end
elseif travelPlane and travelPlane - b.y > 2 then elseif travelPlane and travelPlane - y > 2 then
break break
end end
end end
@ -1300,11 +1295,12 @@ function Builder:build()
for i = self.index, last, direction do for i = self.index, last, direction do
self.index = i self.index = i
local b = schematic.blocks[i] local b = schematic:getComputedBlock(i)
if b.id ~= 'minecraft:air' then if b.id ~= 'minecraft:air' then
if self.mode == 'destroy' then if self.mode == 'destroy' then
b.heading = nil -- don't make the supplier follow the block heading b.heading = nil -- don't make the supplier follow the block heading
self:logBlock(self.index, b) self:logBlock(self.index, b)
if b.y ~= turtle.getPoint().y then if b.y ~= turtle.getPoint().y then
@ -1337,8 +1333,12 @@ function Builder:build()
self:resupply() self:resupply()
return return
end end
if b.y > travelPlane then local y = b.y
travelPlane = b.y if b.twoHigh then
y = b.y + 1
end
if y > travelPlane then
travelPlane = y
end end
self:logBlock(self.index, b) self:logBlock(self.index, b)
@ -1995,8 +1995,8 @@ function startPage:eventHandler(event)
height = 7, height = 7,
form = UI.Form { form = UI.Form {
y = 3, x = 2, height = 4, y = 3, x = 2, height = 4,
text = UI.Text { x = 5, y = 1, value = '1 - ' .. #schematic.blocks, textColor = colors.gray }, text = UI.Text { x = 2, y = 1, value = '1 - ' .. #schematic.blocks, textColor = colors.gray },
textEntry = UI.TextEntry { x = 15, y = 1, value = tostring(Builder.index), width = 7 } textEntry = UI.TextEntry { x = 16, y = 1, value = tostring(Builder.index), width = 10, limit = 8 }
}, },
statusBar = UI.StatusBar(), statusBar = UI.StatusBar(),
} }
@ -2023,6 +2023,9 @@ function startPage:eventHandler(event)
UI:setPage(dialog) UI:setPage(dialog)
elseif event.type == 'assignBlocks' then elseif event.type == 'assignBlocks' then
-- this might be an approximation of the blocks needed
-- as the current level's route may or may not have been
-- computed
Builder:dumpInventory() Builder:dumpInventory()
UI:setPage('listing') UI:setPage('listing')
@ -2059,12 +2062,6 @@ function startPage:eventHandler(event)
turtle.status = 'thinking' turtle.status = 'thinking'
print('Reloading schematic') print('Reloading schematic')
Builder:reloadSchematic() Builder:reloadSchematic()
print('Determining block placement')
schematic:determineBlockPlacement()
print('Optimizing route (' .. #schematic.blocks .. ' blocks)')
schematic:optimizeRoute()
print('Adjusting route')
schematic:setPlacementOrder()
Builder:dumpInventory() Builder:dumpInventory()
Builder:refuel() Builder:refuel()
@ -2084,7 +2081,6 @@ function startPage:eventHandler(event)
} }
Builder:build() Builder:build()
Profile.display()
elseif event.type == 'quit' then elseif event.type == 'quit' then
Event.exitPullEvents() Event.exitPullEvents()
@ -2098,9 +2094,6 @@ local args = {...}
if #args < 1 then if #args < 1 then
error('supply file name') error('supply file name')
end end
--if #args > 1 then
Profile.enable()
--end
if os.version() == 'CraftOS 1.7' then if os.version() == 'CraftOS 1.7' then
Builder.ccVersion = 1.7 Builder.ccVersion = 1.7

View File

@ -6,6 +6,18 @@ local Point = require('point')
local TableDB = require('tableDB') local TableDB = require('tableDB')
local MEProvider = require('meProvider') local MEProvider = require('meProvider')
--[[
A supplier turtle for the builder turtle. For larger builds, use
ender modems.
Setup:
1. chest or ME interface at level 0 (bottom of build area)
2. builder turtle on top facing the build area
3. If facing the build turtle, the supplier turtle is to the right
pointing at the chest/interface
]]--
local ChestProvider = require('chestProvider') local ChestProvider = require('chestProvider')
if os.getVersion() == 1.8 then if os.getVersion() == 1.8 then
ChestProvider = require('chestProvider18') ChestProvider = require('chestProvider18')
@ -70,7 +82,7 @@ function Builder:dumpInventoryWithCheck()
Builder:log('Unable to dump inventory') Builder:log('Unable to dump inventory')
print('Provider is full or missing - make space or replace') print('Provider is full or missing - make space or replace')
print('Press enter to continue') print('Press enter to continue')
turtle.setHeading(0) --turtle.setHeading(0)
self.ready = false self.ready = false
read() read()
end end
@ -397,9 +409,9 @@ if not Builder.itemProvider:isValid() then
south = 'north', south = 'north',
} }
Builder.itemProvider = ChestProvider({ direction = sides[args[2]], wrapSide = args[2] }) Builder.itemProvider = ChestProvider({ direction = sides[args[2]], wrapSide = 'front' })
if not Builder.itemProvider:isValid() then if not Builder.itemProvider:isValid() then
error('A chest or ME interface must be below turtle') error('A chest or ME interface must be in front of turtle')
end end
end end