Last active
March 17, 2024 12:49
-
-
Save andytudhope/b1832dd5146db408d7d56e07a0ba5fda to your computer and use it in GitHub Desktop.
A good bot to begin with
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
-- Initializing global variables to store the latest game state and game host process. | |
LatestGameState = LatestGameState or nil | |
-- Prevents the agent from taking multiple actions at once. | |
InAction = InAction or false | |
Logs = Logs or {} | |
colors = { | |
red = "\27[31m", | |
green = "\27[32m", | |
blue = "\27[34m", | |
reset = "\27[0m", | |
gray = "\27[90m" | |
} | |
function addLog(msg, text) -- Function definition commented for performance, can be used for debugging | |
Logs[msg] = Logs[msg] or {} | |
table.insert(Logs[msg], text) | |
end | |
-- Checks if two points are within a given range. | |
-- @param x1, y1: Coordinates of the first point. | |
-- @param x2, y2: Coordinates of the second point. | |
-- @param range: The maximum allowed distance between the points. | |
-- @return: Boolean indicating if the points are within the specified range. | |
function inRange(x1, y1, x2, y2, range) | |
return math.abs(x1 - x2) <= range and math.abs(y1 - y2) <= range | |
end | |
-- Decides the next action based on player proximity and energy. | |
-- If any player is within range, it initiates an attack. | |
-- If there are more than two players, it tries to run away from the nearest one | |
-- If there are two or less, it goes on the attack if it has gathered enough energy for good attacks | |
-- If there are two or less, but we still lack energy, it runs away until it gathers enough. | |
function decideNextAction() | |
local player = LatestGameState.Players[ao.id] | |
local otherPlayersCount = 0 | |
local nearestPlayerDistance = math.huge | |
local nearestPlayer = nil | |
local targetInRange = false | |
-- Calculate the number of other players and find the nearest one | |
for target, state in pairs(LatestGameState.Players) do | |
if target ~= ao.id then | |
otherPlayersCount = otherPlayersCount + 1 | |
local distance = math.sqrt((player.x - state.x)^2 + (player.y - state.y)^2) | |
if distance < nearestPlayerDistance then | |
nearestPlayerDistance = distance | |
nearestPlayer = state | |
end | |
if distance <= 1 then -- Check if any player is in attack range | |
targetInRange = true | |
end | |
end | |
end | |
local function moveTowards(target) | |
local dx = target.x - player.x | |
local dy = target.y - player.y | |
local direction = nil | |
if math.abs(dx) > math.abs(dy) then | |
direction = dx > 0 and "Right" or "Left" | |
else | |
direction = dy > 0 and "Down" or "Up" | |
end | |
return direction | |
end | |
local function moveAway(target) | |
local dx = player.x - target.x | |
local dy = player.y - target.y | |
local direction = nil | |
if math.abs(dx) > math.abs(dy) then | |
direction = dx > 0 and "Right" or "Left" | |
else | |
direction = dy > 0 and "Down" or "Up" | |
end | |
return direction | |
end | |
if otherPlayersCount > 2 then | |
-- Logic to move away if there are more than two players | |
local direction = nearestPlayer and moveAway(nearestPlayer) or "Up" -- Default to "Up" if no nearest player is found | |
print(colors.blue .. "More than two players nearby, moving away (" .. direction .. ") to avoid conflict." .. colors.reset) | |
ao.send({Target = Game, Action = "PlayerMove", Player = ao.id, Direction = direction}) | |
elseif targetInRange and player.energy > 5 then | |
-- Attack if a player is in range and energy is sufficient | |
print(colors.red .. "Player in range. Attacking." .. colors.reset) | |
ao.send({Target = Game, Action = "PlayerAttack", Player = ao.id, AttackEnergy = tostring(player.energy)}) | |
elseif otherPlayersCount <= 2 and not targetInRange then | |
if player.energy < 40 then | |
-- Move away to gather energy if no one is in range and energy is below 40 | |
local direction = nearestPlayer and moveAway(nearestPlayer) or "Up" -- Default to "Up" if no nearest player is found | |
print(colors.gray .. "Low energy and no target in range, moving away (" .. direction .. ") to gather energy." .. colors.reset) | |
ao.send({Target = Game, Action = "PlayerMove", Player = ao.id, Direction = direction}) | |
elseif nearestPlayer then | |
-- Move towards the nearest player if energy is above 50 | |
local direction = moveTowards(nearestPlayer) | |
print(colors.green .. "Energy above 50, moving towards the nearest player (" .. direction .. ") to prepare for attack." .. colors.reset) | |
ao.send({Target = Game, Action = "PlayerMove", Player = ao.id, Direction = direction}) | |
end | |
else | |
-- Default random movement if no specific conditions are met | |
print(colors.blue .. "No specific action required, moving randomly." .. colors.reset) | |
local directionMap = {"Up", "Down", "Left", "Right"} | |
local randomDirection = directionMap[math.random(#directionMap)] | |
ao.send({Target = Game, Action = "PlayerMove", Player = ao.id, Direction = randomDirection}) | |
end | |
InAction = false -- Ensuring the action cycle can continue | |
end | |
-- Handler to trigger game state updates. | |
Handlers.add( | |
"GetGameStateOnTick", | |
Handlers.utils.hasMatchingTag("Action", "Tick"), | |
function () | |
if not InAction then -- InAction logic added | |
InAction = true -- InAction logic added | |
print(colors.gray .. "Getting game state..." .. colors.reset) | |
ao.send({Target = Game, Action = "GetGameState"}) | |
else | |
print("Previous action still in progress. Skipping.") | |
end | |
end | |
) | |
-- Handler to update the game state upon receiving game state information. | |
Handlers.add( | |
"UpdateGameState", | |
Handlers.utils.hasMatchingTag("Action", "GameState"), | |
function (msg) | |
local json = require("json") | |
LatestGameState = json.decode(msg.Data) | |
ao.send({Target = ao.id, Action = "UpdatedGameState"}) | |
print("Game state updated. Print \'LatestGameState\' for detailed view.") | |
end | |
) | |
-- Handler to decide the next best action. | |
Handlers.add( | |
"decideNextAction", | |
Handlers.utils.hasMatchingTag("Action", "UpdatedGameState"), | |
function () | |
if LatestGameState.GameMode ~= "Playing" then | |
InAction = false -- InAction logic added | |
return | |
end | |
print("Deciding next action.") | |
decideNextAction() | |
ao.send({Target = ao.id, Action = "Tick"}) | |
end | |
) | |
-- Handler to automatically attack when hit by another player. | |
Handlers.add( | |
"ReturnAttack", | |
Handlers.utils.hasMatchingTag("Action", "Hit"), | |
function (msg) | |
if not InAction then -- InAction logic added | |
InAction = true -- InAction logic added | |
local playerEnergy = LatestGameState.Players[ao.id].energy | |
if playerEnergy == undefined then | |
print(colors.red .. "Unable to read energy." .. colors.reset) | |
ao.send({Target = Game, Action = "Attack-Failed", Reason = "Unable to read energy."}) | |
elseif playerEnergy == 0 then | |
print(colors.red .. "Player has insufficient energy." .. colors.reset) | |
ao.send({Target = Game, Action = "Attack-Failed", Reason = "Player has no energy."}) | |
else | |
print(colors.red .. "Returning attack." .. colors.reset) | |
ao.send({Target = Game, Action = "PlayerAttack", Player = ao.id, AttackEnergy = tostring(playerEnergy)}) | |
end | |
InAction = false -- InAction logic added | |
ao.send({Target = ao.id, Action = "Tick"}) | |
else | |
print("Previous action still in progress. Skipping.") | |
end | |
end | |
) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment