ZombieBot
Posted: Sun Jun 30, 2013 9:08 am
Yet another bot, the ZombieBot that I made for bobdaduck
It starts slow and slowly speeds up. The rate that it speeds up is proportional to the lenght of the level. At 80% of the normal speed, it will start using turbo randomly. It's seriously awesome when you add 15 of them
It starts slow and slowly speeds up. The rate that it speeds up is proportional to the lenght of the level. At 80% of the normal speed, it will start using turbo randomly. It's seriously awesome when you add 15 of them
- Code:
- --- from S_bot, modified by fordcars
--- this bot works on all game modes, made for bobdaduck
goalPt = point.new(0,0)
prevtarget = nil
gotoPositionWasNil = true
o = point.new(0,0)
botLoc = nil -- Bot's location, will be updated when onTick() is run
-- This function gets run once during setup. It is the only time we can declare variables
-- and have them be defined as globals.
function main()
botRadius = bot:getRad()
pathTimerMax = 250
pathTimer = pathTimerMax
dirToGo = 0
game = bf:getGameInfo()
difficulty = tonumber(arg[1]) or .5
agression = tonumber(arg[2]) or 0.5
defense = tonumber(arg[3]) or 0
speed = tonumber(arg[4]) or 1
directionThreshold = tonumber(arg[5]) or .25
gameType = game:getGameType()
averageIndex = 1
averageMax = 20
averageArray = { }
for i = 1, averageMax do
averageArray[i] = false
end
myOrbitalDirection = 1
myObjective = math.random(0, 10)
zombSpeed = 0.1
useTurbo = false
turboTimer = false
oldBotSpeed = nil
local totalTime = game:getGameTimeRemaining()
local percTime = totalTime * 80
percTime = percTime / 100
percTime = percTime * 60
zombieRate = 0.8 / percTime
addTimer()
end
items = { } -- Global variable, reusable container for findGlobalObjects. Reusing this table avoids costs of
-- constructing and destructing it every time we call findGlobalObjects(). Be sure to clear the
-- table before reusing it!
function addTimer()
Timer:scheduleRepeating(addBotRate, 16.67)
end
function addBotRate()
zombSpeed = zombSpeed + zombieRate
end
function shieldSelf()
table.clear(items)
bot:findVisibleObjects(items, ObjType.Bullet, ObjType.Seeker, ObjType.Asteroid, ObjType.Mine, ObjType.Burst)
local distToShieldAt = botRadius * 2 + (1 - difficulty) * 100
if (items ~= nil) then
for i,bullet in ipairs(items) do
local bulletLoc = bullet:getPos()
local bulletVel = bullet:getVel()
local angleDiff = math.abs(angleDifference(point.angleTo(o, bulletVel), point.angleTo(bulletLoc, botLoc)))
local bulletfromteam = false
--logprint(angleDiff)
if (game:isTeamGame() == true) and (bullet:getTeamIndex() == bot:getTeamIndex()) then -- If bullet is from team
bulletfromteam = true -- bullet from team is true
else -- If it is not a team game or if bullet is not from team
bulletfromteam = false -- bullet from team is false
end
if (bulletfromteam == false) then
if (point.distanceTo(bulletLoc, botLoc) < distToShieldAt + bullet:getRad() + point.distanceTo(o, bulletVel) * 50 and angleDiff < math.pi / 4) or (bullet == ObjType.Burst and point.distSquared(bulletLoc, botLoc) <= 500 * 500) then
bot:fireModule(Module.Shield)
return(true)
end
end
end
end
end
function angleDifference(angleA, angleB)
return (math.mod(math.mod(angleA - angleB, math.pi * 2) + math.pi * 3, math.pi * 2) - math.pi)
end
function fireAtObjects()
table.clear(items)
bot:findVisibleObjects(items, ObjType.Robot, ObjType.Turret, ObjType.Ship, ObjType.Asteroid,
ObjType.ForceFieldProjector, ObjType.SpyBug, ObjType.Core)
-- Cycle through list of potential items until we find one that we can attack
for index, enemy in ipairs(items) do
if(fireAtObject(enemy, Weapon.Phaser)) then
break
end
end
end
-- Fires at the specified object with the specified weapon if the obj is a good target.
-- Does not fire if object is on the same team or if there is something in the way.
-- Returns whether it fired or not.
function fireAtObject(obj, weapon)
local classId = obj:getObjType()
if(classId == ObjType.Turret or classId == ObjType.ForceFieldProjector) then
if(obj:getHealth() < .1) then
--logprint("He's dead, Jim.")
return(false)
end
end
if(classId == ObjType.Ship or classId == ObjType.Robot or classId == ObjType.ForceFieldProjector or
classId == ObjType.Turret or classId == ObjType.Core) then
if obj:getTeamIndex() == bot:getTeamIndex() and game:isTeamGame() or obj:getTeamIndex() == Team.Neutral then
--logprint("It's an ally.")
return(false)
end
end
local angle = getFiringSolution(obj)
if angle ~= nil and bot:hasWeapon(weapon) then
bot:setAngle(angle + math.rad((math.random()-0.5)*20*(1-difficulty)))
bot:fireWeapon(weapon)
--logprint("bot:fireWeapon() called!");
return(true)
end
--logprint("Firing solution not found.");
return(false)
end
function getName()
return("botdazombie")
end
function shield()
averageArray[averageIndex] = shieldSelf()
averageIndex = math.mod(averageIndex,averageMax) + 1
local shieldPercent = 0
for i = 1, averageMax do
if(averageArray[averageIndex]) then
shieldPercent = shieldPercent + 1
end
end
shieldPercent = shieldPercent / averageMax
if(shieldPercent > directionThreshold) then
myOrbitalDirection = -myOrbitalDirection
for i = 1, averageMax do
averageArray[i] = false
end
end
end
function orbitPoint(pt, dir, inputDist, inputStrictness)
local distAway = botRadius * 7
local strictness = 2
local direction = 1
if(dir ~= nil) then
direction = dir
end
if(inputDist ~= nil) then
distAway = inputDist
end
if(inputStrictness ~= nil) then
strictness = inputStrictness
end
if(pt ~= nil) then
local dist = point.distanceTo(pt, botLoc)
local deltaDistance = (dist - distAway) * strictness / distAway
local sign = 1
if(deltaDistance > 0) then
sign = 1
elseif(deltaDistance < 0) then
sign = -1
end
local changeInAngle = (math.abs(deltaDistance)/(deltaDistance + sign)) * math.pi/2
local angleToPoint = point.angleTo(pt, bot:getPos())
dirToGo = angleToPoint + (math.pi/2 + changeInAngle)*direction
--bot:setThrust(speed, dirToGo)
end
end
function gotoPosition(pt)
if pt ~= nil then
gotoPositionWasNil = false
if pathTimer < .01 then
goalPt = bot:getWaypoint(pt)
if(goalPt ~= nil) then
dirToGo = point.angleTo(botLoc, goalPt)
end
end
end
end
function gotoAndOrbitPosition(pt)
if pt ~= nil then
gotoPositionWasNil = false
if pt~= nil then
gotoPositionWasNil = false
gotoPosition(pt)
else
gotoPositionWasNil = false
orbitPoint(pt, myOrbitalDirection, botRadius * 5, 2)
end
end
end
-- Returns true if it found an enemy to fight
function attackNearbyEnemies(target, agressionLevel)
if target ~= nil then
local targetLoc = target:getPos()
-- local dist = point.distanceTo(botLoc, targetLoc)
local myPow = bot:getEnergy() + bot:getHealth()
table.clear(items)
bot:findVisibleObjects(items, ObjType.Ship, ObjType.Robot)
local otherPow = target:getEnergy() + target:getHealth() * #items
--advantage is between -1 and 1, -1 meaning an extreme disadvantage and 1 meaning an extreme advantage
local advantage = (myPow - otherPow) / math.max(myPow, otherPow)
if(advantage / 2 + .5 >agressionLevel) then
--orbitPoint(targetLoc, myOrbitalDirection, botRadius * 9, 2)
prevtarget = targetLoc
return(false) -- was true
else
end
end
return(false)
end
-- Returns the objective for the bot, in the form of an object the bot can navigate towards. This makes bots choose different defending locations.
-- If onTeam is true, will only return items on specified team. If onTeam is false, will return items *not* on
-- specified team. If called with fewer than three args, will ignore team altogether.
function getObjective(objType, team, onTeam)
table.clear(items)
bf:findAllObjects(items, objType) -- Returns a list of all items of type objType in the game
local itemsOnMyTeam = {}
local currentIndex = 1
for index, item in ipairs(items) do -- Iterate through all found items
local itemTeamIndex = item:getTeamIndex()
if (objType == ObjType.Flag) and (gameType == GameType.Nexus) then
if not item:isOnShip() then
itemsOnMyTeam[currentIndex] = item
currentIndex = currentIndex + 1
end
elseif (objType == ObjType.Flag) and
((gameType == GameType.HTF) or (gameType == GameType.Retrieve)) and
item:isInCaptureZone() then
--logprint(item:getCaptureZone():getTeamIndex());
if item:getCaptureZone():getTeamIndex() ~= bot:getTeamIndex() then
itemsOnMyTeam[currentIndex] = item
currentIndex = currentIndex + 1
end
elseif (objType == ObjType.GoalZone) and (gameType == GameType.HTF or gameType == GameType.Retrieve) then
if onTeam == nil or ((itemTeamIndex == team) == onTeam) then
if not item:hasFlag() then
itemsOnMyTeam[currentIndex] = item
currentIndex = currentIndex + 1
end
end
else
if (itemTeamIndex == Team.Neutral ) and (objType ~= ObjType.GoalZone or gameType ~= GameType.ZC) then
itemTeamIndex = team -- anything Neutral is on our team (except zone control neutral goal zone)
end
if onTeam == nil or ((itemTeamIndex == team) == onTeam) then
itemsOnMyTeam[currentIndex] = item
currentIndex = currentIndex + 1
end
end
end
local listMax = 0
--find max
if itemsOnMyTeam[1] ~= nil then
for index,item in ipairs(itemsOnMyTeam) do
if(item ~= nil) then
listMax = listMax + 1
end
end
local targetNum = math.mod(myObjective, listMax) + 1
return(itemsOnMyTeam[targetNum])
else
return(nil)
end
-- local closestitem = 0 --itemsOnMyTeam[1]
-- local cur = 1
-- local closestdist = 99999999
-- while itemsOnMyTeam[cur] ~= nil do
-- local item1 = itemsOnMyTeam[cur]
-- if item1 ~= nil then
-- local loc = item1.getPos()
-- if loc ~= nil then
-- local dist = point.distanceTo(botLoc, loc)
-- if dist < closestdist then
-- closestdist = dist
-- closesetitem = item1
-- end
-- end
-- end
-- cur=cur+1
-- end
-- return(closestitem)
end
function doObjective(closestEnemy)
gotoPositionWasNil = true
if(gameType == GameType.Bitmatch) then
--Nothing to do here.
elseif(gameType == GameType.Nexus) then
-- Grab any flags that are found, and go to nexus when it opens
local otherFlag = getObjective(ObjType.Flag) -- Find any flags
if otherFlag ~= nil then gotoPosition(otherFlag:getPos()) end
-- logprint(bot:getFlagCount()) -- always 1
if bot:getFlagCount() > 3 and (game:isNexusOpen() or game:getNexusTimeLeft() < 10000) then -- Need to know if nexus is open
local nexusOrFlag = getObjective(ObjType.Nexus) -- unimplemented push function error.
if nexusOrFlag ~= nil then
gotoPosition(nexusOrFlag:getPos())
end
end
elseif(gameType == GameType.Rabbit) then
--Grab a flag, or go after the flag.
if not bot:hasFlag() then
local otherFlag = getObjective(ObjType.Flag, bot:getTeamIndex(), true) -- Find flags on our team
gotoPosition(otherFlag:getPos())
end
elseif(gameType == GameType.HTF or gameType == GameType.Retrieve) then
-- Grab the flag and put it into goal zones
-- Robot keeps trying to pick up the flags that is already in the goalZones
if bot:hasFlag() then
local otherFlag = getObjective(ObjType.GoalZone, bot:getTeamIndex(), true) -- Find GoalZone on our team
if otherFlag ~= nil then
gotoPosition(otherFlag:getPos())
end
else
local otherFlag = getObjective(ObjType.Flag, bot:getTeamIndex(), true) -- Find flags on our team
if otherFlag ~= nil then
gotoPosition(otherFlag:getPos())
end
end
elseif(gameType == GameType.CTF) then
--defend the flag
local myFlag = getObjective(ObjType.Flag, bot:getTeamIndex(), true) -- Find flags on our team
local otherFlag = getObjective(ObjType.Flag, bot:getTeamIndex(), false) -- Find flags not on our team
if(defense < .5) then
if bot:hasFlag() then
if not myFlag:isOnShip() then
gotoPosition(myFlag:getPos())
else
gotoAndOrbitPosition(myFlag:getPos())
end
--gotoPosition(myFlag:getPos())
elseif(otherFlag ~= nil) then
if myFlag:isOnShip() then
gotoPosition(myFlag:getPos())
elseif not otherFlag:isOnShip() then
gotoPosition(otherFlag:getPos())
else
gotoAndOrbitPosition(otherFlag:getPos())
end
end
else
if bot:hasFlag() then
gotoPosition(myFlag:getPos())
elseif myFlag:isInInitLoc() then
gotoAndOrbitPosition(myFlag:getPos())
else
if myFlag:isOnShip() then
gotoAndOrbitPosition(myFlag:getPos())
else
gotoPosition(myFlag:getPos())
end
end
end
elseif(gameType == GameType.Soccer) then
--grab soccer and put into enemy goal
-- How do we know if we are holding soccer ? (not supported when cannot pickup soccer (016)
if bot:getMountedItems(ObjType.SoccerBallItem)[1] ~= nil then
local otherFlag = getObjective(ObjType.GoalZone, bot:getTeamIndex(), false) -- Find GoalZones not on our team
if otherFlag ~= nil then
gotoPosition(otherFlag:getPos())
end
else
local otherFlag = getObjective(ObjType.SoccerBallItem) -- Find SoccerBall
if otherFlag ~= nil then
gotoPosition(otherFlag:getPos())
end
end
elseif(gameType == GameType.ZC) then
-- Grab flag, then go after zones that is not ours.
if not bot:hasFlag() then
local otherFlag = getObjective(ObjType.Flag, bot:getTeamIndex(), true) -- Find flags on our team
if otherFlag ~= nil then
if otherFlag:isOnShip() then
gotoAndOrbitPosition(otherFlag:getPos())
else
gotoPosition(otherFlag:getPos())
end
end
else
local otherFlag = getObjective(ObjType.GoalZone, bot:getTeamIndex(), false) -- Find GoalZones on our team
if otherFlag then
gotoPosition(otherFlag:getPos())
end
end
elseif(gameType == GameType.Core) then
local obj = getObjective(ObjType.Core, bot:getTeamIndex(), false) -- Find enemy Core
if obj ~= nil then
gotoAndOrbitPosition(obj:getPos())
end
end
-- If we have no where to go, go to nearest enemy.
if gotoPositionWasNil then
if(closestEnemy ~= nil) then
prevtarget = closestEnemy:getPos()
end
if(prevtarget ~= nil) then
gotoAndOrbitPosition(prevtarget)
end
end
end
function goInDirection()
bot:setThrust(zombSpeed, dirToGo)
end
function turbo()
useTurbo = true
oldBotSpeed = zombSpeed
zombSpeed = 1
local turboLength = math.random(2000, 6000)
Timer:scheduleOnce(stopTurbo, turboLength)
end
function stopTurbo()
useTurbo = false
turboTimer = false
zombSpeed = oldBotSpeed
end
-- This function gets called every game tick; deltaTime is the time that has elapsed since it was last called
function onTick(deltaTime)
botLoc = bot:getPos()
pathTimer = pathTimer - deltaTime
assert(bot:getPos() ~= nil)
local closestEnemy = bot:findClosestEnemy()
-- attackNearbyEnemies returns true if there is an enemy to fight, false if the bot can do something else
attackNearbyEnemies(closestEnemy, 1 - agression)
doObjective(closestEnemy) -- Set bot's objective
goInDirection() -- Move the ship
fireAtObjects() -- Fire weapons
shield() -- Apply shield
if(pathTimer < 0) then
pathTimer = pathTimerMax + math.random(0, pathTimerMax)
end
if (turboTimer == false) and (zombSpeed >= 0.8) then --------------------
local timerTime = math.random(20000, 30000)
turboTimer = true
Timer:scheduleOnce(turbo, timerTime)
end
if (useTurbo == true) then
bot:fireModule(Module.Turbo)
end
end