From eef67a04a4593396a8d2db9acaf8839e48465a2d Mon Sep 17 00:00:00 2001 From: Jonathan Coates Date: Wed, 5 Apr 2023 20:18:38 +0100 Subject: [PATCH] Handle duplicate pings received by gps.locate Co-authored-by: Wojbie --- .../data/computercraft/lua/rom/apis/gps.lua | 16 +++- .../resources/test-rom/spec/apis/gps_spec.lua | 86 +++++++++++++++++++ .../test-rom/spec/support/fake_computer.lua | 14 ++- 3 files changed, 111 insertions(+), 5 deletions(-) diff --git a/projects/core/src/main/resources/data/computercraft/lua/rom/apis/gps.lua b/projects/core/src/main/resources/data/computercraft/lua/rom/apis/gps.lua index fafd2bea7..182936912 100644 --- a/projects/core/src/main/resources/data/computercraft/lua/rom/apis/gps.lua +++ b/projects/core/src/main/resources/data/computercraft/lua/rom/apis/gps.lua @@ -157,12 +157,22 @@ function locate(_nTimeout, _bDebug) if tFix.nDistance == 0 then pos1, pos2 = tFix.vPosition, nil else - table.insert(tFixes, tFix) + -- Insert our new position in our table, with a maximum of three items. If this is close to a + -- previous position, replace that instead of inserting. + local insIndex = math.min(3, #tFixes + 1) + for i, older in pairs(tFixes) do + if (older.vPosition - tFix.vPosition):length() < 5 then + insIndex = i + break + end + end + tFixes[insIndex] = tFix + if #tFixes >= 3 then if not pos1 then - pos1, pos2 = trilaterate(tFixes[1], tFixes[2], tFixes[#tFixes]) + pos1, pos2 = trilaterate(tFixes[1], tFixes[2], tFixes[3]) else - pos1, pos2 = narrow(pos1, pos2, tFixes[#tFixes]) + pos1, pos2 = narrow(pos1, pos2, tFixes[3]) end end end diff --git a/projects/core/src/test/resources/test-rom/spec/apis/gps_spec.lua b/projects/core/src/test/resources/test-rom/spec/apis/gps_spec.lua index bf0755e7b..8a2cdcb61 100644 --- a/projects/core/src/test/resources/test-rom/spec/apis/gps_spec.lua +++ b/projects/core/src/test/resources/test-rom/spec/apis/gps_spec.lua @@ -16,4 +16,90 @@ describe("The gps library", function() expect.error(gps.locate, 1, ""):eq("bad argument #2 (boolean expected, got string)") end) end) + + describe("on fake computers", function() + local fake_computer = require "support.fake_computer" + + local function gps_reciever(x, y, z, fn) + local computer = fake_computer.make_computer(1, fn) + computer.position = vector.new(x, y, z) + fake_computer.add_api(computer, "rom/apis/gps.lua") + fake_computer.add_api(computer, "rom/apis/vector.lua") + + local modem = fake_computer.add_modem(computer, "back") + return computer, modem + end + + local function gps_hosts(computer, modem, positions) + local computers = {} + for _, position in pairs(positions) do + local position = vector.new(position[1], position[2], position[3]) + local host = fake_computer.make_computer(99, function(env) + env.loadfile("/rom/programs/gps.lua")("host", position.x, position.y, position.z) + end) + host.position = position + fake_computer.add_api(host, "rom/apis/gps.lua") + fake_computer.add_api(host, "rom/apis/vector.lua") + + local host_modem = fake_computer.add_modem(host, "back") + fake_computer.add_modem_edge(modem, host_modem) + + computers[#computers + 1] = host + end + + computers[#computers + 1] = computer -- Run the computer after hosts have started + return computers + end + + it("locates a computer", function() + local computer, modem = gps_reciever(12, 23, 52, function(env) + local x, y, z = env.gps.locate() + expect({ x, y, z }):same { 12, 23, 52 } + end) + local computers = gps_hosts(computer, modem, { + { 5, 5, 5 }, + { 10, 5, 5 }, + { 5, 10, 5 }, + { 5, 5, 10 }, + }) + + fake_computer.run_all(computers, false) + fake_computer.advance_all(computers, 2) + fake_computer.run_all(computers, { computer }) + end) + + it("fails to locate a computer with insufficient hosts", function() + local computer, modem = gps_reciever(12, 23, 52, function(env) + local x, y, z = env.gps.locate() + expect({ x, y, z }):same { nil, nil, nil } + end) + local computers = gps_hosts(computer, modem, { + { 5, 5, 5 }, + { 10, 5, 5 }, + { 5, 10, 5 }, + }) + + fake_computer.run_all(computers, false) + fake_computer.advance_all(computers, 2) + fake_computer.run_all(computers, { computer }) + end) + + it("doesn't fail on duplicate hosts", function() + local computer, modem = gps_reciever(12, 23, 52, function(env) + local x, y, z = env.gps.locate() + expect({ x, y, z }):same { 12, 23, 52 } + end) + local computers = gps_hosts(computer, modem, { + { 5, 5, 5 }, + { 5, 5, 5 }, + { 10, 5, 5 }, + { 5, 10, 5 }, + { 5, 5, 10 }, + }) + + fake_computer.run_all(computers, false) + fake_computer.advance_all(computers, 2) + fake_computer.run_all(computers, { computer }) + end) + end) end) diff --git a/projects/core/src/test/resources/test-rom/spec/support/fake_computer.lua b/projects/core/src/test/resources/test-rom/spec/support/fake_computer.lua index 28d53dd6e..1529a25d2 100644 --- a/projects/core/src/test/resources/test-rom/spec/support/fake_computer.lua +++ b/projects/core/src/test/resources/test-rom/spec/support/fake_computer.lua @@ -56,7 +56,12 @@ local function make_computer(id, fn) repeat local _, id = env.os.pullEvent("timer") until id == timer end, } + env.redstone = { + getSides = redstone.getSides, + } + env.rs = env.redstone env.sleep = env.os.sleep + env.loadfile = function(path, mode, new_env) return loadfile(path, mode, new_env or env) end env.dofile = function(path) local fn, err = loadfile(path, nil, env) if fn then return fn() else error(err, 2) end @@ -91,7 +96,9 @@ local function make_computer(id, fn) end end - return { env = env, peripherals = peripherals, queue_event = queue_event, step = step, co = co, advance = advance } + local position = vector.new(0, 0, 0) + + return { env = env, peripherals = peripherals, queue_event = queue_event, step = step, co = co, advance = advance, position = position } end local function parse_channel(c) @@ -112,10 +119,13 @@ local function add_modem(owner, side) for _, adjacent in pairs(adjacent) do if adjacent.open[channel] then - adjacent.owner.queue_event("modem_message", adjacent.side, channel, reply_channel, payload, 123) + local distance = (adjacent.owner.position - owner.position):length() + print(("Distance %s .. %s => %d"):format(adjacent.owner.position, owner.position, distance)) + adjacent.owner.queue_event("modem_message", adjacent.side, channel, reply_channel, payload, distance) end end end, + isWireless = function() return true end, }, { type = "modem" }) owner.peripherals[side] = peripheral return { adjacent = adjacent, side = side, owner = owner, open = open }