Advertisement
Tatantyler

Turtle Logistical Network

Nov 28th, 2014
690
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 46.14 KB | None | 0 0
  1. -- lua: turtle logistical network
  2.  
  3. local node_list = {}
  4. -- given a turtle with id X:
  5. -- node_list[X] = { x-coord, y-coord, z-coord }
  6.  
  7. local msg_ids = {}
  8.  
  9. local current_pos = vector.new( 0, 0, 0 )
  10. local current_facing = 0
  11.  
  12. local known_position_accurate = false
  13.  
  14. local facingTable = {
  15.     [0] = "south", -- increase in z
  16.     [1]= "west",   -- decrease in x
  17.     [2] = "north", -- decrease in z
  18.     [3] = "east",  -- increase in x
  19.     ["south"] = 0,
  20.     ["west"] = 1,
  21.     ["north"] = 2,
  22.     ["east"] = 3,
  23. } -- turning right increases facing (modulo 4)
  24.  
  25. local modem = {}
  26. local modem_side = ""
  27. local query_channel = 515
  28. local transaction_channel = 516
  29. local gps_channel = 65534
  30. local header = "TLogistics"
  31. local transaction_header = "TLogistics_Local"
  32.  
  33. local logistical_data_packet = { id=os.computerID() }
  34. if turtle ~= nil then
  35.     logistical_data_packet.turtle = true
  36.     logistical_data_packet.crafty = (turtle.craft ~= nil)
  37.     logistical_data_packet.equipped = (turtle.dig ~= nil)
  38.     logistical_data_packet.attack = (turtle.attack ~= nil)
  39. else
  40.     logistical_data_packet.turtle = false
  41.     logistical_data_packet.crafty = false
  42.     logistical_data_packet.equipped = false
  43.     logistical_data_packet.attack = false
  44. end
  45.  
  46. local rendezvous_timing_margin = 5
  47. local rendezvous_waiting_time = 15
  48. local operation_max_headstart = 7
  49.  
  50. -- the time it takes for a turtle to move n blocks forward, or turn n times approximately equals
  51. -- .4(n-1)
  52. -- for example, it takes 1.65 (0.05 + .4(4)) seconds to rotate five times.
  53.  
  54. -- the time it takes for a turtle to dig n blocks approximately equals
  55. -- .5n
  56. -- for example, it takes 2 seconds to dig five blocks.
  57.  
  58. -- there also seems to be a 0.05 second delay before starting any action.
  59. -- do note that timings were performed using os.clock().
  60.  
  61. local function get_move_time( n ) return (.4 * (n-1))  end
  62. local function get_dig_time( n ) return (.5 * n)  end
  63.  
  64. local function euclidean_distance( vec1, vec2 ) return math.sqrt((( vec1.x - vec2.x )^2) + (( vec1.y - vec2.y )^2) + (( vec1.z - vec2.z )^2)) end
  65.  
  66. local function mc_time_to_absolute( t )
  67.     -- t = (os.day()*24)+os.time()
  68.    
  69.     local hours = math.floor(t)
  70.     local dec = t - hours
  71.    
  72.     local tenths = math.floor(dec*10)
  73.     local hundredths = math.floor(dec*100) - (tenths*10)
  74.     local thousandths = math.floor(dec*1000) - ((tenths*100) + (hundredths*10))
  75.    
  76.     return (hours * 50) + (tenths * 5) + (hundredths * 0.5) + (thousandths * 0.05)
  77. end
  78.  
  79. local function get_mc_time()
  80.     return (os.day()*24) + os.time()
  81. end
  82.  
  83. -- get time since world start (since the start of the world's day/night cycle) in seconds (similar to os.clock() but for the whole world)
  84. local function get_global_clock()
  85.     return mc_time_to_absolute( get_mc_time() )
  86. end
  87.  
  88. local function wait_until_time( t )
  89.     local diff = t - get_global_clock()
  90.    
  91.     os.sleep(diff)
  92. end
  93.  
  94. --[[
  95. local node_class = {
  96.     id = 0,
  97.     connected = {},
  98. }
  99.  
  100. local node_connection = { to_id, distance }
  101. ]]
  102.  
  103. local move_queue = {} -- list of rendezvous objects: { vec=position-vector, op=operation, item=item, count=count, id=unique-id, best_time=best_wait_time, worst_time=worst_wait_time }
  104.  
  105. -- operations supported:
  106. -- "put": requestor places count items in another turtle
  107. -- "get": requestor gets count items from another turtle
  108. -- "wait": requestor waits count seconds (for a furnace or etc.)
  109.  
  110. local move_queue_locked = false
  111. local move_queue_lock_queue = {}
  112. local function lock_queue()
  113.     local id = math.random(2^30)
  114.    
  115.     if move_queue_locked then
  116.         table.insert( move_queue_lock_queue, id )
  117.         while true do
  118.             local ev, i2 = os.pullEvent("logistics_move_queue_unlock");
  119.             if i2 == id then
  120.                 return
  121.             end
  122.         end
  123.     else
  124.         move_queue_locked = true
  125.     end
  126.    
  127. end
  128.  
  129. local function unlock_queue( id )
  130.     if #move_queue_lock_queue > 0 then
  131.         local next_id = table.remove( move_queue_lock_queue, 1 )
  132.         os.raiseEvent("logistics_move_queue_unlock", next_id)
  133.     else
  134.         move_queue_locked = false
  135.     end
  136. end
  137.  
  138. local setting_rendezvous_times = false
  139.  
  140. local function send_message( data, modem )
  141.     new_id = math.random( 1, 2^30 )
  142.     while msg_ids[new_id] do
  143.         new_id = math.random( 1, 2^30 )
  144.     end
  145.    
  146.     data.header = header
  147.     data.msg_id = new_id
  148.     data.timestamp = get_global_clock()
  149.     data.sender = os.computerID()
  150.    
  151.     modem.transmit( query_channel, query_channel, data )
  152. end
  153.  
  154. local position_file = "current.position"
  155.  
  156. local function load_position()
  157.     f = fs.open(position_file, 'r')
  158.     local x = tonumber( f.readLine() )
  159.     local y = tonumber( f.readLine() )
  160.     local z = tonumber( f.readLine() )
  161.     current_facing = tonumber( f.readLine() )
  162.     current_pos = vector.new( x, y, z )
  163.     f.close()
  164. end
  165.  
  166. local function save_position()
  167.     --print("saving position...")
  168.     f = fs.open(position_file, 'w')
  169.     f.writeLine(tostring(current_pos.x))
  170.     f.writeLine(tostring(current_pos.y))
  171.     f.writeLine(tostring(current_pos.z))
  172.     f.writeLine(tostring(current_facing))
  173.     f.close()
  174. end
  175.  
  176. local position_lock = "position.lock"
  177.  
  178. local function lock_position( type )
  179.     f = fs.open(position_lock, 'w')
  180.     f.write(type)
  181.     f.close()
  182. end
  183.  
  184. local native_turtle = { up = turtle.up, down = turtle.down, forward = turtle.forward, back = turtle.back, turnLeft = turtle.turnLeft, turnRight = turtle.turnRight }
  185.  
  186. turtle.up = function()
  187.     lock_position("up")
  188.    
  189.     local stat = false
  190.     if native_turtle.up() then
  191.         current_pos.y = current_pos.y + 1
  192.         if modem.transmit then
  193.             send_message( { receiver = -1, type="position", vec=current_pos }, modem )
  194.         end
  195.         save_position()
  196.         stat = true
  197.     end
  198.    
  199.     fs.delete(position_lock)
  200.     return stat
  201. end
  202.  
  203. turtle.down = function()
  204.     lock_position("down")
  205.    
  206.     local stat = false
  207.     if native_turtle.down() then
  208.         current_pos.y = current_pos.y - 1
  209.         if modem.transmit then
  210.             send_message( { receiver = -1, type="position", vec=current_pos }, modem )
  211.         end
  212.         save_position()
  213.         stat = true
  214.     end
  215.    
  216.     fs.delete(position_lock)
  217.     return stat
  218. end
  219.  
  220. turtle.forward = function()
  221.     lock_position("forward")
  222.    
  223.     if not native_turtle.forward() then
  224.         fs.delete( position_lock )
  225.         return false
  226.     end
  227.    
  228.     if current_facing == 0 then
  229.         current_pos.z = current_pos.z + 1
  230.     elseif current_facing == 1 then
  231.         current_pos.x = current_pos.x - 1
  232.     elseif current_facing == 2 then
  233.         current_pos.z = current_pos.z - 1
  234.     elseif current_facing == 3 then
  235.         current_pos.x = current_pos.x + 1
  236.     end
  237.    
  238.     if modem.transmit then
  239.         send_message( { receiver = -1, type="position", vec=current_pos }, modem )
  240.     end
  241.    
  242.     save_position()
  243.     fs.delete(position_lock)
  244.    
  245.     return true
  246. end
  247.  
  248. turtle.back = function()
  249.     lock_position("back")
  250.    
  251.     if not native_turtle.back() then
  252.         fs.delete( position_lock )
  253.         return false
  254.     end
  255.    
  256.     if current_facing == 0 then
  257.         current_pos.z = current_pos.z - 1
  258.     elseif current_facing == 1 then
  259.         current_pos.x = current_pos.x + 1
  260.     elseif current_facing == 2 then
  261.         current_pos.z = current_pos.z + 1
  262.     elseif current_facing == 3 then
  263.         current_pos.x = current_pos.x - 1
  264.     end
  265.    
  266.     if modem.transmit then
  267.         send_message( { receiver = -1, type="position", vec=current_pos }, modem )
  268.     end
  269.    
  270.     save_position()
  271.    
  272.     fs.delete( position_lock )
  273.     return true
  274. end
  275.  
  276. turtle.turnLeft = function()
  277.     lock_position("left")
  278.     if not native_turtle.turnLeft() then
  279.         return false
  280.     end
  281.    
  282.     current_facing = (current_facing-1) % 4
  283.     save_position()
  284.     fs.delete( position_lock )
  285.     return true
  286. end
  287.  
  288. turtle.turnRight = function()
  289.     lock_position("right")
  290.     if not native_turtle.turnRight() then
  291.         return false
  292.     end
  293.    
  294.     current_facing = (current_facing+1) % 4
  295.     save_position()
  296.     fs.delete( position_lock )
  297.     return true
  298. end
  299.  
  300. local function move_up()
  301.     while true do
  302.         present, data = turtle.inspectUp()
  303.         if present then
  304.             if string.sub(data.name, 1, 13) ~= "ComputerCraft" then -- don't damage infrastructure
  305.                 turtle.digUp()
  306.                 break
  307.             end
  308.         else
  309.             break
  310.         end
  311.         os.sleep(0)
  312.     end
  313.    
  314.     turtle.up()
  315. end
  316.  
  317. local function move_down()
  318.     while true do
  319.         present, data = turtle.inspectDown()
  320.         if present then
  321.             if string.sub(data.name, 1, 13) ~= "ComputerCraft" then
  322.                 turtle.digDown()
  323.                 break
  324.             end
  325.         else
  326.             break
  327.         end
  328.         os.sleep(0)
  329.     end
  330.    
  331.     turtle.down()
  332. end
  333.  
  334. local function move_forward()
  335.     while true do
  336.         present, data = turtle.inspect()
  337.         if present then
  338.             if string.sub(data.name, 1, 13) ~= "ComputerCraft" then
  339.                 turtle.dig()
  340.                 break
  341.             end
  342.         else
  343.             break
  344.         end
  345.         os.sleep(0)
  346.     end
  347.    
  348.     turtle.forward()
  349. end
  350.  
  351. local function move_in_direction( dir, n_moves )
  352.     while current_facing ~= dir do
  353.         turtle.turnRight()
  354.     end
  355.    
  356.     for i=1,n_moves do
  357.         move_forward()
  358.     end
  359. end
  360.  
  361. function goTo( vec )
  362.     displacement = current_pos - vec
  363.    
  364.     --[[
  365.     print( "displacement:" )
  366.     print( "x: " .. tostring(displacement.x) )
  367.     print( "y: " .. tostring(displacement.y) )
  368.     print( "z: " .. tostring(displacement.z) )
  369.     ]]
  370.    
  371.     if( displacement.y > 0 ) then
  372.         -- move down
  373.         for i=1,math.abs(displacement.y) do
  374.             move_down()
  375.         end
  376.     elseif (displacement.y < 0) then
  377.         -- move up
  378.         for i=1,math.abs(displacement.y) do
  379.             move_up()
  380.         end
  381.     end
  382.    
  383.     if( displacement.x > 0 ) then
  384.         -- move west
  385.         move_in_direction( facingTable.west, math.abs(displacement.x) )
  386.     elseif (displacement.x < 0) then
  387.         -- move east
  388.         move_in_direction( facingTable.east, math.abs(displacement.x) )
  389.     end
  390.    
  391.     if( displacement.z > 0 ) then
  392.         -- move north
  393.         move_in_direction( facingTable.north, math.abs(displacement.z) )
  394.     elseif (displacement.z < 0) then
  395.         -- move south
  396.         move_in_direction( facingTable.south, math.abs(displacement.z) )
  397.     end
  398.    
  399.     os.sleep(0.15) -- sleep for a bit to give us time to fully move into position
  400. end
  401.  
  402. local function create_node_graph()
  403.     local graph = {}
  404.     for i,vec1 in pairs(node_list) do
  405.         local distances = {}
  406.         for i2,vec2 in pairs(node_list) do
  407.             if ( i2 ~= node_id ) then
  408.                 local dist = euclidean_distance( vec1, vec2 )
  409.                 table.insert( distances, { i2, dist } )
  410.             end
  411.         end
  412.         table.sort( distances, function(a,b) return (a[2] < b[2]) end )
  413.         graph[i] = distances
  414.     end
  415.    
  416.     local distances = {}
  417.     for i,v in pairs(node_list) do
  418.         local vec2 = vector.new( unpack(v) )
  419.         local dist = euclidean_distance( current_pos, vec2 )
  420.         table.insert( distances, { i, dist } )
  421.     end
  422.     table.sort( distances, function(a,b) return (a[2] < b[2]) end )
  423.     graph[os.computerID()] = distances
  424.    
  425.     return graph
  426. end
  427.  
  428. local function dijkstra( graph, from_node, to_node )
  429.     -- graph is of the format returned by create_node_graph().
  430.     -- i.e graph[id] is a sorted list of distances to all other nodes in the format { id, distance }
  431.     local unvisited = {}
  432.     local tent_distances = {}
  433.     local previous = {}
  434.     local current_node = from_node
  435.    
  436.     for i,v in pairs(graph) do
  437.         tent_distances[i] = -1
  438.         unvisited[i] = false
  439.     end
  440.     tent_distances[from_node] = 0
  441.    
  442.     while true do
  443.         neighbors = graph[current_node]
  444.         for i=1, #neighbors do
  445.             if unvisited[ neighbors[i][1] ] then -- if the neighbor we're looking at is unvisited...
  446.                 local poss_dist = tent_distances[current_node] + neighbors[i][2] -- poss_dist = tentative_distance[current_node] + distance( current_node, current_neighbor )
  447.                 if poss_dist < tent_distances[ neighbors[i][1] ] then
  448.                     tent_distances[ neighbors[i][1] ] = poss_dist
  449.                     previous[ neighbors[i][1] ] = current_node
  450.                 end
  451.             end
  452.         end
  453.        
  454.         unvisited[ current_node ] = false
  455.         if( current_node == to_node ) then
  456.             break
  457.         end
  458.        
  459.         found_pair = false
  460.         next_node = 0
  461.         next_dist = 0
  462.         for i,v in pairs(graph) do
  463.             if unvisited[i] then
  464.                 if found_pair then
  465.                     if tent_distances[i] < next_dist then
  466.                         next_node = i
  467.                         next_dist = tent_distances[i]
  468.                     end
  469.                 else
  470.                     next_node = i
  471.                     next_dist = tent_distances[i]
  472.                     found_pair = true
  473.                 end
  474.             end
  475.         end
  476.        
  477.         if not found_pair then
  478.             break
  479.         end
  480.     end
  481.    
  482.     return tent_distances, previous
  483. end
  484.  
  485. local function wait_for_message( modem )
  486.     modem.open( query_channel )
  487.     modem.open( gps_channel )
  488.    
  489.     while true do
  490.         local msg = { os.pullEvent("modem_message") }
  491.        
  492.         --print("received message on channel " .. msg[3] .. " with replies on " .. msg[4] )
  493.            
  494.         if ( msg[3] == gps_channel ) and ( msg[5] == "PING" ) then
  495.             if known_position_accurate then
  496.                 modem.transmit( msg[4], gps_channel, { current_pos.x, current_pos.y, current_pos.z, logistical = logistical_data_packet } )
  497.             end
  498.         elseif (msg[3] == query_channel) then
  499.             local data = msg[5]
  500.            
  501.             if (type(data) == "table") and (data.header == header) then
  502.                 if not msg_ids[ data.msg_id ] then
  503.                     modem.transmit( query_channel, query_channel, msg[5] ) -- retransmit
  504.                     msg_ids[ data.msg_id ] = true
  505.                
  506.                     --print("received message from " .. tostring( data.sender ) .. " to " .. tostring(data.receiver) )
  507.                    
  508.                     if (data.receiver == os.computerID()) or (data.receiver == -1) then
  509.                         return msg
  510.                     end
  511.                 end
  512.             end
  513.         end
  514.     end
  515. end
  516.  
  517. local function send_transaction_message( data, modem )
  518.     data.header = transaction_header
  519.     data.timestamp = get_global_clock()
  520.     data.sender = os.computerID()
  521.    
  522.     modem.transmit( transaction_channel, transaction_channel, data )
  523. end
  524.  
  525. local function wait_for_transaction_message( modem, timeout )
  526.     modem.open( transaction_channel )
  527.     modem.open( query_channel )
  528.     modem.open( gps_channel )
  529.    
  530.     if timeout > 0 then
  531.         local t = os.startTimer( timeout )
  532.         while true do
  533.             local msg = { os.pullEvent() }
  534.            
  535.             if msg[1] == "timer" then
  536.                 if msg[2] == t then
  537.                     return
  538.                 end
  539.             elseif msg[1] == "modem_message" then
  540.                 if (msg[3] == transaction_channel) then
  541.                     local data = msg[5]
  542.                    
  543.                     if (type(data) == "table") and (data.header == transaction_header) then
  544.                         print("received transaction message from " .. tostring( data.sender ) .. " to " .. tostring(data.receiver) .. " type: " .. tostring(data.type) )
  545.                         if (data.receiver == os.computerID()) or (data.receiver == -1) then
  546.                             return msg
  547.                         end
  548.                     end
  549.                 end
  550.             end
  551.         end
  552.     else
  553.         while true do
  554.             local msg = { os.pullEvent("modem_message") }
  555.                
  556.             if (msg[3] == transaction_channel) then
  557.                 local data = msg[5]
  558.                
  559.                 if (type(data) == "table") and (data.header == transaction_header) then
  560.                     print("received transaction message from " .. tostring( data.sender ) .. " to " .. tostring(data.receiver) .. " type: " .. tostring(data.type) )
  561.                     if (data.receiver == os.computerID()) or (data.receiver == -1) then
  562.                         return msg
  563.                     end
  564.                 end
  565.             end
  566.         end
  567.     end
  568. end
  569.  
  570. local function wait_for_unit( side, id, timeout )
  571.     local start = os.clock()
  572.     while true do
  573.         if (peripheral.getType(side) == "turtle") or (peripheral.getType(side) == "computer") then
  574.             -- peripheral.call(side, "getID") is not reliable for some reason
  575.             --if peripheral.call(side, "getID") == id then
  576.                 return true
  577.             --end
  578.         end
  579.         if (os.clock() - start) >= timeout then
  580.             return false
  581.         end
  582.         os.sleep( 0.5 )
  583.     end
  584. end
  585.  
  586. local function do_transfer( direction, op )
  587.     local fulfilled = {}
  588.     for j=1, #op.item do
  589.         fulfilled[j] = false
  590.     end
  591.    
  592.     for i=1,16 do
  593.         -- find slots
  594.         d = turtle.getItemDetail(i)
  595.         if d then
  596.             print( "item in slot " .. tostring(i) .. ": " .. d.name)
  597.             for j=1,#op.item do
  598.                 if not fulfilled[j] then
  599.                     if d.name == op.item[j] then
  600.                         turtle.select( i )
  601.                         if direction == "up" then
  602.                             turtle.dropUp( op.count[j] )
  603.                         elseif direction == "down" then
  604.                             turtle.dropDown( op.count[j] )
  605.                         else
  606.                             turtle.drop( op.count[j] )
  607.                         end
  608.                         if d.count >= op.count[j] then
  609.                             fulfilled[j] = true
  610.                         else
  611.                             op.count[j] = op.count[j] - d.count
  612.                         end
  613.                         d = turtle.getItemDetail(i)
  614.                         if not d then
  615.                             break
  616.                         end
  617.                     end
  618.                 end
  619.             end
  620.         end
  621.     end
  622.    
  623. end
  624.  
  625. local function rendezvous_transaction( op, modem )
  626.     modem.open(transaction_channel)
  627.     if op.requestee == os.computerID() then
  628.         send_transaction_message( { type="arrived", receiver = op.requestor }, modem )
  629.     else
  630.         send_transaction_message( { type="arrived", receiver = op.requestee }, modem )
  631.     end
  632.     print("requestor: " .. tostring(op.requestor))
  633.     print("requestee: " .. tostring(op.requestee))
  634.     print("we are: " .. tostring(os.computerID()))
  635.     while true do
  636.         local msg = wait_for_transaction_message( modem, rendezvous_waiting_time )
  637.        
  638.         if not msg then
  639.             return
  640.         end
  641.        
  642.         local data = msg[5]
  643.        
  644.          print("got transaction message, type=" .. data.type)
  645.          
  646.          if (data.type == "end_transaction") or (data.type == "transaction_error") then
  647.             return
  648.         end
  649.        
  650.         -- requestor is always 1 y-level higher than the requestee at the rendezvous point
  651.         -- they should also have the same x/z coordinates
  652.         -- though we need to check to see if this is true.
  653.        
  654.         if data.type == "arrived" then
  655.            
  656.             if op.requestor == os.computerID() then -- we are requestor
  657.                 if wait_for_unit( "bottom", op.requestee, rendezvous_waiting_time ) then
  658.                     if op.op == "put" then
  659.                         print("performing transaction...")
  660.                         -- we need to put the things in now
  661.                         do_transfer( "down", op )
  662.                        
  663.                         send_transaction_message( { type="end_transaction", receiver = op.requestee }, modem )
  664.                         return
  665.                     else
  666.                         print("waiting for requestee to perform transaction...")
  667.                         send_transaction_message( { type="transaction", receiver = op.requestee }, modem )
  668.                     end
  669.                 else print("error: requestee not in position -- nothing's there") send_transaction_message( { type="transaction_error", receiver = op.requestor }, modem ) return end -- if there isn't a turtle beneath us, then something went wrong; exit early
  670.             else            -- we are requestee
  671.                 if wait_for_unit( "top", op.requestor, rendezvous_waiting_time ) then
  672.                     if op.op == "get" then
  673.                         print("performing transaction...")
  674.                         do_transfer( "up", op )
  675.                        
  676.                         send_transaction_message( { type="end_transaction", receiver = op.requestor }, modem )
  677.                         return
  678.                     else
  679.                         print("waiting for requestor to perform transaction...")
  680.                         send_transaction_message( { type="transaction", receiver = op.requestor }, modem )
  681.                     end
  682.                 else print("error: requestor not in position -- nothing's there") send_transaction_message( { type="transaction_error", receiver = op.requestee }, modem ) return end
  683.             end
  684.            
  685.         elseif data.type == "transaction" then
  686.             print("are we requestor: " .. tostring(op.requestor == os.computerID()))
  687.             if op.requestor == os.computerID() then -- we are requestor
  688.                 print("Waiting for requestee...")
  689.                 if wait_for_unit( "bottom", op.requestee, rendezvous_waiting_time ) then
  690.                     if op.op == "put" then
  691.                         print("performing transaction...")
  692.                         do_transfer( "down", op )
  693.                     end
  694.                 end -- if something goes wrong, then fall through
  695.                 send_transaction_message( { type="end_transaction", receiver = op.requestee }, modem )
  696.             else            -- we are requestee
  697.                 print("Waiting for requestor...")
  698.                 if wait_for_unit( "top", op.requestor, rendezvous_waiting_time ) then
  699.                     if op.op == "get" then
  700.                         print("performing transaction...")
  701.                         do_transfer( "up", op )
  702.                     end
  703.                 end -- if something goes wrong, then fall through
  704.                 send_transaction_message( { type="end_transaction", receiver = op.requestor }, modem )
  705.             end
  706.             return
  707.         elseif data.type == "transaction_error" then
  708.             return
  709.         elseif data.type == "end_transaction" then
  710.             return
  711.         end
  712.     end
  713. end
  714.  
  715. function do_moves()
  716.     local last_move_end = 0
  717.    
  718.     while true do
  719.         if #move_queue == 0 then
  720.             os.pullEvent("logistics_queue_push")
  721.         end
  722.         print("move queue length: " .. tostring(#move_queue))
  723.         op = table.remove(move_queue, 1)
  724.        
  725.         if op.op ~= "empty" then
  726.             goTo( op.vec )
  727.            
  728.             if op.op == "wait" then
  729.                 os.queueEvent("logistics_arrived", op.id)
  730.                 while true do
  731.                     local event, id = os.pullEvent("logistics_continue")
  732.                     if id == op.id then
  733.                         break
  734.                     end
  735.                 end
  736.             elseif op.op == "get" or op.op == "put" then
  737.                 rendezvous_transaction( op, modem )
  738.             elseif op.op == "static_get" then
  739.                 local u, ud = turtle.inspectUp()
  740.                 local d, dd = turtle.inspectDown()
  741.                
  742.                 if u and ud.name == op.container then
  743.                     for i=1, #op.slot do
  744.                         turtle.select( op.slot[i] or j )
  745.                         if type(op.count[i]) == "number" then
  746.                             turtle.suckUp( op.count[i] )
  747.                         else
  748.                             turtle.suckUp()
  749.                         end
  750.                     end
  751.                 elseif d and dd.name == op.container then
  752.                     for i=1, #op.slot do
  753.                         turtle.select( op.slot[i] or j )
  754.                         if type(op.count[i]) == "number" then
  755.                             turtle.suckDown( op.count[i] )
  756.                         else
  757.                             turtle.suckDown()
  758.                         end
  759.                     end
  760.                 else
  761.                     for i=1, 4 do
  762.                         local f, fd = turtle.inspect()
  763.                         if f and fd.name == op.container then
  764.                             for j=1, #op.slot do
  765.                                 turtle.select( op.slot[j] or j )
  766.                                 if type(op.count[i]) == "number" then
  767.                                     turtle.suck( op.count[i] )
  768.                                 else
  769.                                     turtle.suck()
  770.                                 end
  771.                             end
  772.                             break
  773.                         end
  774.                         turtle.turnRight()
  775.                     end
  776.                 end
  777.             elseif op.op == "static_put" then
  778.                 local u, ud = turtle.inspectUp()
  779.                 local d, dd = turtle.inspectDown()
  780.                
  781.                 if u and ud.name == op.container then
  782.                     for i=1, #op.slot do
  783.                         turtle.select( op.slot[i] or j )
  784.                         if type(op.count[i]) == "number" then
  785.                             turtle.dropUp( op.count[i] )
  786.                         else
  787.                             turtle.dropUp()
  788.                         end
  789.                     end
  790.                 elseif d and dd.name == op.container then
  791.                     for i=1, #op.slot do
  792.                         turtle.select( op.slot[i] or j )
  793.                         if type(op.count[i]) == "number" then
  794.                             turtle.dropDown( op.count[i] )
  795.                         else
  796.                             turtle.dropDown()
  797.                         end
  798.                     end
  799.                 else
  800.                     for i=1, 4 do
  801.                         local f, fd = turtle.inspect()
  802.                         if f and fd.name == op.container then
  803.                             for j=1, #op.slot do
  804.                                 turtle.select( op.slot[j] or j )
  805.                                 if type(op.count[j]) == "number" then
  806.                                     turtle.drop( op.count[j] )
  807.                                 else
  808.                                     turtle.drop()
  809.                                 end
  810.                             end
  811.                             break
  812.                         end
  813.                         turtle.turnRight()
  814.                     end
  815.                 end
  816.             end
  817.            
  818.             --[[
  819.             if #move_queue == 1 then
  820.                 if move_queue[1].id == op.id then
  821.                     table.remove(move_queue, 1)
  822.                 end
  823.             end
  824.             ]]
  825.            
  826.             os.queueEvent("logistics_operation_complete", op.id)
  827.            
  828.             print("transaction complete: " .. tostring(#move_queue) .. " transactions remaining")
  829.             if #move_queue == 0 then
  830.                 if _G.homeVec ~= nil then
  831.                     goTo( homeVec )
  832.                 end
  833.             --[[else
  834.                 -- if we got things done pretty quickly, then just wait until the next agreed time - operation_max_headstart
  835.                 -- this is to avoid timing out while waiting for the other party (ex. with rendezvouses)
  836.                 if get_global_clock() < (op.time - operation_max_headstart) then
  837.                     print("very early -- sleeping till operation_max_headstart (" .. tonumber((op.time - operation_max_headstart) - get_global_clock()) .. " seconds)")
  838.                     wait_until_time(op.time - operation_max_headstart)
  839.                 end]]
  840.             end
  841.         else
  842.             os.queueEvent("logistics_waiting", op.id)
  843.             while true do
  844.                 local event, id = os.pullEvent("logistics_continue")
  845.                 if id == op.id then
  846.                     break
  847.                 end
  848.             end
  849.         end
  850.     end
  851. end
  852.  
  853. function handle_requests()
  854.     modem.open( transaction_channel )
  855.     modem.open( query_channel )
  856.     modem.open( gps_channel )
  857.    
  858.     -- gps:
  859.     -- clients send 1 string: "PING"
  860.     -- servers send tables of coordinates: { [1]=x, y, z }
  861.    
  862.     while true do
  863.         msg = wait_for_message( modem )
  864.        
  865.         local data = msg[5]
  866.         local sender = data.sender
  867.         local receiver = data.receiver
  868.         local timestamp = data.timestamp
  869.        
  870.         if data.type == "rendezvous" then
  871.             if setting_rendezvous_times then
  872.                 send_message( { sender = os.computerID(), receiver = sender, type="rendezvous_retry", op = data.op }, modem )
  873.             else
  874.                 -- Rendezvous request: Ask to meet at a certain location (for item swapping, etc.)
  875.                 -- Format: { start=start-time, vec=rendezvous-position, op=operation, item=item, count=count }
  876.                 -- Response: affirmative, move start time, projected move end time (optimistic), projected move end time (worst-case)
  877.                
  878.                 -- the optimistic end time assumes that all moves before this take their optimistic move times (no obstructions).
  879.                 -- the worst-case end time assumes that all moves before this take their worst-case move times (every position obstructed by a block).
  880.                
  881.                 -- the worst case end time does not take other computers / infrastructure (modems, etc.) into account.
  882.                
  883.                 local displacement = vector.new(0,0,0)
  884.                 local op_vec = vector.new( data.op.vec.x, data.op.vec.y, data.op.vec.z )
  885.                
  886.                 if #move_queue == 0 then
  887.                     displacement = current_pos - op_vec
  888.                 else
  889.                     displacement = move_queue[#move_queue].vec - op_vec
  890.                 end
  891.                
  892.                 -- calculate movement times
  893.                 local move_time = get_move_time( math.abs(displacement.x) ) + get_move_time( math.abs(displacement.y) ) + get_move_time( math.abs(displacement.z) )
  894.                 local dig_time = get_dig_time( math.abs(displacement.x) ) + get_dig_time( math.abs(displacement.y) ) + get_dig_time( math.abs(displacement.z) )
  895.                
  896.                 local worst_case = move_time + dig_time
  897.                 local start = worst_case
  898.                
  899.                 if #move_queue == 0 then
  900.                     worst_case = worst_case + get_global_clock() + rendezvous_timing_margin + rendezvous_waiting_time
  901.                     start = start + get_global_clock()
  902.                 else
  903.                     worst_case = worst_case + move_queue[#move_queue].time + rendezvous_timing_margin + rendezvous_waiting_time
  904.                     start = start + move_queue[#move_queue].time
  905.                 end
  906.                
  907.                 local best_rendezvous_time = math.max( data.op.time, worst_case );
  908.                 local best_rendezvous_start = math.max( data.op.start, start );
  909.                
  910.                 local op = { }
  911.                
  912.                 op.vec=vector.new( data.op.vec.x, data.op.vec.y, data.op.vec.z )
  913.                 op.op=data.op.op
  914.                 op.item=data.op.item
  915.                 op.count=data.op.count
  916.                 op.time = best_rendezvous_time
  917.                 op.start = best_rendezvous_start
  918.                 op.id = data.op.id
  919.                 op.requestor = data.op.requestor
  920.                 op.requestee = os.computerID()
  921.                 op.init = false
  922.                
  923.                 table.insert(move_queue, op)
  924.                 send_message( { receiver = data.sender, type="rendezvous_set", ["op"]=op }, modem )
  925.                 os.queueEvent("logistics_queue_push")
  926.             end
  927.         elseif (data.type == "rendezvous_set") or (data.type == "rendezvous_retry") then
  928.             os.queueEvent("logistics_rendezvous_reply", msg)
  929.         elseif data.type == "unit_query" then
  930.             -- Query request: Get information on items / unit info
  931.             -- Format: { type="unit_query" }
  932.             -- Response: { type="unit_query_response", inv=<inv-data>, unit_info=<logistical-data-packet>, position, facing, n_tasks, etc. }
  933.             local inv = {}
  934.             for i=1, 16 do
  935.                 local s = turtle.getItemDetail(i)
  936.                 if s then
  937.                     inv[i] = s
  938.                 end
  939.             end
  940.             send_message( { receiver = data.sender, type="unit_query_response", inv=inv, unit_info=logistical_data_packet, position = current_pos, facing = current_facing, n_tasks = #move_queue }, modem )
  941.         elseif data.type == "search_query" then
  942.             -- Broadcast Query request: locate resources (other nodes, certain materials, etc.)
  943.             -- Format: { type="search_query", resource={ <resource-type: item/block name ("minecraft:stone", "minecraft:sign"), unit type ("crafty", "chest", "control", "mining"), etc.> .. }, types={ <"item"/"unit"> .. } count = { counts .. } }
  944.             --  * on counts: if count == 0, then number doesn't matter. (match any number)
  945.             -- Response: If one of the terms is fulfilled:
  946.             -- { type="search_query_response", resource={ <fulfilled terms> }, types={ <ditto> }, count={ <number_provided or number_searched> } }
  947.             local fulfilled_resource = {}
  948.             local fulfilled_type = {}
  949.             local fulfilled_count = {}
  950.            
  951.             print("received search query.")
  952.            
  953.             for i=1, #data.resource do
  954.                 if data.types[i] == "item" then
  955.                     for j=1, 16 do
  956.                         local s = turtle.getItemDetail(j)
  957.                         if s then
  958.                             if s.name == data.resource[i] then
  959.                                 if s.count >= data.count[i] then
  960.                                     print("match at slot " .. tostring(j) .. " for request " .. tostring(i))
  961.                                     table.insert( fulfilled_resource, data.resource[i] )
  962.                                     table.insert( fulfilled_type, data.types[i] )
  963.                                     table.insert( fulfilled_count, s.count )
  964.                                 end
  965.                             end
  966.                         end
  967.                     end
  968.                 elseif data.types[i] == "unit" then
  969.                     if logistical_data_packet[data.resource[i]] then
  970.                         print("match for request " .. tostring(i))
  971.                         table.insert( fulfilled_resource, data.resource[i] )
  972.                         table.insert( fulfilled_type, data.types[i] )
  973.                         table.insert( fulfilled_count, 1 )
  974.                     end
  975.                 end
  976.             end
  977.            
  978.             if #fulfilled_resource > 0 then
  979.                 print("sending back query matches...")
  980.                 send_message( { receiver = data.sender, type="search_query_response", resource=fulfilled_resource, types=fulfilled_type, count=fulfilled_count }, modem )
  981.             end
  982.         elseif data.type == "unit_query_response" then
  983.             os.queueEvent("logistics_unit_query_response", msg)
  984.         elseif data.type == "search_query_response" then
  985.             os.queueEvent("logistics_search_query_response", msg)
  986.         end
  987.     end
  988. end
  989.  
  990. local function get_op_times( vec, additional_time )
  991.     local displacement = vector.new(0,0,0)
  992.    
  993.     if #move_queue == 0 then
  994.         displacement = current_pos - vec
  995.     else
  996.         displacement = move_queue[#move_queue].vec - vec
  997.     end
  998.    
  999.     -- calculate movement times
  1000.     local move_time = get_move_time( math.abs(displacement.x) ) + get_move_time( math.abs(displacement.y) ) + get_move_time( math.abs(displacement.z) )
  1001.     local dig_time = get_dig_time( math.abs(displacement.x) ) + get_dig_time( math.abs(displacement.y) ) + get_dig_time( math.abs(displacement.z) )
  1002.    
  1003.     local worst_case = move_time + dig_time
  1004.    
  1005.     if #move_queue == 0 then
  1006.         worst_case = worst_case + get_global_clock() + additional_time
  1007.     else
  1008.         worst_case = worst_case + move_queue[#move_queue].time + additional_time
  1009.     end
  1010.    
  1011.     local start = worst_case - additional_time
  1012.    
  1013.     return worst_case, start
  1014. end
  1015.  
  1016. function set_rendezvous( id, op_type, vec, item, count )
  1017.     op = { op=op_type, item=item, count=count, id = math.random(0, (2^30)), ["vec"] = vec, requestor = os.computerID(), requestee = id, init = true }
  1018.     op.time, op.start = get_op_times( vec, rendezvous_timing_margin + rendezvous_waiting_time )
  1019.    
  1020.     setting_rendezvous_times = true -- lock the queue
  1021.     send_message( { receiver = id, type = "rendezvous", op=op }, modem )
  1022.    
  1023.     local n_retries = 0
  1024.    
  1025.     local timer = os.startTimer(5)
  1026.     while true do
  1027.         local ev, d = os.pullEvent()
  1028.         if ev == "timer" then
  1029.             if d == timer then -- timeout, give up
  1030.                 return
  1031.             end
  1032.         elseif ev == "logistics_rendezvous_reply" then
  1033.             local msg = d
  1034.             if( msg[5].op.id == op.id ) then
  1035.                 if( msg[5].type == "rendezvous_set") then
  1036.                     msg[5].op.init = true
  1037.                     msg[5].op.vec.y = msg[5].op.vec.y+1
  1038.                     table.insert(move_queue, msg[5].op)
  1039.                
  1040.                     setting_rendezvous_times = false
  1041.                     return msg[5].op
  1042.                 elseif (msg[5].type == "rendezvous_retry") then
  1043.                     if n_retries > 3 then -- target unit too busy, give up
  1044.                         return
  1045.                     end
  1046.                     n_retries = n_retries+1
  1047.                     os.sleep( math.random(1,5) )
  1048.                     send_message( { receiver = id, type = "rendezvous", op=op }, modem )
  1049.                     timer = os.startTimer(5)
  1050.                 end
  1051.             end
  1052.         end
  1053.     end
  1054. end
  1055.  
  1056. function do_background_processes( m )
  1057.     if m ~= nil then
  1058.         modem = m
  1059.     end
  1060.     modem.open( transaction_channel )
  1061.     modem.open( query_channel )
  1062.     modem.open( gps_channel )
  1063.     parallel.waitForAll( handle_requests, do_moves )
  1064. end
  1065.  
  1066. local function round( a ) if( type(a) ~= "number" ) then return a else if( a < 0 ) then return math.ceil(a) else return math.floor(a) end end end
  1067.  
  1068. function gps_bootstrap( facing )
  1069.     current_facing = facing
  1070.    
  1071.     local x, y, z = gps.locate( 5 )
  1072.    
  1073.     current_pos = vector.new( round(x), round(y), round(z) )
  1074.    
  1075.     known_position_accurate = true
  1076.    
  1077.     local modems = {}
  1078.     local found_wireless = false
  1079.    
  1080.     for _, v in pairs(rs.getSides()) do
  1081.         if peripheral.getType( v ) == "modem" then
  1082.             if not peripheral.call( v, "isWireless" ) then
  1083.                 table.insert(modems, v)
  1084.             else
  1085.                 modem = peripheral.wrap(v)
  1086.                 modem_side = v
  1087.                 found_wireless = true
  1088.                 break
  1089.             end
  1090.         end
  1091.     end
  1092.    
  1093.     if not found_wireless then
  1094.         if #modems > 0 then
  1095.             modem_side = table.remove(modems, 1)
  1096.             modem = peripheral.wrap( modem_side )
  1097.         end
  1098.     end
  1099.    
  1100.     if modem.transmit ~= nil then
  1101.         modem.open( transaction_channel )
  1102.         modem.open( query_channel )
  1103.         modem.open( gps_channel )
  1104.     end
  1105. end
  1106.  
  1107. function bootstrap()
  1108.     if fs.exists(position_file) then
  1109.         load_position()
  1110.         if fs.exists(position_lock) then
  1111.             -- position is unreliable, let's see how:
  1112.             local f = fs.open(position_lock, 'r')
  1113.             local type = f.readAll()
  1114.             f.close()
  1115.             if type == "right" then
  1116.                 -- for now, let's assume that we did do the turn
  1117.                 current_facing = (current_facing + 1) % 4
  1118.             elseif type == "left" then
  1119.                 current_facing = (current_facing - 1) % 4
  1120.             else
  1121.                 gps_bootstrap( current_facing )
  1122.                 save_position()
  1123.                 fs.delete(position_lock)
  1124.                 return
  1125.             end
  1126.         end
  1127.     else
  1128.         print("determining facing.")
  1129.        
  1130.         local x, y, z = gps.locate( 5 )
  1131.         pos1 = vector.new( round(x), round(y), round(z) )
  1132.        
  1133.         print(" initial position: " .. tostring(pos1.x) .. ', ' .. tostring(pos1.y) .. ', ' .. tostring(pos1.z) )
  1134.        
  1135.         local n_turns = 0
  1136.         while turtle.detect() do
  1137.             n_turns = n_turns + 1
  1138.             if n_turns >= 4 then
  1139.                 print("all positions blocked, returning now...")
  1140.                 return false
  1141.             end
  1142.             native_turtle.turnRight()
  1143.         end
  1144.        
  1145.         native_turtle.forward()
  1146.         local x, y, z = gps.locate( 5 )
  1147.         pos2 = vector.new( round(x), round(y), round(z) )
  1148.         print(" secondary position: " .. tostring(pos2.x) .. ', ' .. tostring(pos2.y) .. ', ' .. tostring(pos2.z) )
  1149.        
  1150.         displacement = pos2 - pos1
  1151.        
  1152.         print(" displacement: " .. tostring(displacement.x) .. ', ' .. tostring(displacement.y) .. ', ' .. tostring(displacement.z) )
  1153.         if displacement.x ~= 0 then
  1154.             if displacement.x > 0 then
  1155.                 -- we moved east.
  1156.                 current_facing = facingTable.east
  1157.             else
  1158.                 current_facing = facingTable.west
  1159.             end
  1160.         elseif displacement.z ~= 0 then
  1161.             if displacement.z > 0 then
  1162.                 -- we moved south.
  1163.                 current_facing = facingTable.south
  1164.             else
  1165.                 current_facing = facingTable.north
  1166.             end
  1167.         end
  1168.        
  1169.         gps_bootstrap( current_facing )
  1170.        
  1171.         save_position()
  1172.        
  1173.         return
  1174.     end
  1175.    
  1176.     known_position_accurate = true
  1177.    
  1178.     local modems = {}
  1179.     local found_wireless = false
  1180.    
  1181.     for _, v in pairs(rs.getSides()) do
  1182.         if peripheral.getType( v ) == "modem" then
  1183.             if not peripheral.call( v, "isWireless" ) then
  1184.                 table.insert(modems, v)
  1185.             else
  1186.                 modem = peripheral.wrap(v)
  1187.                 modem_side = v
  1188.                 found_wireless = true
  1189.                 break
  1190.             end
  1191.         end
  1192.     end
  1193.    
  1194.     if not found_wireless then
  1195.         if #modems > 0 then
  1196.             modem_side = table.remove(modems, 1)
  1197.             modem = peripheral.wrap( modem_side )
  1198.         end
  1199.     end
  1200.    
  1201.     if modem.transmit ~= nil then
  1202.         modem.open( transaction_channel )
  1203.         modem.open( query_channel )
  1204.         modem.open( gps_channel )
  1205.     end
  1206. end
  1207.  
  1208. function set_current_position( x, y, z, f )
  1209.     current_facing = f
  1210.     current_pos = vector.new( x, y, z )
  1211.     known_position_accurate = true
  1212. end
  1213.  
  1214. function print_debug_info()
  1215.     print( "x:", current_pos.x )
  1216.     print( "y:", current_pos.y )
  1217.     print( "z:", current_pos.z )
  1218.     print( "f:", current_facing )
  1219.     print( "accurate: ", known_position_accurate )
  1220.     print( "modem initialized: ", (modem.transmit ~= nil))
  1221.     print( "modem side: ", modem_side)
  1222. end
  1223.  
  1224. function schedule_move_to( vec, time_spent )
  1225.     op = { op="wait", item=item, count=time_spent, id = math.random(0, (2^30)), ["vec"] = vec }
  1226.     op.time, op.start = get_op_times( vec, time_spent )
  1227.    
  1228.     table.insert(move_queue, op)
  1229.    
  1230.     return op.id
  1231. end
  1232.  
  1233. function wait_move_to( vec, time_spent )
  1234.     local id = schedule_move_to( vec, time_spent )
  1235.    
  1236.     while true do
  1237.         local ev, id2 = os.pullEvent("logistics_arrived")
  1238.        
  1239.         if id == id2 then
  1240.             return id
  1241.         end
  1242.     end
  1243. end
  1244.  
  1245. function schedule_wait( time_spent )
  1246.     op = { op="empty", count=time_spent, id = math.random(0, (2^30)) }
  1247.    
  1248.     if #move_queue == 0 then
  1249.         op.time = get_global_clock() + time_spent
  1250.         op.start = get_global_clock()
  1251.     else
  1252.         op.time = move_queue[#move_queue].time + time_spent
  1253.         op.start = move_queue[#move_queue].time
  1254.     end
  1255.    
  1256.     table.insert(move_queue, op)
  1257.    
  1258.     return op.id
  1259. end
  1260.  
  1261. function schedule_static_rendezvous( vec, op, container, slot, count )
  1262.     op = { op="static_" .. op, container=container, slot=slot, count=count, id = math.random(0, (2^30)), ["vec"] = vec }
  1263.     op.time, op.start = get_op_times( vec, rendezvous_timing_margin + rendezvous_waiting_time )
  1264.    
  1265.     table.insert(move_queue, op)
  1266.    
  1267.     return op.id
  1268. end
  1269.  
  1270. function get_current_pos()
  1271.     return vector.new( current_pos.x, current_pos.y, current_pos.z )
  1272. end
  1273.  
  1274. function continue_moves(id)
  1275.     os.queueEvent("logistics_continue", id)
  1276. end
  1277.  
  1278. function push_moves()
  1279.     os.queueEvent("logistics_queue_push")
  1280. end
  1281.  
  1282. function network_search( items, types, counts )
  1283.     send_message( { receiver = -1, type="search_query", resource=items, types=types, count = counts }, modem )
  1284.    
  1285.     local responses = {}
  1286.     local timer = os.startTimer(5)
  1287.     while true do
  1288.         local ev = { os.pullEvent() }
  1289.         if ev[1] == "timer" then
  1290.             break
  1291.         elseif ev[1] == "logistics_search_query_response" then
  1292.             local msg = ev[2]
  1293.             local data = msg[5]
  1294.             table.insert( responses, { id = data.sender, resource=data.resource, types=data.types, count=data.count })
  1295.         end
  1296.     end
  1297.    
  1298.     return responses
  1299. end
  1300.  
  1301. function unit_query( id )
  1302.     send_message( {receiver = id, type="unit_query"}, modem )
  1303.    
  1304.     local responses = {}
  1305.     local timer = os.startTimer(5)
  1306.     while true do
  1307.         local ev = { os.pullEvent() }
  1308.         if ev[1] == "timer" then
  1309.             break
  1310.         elseif ev[1] == "logistics_unit_query_response" then
  1311.             local msg = ev[2]
  1312.             local data = msg[5]
  1313.             table.insert( responses, { id = data.sender, inv=data.inv, unit_info=data.unit_info, position = data.position, facing = data.facing, n_tasks = data.n_tasks })
  1314.         end
  1315.     end
  1316.    
  1317.     return responses
  1318. end
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement