Writing your first script with anacreonlib
#
This tutorial will guide you through creating your first script with
anacreonlib
. This script will use all preexisting explorer fleets to visit
every planet visible to you in the galaxy (taking into account planets that are
newly discovered during script execution).
At a high level, the steps we will want our script to do are
Log into anacreon (obviously)
Find all of our preexisting explorer fleets
For each fleet,
Find the next planet it should go to
Go to that planet
Add this planet to the set of planets visited
Wait for the fleet to get to the planet in the game
First, we need to set up our Anacreon
instance. Import
it, and use the log_in()
class method inside a
main
coroutine.
We will also need to import asyncio
to run our main
coroutine.
from anacreonlib import Anacreon
import asyncio
async def main():
## Step 1: Log into anacreon
print("Logging in")
# Replace `username` and `password` with real values
client = await Anacreon.log_in("8JNJ7FNZ", "username", "password")
print("Logged in!")
## Now what?
if __name__ == "__main__":
asyncio.run(main())
Next, we will need to find all of our existing explorer fleets. All space
objects in the game (i.e all of the World
and
Fleet
objects that are in the game), are
stored in the dict
client.space_objects
.
Each world and fleet has a sovereign_id
attribute indicating
who the owner is, which we can compare to sov_id
, which is
our own sovereign ID. Additionally, each Fleet
has a
ftl_type
attribute indicating whether the fleet is jump
,
warp
, or explorer
.
explorer_fleets = [
fleet_id
for fleet_id, fleet in client.space_objects.items()
if isinstance(fleet, Fleet)
and fleet.ftl_type == "explorer"
and fleet.sovereign_id == client.sov_id
]
Great! Now we have a list of all of our explorer fleets. Next, we will need some kind of function that manages an individual fleet which finds the next planet to go to, sends the fleet to that planet, and waits for it to arrive.
For simplicity, we will choose the nearest planet to the fleet that has not been visited before. To do this we will need
A key function to pass to
min()
that computes the distance between a given world and the fleetA list of worlds we have not visited
from typing import Set
from anacreonlib.utils import dist
async def explorer_fleet_manager(
client: Anacreon, fleet_id: int, visited_world_ids: Set[int]
) -> None:
def dist_to_world(world: World) -> float:
"""Distance between world and current position of fleet"""
this_fleet = client.space_objects[fleet_id]
return dist(this_fleet.pos, world.pos)
while True:
## Step 3a: find which world we should go to next
world_ids_i_could_go_to = [
world
for world_id, world in client.space_objects.items()
if isinstance(world, World) and world_id not in visited_world_ids
]
try:
next_world_to_go_to = min(world_ids_i_could_go_to, key=dist_to_world)
except ValueError: # Thrown when `world_ids_i_could_go_to` is empty
print(f"Fleet {fleet_id}: done going to planets")
return # exit
# TODO: go to the world
# TODO: wait to arrive at the world
Next, we need to send the fleet to the world. This is a simple call to
client.set_fleet_destination
.
async def explorer_fleet_manager(
client: Anacreon, fleet_id: int, visited_world_ids: Set[int]
) -> None:
def dist_to_world(world: World) -> float:
"""Distance between world and current position of fleet"""
this_fleet = client.space_objects[fleet_id]
return dist(this_fleet.pos, world.pos)
while True:
## Step 3a: find which world we should go to next
world_ids_i_could_go_to = (
world
for world_id, world in client.space_objects.items()
if isinstance(world, World) and world_id not in visited_world_ids
)
try:
next_world_to_go_to = min(world_ids_i_could_go_to, key=dist_to_world)
except ValueError: # Thrown when `world_ids_i_could_go_to` is empty
print(f"Fleet {fleet_id}: done going to planets")
return # exit
## Step 3b: go to that world
print(
f"Fleet {fleet_id}: going to planet ID {next_world_to_go_to.id} (name: {next_world_to_go_to.name})"
)
visited_world_ids.add(next_world_to_go_to.id)
await client.set_fleet_destination(fleet_id, next_world_to_go_to.id)
# TODO: wait to arrive at the world
After sending the fleet, we need to wait for our fleet to arrive at the planet.
An easy way to do this is to just repeatedly check
client.space_objects
to see
whether or not the fleet eta
has
gone away, which will happen once the fleet reaches its destination.
async def explorer_fleet_manager(
client: Anacreon, fleet_id: int, visited_world_ids: Set[int]
) -> None:
def dist_to_world(world: World) -> float:
"""Distance between world and current position of fleet"""
this_fleet = client.space_objects[fleet_id]
return dist(this_fleet.pos, world.pos)
while True:
## Step 3a: find which world we should go to next
world_ids_i_could_go_to = (
world
for world_id, world in client.space_objects.items()
if isinstance(world, World) and world_id not in visited_world_ids
)
try:
next_world_to_go_to = min(world_ids_i_could_go_to, key=dist_to_world)
except ValueError: # Thrown when `world_ids_i_could_go_to` is empty
print(f"Fleet {fleet_id}: done going to planets")
return # exit
## Step 3b: go to that world
print(
f"Fleet {fleet_id}: going to planet ID {next_world_to_go_to.id} (name: {next_world_to_go_to.name})"
)
visited_world_ids.add(next_world_to_go_to.id)
await client.set_fleet_destination(fleet_id, next_world_to_go_to.id)
# Wait to arrive at that world
while True:
await client.wait_for_get_objects()
fleet = client.space_objects[fleet_id]
if fleet.eta is None:
break
else:
print(f"Fleet {fleet_id}: still waiting to arrive at planet")
As written, our explorer_fleet_manager
will first send fleets to the planet
that they are currently stationed on, and then wait around a minute for the
fleet to get there. It would be smarter to check to see if the fleet being
managed is sitting at a world, and to mark that world as visited.
Let’s do that.
async def explorer_fleet_manager(
client: Anacreon, fleet_id: int, visited_world_ids: Set[int]
) -> None:
if (anchor_obj_id := client.space_objects[fleet_id].anchor_obj_id) is not None:
visited_world_ids.add(anchor_obj_id)
def dist_to_world(world: World) -> float:
"""Distance between world and current position of fleet"""
this_fleet = client.space_objects[fleet_id]
return dist(this_fleet.pos, world.pos)
...
Finally, in our main function, we need to call asyncio.create_task()
to create a fleet manager for each of our fleets
async def main() -> None:
print("Logging in")
# Replace `username` and `password` with real values
client = await Anacreon.log_in("8JNJ7FNZ", "username", "password")
print("Logged in!")
watch_refresh_task = client.call_get_objects_periodically()
explorer_fleets = [
fleet_id
for fleet_id, fleet in client.space_objects.items()
if isinstance(fleet, Fleet)
and fleet.ftl_type == "explorer"
and fleet.sovereign_id == client.sov_id
]
visited_world_ids = set()
explorer_fleet_tasks = [
asyncio.create_task(explorer_fleet_manager(client, fleet_id, visited_world_ids))
for fleet_id in explorer_fleets
]
print("Spawned fleet managers, waiting for completion")
for task in explorer_fleet_tasks:
await task
watch_refresh_task.cancel()
Now, our script is complete!
Below is the complete code for the example
1"""This is an example script that takes up to 10 pre-existing explorer fleets,
2and sends them around the map. Fleets are only sent to worlds that have not been
3visited before. This script could be used to explore the galaxy + remove fog of
4war, but more efficient implementations are possible.
5"""
6from typing import Set
7from anacreonlib import Anacreon, Fleet, World
8from anacreonlib.utils import dist
9import asyncio
10
11
12async def explorer_fleet_manager(
13 client: Anacreon, fleet_id: int, visited_world_ids: Set[int]
14) -> None:
15 if (anchor_obj_id := client.space_objects[fleet_id].anchor_obj_id) is not None:
16 visited_world_ids.add(anchor_obj_id)
17
18 def dist_to_world(world: World) -> float:
19 this_fleet = client.space_objects[fleet_id]
20 return dist(this_fleet.pos, world.pos)
21
22 while True:
23 # Step 1: find which world we should go to next
24 world_ids_i_could_go_to = (
25 world
26 for world_id, world in client.space_objects.items()
27 if isinstance(world, World) and world_id not in visited_world_ids
28 )
29
30 try:
31 next_world_to_go_to = min(world_ids_i_could_go_to, key=dist_to_world)
32 except ValueError: # Thrown when `world_ids_i_could_go_to` is empty
33 print(f"Fleet {fleet_id}: done going to planets")
34 return # exit
35
36 # Step 2: go to that world
37 print(
38 f"Fleet {fleet_id}: going to planet ID {next_world_to_go_to.id} (name: {next_world_to_go_to.name})"
39 )
40 visited_world_ids.add(next_world_to_go_to.id)
41
42 await client.set_fleet_destination(fleet_id, next_world_to_go_to.id)
43
44 # Wait to arrive at that world
45 while True:
46 await client.wait_for_get_objects()
47 fleet = client.space_objects[fleet_id]
48 if fleet.eta is None:
49 break
50 else:
51 print(f"Fleet {fleet_id}: still waiting to arrive at planet")
52
53
54async def main() -> None:
55 print("Logging in")
56 # Replace `username` and `password` with real values
57 client = await Anacreon.log_in("8JNJ7FNZ", "username", "password")
58 print("Logged in!")
59 watch_refresh_task = client.call_get_objects_periodically()
60
61 explorer_fleets = [
62 fleet_id
63 for fleet_id, fleet in client.space_objects.items()
64 if isinstance(fleet, Fleet)
65 and fleet.ftl_type == "explorer"
66 and fleet.sovereign_id == client.sov_id
67 ]
68
69 visited_world_ids = set()
70
71 explorer_fleet_tasks = [
72 asyncio.create_task(explorer_fleet_manager(client, fleet_id, visited_world_ids))
73 for fleet_id in explorer_fleets
74 ]
75
76 print("Spawned fleet managers, waiting for completion")
77 for task in explorer_fleet_tasks:
78 await task
79
80 watch_refresh_task.cancel()
81
82
83if __name__ == "__main__":
84 asyncio.run(main())
85