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

  1. Log into anacreon (obviously)

  2. Find all of our preexisting explorer fleets

  3. For each fleet,

    1. Find the next planet it should go to

    2. Go to that planet

    3. Add this planet to the set of planets visited

    4. 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 fleet

  • A 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