MysticT

Location System

Jun 14th, 2012
422
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 6.28 KB | None | 0 0
  1. -- Location System
  2. -- by MysticT
  3.  
  4. local tLocations = {}
  5. local tKnownLocations = {}
  6. local bDebug = false
  7.  
  8. -- Helper functions
  9.  
  10. local function clear()
  11.     term.clear()
  12.     term.setCursorPos(1, 1)
  13. end
  14.  
  15. local function table_size(t)
  16.     local i = 0
  17.     for _,_ in pairs(t) do
  18.         i = i + 1
  19.     end
  20.     return i
  21. end
  22.  
  23. local function connect()
  24.     print("Starting location system...")
  25.     print("Connecting to rednet...")
  26.     for _,s in ipairs(rs.getSides()) do
  27.         if peripheral.isPresent(s) and peripheral.getType(s) == "modem" then
  28.             rednet.open(s)
  29.             return true
  30.         end
  31.     end
  32.     print("No modem found")
  33.     print("Aborting")
  34.     return false
  35. end
  36.  
  37. local function _print(...)
  38.     if bDebug then
  39.         print(...)
  40.     end
  41. end
  42.  
  43. local function vector_equal(v1, v2)
  44.     return v1.x == v2.x and v1.y == v2.y and v1.z == v2.z
  45. end
  46.  
  47. -- Load/Save functions
  48.  
  49. local function save()
  50.     _print("Saving locations...")
  51.     local file = fs.open("locations.dat", "w")
  52.     if file then
  53.         for id, loc in pairs(tKnownLocations) do
  54.             file.writeLine(tostring(id)..": "..tostring(loc.x)..", "..tostring(loc.y)..", "..tostring(loc.z))
  55.         end
  56.         file.close()
  57.         _print("Locations saved")
  58.         return true
  59.     end
  60.     _print("Error saving locations")
  61.     return false
  62. end
  63.  
  64. local function load()
  65.     _print("Loading locations")
  66.     local file = fs.open("locations.dat", "r")
  67.     if file then
  68.         local i = 0
  69.         local sLine = file.readLine()
  70.         while sLine do
  71.             local sId, sX, sY, sZ = string.match(sLine, "(%d+): (%-?%d+), (%-?%d+), (%-?%d+)")
  72.             local id, x, y, z = tonumber(sId), tonumber(sX), tonumber(sY), tonumber(sZ)
  73.             if id and x and y and z then
  74.                 tKnownLocations[id] = vector.new(x, y, z)
  75.                 i = i + 1
  76.             end
  77.             sLine = file.readLine()
  78.         end
  79.         file.close()
  80.         _print(n, " locations loaded")
  81.         return true, i
  82.     end
  83.     _print("Error loading locations")
  84.     return false
  85. end
  86.  
  87. -- Location functions
  88.  
  89. local function trilaterate(A, B, C)
  90.     local a2b = B.position - A.position
  91.     local a2c = C.position - A.position
  92.     if math.abs( a2b:normalize():dot( a2c:normalize() ) ) > 0.999 then
  93.         return nil
  94.     end
  95.     local d = a2b:length()
  96.     local ex = a2b:normalize( )
  97.     local i = ex:dot( a2c )
  98.     local ey = (a2c - (ex * i)):normalize()
  99.     local j = ey:dot( a2c )
  100.     local ez = ex:cross( ey )
  101.     local r1 = A.distance
  102.     local r2 = B.distance
  103.     local r3 = C.distance
  104.     local x = (r1*r1 - r2*r2 + d*d) / (2*d)
  105.     local y = (r1*r1 - r3*r3 - x*x + (x-i)*(x-i) + j*j) / (2*j)
  106.     local result = A.position + (ex * x) + (ey * y)
  107.     local zSquared = r1*r1 - x*x - y*y
  108.     if zSquared > 0 then
  109.         local z = math.sqrt(zSquared)
  110.         local result1 = result + (ez * z)
  111.         local result2 = result - (ez * z)
  112.         local rounded1, rounded2 = result1:round(), result2:round()
  113.         if rounded1.x ~= rounded2.x or rounded1.y ~= rounded2.y or rounded1.z ~= rounded2.z then
  114.             return result1, result2
  115.         else
  116.             return rounded1
  117.         end
  118.     end
  119.     return result:round()
  120. end
  121.  
  122. local function narrow(p1, p2, fix)
  123.     local dist1 = math.abs((p1 - fix.position):length() - fix.distance)
  124.     local dist2 = math.abs((p2 - fix.position):length() - fix.distance)
  125.     if math.abs(dist1 - dist2) < 0.05 then
  126.         return p1, p2
  127.     elseif dist1 < dist2 then
  128.         return p1:round()
  129.     else
  130.         return p2:round()
  131.     end
  132. end
  133.  
  134. function locate(id)
  135.     local tFixes = {}
  136.     local p1, p2
  137.     for _,t in pairs(tLocations[id]) do
  138.         table.insert(tFixes, { ["position"] = vector.new(t.x, t.y, t.z), ["distance"] = t.d })
  139.     end
  140.     local i = 3
  141.     while (p1 == nil or p2 ~= nil) and i <= #tFixes do
  142.         if not p1 then
  143.             p1, p2 = trilaterate(tFixes[1], tFixes[2], tFixes[i])
  144.         else
  145.             p1, p2 = narrow(p1, p2, tFixes[i])
  146.         end
  147.         i = i + 1
  148.     end
  149.     if p1 and p2 then
  150.         _print("Ambiguous position")
  151.         _print("Could be "..p1.x..","..p1.y..","..p1.z.." or "..p2.x..","..p2.y..","..p2.z)
  152.     elseif p1 then
  153.         if not tKnownLocations[id] or not vector_equal(tKnownLocations[id], p1) then
  154.             print("Location added:")
  155.             print(id, ": ", p1)
  156.             tKnownLocations[id] = p1
  157.             save()
  158.         end
  159.     else
  160.         _print("Couldn't determine location for ", id)
  161.     end
  162. end
  163.  
  164. local function addLocation(id, t)
  165.     if not tLocations[t.id] then
  166.         tLocations[t.id] = {}
  167.     end
  168.     local tLoc = {}
  169.     tLoc.x = t.x
  170.     tLoc.y = t.y
  171.     tLoc.z = t.z
  172.     tLoc.d = t.d
  173.     tLocations[t.id][id] = tLoc
  174.     if table_size(tLocations[t.id]) >= 3 then
  175.         _print("Trying to locate #", t.id)
  176.         locate(t.id)
  177.     end
  178. end
  179.  
  180. -- Event handlers
  181.  
  182. local function handle_message(id, msg)
  183.     local st = string.match(msg, "<LOCATION> (.+)")
  184.     if st then
  185.         local t = textutils.unserialize(st)
  186.         if t and type(t) == "table" then
  187.             _print("Location received from ", id, ":")
  188.             _print(t.id, ": ", "(", t.x, ", ", t.y, ", ", t.z, ", ", t.d, ")")
  189.             addLocation(id, t)
  190.         end
  191.     end
  192. end
  193.  
  194. local function handle_char(c)
  195.     c = string.lower(c)
  196.     if c == "l" then
  197.         print("Locations:")
  198.         for id, loc in pairs(tKnownLocations) do
  199.             print(id, ": ", loc)
  200.         end
  201.     elseif c == "c" then
  202.         clear()
  203.     end
  204. end
  205.  
  206. -- Start program
  207.  
  208. local function printUsage()
  209.     local sName = fs.getName(shell.getRunningProgram())
  210.     print("Usage:")
  211.     print(sName, " receive [debug]")
  212.     print(sName, " host id x y z")
  213. end
  214.  
  215. local tArgs = {...}
  216. local sAction = tArgs[1]
  217. if not sAction then
  218.     printUsage()
  219.     return
  220. end
  221.  
  222. if sAction == "receive" then
  223.     if #tArgs > 1 then
  224.         if string.lower(tArgs[2]) == "debug" then
  225.             bDebug = true
  226.         else
  227.             printUsage()
  228.             return
  229.         end
  230.     end
  231.     clear()
  232.     if not connect() then
  233.         return
  234.     end
  235.     local ok, n = load()
  236.     if ok then
  237.         print("Loaded ", n, " locations")
  238.     end
  239.     print("Waiting for messages...")
  240.     print("Press l to list located computers")
  241.     print("Press c to clear the screen")
  242.     while true do
  243.         local evt, arg1, arg2 = os.pullEvent()
  244.         if evt == "rednet_message" then
  245.             handle_message(arg1, arg2)
  246.         elseif evt == "char" then
  247.             handle_char(arg1)
  248.         end
  249.     end
  250. elseif sAction == "host" then
  251.     local nMasterID = tonumber(tArgs[2])
  252.     local tLoc = {}
  253.     tLoc.x = tonumber(tArgs[3])
  254.     tLoc.y = tonumber(tArgs[4])
  255.     tLoc.z = tonumber(tArgs[5])
  256.     if not nMasterID or not tLoc.x or not tLoc.y or not tLoc.z then
  257.         printUsage()
  258.         return
  259.     end
  260.     clear()
  261.     if not connect() then
  262.         return
  263.     end
  264.     print("Waiting for messages...")
  265.     while true do
  266.         local id, msg, dist = rednet.receive()
  267.         print("Message received from ", id, " at ", dist, " meters.")
  268.         tLoc.id = id
  269.         tLoc.d = dist
  270.         rednet.send(nMasterID, "<LOCATION> "..textutils.serialize(tLoc))
  271.     end
  272. else
  273.     printUsage()
  274. end
Advertisement
Add Comment
Please, Sign In to add comment