This is a read-only snapshot of the ComputerCraft forums, taken in April 2020.
Andrew2060's profile picture

[ICBM] Anti-Ballistic Missile Defense System

Started by Andrew2060, 27 May 2016 - 03:21 PM
Andrew2060 #1
Posted 27 May 2016 - 05:21 PM
Hello,

I am a ModPack developer, and the creator of the Technic pack of PostBlast.
We are working towards an official server and certain realities must be met.
Such as how players will be able to protect themselves from ICBM missiles.
As a result, we have created a code to be used as a baseline for players.

However, I cannot figure out why my code is failing, much less why it wont fire.
The issue at hand is that the DefenseTech mod provides ICBM's and Radars.
The radar has many methods including one of "getEntities" allowing for missile tracking.
What is apparent is that my rednet message is not providing the right message to a silo.
Thus coordinates and launch confirmation cannot be given when a radar outputs a signal.

Basically if a missile comes too close, the radar outputs the coordinates of a missile.
It also outputs a redstone signal in all directions as a secondary verification means.
Thus my program utilizes both to send coordinates and approve launch wirelessly.
It controls a Anti-Ballistic missile hooked up to another Computer with a modem.
This computer is awaiting for a message from the main computer of coords.

ABM Server:
http://pastebin.com/H4kALgKf

Silo PC:
~Note to use you must change the message from pong to ABM
http://pastebin.com/H4kALgKf

Many thanks,

Hope someone can fix this for me!
Andrew2060 #2
Posted 27 May 2016 - 08:47 PM
Nevermind i fixed the issue.
Dragon53535 #3
Posted 28 May 2016 - 12:32 AM
And what was the issue?
Andrew2060 #4
Posted 28 May 2016 - 08:49 AM
Multiple things,

Identified x,y,z as locals rather than individuals, instead of having for silos[i+1] i had silos.
Just put those in and now it functions, i updated the links if anyone would like to use the code.
Andrew2060 #5
Posted 28 May 2016 - 10:57 AM
Hi,

I have created a program to be used on a monitor for displaying incoming targets,
My question is how can i make this system more efficient and resourceful than it is.
The mod im using is DefenseTech, which contributes ICBM Mod blocks/missiles.

This is basically what i am looking to achieve, but do not know how to.

1. Better Target Listing:
Instead of http://imgur.com/7pEBSh3
Im trying to get it to do this:

(Where it updates the coordinates in the same line rather than create another Target1 line with new coords)
Target 1: X: , Y: , Z: (Distance)
Target 2: X: , Y:, Z: (Distance)
Target 3: X: , Y:, Z: (Distance)

That way the Target # remains constant, while only the coordinates update themselves.
Such as where if Target 1 is at 50, 50, 50 it will list that location, till the target moves.
Then when the Target 1 moves to 75, 75, 75 it will update that target not list it again.

Basically it just keeps spamming Target1:0,0,0 Target1: 10,10,10,
Im looking for it to continously update in a single line, with Target being constant till another target is identified.

~Thanks in advance.
~I would like any other feedback to improve it!

~Also is there any additional way for it to tell the distance from the radar?
~I do not know the math function to convert coordinates to distance in blocks.

Code: http://pastebin.com/zqpnccq2
Edited on 28 May 2016 - 09:01 AM
Lyqyd #6
Posted 28 May 2016 - 08:08 PM
Threads merged. Please stick to one topic for all questions about a given program or piece of code.

Don't uselessly bump your topics, especially when it's been an extremely short amount of time since you posted it in the first place. You could start considering a bump after your post has gone two or three weeks with no other new posts.
Dragon53535 #7
Posted 28 May 2016 - 08:15 PM
Let's start from the top.

As for your targets, you can set the cursor back to the top left, and just clear the line you're about to write onto before printing out coordinates.
However if you're not keeping track of the targets, your coordinates might jump around. For example, say we have two missiles coming in, A and B, if we print out the targets, A might be target 1, B might be 2. However if we print them again, then A might be 2, and B might be 1.

As for distance, that's simple math. Sort of.
There's a theorem in math call the Pythagorean Theorem, the theorem is used to get the hypotenuse of a right triangle. Translating that a bit, it gets the distance of x/y coordinates.

The formula for it is a^2 + b^2 = c^2.
Now the cool thing here is that while the formula itself only works with 2 operands, it actually works with 3 or more, as long as it always would make a right triangle.

So the math you have to do, is find the difference between your x,y,z of the computer and the target. Square them all, add those together, take the square root, and you've got your distance.

So in a line:


local xDiff,yDiff,zDiff = math.abs(compX-targX),math.abs(compY-targY),math.abs(compZ-targZ)
--#Just get the difference of x,y,z
local distance = math.sqrt( math.pow(xDiff,2) + math.pow(yDiff,2) + math.pow(zDiff,2) )
--#Get the square root, of the all the squares of your differences added together.

Edit: Was typing this up and then Lyqyd merged the threads :P/>.
But yeah, a lot of us have different hours we check in around, a bump is useless because for extremely simple problems like yours we're usually able to help really easily when we see it.
Edited on 28 May 2016 - 06:21 PM
Andrew2060 #8
Posted 28 May 2016 - 08:42 PM
Yeah thanks a lot, sorry for the bump/multiple topics.

I managed to fix it on my own though with the help of some beta-testing friends.
I basically just made it able to calculate distance and list that in a print.

However i am still wondering how i can make it so Target1 is constant while only the distance is updating.
For example, Target1:50 blocks, Target:1 (after 10 seconds) 10 blocks, so and so forth.

~BTW updated code remains in the same link, i edited it in that link.

~Thanks again.

http://pastebin.com/zqpnccq2
Edited on 28 May 2016 - 06:45 PM
Dragon53535 #9
Posted 28 May 2016 - 09:21 PM
You'd have to keep track of what targets you're tracking, and what position on the list they are. There's quite a few ways you could achieve this, and each one that I can think of off the top of my head requires a few tables.


local playerTable = {"ThisPlayerIsInside"}

--#Players currently being tracked, the key is their number


local targetNames = {ThisPlayerIsInside = true}
--#A table so that I can keep track of what players are inside the playerTable without having to loop.


local tempPlayerTable = radar.getTargets()

--#grab players or whatever.
--#Need the table to be in the form of key as name, value as whatever.
--#If it's not, it's not hard to convert.

--#Loop through playerTable and figure out what isn't there any more.
local deleteTable = {}

--#I have to use a separate table because you cannot edit a table while enumerating through it
for a,v in ipairs(playerTable) do
  if not tempPlayerTable[v] then
    targetNames[v] = nil--#Delete the player in this
    table.insert(deleteTable,a) --#The index to delete
  end
end
for a,v in ipairs(deleteTable) do
  table.remove(v-a+1)
--#v is the index to delete from playerTable, a is the amount of items already deleted, and I add 1 to correctly get the number
end
for a,v in pairs(tempPlayerTable) do
  if not targetNames[a] then --#We need to add this player/target
    table.insert(playerTable,a)
  end
end
targetNames = tempPlayerTable

This technically should work.

To start we have 2 tables that are going to be used for printing and keeping track, these two need to be at the top of your program, and shouldn't be redeclared. Those being playerTable and targetNames. playerTable holds the targets and their names in order, for doing getTargetDetails, while targetNames holds the names of the targets in range as keys for easy look ups.

tempPlayerTable is going to be a table that holds the new target names as keys, if they're not like that which I doubt they will be, you would want to convert it to be so. We then create a dummy table, deleteTable, that holds the indexes from playerTable that aren't in range or don't exist anymore, we also delete from the targetNames table names that aren't there anymore. We do this because as I said in the comment, it would error if we tried to edit playerTable while looping through it. When we've listed out all the indexes from playerTable that need to be deleted, we need to do so.

After we've deleted targets that aren't in range or exist anymore, we need to add new ones in as well, so we loop through the tempPlayerTable, which is the one with the up to date targets in it, and compare it to the old targetNames table, if there is an index inside the tempPlayerTable that isn't in targetNames, then we add it into the playerTable.
Andrew2060 #10
Posted 28 May 2016 - 09:26 PM
A bit hardcore for my understanding, it's been about a year since i picked up Lua again.
However i do understand the logic behind what you are trying to say.

Create a database for targets, and a shadow one as we cannot edit something in a loop.

Then simply call the data within the ranges required?

I appreciate the effort but could you try an example with my code?
Im basically trying to create a defense shield for my base on a factions server.

~Thanks in advance.
Dragon53535 #11
Posted 28 May 2016 - 10:54 PM
I'm going to copy paste the code here but add markers.


--#Part 1
local playerTable = {}
local targetNames = {}

--#Part 2
local tempPlayerTable = radar.getTargets()

--#Part 3
local deleteTable = {}
for a,v in ipairs(playerTable) do
  if not tempPlayerTable[v] then
	targetNames[v] = nil
	table.insert(deleteTable,a)
  end
end
for a,v in ipairs(deleteTable) do
  table.remove(playerTable,v-a+1)
end

--#Part 4
for a,v in pairs(tempPlayerTable) do
  if not targetNames[a] then
	table.insert(playerTable,a)
  end
end
targetNames = tempPlayerTable

So, for the start here, you'd want to put part 1 at the top of your script, so that they're accessible to the entire rest of your code.

The rest of the code is for just updating what targets are in range.

Part 2 would be you getting the targets in range from your radar, in reality that table just needs to be filled with the names of all targets in range as the keys.

Part 3 is clearing old targets that aren't in range anymore.

Part 4 is adding NEW targets that are now in range.

After you run part 4, you need to loop through the playerTable and start printing it out like you would normally, however now as long as something stays in range, it's target# will not change.


Edit: Also, there was a major bug in part 3, in which I never actually deleted anything from the playerTable because I forgot to tell it that it should delete from playerTable


Edit2: Going to go a little bit more in depth.

Part 1 is simple, playerTable is just going to be holding the current targets in range, and making sure that each one is the same number. targetNames is just a table saying what the contents of playerTable are, in no particular order.

Part 2 is also simple, it's just whatever you get back from your radar. However now that I'm actually reading your code (I didn't before) I see that you're calling getEntities, which returns a weird table…

Given that nature, since it doesn't give you a name at all, I find no way to keep static reference to an entity short of potentially calculating it's velocity and keeping track of what the previous coordinates were…
Edited on 28 May 2016 - 09:30 PM
Andrew2060 #12
Posted 29 May 2016 - 11:22 AM
I don't understand,

is it possible?

Like i don't think i have to store coordinates, i was thinking maybe do something like this
Print("Target 1")
Print("Target 2")
Print("Target 3")
while true do
if missile = detected then
term.setCursorPos(15,1)
print("Distance:" ..distance)
sleep(0)
end
os.reboot()

Where the computer prints as many target numbers in the screen before hand.
Then if a missile is detected its coordinates are tagged to each number in order.
For example the first missile detected will be tagged to Target 1 and its coordinates will update there.

However the only issue in the system is the targets will get jumbled like you said, and will switcha round coordinates.
I have literally no idea how to correct this, i mean i have seen other peoples radars based on Openperipherals but still.
Andrew2060 #13
Posted 29 May 2016 - 01:42 PM
Alright well if we cannot fix logging, then i have another question:

My ABM Server:
Basically the issue is that the Anti-Ballistic Missile server is too collectivized.
The Anti-Ballistic Missile (ABM) shoots down other missiles in a 30 block radius.
It also shoots down other ABM's which get launched simultaneously from the server.
Problem being without a delay the missiles will all launch and kill each other before killing an actual threat.

I am trying to get a delay into the sequence but i cannot figure out how,
As i would have to pull each computer ID automatically from the table, and fire each one by one with a delay.
Meaning, instead of launching all at once, it would select each id in order and fire with a delay between each.

The issue lies in this segment of the code:

local function findSilos()
rednet.broadcast("ABM1")
local timerID = os.startTimer(waitDelay)
while true do
event, id, msg, distance = os.pullEvent()
if event == "rednet_message" and msg == "ABM" then
table.insert(silos, id)
timerID = os.startTimer(waitDelay)
elseif event == "timer" and id == timerID then
return
end
end
end

local function launch(count, x, y, z)
local msg = {x = x, y = y, z = z}
local count = math.max(count, #silos)
print("launching ABM Missile CounterMeasures at " .. x .. ", " .. y .. ", " .. z)
for i = 1, count do

rednet.send(silos, msg)
end

whole code can be found here:

Spoiler–[[Skynet Nuclear ABM Defense Program by Andrew2060]]–

–[[Settings]]–
local modemSide = "top"
local waitDelay = 2

–[[Initialization]]–
rednet.open(modemSide)
local silos = {}
term.setBackgroundColor(colors.blue)
term.clear()

–[[Functions List]]–
local function clear()
term.clear()
term.setCursorPos(1, 1)
end

local function findSilos()
rednet.broadcast("ABM1")
local timerID = os.startTimer(waitDelay)
while true do
event, id, msg, distance = os.pullEvent()
if event == "rednet_message" and msg == "ABM" then
table.insert(silos, id)
timerID = os.startTimer(waitDelay)
elseif event == "timer" and id == timerID then
return
end
end
end

local function launch(count, x, y, z)
local msg = {x = x, y = y, z = z}
local count = math.max(count, #silos)
print("launching ABM Missile CounterMeasures at " .. x .. ", " .. y .. ", " .. z)
for i = 1, count do

rednet.send(silos, msg)
end
sleep(3)
end

local function printSilos()
print("===================================================")
print(" [STANDBY ABM Silos] ")
for k, v in ipairs(silos) do
print(" silo #" .. k .. " id = "..v)
end
print("===================================================")
term.setBackgroundColor(colors.red)
print(" ")
print(" ")
term.setBackgroundColor(colors.blue)
end

local count, x, y, z

–[[Main Programs]]–
findSilos()
print("===================================================")
print(" SKYNET DEFENSE SYSTEMS ACTIVATED ")
print("===================================================")
print(" ANTI-BALLISTIC MISSILE SHIELD ONLINE ")
print("===================================================")
printSilos()
while not redstone.getInput("back") do
os.queueEvent("randomEvent")
os.pullEvent()
i = 1
end

term.clear()
term.setCursorPos(1,1)
term.setTextColor(colors.red)
print("Incoming Missiles:")
print("Firing CounterMeasures")
term.setTextColor(colors.white)
maptab = peripheral.call("back","getEntities")
maptxt = textutils.serialize(maptab)
if maptxt ~= "{}" then
allDat = 0
for num in pairs(maptab) do
allDat = allDat+1
end
targets = allDat/3
for i=0,targets-1 do
local msg = {x = x, y = y, z = z}
while not (type(count) == "number" and type(x) == "number" and type(y) == "number" and type(z) == "number") do
count = tonumber(1)
x = math.floor(tonumber(maptab["x_"..i])/1)
y = math.floor(tonumber(maptab["y_"..i])/1)
z = math.floor(tonumber(maptab["z_"..i])/1)

if silos[i+1] ~= nil then
print("Incoming Missile Threat #"..i.." at X:"..x.." Y:"..y.." Z:"..z)
launch(count, x, y, z)
end
end
end
os.reboot()
end
Bomb Bloke #14
Posted 29 May 2016 - 03:16 PM
I am trying to get a delay into the sequence but i cannot figure out how,
As i would have to pull each computer ID automatically from the table, and fire each one by one with a delay.
Meaning, instead of launching all at once, it would select each id in order and fire with a delay between each.

Erm, haven't you just described exactly how you'd do it? Just stick a sleep inside your loop instead of under it:

local function launch(count, x, y, z)
  local msg = {x = x, y = y, z = z}
  local count = math.min(count, #silos)  --# Pretty sure you meant "min" here!
  print("launching ABM Missile CounterMeasures at " .. x .. ", " .. y .. ", " .. z)
  for i = 1, count do
    rednet.send(silos[i], msg)
    sleep(10)  --# zzz
  end
end

Another thing, this can be changed:

while not redstone.getInput("back") do
os.queueEvent("randomEvent")
os.pullEvent()
i = 1
end

… to something less stressful on your server's processor:

while not redstone.getInput("back") do os.pullEvent("redstone") end

And this could be simplified:

        maptxt = textutils.serialize(maptab)
        if maptxt ~= "{}" then

… to a quick check of maptab:

        if #maptab > 0 then

The functionality of your findSilos() function is about identical to rednet.lookup(); you may like to switch to that.

All you need to do is have the silos register themselves by sticking something like this up the top of their code:

peripheral.find("modem", rednet.open)  --# Open any attached modems for rednet traffic.
rednet.host("ABM", os.getComputerLabel() or "Silo" .. tostring(os.getComputerID()))

Then on the control system do:

local function findSilos()
  silos = {rednet.lookup("ABM")}
end

The silos will respond to the lookup with no further coding on your part. You can also use protocols with your other messages, to help your systems filter out some potentially unwanted traffic.

You'll also want to ditch that "os.reboot()" at the bottom of the script - there's absolutely no need to be re-initialising the computer over and over when you only need to load the script once and then just keep it running. Instead, wrap that lower section of code in another "while true do" loop, and have it sleep a few seconds between each iteration.
Andrew2060 #15
Posted 29 May 2016 - 06:36 PM
Thanks, it helped a lot!

However i still cannot get multiple silos to fire,
Like all i am looking to achieve is to get it to fire 1, let it reload and fire another one from the other silo,
Basically like a reloading mechanism, where it waits for the empty silos to reload and fires the loaded ones in the mean time.
One by one action, so they dont all fire and kill each other, right now it fires from one silo only but the other two do exist, yet dont fire.
Andrew2060 #16
Posted 29 May 2016 - 09:56 PM
So is there anyway to accomplish this mechanism?
Dragon53535 #17
Posted 29 May 2016 - 11:12 PM
Yeah there's a way to accomplish just about anything. You've got the know how on how to be able to do so yourself.

Loop through the amount of silo's you need to fire, command one to fire, sleep for a half second, command it to reload, move onto the next.


As for the earlier thing with the radar and keeping track of missiles. It's not impossible, just extremely hard to keep accurate. Since the radar you're using just dumps all of the entities in your lap saying here's 1-x entities in no particular order, that makes it hard. If you can find a function in your radar that allows you to get a unique name for an entity that doesn't change, then you can easily rig up a system to print out their locations.
Edited on 29 May 2016 - 09:14 PM
Andrew2060 #18
Posted 30 May 2016 - 11:06 AM
Hi,

Yeah it didn't fix it,
Now if i do everything you said except for the rednet lookup, it fires 1 missile then freezes.

I have played with it changed functions and re ordered the loop in multiple places.
It just does not do what i am trying to get it to do, now it only fires 1 missile instead of 10.

All i am trying to achieve is:

1. Radar Detects incoming swarm of missiles.
2. Radar triggers ABM server to shoot down missiles.
3. ABM Server alerts silo 1 out of all silos to fire at the 1st missile.
4. Silo 1 then fires the missile at the incoming missile to intercept it.
5. Meanwhile ABM server fires silo 2 while silo 1 is reloading itself.
6. ABM server then fires silo 3 while silo 2 and 1 are loading themselves.
7. ABM continously repeats the steps 2-6 till all missiles are dead (redstone signal = off).

That is all i am trying to get,
And i have no idea how to get it done.

The pastebin links have been edited with the new code.

Can someone please look at it and tell me where to put what.


The issue is that if i only fire from a single silo while it reloads, there is a chance it will blow up.
Because it fires until its told to stop, if it reloads too quickly the missiles will hit each other and blow my base up.
Meaning the ABM will intercept the ABM before it reaches the target, like 5 blocks up from the silo, and hurt me.

Thus i need to delay the launches, and make it a cycle like in the steps stated above.

Server:
http://pastebin.com/H4kALgKf
Client:
http://pastebin.com/aNsduCem

Right now all it does is tell the closest silo computer to fire.
It has no mechanism for telling other silos to fire when the closest one is empty of missiles.
Edited on 30 May 2016 - 09:42 AM
Bomb Bloke #19
Posted 30 May 2016 - 11:41 AM
It just does not do what i am trying to get it to do, now it only fires 1 missile instead of 10.

Previously, you were attempting to fire all silos at each target. Changing from math.max to math.min means you're now trying to fire one silo per target (that is to say, the script now respects the setting on line 87).

However, each target gets its own launch command, and each launch command starts from the first silo. If there are multiple targets, and re-arming takes more than a second, then you've got a problem.

We can add some persistence to the silo usage tracker via an upvalue:

local curSilo = 1
local function launch(count, x, y, z)
	local msg = {x = x, y = y, z = z}
	print("launching ABM Missile CounterMeasures at " .. x .. ", " .. y .. ", " .. z)
	for i = 1, count do
		rednet.send(silos[curSilo], msg)
		curSilo = (curSilo == #silos) and 1 or (curSilo + 1)
		sleep(1)
	end
end

This makes it possible to change targets without resetting back to the first silo. It also makes it possible to fire more missiles at a target than you have silos, if you later decide to do such a thing (and if the silos can keep on re-arming fast enough…).

No need to tonumber() numbers, by the way.
Edited on 30 May 2016 - 09:43 AM
Andrew2060 #20
Posted 30 May 2016 - 12:10 PM
Hey thanks for the help but

Its erroring on line 87:

Expected number.


Edit: Nvm now, i forgot to put a capital case in curSilo.

Anyhow, it still repeats the previous process of a single silo launch.
Is there a problem in the loop perhaps or the launch sequence?
Edited on 30 May 2016 - 10:15 AM
Bomb Bloke #21
Posted 30 May 2016 - 12:33 PM
You've got it rebooting immediately after each target (line 94) - meaning it'll never proceed past the first one… was that call there before?!
Andrew2060 #22
Posted 30 May 2016 - 12:50 PM
Should i just take the reboot out and put a while true do loop after the redstone input line?
Bomb Bloke #23
Posted 30 May 2016 - 01:02 PM
As I said earlier, there's no point in making this script reboot the computer at all. Beats me why you'd even consider it.

If you add an additional loop after the redstone check, then you won't be able to pause the system by removing the rear signal. Just leave that check where it is, sitting inside the loop you start on line 65.
Andrew2060 #24
Posted 30 May 2016 - 03:16 PM
If i leave the loop there it fires once and never fires again, requiring a reboot.
Andrew2060 #25
Posted 30 May 2016 - 10:09 PM
yeah,

I dont know, if the loop is placed before X,Y,Z it fires once and never again,
however if it is after the X Y Z determination then it fires multiple times even after the redstone signal dies.
Is there anyway to loop it after the redstone input checker and break the loop once the redstonee signal has died?
Andrew2060 #26
Posted 31 May 2016 - 09:30 AM
Alright so i checked it out again,
There are three possible versions by looping it in different areas:
1. Where the system rapid fires from each individual silo like required, but continues firing even after redstone is off.
2. Where the system fires a single missile from the same silo and can hit 3/5 missiles before they impact. (reboot enabled).
3. Where the system fires one missile then does not fire again (while true do loop above the redstone listener).


Anyway i can get option 1, except with it to turn off?
Bomb Bloke #27
Posted 31 May 2016 - 01:12 PM
At this point your script is turning into a pure mess; beats me what you're thinking with that indentation…

I decided to sit down and re-write it, generally ripping out the redundant code and formatting it back into something readable. While doing this, I believe I finally spotted the problem: the script would only fire missiles so long as x/y/z weren't defined. Given that it defined them at the first opportunity and then never removed their assigned values, this meant it fired once.

Anyway, here's what I came up with. Obviously I haven't tested it, as I don't have access to your server (or any other ICBM setups for that matter), but with any luck it would require minimal tweaking (if any) to work from that state.
Andrew2060 #28
Posted 31 May 2016 - 07:24 PM
Hey, Thanks for all that, i got really frustrated and gave it to a friend of mine to fix, he ended up giving me a somewhat working version which i uploaded to pastebin, in the excitement i forgot to look through it and didnt see the obnoxious tabs, anyhow your code gives me a table error on the client, and a "Too long without yielding" error on the server?

client: http://pastebin.com/aNsduCem
Bomb Bloke #29
Posted 01 June 2016 - 03:03 AM
Ah right, the server will probably need to yield in circumstances where a signal is applied but no targets are found. A single sleep down the bottom of the loop sorts that out.

But it beats me as to what's going on with the silos. Try printing out some types and values, see what's actually coming through. If any tables are being sent at all then the co-ords should be showing up on the server anyways.
Andrew2060 #30
Posted 02 June 2016 - 06:39 PM
Anyhow, i fixed it.

Works perfectly well now!

Thanks!

http://pastebin.com/H4kALgKf