Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- -- TOTORO TERMINATORS TEAM
- -- Version: 0.2.0 - Fire
- -- Special for UT2 event
- -- MIT, 2017 (c) Totoro
- local robot = require('robot')
- local event = require('event')
- local com = require('component')
- local sides = require('sides')
- local serial = require('serialization')
- local plasma = com.plasma
- local geo = com.geolyzer
- local nav = com.navigation
- local modem = com.modem
- local colors = com.colors
- -- initialize random numbers generator with modem address
- math.randomseed(string.byte(modem.address, 1) * 1000000 + string.byte(modem.address, 2) * 10000 +
- string.byte(modem.address, 3) * 100 + string.byte(modem.address, 4));
- -- constants
- local TEAM = "green"
- local TEAMSIZE = 8
- local CODENAME = string.char(65 + math.random(0, 25)) .. string.char(65 + math.random(0, 25))
- local COLORS = { red = 0xFF0000, green = 0x00FF00, blue = 0x0000FF, yellow = 0xFFFF00 }
- local OUTSIDE, ENEMY = -1, 2 -- density values
- local SERVERPORT, INTERNALPORT = 12345, 99
- local SERVERADDRESS = nil
- local MODEMSTRENGTH = 50
- local BOUNDS = { X = -23, Y = 64, Z = -23, W = 47, H = 6, D = 47 }
- local GEOMETRY = {
- "###############################################",
- "####################### #######################",
- "###################### ######################",
- "##################### #####################",
- "#################### ####################",
- "#################### ####################",
- "#################### ####################",
- "#################### # # ####################",
- "#################### ####################",
- "#################### # # ####################",
- "########## ##########",
- "########## ##########",
- "########## ##########",
- "########## ##########",
- "########## ##########",
- "########## ##########",
- "########## ##########",
- "########## ##########",
- "########## ##########",
- "########## ##########",
- "#### ####",
- "### ###",
- "## # # # # ##",
- "# #",
- "## # # # # ##",
- "### ###",
- "#### ####",
- "########## ##########",
- "########## ##########",
- "########## ##########",
- "########## ##########",
- "########## ##########",
- "########## ##########",
- "########## ##########",
- "########## ##########",
- "########## ##########",
- "########## ##########",
- "#################### # # ####################",
- "#################### ####################",
- "#################### # # ####################",
- "#################### ####################",
- "#################### ####################",
- "#################### ####################",
- "##################### #####################",
- "###################### ######################",
- "####################### #######################",
- "###############################################",
- }
- -- variables
- local map = {}
- local chunks, unscanned = {}, 49 -- information about explored map chunks
- local pos = { changed = true, facing = 0, x = 0, y = 0, z = 0 }
- local mates = {}
- -- functions
- -- navigation
- function fetchPosition()
- pos.facing = nav.getFacing()
- local x, y, z = nav.getPosition()
- pos.x, pos.y, pos.z = math.floor(x), math.floor(y), math.floor(z)
- end
- local _tt = {
- left = { left = 0, front = 1, right = 2, back = -1 },
- front = { left = -1, front = 0, right = 1, back = 2 },
- right = { left = 2, front = -1, right = 0, back = 1 },
- back = { left = 1, front = 2, right = -1, back = 0 }
- }
- function turn(facing)
- if facing ~= pos.facing then
- local diff = _tt[sides[pos.facing]][sides[facing]]
- if diff == -1 then robot.turnLeft()
- elseif diff == 2 then robot.turnAround()
- elseif diff == 1 then robot.turnRight() end
- pos.facing = facing
- end
- end
- function turnLeft()
- robot.turnLeft()
- if pos.facing == sides.left then pos.facing = sides.back
- elseif pos.facing == sides.front then pos.facing = sides.left
- elseif pos.facing == sides.right then pos.facing = sides.front
- else pos.facing = sides.right end
- end
- function turnRight()
- robot.turnRight()
- if pos.facing == sides.left then pos.facing = sides.front
- elseif pos.facing == sides.front then pos.facing = sides.right
- elseif pos.facing == sides.right then pos.facing = sides.back
- else pos.facing = sides.left end
- end
- function turnAround()
- robot.turnRight()
- if pos.facing == sides.left then pos.facing = sides.right
- elseif pos.facing == sides.front then pos.facing = sides.back
- elseif pos.facing == sides.right then pos.facing = sides.left
- else pos.facing = sides.front end
- end
- function up()
- if robot.up() then pos.y = pos.y + 1; pos.changed = true; return true end
- return false
- end
- function down()
- if robot.down() then pos.y = pos.y - 1; pos.changed = true; return true end
- return false
- end
- function forward()
- if robot.forward() then
- if pos.facing == sides.north then pos.z = pos.z - 1
- elseif pos.facing == sides.south then pos.z = pos.z + 1
- elseif pos.facing == sides.west then pos.x = pos.x - 1
- else pos.x = pos.x + 1 end
- pos.changed = true; return true
- end
- return false
- end
- function back()
- if robot.back() then
- if pos.facing == sides.north then pos.z = pos.z + 1
- elseif pos.facing == sides.south then pos.z = pos.z - 1
- elseif pos.facing == sides.west then pos.x = pos.x + 1
- else pos.x = pos.x - 1 end
- pos.changed = true; return true
- end
- return false
- end
- -- mapping
- function set(x, y, z, value)
- local mx, my, mz = math.floor(x) - BOUNDS.X, math.floor(y) - BOUNDS.Y, math.floor(z) - BOUNDS.Y
- map[ my * BOUNDS.W * BOUNDS.D + mz * BOUNDS.W + mx ] = value
- end
- function get(x, y, z)
- local mx, my, mz = math.floor(x) - BOUNDS.X, math.floor(y) - BOUNDS.Y, math.floor(z) - BOUNDS.Y
- local cell = map[ my * BOUNDS.W * BOUNDS.D + mz * BOUNDS.W + mx ]
- if cell == nil then return OUTSIDE
- else return cell end
- end
- function initializeMap()
- for x = 1, 47 do
- for y = 2, 5 do
- for z = 1, 47 do
- if GEOMETRY[x]:sub(z, z) == " " then
- set(BOUNDS.X + x - 1, BOUNDS.Y + y - 1, BOUNDS.Z + z - 1, 0)
- end
- end
- end
- end
- end
- -- spacial calculations
- function aim(x, y, z)
- local facing, yaw, pitch
- local quadrant = (math.deg(math.atan2(x - pos.x, z - pos.z)) + 360) % 360
- if quadrant < 45 or quadrant >= 315 then facing = sides.south
- elseif quadrant >= 45 and quadrant < 135 then facing = sides.east
- elseif quadrant >= 135 and quadrant < 225 then facing = sides.north
- else facing = sides.west end
- if facing == sides.west then
- yaw = math.deg(math.atan2(z - pos.z + 0.5, pos.x - x))
- pitch = math.deg(math.atan2(y - pos.y, pos.x - x))
- elseif facing == sides.east then
- yaw = math.deg(math.atan2(pos.z + 0.5 - z, x - pos.x))
- pitch = math.deg(math.atan2(y - pos.y, x - pos.x))
- elseif facing == sides.south then
- yaw = math.deg(math.atan2(x - pos.x + 0.5, z - pos.z))
- pitch = math.deg(math.atan2(y - pos.y, z - pos.z))
- else
- yaw = math.deg(math.atan2(pos.x + 0.5 - x, pos.z - z))
- pitch = math.deg(math.atan2(y - pos.y, pos.z - z))
- end
- return facing, yaw, pitch
- end
- function raytrace(x0, y0, z0, x1, y1, z1)
- local ray = {}
- local dx = math.abs(x1 - x0)
- local dy = math.abs(y1 - y0)
- local dz = math.abs(z1 - z0)
- local x = x0 --math.floor(x0)
- local y = y0 --math.floor(y0)
- local z = z0 --math.floor(z0)
- local dt_dx = 1.0 / dx
- local dt_dy = 1.0 / dy
- local dt_dz = 1.0 / dz
- local t = 0
- local n = 1
- local x_inc, y_inc, z_inc = 0, 0, 0
- local t_next_vertical, t_next_horizontal, t_next_abyssal = 0, 0, 0
- if dx == 0 then
- x_inc = 0
- t_next_horizontal = dt_dx -- infinity
- elseif x1 > x0 then
- x_inc = 1
- n = n + math.floor(x1) - math.floor(x)
- t_next_horizontal = (math.floor(x0) + 1 - x0) * dt_dx
- else
- x_inc = -1
- n = n + math.floor(x) - math.floor(x1)
- t_next_horizontal = (x0 - math.floor(x0)) * dt_dx
- end
- if dy == 0 then
- y_inc = 0
- t_next_vertical = dt_dy; -- infinity
- elseif y1 > y0 then
- y_inc = 1
- n = n + math.floor(y1) - math.floor(y)
- t_next_vertical = (math.floor(y0) + 1 - y0) * dt_dy
- else
- y_inc = -1
- n = n + math.floor(y) - math.floor(y1)
- t_next_vertical = (y0 - math.floor(y0)) * dt_dy
- end
- if dz == 0 then
- z_inc = 0
- t_next_abyssal = dt_dz; -- infinity
- elseif z1 > z0 then
- z_inc = 1
- n = n + math.floor(z1) - math.floor(z)
- t_next_abyssal = (math.floor(z0) + 1 - z0) * dt_dz
- else
- z_inc = -1
- n = n + math.floor(z) - math.floor(z1)
- t_next_abyssal = (z0 - math.floor(z0)) * dt_dz
- end
- while n > 0 do
- table.insert(ray, { x = math.floor(x), y = math.floor(y), z = math.floor(z) })
- if t_next_vertical < t_next_horizontal then
- if t_next_vertical < t_next_abyssal then
- y = y + y_inc
- t = t_next_vertical
- t_next_vertical = t_next_vertical + dt_dy
- else
- z = z + z_inc
- t = t_next_abyssal
- t_next_abyssal = t_next_abyssal + dt_dz
- end
- else
- if t_next_horizontal < t_next_abyssal then
- x = x + x_inc
- t = t_next_horizontal
- t_next_horizontal = t_next_horizontal + dt_dx
- else
- z = z + z_inc
- t = t_next_abyssal
- t_next_abyssal = t_next_abyssal + dt_dz
- end
- end
- n = n - 1
- end
- return ray
- end
- function friendlyFire(x, y, z)
- for k, v in pairs(mates) do
- if v.x == x and v.y == y and v.z == z then return true end
- end
- return false
- end
- function detectEnemies()
- local res = {}
- for x = BOUNDS.X + 1, BOUNDS.X + BOUNDS.W - 2 do
- for y = BOUNDS.Y + 1, BOUNDS.Y + BOUNDS.H - 2 do
- for z = BOUNDS.Z + 1, BOUNDS.Z + BOUNDS.D - 2 do
- if x ~= pos.x or y ~= pos.y or z ~= pos.z then
- if get(x, y, z) == ENEMY then
- if not friendlyFire(x, y, z) then
- table.insert(res,
- { x = x, y = y, z = z,
- d = math.sqrt(math.pow(pos.x - x, 2) + math.pow(pos.y - y, 2) + math.pow(pos.z - z, 2))
- })
- end
- end
- end
- end
- end
- end
- table.sort(res, function(a, b) return a.d < b.d end)
- return res
- end
- function targetIsClear(x, y, z, facing, yaw, pitch)
- -- adjust coordinates
- local offx, offy, offz = 0, 0.2, 0
- if facing == sides.south then offx = -0.35
- elseif facing == sides.north then offx = 0.35; yaw = yaw + 180
- elseif facing == sides.east then offz = 0.35; yaw = yaw + 90
- else offz = -0.35; yaw = yaw + 270 end
- offx = offx + math.sin(math.rad(yaw)) * math.cos(math.rad(pitch))
- offy = offy + math.sin(math.rad(pitch))
- offz = offz + math.cos(math.rad(yaw)) * math.cos(math.rad(pitch))
- -- trace
- local ray = raytrace(pos.x + offx, pos.y + offy, pos.z + offz, x, y, z)
- for k, v in pairs(ray) do
- local block = get(v.x, v.y, v.z)
- if friendlyFire(v.x, v.y, v.z) or (block ~= 0 and block ~= ENEMY) then return false end
- end
- return true
- end
- function fire(x, y, z)
- local facing, yaw, pitch = aim(x, y, z)
- if targetIsClear(x, y, z, facing, yaw, pitch) then
- if math.abs(yaw) <= 20 and math.abs(pitch) <= 90 then
- turn(facing)
- plasma.turn(yaw, pitch)
- plasma.fire()
- set(x, y, z, 0)
- --modem.broadcast(INTERNALPORT, "hit", x, y, z)
- return true
- end
- end
- return false
- end
- function fillChunk(x, z, chunk, num)
- local index = 1
- for dy = 1, 4 do
- for dz = 0, 3 do
- for dx = 0, 3 do
- set(x + dx, BOUNDS.Y + dy, z + dz, chunk[index])
- index = index + 1
- end
- end
- end
- if chunks[num] == nil then unscanned = unscanned - 1 end
- chunks[num] = true
- end
- function scanChunk(chunk)
- local x, z, num = BOUNDS.X + 10 + chunk.x * 4, BOUNDS.Z + 10 + chunk.z * 4, chunk.z * 7 + chunk.x
- -- check if we need to scan this chunk
- if chunks[num] == nil then
- -- check if the chunk can be scanned at all
- local offx, offz, offy = x - pos.x, z - pos.z, BOUNDS.Y - pos.y + 1
- if math.abs(offx) <= 32 and math.abs(offz) <= 32 then
- -- perform scanning
- local data = geo.scan(offx, offz, offy, 4, 4, 4)
- -- fill in the data
- fillChunk(x, z, data, num)
- -- send scanned chunk to teammates
- modem.broadcast(INTERNALPORT, "chunk", x, z, serial.serialize(data), num)
- end
- end
- end
- function unscannedChunk()
- local selector = math.random(1, unscanned)
- for z = 0, 6 do
- for x = 0, 6 do
- if chunks[z * 7 + x] == nil then selector = selector - 1 end
- if selector <= 0 then return { x = x, z = z } end
- end
- end
- return nil
- end
- -- movement
- function randomMove()
- local selector = math.random(1, 6)
- if selector == 1 then up()
- elseif selector == 2 then down()
- elseif selector == 3 then forward()
- elseif selector == 4 then back()
- elseif selector == 5 then turnLeft(); forward()
- else turnRight(); forward() end
- end
- function checkBlock(x, y, z)
- local block = get(x, y, z)
- if block == ENEMY then return fire(x, y, z)
- else return block == 0 end
- end
- function move(side)
- -- check location first
- local check = false
- if side == sides.up then check = checkBlock(pos.x, pos.y + 1, pos.z)
- elseif side == sides.down then check = checkBlock(pos.x, pos.y - 1, pos.z)
- elseif side == sides.south then check = checkBlock(pos.x, pos.y, pos.z + 1)
- elseif side == sides.north then check = checkBlock(pos.x, pos.y, pos.z - 1)
- elseif side == sides.west then check = checkBlock(pos.x - 1, pos.y, pos.z)
- else check = checkBlock(pos.x + 1, pos.y, pos.z) end
- -- move!
- if check then
- if side ~= sides.up and side ~= sides.down then
- turn(side)
- end
- if side == sides.up then return up()
- elseif side == sides.down then return down()
- else return forward() end
- end
- end
- local point = nil
- function pointMove()
- -- generate new point
- if point == nil or (point.x == pos.x and point.y == pos.y and point.z == pos.z) then
- while true do
- local x = math.random(BOUNDS.X + 1, BOUNDS.X + BOUNDS.W - 2)
- local y = math.random(BOUNDS.Y + 1, BOUNDS.Y + BOUNDS.H - 2)
- local z = math.random(BOUNDS.Z + 1, BOUNDS.Z + BOUNDS.D - 2)
- if get(x, y, z) == 0 then point = { x = x, y = y, z = z }; break end
- end
- end
- -- move to the point
- local moved = false
- if point.x < pos.x then moved = move(sides.west) end
- if point.x > pos.x then moved = move(sides.east) end
- if point.y < pos.y then moved = move(sides.down) end
- if point.y > pos.y then moved = move(sides.up) end
- if point.z < pos.z then moved = move(sides.north) end
- if point.z > pos.z then moved = move(sides.south) end
- -- if unreachable then change the point
- if not moved then point = nil end
- end
- -- initialization
- local game, timeout = false, 2.0
- local byMyself, byTeammates = 0, 0
- -- set team colors
- plasma.setColor(COLORS[TEAM])
- colors.setColor(COLORS[TEAM])
- -- check current position
- fetchPosition()
- -- generate map by scheme
- initializeMap()
- -- open ports
- modem.setStrength(MODEMSTRENGTH)
- modem.open(SERVERPORT)
- modem.open(INTERNALPORT)
- -- subscribe to events
- event.listen("modem_message", function(name, address, sender, port, _, a, b, c, d, e)
- if port == SERVERPORT then
- if not game and a == "gamestart" then
- SERVERADDRESS = sender; game = true; timeout = 0.01
- print("Game started!")
- end
- if sender == SERVERADDRESS then
- if a == "gamestop" then
- game = false; timeout = 2.0
- print("Game stopped!")
- print("SQUAD: " .. TEAM .. "-" .. TEAMSIZE)
- print("CODENAME: " .. CODENAME)
- print("Waiting...")
- elseif a == "whoalive" then
- modem.send(SERVERADDRESS, SERVERPORT, "i'm alive", TEAM, pos.x, pos.y, pos.z)
- end
- end
- elseif port == INTERNALPORT then
- -- get info about teammate position
- if a == "position" then
- if mates[b] == nil then mates[b] = {} end
- mates[b].x = c
- mates[b].y = d
- mates[b].z = e
- print("Mate moved: ", c, d, e)
- -- get new chunk of map
- elseif a == "chunk" then
- fillChunk(b, c, serial.unserialize(d), e)
- byTeammates = byTeammates + 1
- print("Got chunk #" .. e .. " from teammate.")
- print("(Left " .. unscanned .. "/49, scanned: " .. byMyself .. "m, " .. byTeammates .. "t)")
- -- teammates have destroyed one target
- elseif a == "hit" then
- set(b, c, d, 0)
- end
- end
- end)
- -- let's go!
- print("SQUAD: " .. TEAM .. "-" .. TEAMSIZE)
- print("CODENAME: " .. CODENAME)
- print("Waiting...")
- while true do
- -- send my position to teammates
- if pos.changed then
- modem.broadcast(INTERNALPORT, "position", CODENAME, pos.x, pos.y, pos.z)
- pos.changed = false
- end
- -- process event queue
- os.sleep(timeout)
- -- do something else
- if game then
- -- select next action
- local selector = math.random(1, 6)
- -- move to new position
- if selector == 1 then
- pointMove()
- -- scan chunk of map
- elseif selector == 2 and unscanned > 0 then
- local chunk = unscannedChunk()
- scanChunk(chunk)
- byMyself = byMyself + 1
- print("Scanned chunk #" .. (chunk.z * 7 + chunk.x) .. ".")
- print("(Left " .. unscanned .. "/49, scanned: " .. byMyself .. "m, " .. byTeammates .. "t)")
- -- fire!
- elseif selector == 3 then
- local targets = detectEnemies()
- local shooted = false
- if #targets > 0 then
- -- try to shoot one of targets
- for k, v in pairs(targets) do
- if fire(v.x, v.y, v.z) then shooted = true; break end
- end
- end
- if not shooted then
- -- take some random shots
- while true do
- local x = math.random(BOUNDS.X + 1, BOUNDS.X + BOUNDS.W - 2)
- local y = math.random(BOUNDS.Y + 1, BOUNDS.Y + BOUNDS.H - 2)
- local z = math.random(BOUNDS.Z + 1, BOUNDS.Z + BOUNDS.D - 2)
- if fire(x, y, z) then break end
- end
- end
- end
- end
- end
- -- finalize
- modem.close()
- print("Game over.")
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement