n_bot
Posted: Wed Mar 19, 2014 12:05 am
Our beloved N_Bot, short for normal bot..
He eschews shield in favor of turbo. very minor change (in the code) that you will appreciate
It gets beat by shield users pretty easily... so you practice fighting without shield!
He eschews shield in favor of turbo. very minor change (in the code) that you will appreciate
It gets beat by shield users pretty easily... so you practice fighting without shield!
- Code:
- -------------------------------------------------------------------------------
-------------------------------------------------------------------------------
--
-- N_Bot, our beloved Normal Bot
--
-- Works on all game modes, some better than others
--
-- Authors:
-- - Unknown (original QuickBot v2 code)
-- - sam686 (AI code for gametypes, other improvements)
-- - watusimoto (maintenance, upgrades)
-- - raptor (maintenance, upgrades)
-- - Fordcars (AI improvements)
-- - Tazinator (Changed shield to Turbo)
-------------------------------------------------------------------------------
-------------------------------------------------------------------------------
-------------------------------------------------------------------------------
-------------------------------------------------------------------------------
goalPt = point.new(0,0)
prevtarget = nil
gotoPositionWasNil = true
o = point.new(0,0)
botPos = 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)
end
items = { } -- Global variable, reusable container for bf:findAllObjects. Reusing this table avoids costs of
-- constructing and destructing it every time we call bf:findAllObjects(). Be sure to clear the
-- table before reusing it!
function shieldSelf()
table.clear(items)
bot:findVisibleObjects(items, ObjType.Bullet, ObjType.Seeker, ObjType.Asteroid, ObjType.Mine, ObjType.Burst)
local distToTurbo = botRadius * 2 + (1 - difficulty) * 100
if (items ~= nil) then
for i,bullet in ipairs(items) do
local bulletPos = bullet:getPos()
local bulletVel = bullet:getVel()
local angleDiff = math.abs(angleDifference(point.angleTo(o, bulletVel), point.angleTo(bulletPos, botPos)))
--logprint(angleDiff)
local bulletFromTeam = false
if (game:isTeamGame() == true) and (bullet:getTeamIndex() == bot:getTeamIndex()) then -- If bullet is from team
bulletFromTeam = true -- bullet from team is true
end
if (bulletFromTeam == false) and (point.distanceTo(bulletPos, botPos) < distToTurbo + bullet:getRad() + point.distanceTo(o, bulletVel) * 50 and angleDiff < math.pi / 4) then
bot:fireModule(Module.Turbo)
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)
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
-- Ignore all same-team engineered objects... even in single-team games
if obj:getTeamIndex() == bot:getTeamIndex() then
return false -- Cancel fireAtObject function
end
if obj:getHealth() < .1 then -- If item is essentially dead
return false
end
end
-- No shooting various team related objects
if (obj:getTeamIndex() == bot:getTeamIndex() and game:isTeamGame()) or -- Same team
obj:getTeamIndex() == Team.Neutral then -- Neutral team
if (classId == ObjType.Ship or classId == ObjType.Robot or -- Turrets and FFs handled above
classId == ObjType.Core or classId == ObjType.SpyBug) then
return false
end
end
-- No shooting non-flag carriers in single player rabbit
if gameType == GameType.Rabbit and not game:isTeamGame() and
(classId == ObjType.Ship or classId == ObjType.Robot) and
not bot:hasFlag() and not obj:hasFlag() then
return false
end
-- We made it here! We have a valid target..
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("Nbot")
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, botPos)
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(botPos, goalPt)
end
end
end
end
function gotoAndOrbitPosition(pt)
if pt ~= nil then
gotoPositionWasNil = false
if not bot:canSeePoint(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 targetPos = target:getPos()
-- local dist = point.distanceTo(botPos, targetPos)
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(targetPos, myOrbitalDirection, botRadius * 9, 2)
prevtarget = targetPos
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(botPos, 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 -- If there is a flag avalible
gotoPosition(otherFlag:getPos())
end
-- If bot has more than 4 flags and the nexus is open or we're within 10 seconds of opening
if bot:getFlagCount() > 4 and (game:isNexusOpen() or game:getNexusTimeLeft() < 10) 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 -- If the bot has the flag
local otherFlag = getObjective(ObjType.GoalZone, bot:getTeamIndex(), true) -- Find an avalible GoalZone on our team
if otherFlag ~= nil then -- If there is an avalible GoalZone
gotoPosition(otherFlag:getPos()) -- Go to it
end
else -- If the bot doesn't have the flag
local otherFlag = getObjective(ObjType.Flag, bot:getTeamIndex(), true) -- Find flags on our team
if otherFlag ~= nil then -- If there is a flag avalible
gotoPosition(otherFlag:getPos()) -- Go to it
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 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:getPos()) -- Go to position of my flag
else
gotoAndOrbitPosition(myFlag:getPos()) -- Otherwise, go and orbit the flag on enemy
end
--gotoPosition(myFlag:getPos())
else -- If the bot doesn't have the flag
local retrievingFlag = false
if myFlag ~= nil then
if not myFlag:isInInitLoc() and not myFlag:isOnShip() and -- If my flag is not in its initial location and my flag is not on a ship
point.distSquared(myFlag:getPos(), bot:getPos()) <= 2000 * 2000 then -- .. and we're within some sane range of the flag
gotoPosition(myFlag:getPos()) -- Go to and return my flag
retrievingFlag = true
end
end
if otherFlag ~= nil then -- If there is an enemy flag
if not retrievingFlag then -- .. and this bot isn't already retrieving a flag
if myFlag:isOnShip() then -- If my flag is on a ship
gotoPosition(myFlag:getPos()) -- Go to position of my flag
elseif not otherFlag:isOnShip() then -- If enemy flag is not on a ship
gotoPosition(otherFlag:getPos()) -- Go to that flag
else
gotoAndOrbitPosition(otherFlag:getPos()) -- Go and orbit our team flag's carrier
end
end
end
end
else
if bot:hasFlag() then -- If the bot has a flag and has more than .5 defense
gotoPosition(myFlag:getPos()) -- 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:getPos()) -- Go and orbit team flag
else
if myFlag:isOnShip() then -- If team flag is on a ship
gotoAndOrbitPosition(myFlag:getPos()) -- Go and orbit team flag
else
gotoPosition(myFlag:getPos()) -- 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: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(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)
botPos = 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
end