S_bot2
- Bots now return flags in Capture the flag when they are close to it!
- Bots don't use sheild when team players shoot them!
- Bots don't shoot team or neutral spy bugs!
- Bot code is allot clearer now!
- Code:
- --- from quickBot v2, modified by sam686 and fordcars
--- works on all game modes, except this bot don't work on non-pickup soccer
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 = GameInfo()
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)
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 shieldSelf()
table.clear(items)
findObjects(items, ObjType.Bullet, ObjType.Seeker, ObjType.Asteroid, ObjType.Mine)
local distToShieldAt = botRadius * 2 + (1 - difficulty) * 100
if (items ~= nil) then
for i,bullet in ipairs(items) do
local bulletLoc = bullet:getLoc()
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:getTeamIndx() == bot:getTeamIndx()) 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 ~= true) and (point.distanceTo(bulletLoc, botLoc) < distToShieldAt + bullet:getRad() + point.distanceTo(o, bulletVel) * 50 and angleDiff < math.pi / 4) then
bot:activateModule(Module.Shield)
return(true)
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)
findObjects(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:getClassId()
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.SpyBug or classId == ObjType.Core) then
if obj:getTeamIndx() == bot:getTeamIndx() and game:isTeamGame() or obj:getTeamIndx() == 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:setWeapon(weapon)
bot:fire()
--logprint("bot:fire() called!");
return(true)
end
--logprint("Firing solution not found.");
return(false)
end
function getName()
return("S_Bot2")
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:getLoc())
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 not bot:hasLosPt(pt) 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:getLoc()
-- local dist = point.distanceTo(botLoc, targetLoc)
local myPow = bot:getEnergy() + bot:getHealth()
table.clear(items)
findObjects(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)
bot:findGlobalObjects(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:getTeamIndx()
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():getTeamIndx());
if item:getCaptureZone():getTeamIndx() ~= bot:getTeamIndx() 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.getLoc()
-- 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:getLoc()) 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:getLoc())
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:getTeamIndx(), true) -- Find flags on our team
gotoPosition(otherFlag:getLoc())
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 -- If the bot has the flag
local otherFlag = getObjective(ObjType.GoalZone, bot:getTeamIndx(), true) -- Find an avalible GoalZone on our team
if otherFlag ~= nil then -- If there is an avalible GoalZone
gotoPosition(otherFlag:getLoc()) -- Go to it
end
else -- If the bot doesn't have the flag
local otherFlag = getObjective(ObjType.Flag, bot:getTeamIndx(), true) -- Find flags on our team
if otherFlag ~= nil then -- If there is a flag avalible
gotoPosition(otherFlag:getLoc()) -- Go to it
end
end
elseif(gameType == GameType.CTF) then
--defend the flag
local myFlag = getObjective(ObjType.Flag, bot:getTeamIndx(), true) -- Find flags on our team
local otherFlag = getObjective(ObjType.Flag, bot:getTeamIndx(), false) -- Find flags not on our team
if(defense < .5) then -- If bot doesn't defend allot (default is 0)
if bot:hasFlag() then -- If the bot has a flag
if not myFlag:isOnShip() then -- If my flag is not on a ship
gotoPosition(myFlag:getLoc()) -- Go to position of my flag
else
gotoAndOrbitPosition(myFlag:getLoc()) -- Otherwise, go and orbit the flag on enemy
end
--gotoPosition(myFlag:getLoc())
else -- If the bot doesn't have the flag
if not myFlag:isInInitLoc() then -- If my flag is not in its initial location
if not myFlag:isOnShip() then -- If my flag is not on a ship
if point.distanceTo(myFlag:getLoc(), bot:getLoc()) <= 2000000 then
gotoPosition(myFlag:getLoc()) -- Go to my flag
end
end
end
end
if otherFlag ~= nil and not bot:hasFlag() then -- If there is an enemy flag and bot doesn't have a flag
if myFlag:isInInitLoc() or point.distanceTo(myFlag:getLoc(), bot:getLoc()) > 2000000 then
if myFlag:isOnShip() then -- If my flag is on a ship
gotoPosition(myFlag:getLoc()) -- Go to position of my flag
elseif not otherFlag:isOnShip() then -- If enemy flag is not on a ship
gotoPosition(otherFlag:getLoc()) -- Go to that flag
else
gotoAndOrbitPosition(otherFlag:getLoc()) -- Go and orbit our team flag's carrier
end
end
end
else
if bot:hasFlag() then -- If the bot has a flag and has more than .5 defense
gotoPosition(myFlag:getLoc()) -- Go to team flag
elseif myFlag:isInInitLoc() then -- If the bot doesn't have a flag and the team flag is in intial location
gotoAndOrbitPosition(myFlag:getLoc()) -- Go and orbit team flag
else
if myFlag:isOnShip() then -- If team flag is on a ship
gotoAndOrbitPosition(myFlag:getLoc()) -- Go and orbit team flag
else
gotoPosition(myFlag:getLoc()) -- If team flag is not on a ship, go to team flag
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:getTeamIndx(), false) -- Find GoalZones not on our team
if otherFlag ~= nil then
gotoPosition(otherFlag:getLoc())
end
else
local otherFlag = getObjective(ObjType.SoccerBallItem) -- Find SoccerBall
if otherFlag ~= nil then
gotoPosition(otherFlag:getLoc())
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:getTeamIndx(), true) -- Find flags on our team
if otherFlag ~= nil then
if otherFlag:isOnShip() then
gotoAndOrbitPosition(otherFlag:getLoc())
else
gotoPosition(otherFlag:getLoc())
end
end
else
local otherFlag = getObjective(ObjType.GoalZone, bot:getTeamIndx(), false) -- Find GoalZones on our team
if otherFlag then
gotoPosition(otherFlag:getLoc())
end
end
elseif(gameType == GameType.Core) then
local obj = getObjective(ObjType.Core, bot:getTeamIndx(), false) -- Find enemy Core
if obj ~= nil then
gotoAndOrbitPosition(obj:getLoc())
end
end
-- If we have no where to go, go to nearest enemy.
if gotoPositionWasNil then
if(closestEnemy ~= nil) then
prevtarget = closestEnemy:getLoc()
end
if(prevtarget ~= nil) then
gotoAndOrbitPosition(prevtarget)
end
end
end
function goInDirection()
bot:setThrust(speed, dirToGo)
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:getLoc()
pathTimer = pathTimer - deltaTime
assert(bot:getLoc() ~= 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
end
Edit to add, by raptor:
These improvements have been merged into S_Bot for the next release. To get the latest S_Bot, go here:
https://bitfighter.googlecode.com/hg/re ... /s_bot.bot
skybax: Why is this health pack following me?
bobdaduck: uh, it likes you.